Commit | Line | Data |
---|---|---|
9ccc6d01 FC |
1 | /******************************************************************************* |
2 | * Copyright (c) 2010 Ericsson | |
3 | * | |
4 | * All rights reserved. This program and the accompanying materials are | |
5 | * made available under the terms of the Eclipse Public License v1.0 which | |
6 | * accompanies this distribution, and is available at | |
7 | * http://www.eclipse.org/legal/epl-v10.html | |
8 | * | |
9 | * Contributors: | |
10 | * Matthew Khouzam - Initial API and implementation | |
11 | * Francois Chouinard - Refactoring, slider support, bug fixing | |
b21305c2 | 12 | * Patrick Tasse - Improvements and bug fixing |
9ccc6d01 FC |
13 | ******************************************************************************/ |
14 | ||
15 | package org.eclipse.linuxtools.tmf.ui.widgets; | |
16 | ||
17 | import org.eclipse.swt.SWT; | |
b21305c2 | 18 | import org.eclipse.swt.custom.TableEditor; |
9ccc6d01 FC |
19 | import org.eclipse.swt.events.ControlAdapter; |
20 | import org.eclipse.swt.events.ControlEvent; | |
21 | import org.eclipse.swt.events.KeyEvent; | |
22 | import org.eclipse.swt.events.KeyListener; | |
23 | import org.eclipse.swt.events.MouseEvent; | |
b21305c2 | 24 | import org.eclipse.swt.events.MouseListener; |
9ccc6d01 FC |
25 | import org.eclipse.swt.events.MouseWheelListener; |
26 | import org.eclipse.swt.events.SelectionAdapter; | |
27 | import org.eclipse.swt.events.SelectionEvent; | |
b21305c2 FC |
28 | import org.eclipse.swt.events.SelectionListener; |
29 | import org.eclipse.swt.graphics.Point; | |
9ccc6d01 FC |
30 | import org.eclipse.swt.layout.GridData; |
31 | import org.eclipse.swt.layout.GridLayout; | |
32 | import org.eclipse.swt.widgets.Composite; | |
b21305c2 FC |
33 | import org.eclipse.swt.widgets.Control; |
34 | import org.eclipse.swt.widgets.Display; | |
9ccc6d01 FC |
35 | import org.eclipse.swt.widgets.Event; |
36 | import org.eclipse.swt.widgets.Listener; | |
b21305c2 | 37 | import org.eclipse.swt.widgets.Menu; |
9ccc6d01 FC |
38 | import org.eclipse.swt.widgets.Slider; |
39 | import org.eclipse.swt.widgets.Table; | |
40 | import org.eclipse.swt.widgets.TableColumn; | |
41 | import org.eclipse.swt.widgets.TableItem; | |
42 | ||
43 | /** | |
44 | * <b><u>TmfVirtualTable</u></b> | |
45 | * <p> | |
46 | * TmfVirtualTable allows for the tabular display of arbitrarily large data sets | |
47 | * (well, up to Integer.MAX_VALUE or ~2G rows). | |
48 | * | |
49 | * It is essentially a Composite of Table and Slider, where the number of rows | |
50 | * in the table is set to fill the table display area. The slider is rank-based. | |
51 | * | |
52 | * It differs from Table with the VIRTUAL style flag where an empty entry is | |
53 | * created for each virtual row. This does not scale well for very large data sets. | |
b21305c2 FC |
54 | * |
55 | * Styles: | |
56 | * H_SCROLL, V_SCROLL, SINGLE, CHECK, FULL_SELECTION, HIDE_SELECTION, NO_SCROLL | |
9ccc6d01 FC |
57 | */ |
58 | public class TmfVirtualTable extends Composite { | |
59 | ||
60 | // The table | |
2cc874d6 FC |
61 | private Table fTable; |
62 | private int fTableRows = 0; // Number of table rows | |
b21305c2 FC |
63 | private int fFullyVisibleRows = 0; // Number of fully visible table rows |
64 | private int fFrozenRowCount = 0; // Number of frozen table rows at top of table | |
9ccc6d01 | 65 | |
2cc874d6 FC |
66 | private int fTableTopEventRank = 0; // Global rank of the first entry displayed |
67 | private int fSelectedEventRank = 0; // Global rank of the selected event | |
b21305c2 | 68 | private boolean fPendingSelection = false; // Pending selection update |
2cc874d6 | 69 | |
9ccc6d01 | 70 | private int fTableItemCount = 0; |
9ccc6d01 FC |
71 | |
72 | // The slider | |
2cc874d6 | 73 | private Slider fSlider; |
9ccc6d01 | 74 | |
b21305c2 FC |
75 | private int fLinuxItemHeight = 0; // Calculated item height for Linux workaround |
76 | ||
9ccc6d01 FC |
77 | // ------------------------------------------------------------------------ |
78 | // Constructor | |
79 | // ------------------------------------------------------------------------ | |
80 | ||
81 | /** | |
82 | * @param parent | |
83 | * @param style | |
84 | */ | |
85 | public TmfVirtualTable(Composite parent, int style) { | |
b21305c2 | 86 | super(parent, style & (~SWT.H_SCROLL) & (~SWT.V_SCROLL) & (~SWT.SINGLE) & (~SWT.FULL_SELECTION) & (~SWT.HIDE_SELECTION) & (~SWT.CHECK)); |
9ccc6d01 FC |
87 | |
88 | // Create the controls | |
b21305c2 FC |
89 | createTable(style & (SWT.H_SCROLL | SWT.SINGLE | SWT.FULL_SELECTION | SWT.HIDE_SELECTION | SWT.CHECK)); |
90 | createSlider(style & SWT.V_SCROLL); | |
91 | ||
92 | // Prevent the slider from being traversed | |
93 | setTabList(new Control[] { fTable }); | |
9ccc6d01 FC |
94 | |
95 | // Set the layout | |
96 | GridLayout gridLayout = new GridLayout(); | |
97 | gridLayout.numColumns = 2; | |
98 | gridLayout.horizontalSpacing = 0; | |
2cc874d6 FC |
99 | gridLayout.verticalSpacing = 0; |
100 | gridLayout.marginWidth = 0; | |
101 | gridLayout.marginHeight = 0; | |
9ccc6d01 FC |
102 | setLayout(gridLayout); |
103 | ||
104 | GridData tableGridData = new GridData(SWT.FILL, SWT.FILL, true, true); | |
105 | fTable.setLayoutData(tableGridData); | |
106 | ||
107 | GridData sliderGridData = new GridData(SWT.FILL, SWT.FILL, false, true); | |
108 | fSlider.setLayoutData(sliderGridData); | |
109 | ||
110 | // Add the listeners | |
2cc874d6 | 111 | fTable.addMouseWheelListener(new MouseWheelListener() { |
d4011df2 | 112 | @Override |
9ccc6d01 | 113 | public void mouseScrolled(MouseEvent event) { |
b21305c2 FC |
114 | if (fTableItemCount <= fFullyVisibleRows) { |
115 | return; | |
116 | } | |
2cc874d6 FC |
117 | fTableTopEventRank -= event.count; |
118 | if (fTableTopEventRank < 0) { | |
119 | fTableTopEventRank = 0; | |
120 | } | |
b21305c2 | 121 | int latestFirstRowOffset = fTableItemCount - fFullyVisibleRows; |
2cc874d6 FC |
122 | if (fTableTopEventRank > latestFirstRowOffset) { |
123 | fTableTopEventRank = latestFirstRowOffset; | |
9ccc6d01 | 124 | } |
2cc874d6 FC |
125 | |
126 | fSlider.setSelection(fTableTopEventRank); | |
127 | refreshTable(); | |
9ccc6d01 FC |
128 | } |
129 | }); | |
130 | ||
b21305c2 FC |
131 | fTable.addListener(SWT.MouseWheel, new Listener() { |
132 | // disable mouse scroll of horizontal scroll bar | |
133 | @Override | |
134 | public void handleEvent(Event event) { | |
135 | event.doit = false; | |
136 | } | |
137 | }); | |
138 | ||
139 | fTable.addControlListener(new ControlAdapter() { | |
140 | @Override | |
141 | public void controlResized(ControlEvent event) { | |
142 | int tableHeight = Math.max(0, fTable.getClientArea().height - fTable.getHeaderHeight()); | |
143 | fFullyVisibleRows = tableHeight / getItemHeight(); | |
144 | if (fTableItemCount > 0) { | |
145 | fSlider.setThumb(Math.max(1, Math.min(fTableRows, fFullyVisibleRows))); | |
146 | } | |
147 | } | |
148 | }); | |
149 | ||
9ccc6d01 FC |
150 | addControlListener(new ControlAdapter() { |
151 | @Override | |
152 | public void controlResized(ControlEvent event) { | |
153 | resize(); | |
154 | } | |
b21305c2 | 155 | }); |
9ccc6d01 FC |
156 | |
157 | // And display | |
158 | refresh(); | |
159 | } | |
160 | ||
161 | // ------------------------------------------------------------------------ | |
162 | // Table handling | |
163 | // ------------------------------------------------------------------------ | |
164 | ||
165 | /** | |
166 | * Create the table and add listeners | |
167 | */ | |
2cc874d6 | 168 | private void createTable(int style) { |
b21305c2 | 169 | fTable = new Table(this, style | SWT.NO_SCROLL); |
9ccc6d01 FC |
170 | |
171 | fTable.addSelectionListener(new SelectionAdapter() { | |
172 | @Override | |
173 | public void widgetSelected(SelectionEvent event) { | |
b21305c2 FC |
174 | if (fTable.getSelectionIndices().length > 0) { |
175 | handleTableSelection(); | |
176 | } | |
9ccc6d01 FC |
177 | } |
178 | }); | |
179 | ||
2cc874d6 | 180 | fTable.addKeyListener(new KeyListener() { |
d4011df2 | 181 | @Override |
9ccc6d01 FC |
182 | public void keyPressed(KeyEvent event) { |
183 | handleTableKeyEvent(event); | |
184 | } | |
d4011df2 | 185 | @Override |
9ccc6d01 FC |
186 | public void keyReleased(KeyEvent event) { |
187 | } | |
188 | }); | |
b21305c2 | 189 | } |
9ccc6d01 FC |
190 | |
191 | /** | |
192 | * Update the rows and selected item | |
193 | */ | |
194 | private void handleTableSelection() { | |
b21305c2 FC |
195 | int selectedRow = fTable.getSelectionIndices()[0]; |
196 | if (selectedRow < fFrozenRowCount) { | |
197 | fSelectedEventRank = selectedRow; | |
198 | } else { | |
199 | fSelectedEventRank = fTableTopEventRank + selectedRow; | |
200 | } | |
201 | ||
202 | /* | |
203 | * Feature in Windows. When a partially visible table item is selected, | |
204 | * after ~500 ms the top index is changed to ensure the selected item is | |
205 | * fully visible. This leaves a blank space at the bottom of the virtual | |
206 | * table. The workaround is to update the top event rank, refresh the | |
207 | * table and reset the top index to 0 after a sufficient delay. | |
208 | */ | |
209 | if (selectedRow >= fFullyVisibleRows) { | |
210 | final Display display = fTable.getDisplay(); | |
211 | Thread thread = new Thread("Top index check") { //$NON-NLS-1$ | |
212 | @Override | |
213 | public void run() { | |
214 | try { | |
215 | Thread.sleep(600); | |
216 | } catch (InterruptedException e) { | |
217 | e.printStackTrace(); | |
218 | } | |
219 | display.asyncExec(new Runnable() { | |
220 | @Override | |
221 | public void run() { | |
222 | if (fTable.isDisposed()) return; | |
223 | int topIndex = fTable.getTopIndex(); | |
224 | if (topIndex != 0) { | |
225 | fTableTopEventRank += topIndex; | |
226 | refreshTable(); | |
227 | fSlider.setSelection(fTableTopEventRank); | |
228 | fTable.setTopIndex(0); | |
229 | } | |
230 | } | |
231 | }); | |
232 | } | |
233 | }; | |
234 | thread.start(); | |
235 | } | |
9ccc6d01 FC |
236 | } |
237 | ||
238 | /** | |
239 | * Handle key-based navigation in table. | |
240 | * | |
9ccc6d01 FC |
241 | * @param event |
242 | */ | |
243 | private void handleTableKeyEvent(KeyEvent event) { | |
244 | ||
2cc874d6 | 245 | int lastEventRank = fTableItemCount - 1; |
b21305c2 | 246 | int lastPageTopEntryRank = Math.max(0, fTableItemCount - fFullyVisibleRows); |
9ccc6d01 | 247 | |
b21305c2 FC |
248 | int previousSelectedEventRank = fSelectedEventRank; |
249 | int selectedRow = fSelectedEventRank - fTableTopEventRank; | |
2cc874d6 FC |
250 | boolean needsRefresh = false; |
251 | ||
2cc874d6 FC |
252 | // In all case, perform the following steps: |
253 | // - Update the selected entry rank (within valid range) | |
254 | // - Update the selected row | |
255 | // - Update the page's top entry if necessary (which also adjusts the selected row) | |
256 | // - If the top displayed entry was changed, table refresh is needed | |
9ccc6d01 FC |
257 | switch (event.keyCode) { |
258 | ||
259 | case SWT.ARROW_DOWN: { | |
b21305c2 | 260 | event.doit = false; |
2cc874d6 FC |
261 | if (fSelectedEventRank < lastEventRank) { |
262 | fSelectedEventRank++; | |
b21305c2 FC |
263 | selectedRow = fSelectedEventRank - fTableTopEventRank; |
264 | if (selectedRow >= fFullyVisibleRows) { | |
2cc874d6 | 265 | fTableTopEventRank++; |
2cc874d6 | 266 | needsRefresh = true; |
9ccc6d01 FC |
267 | } |
268 | } | |
269 | break; | |
270 | } | |
271 | ||
2cc874d6 | 272 | case SWT.ARROW_UP: { |
b21305c2 | 273 | event.doit = false; |
2cc874d6 FC |
274 | if (fSelectedEventRank > 0) { |
275 | fSelectedEventRank--; | |
b21305c2 FC |
276 | selectedRow = fSelectedEventRank - fTableTopEventRank; |
277 | if (selectedRow < fFrozenRowCount && fTableTopEventRank > 0) { | |
2cc874d6 | 278 | fTableTopEventRank--; |
2cc874d6 | 279 | needsRefresh = true; |
9ccc6d01 | 280 | } |
9ccc6d01 FC |
281 | } |
282 | break; | |
283 | } | |
284 | ||
285 | case SWT.END: { | |
b21305c2 | 286 | event.doit = false; |
2cc874d6 FC |
287 | fTableTopEventRank = lastPageTopEntryRank; |
288 | fSelectedEventRank = lastEventRank; | |
2cc874d6 | 289 | needsRefresh = true; |
9ccc6d01 FC |
290 | break; |
291 | } | |
292 | ||
2cc874d6 | 293 | case SWT.HOME: { |
b21305c2 FC |
294 | event.doit = false; |
295 | fSelectedEventRank = fFrozenRowCount; | |
2cc874d6 | 296 | fTableTopEventRank = 0; |
b21305c2 | 297 | needsRefresh = true; |
9ccc6d01 FC |
298 | break; |
299 | } | |
300 | ||
2cc874d6 | 301 | case SWT.PAGE_DOWN: { |
b21305c2 | 302 | event.doit = false; |
2cc874d6 | 303 | if (fSelectedEventRank < lastEventRank) { |
b21305c2 | 304 | fSelectedEventRank += fFullyVisibleRows; |
2cc874d6 FC |
305 | if (fSelectedEventRank > lastEventRank) { |
306 | fSelectedEventRank = lastEventRank; | |
307 | } | |
b21305c2 FC |
308 | selectedRow = fSelectedEventRank - fTableTopEventRank; |
309 | if (selectedRow > fFullyVisibleRows - 1) { | |
310 | fTableTopEventRank += fFullyVisibleRows; | |
2cc874d6 FC |
311 | if (fTableTopEventRank > lastPageTopEntryRank) { |
312 | fTableTopEventRank = lastPageTopEntryRank; | |
313 | } | |
2cc874d6 | 314 | needsRefresh = true; |
9ccc6d01 | 315 | } |
9ccc6d01 FC |
316 | } |
317 | break; | |
318 | } | |
319 | ||
2cc874d6 | 320 | case SWT.PAGE_UP: { |
b21305c2 | 321 | event.doit = false; |
2cc874d6 | 322 | if (fSelectedEventRank > 0) { |
b21305c2 FC |
323 | fSelectedEventRank -= fFullyVisibleRows; |
324 | if (fSelectedEventRank < fFrozenRowCount) { | |
325 | fSelectedEventRank = fFrozenRowCount; | |
2cc874d6 | 326 | } |
b21305c2 FC |
327 | selectedRow = fSelectedEventRank - fTableTopEventRank; |
328 | if (selectedRow < 0) { | |
329 | fTableTopEventRank -= fFullyVisibleRows; | |
2cc874d6 FC |
330 | if (fTableTopEventRank < 0) { |
331 | fTableTopEventRank = 0; | |
332 | } | |
2cc874d6 FC |
333 | needsRefresh = true; |
334 | } | |
335 | } | |
9ccc6d01 FC |
336 | break; |
337 | } | |
b21305c2 FC |
338 | default: { |
339 | return; | |
340 | } | |
9ccc6d01 | 341 | } |
b21305c2 FC |
342 | |
343 | boolean done = true; | |
2cc874d6 | 344 | if (needsRefresh) { |
b21305c2 FC |
345 | done = refreshTable(); // false if table items not updated yet in this thread |
346 | } else { | |
347 | fTable.select(selectedRow); | |
348 | } | |
349 | ||
350 | if (fFullyVisibleRows < fTableItemCount) { | |
351 | fSlider.setSelection(fTableTopEventRank); | |
352 | } | |
353 | ||
354 | if (fSelectedEventRank != previousSelectedEventRank && fTable.getSelection().length > 0) { | |
355 | if (done) { | |
356 | Event e = new Event(); | |
357 | e.item = fTable.getSelection()[0]; | |
358 | fTable.notifyListeners(SWT.Selection, e); | |
359 | } else { | |
360 | fPendingSelection = true; | |
361 | } | |
362 | } | |
363 | } | |
364 | ||
365 | private boolean setDataItem(int index, TableItem item) { | |
366 | if (index != -1) { | |
367 | Event event = new Event(); | |
368 | event.item = item; | |
369 | if (index < fFrozenRowCount) { | |
370 | event.index = index; | |
371 | } else { | |
372 | event.index = index + fTableTopEventRank; | |
9ccc6d01 | 373 | } |
b21305c2 FC |
374 | event.doit = true; |
375 | notifyListeners(SWT.SetData, event); | |
376 | return event.doit; // false if table item not updated yet in this thread | |
9ccc6d01 | 377 | } |
b21305c2 FC |
378 | return true; |
379 | } | |
bbb3457d | 380 | |
9ccc6d01 FC |
381 | // ------------------------------------------------------------------------ |
382 | // Slider handling | |
383 | // ------------------------------------------------------------------------ | |
384 | ||
b21305c2 FC |
385 | private void createSlider(int style) { |
386 | fSlider = new Slider(this, SWT.VERTICAL | SWT.NO_FOCUS); | |
9ccc6d01 FC |
387 | fSlider.setMinimum(0); |
388 | fSlider.setMaximum(0); | |
b21305c2 FC |
389 | if ((style & SWT.V_SCROLL) == 0) { |
390 | fSlider.setVisible(false); | |
391 | } | |
9ccc6d01 | 392 | |
9ccc6d01 | 393 | fSlider.addListener(SWT.Selection, new Listener() { |
d4011df2 | 394 | @Override |
9ccc6d01 FC |
395 | public void handleEvent(Event event) { |
396 | switch (event.detail) { | |
397 | case SWT.ARROW_DOWN: | |
398 | case SWT.ARROW_UP: | |
399 | case SWT.NONE: | |
400 | case SWT.END: | |
401 | case SWT.HOME: | |
402 | case SWT.PAGE_DOWN: | |
403 | case SWT.PAGE_UP: { | |
2cc874d6 FC |
404 | fTableTopEventRank = fSlider.getSelection(); |
405 | refreshTable(); | |
9ccc6d01 FC |
406 | break; |
407 | } | |
408 | } | |
409 | } | |
410 | }); | |
411 | } | |
412 | ||
413 | // ------------------------------------------------------------------------ | |
414 | // Simulated Table API | |
415 | // ------------------------------------------------------------------------ | |
416 | ||
417 | public void setHeaderVisible(boolean b) { | |
418 | fTable.setHeaderVisible(b); | |
419 | } | |
420 | ||
421 | public void setLinesVisible(boolean b) { | |
422 | fTable.setLinesVisible(b); | |
423 | } | |
424 | ||
425 | public TableItem[] getSelection() { | |
b21305c2 FC |
426 | return fTable.getSelection(); |
427 | } | |
428 | ||
429 | @Override | |
430 | public void addKeyListener(KeyListener listener) { | |
431 | fTable.addKeyListener(listener); | |
432 | } | |
433 | ||
434 | @Override | |
435 | public void addMouseListener(MouseListener listener) { | |
436 | fTable.addMouseListener(listener); | |
437 | } | |
438 | ||
439 | public void addSelectionListener(SelectionListener listener) { | |
440 | fTable.addSelectionListener(listener); | |
441 | } | |
442 | ||
443 | @Override | |
444 | public void setMenu(Menu menu) { | |
445 | fTable.setMenu(menu); | |
446 | } | |
447 | ||
448 | public void clearAll() { | |
449 | setItemCount(0); | |
9ccc6d01 | 450 | } |
9ccc6d01 FC |
451 | |
452 | public void setItemCount(int nbItems) { | |
453 | nbItems = Math.max(0, nbItems); | |
b21305c2 | 454 | |
9ccc6d01 FC |
455 | if (nbItems != fTableItemCount) { |
456 | fTableItemCount = nbItems; | |
b21305c2 | 457 | fTable.remove(fTableItemCount, fTable.getItemCount() - 1); |
9ccc6d01 FC |
458 | fSlider.setMaximum(nbItems); |
459 | resize(); | |
b21305c2 FC |
460 | int tableHeight = Math.max(0, fTable.getClientArea().height - fTable.getHeaderHeight()); |
461 | fFullyVisibleRows = tableHeight / getItemHeight(); | |
462 | if (fTableItemCount > 0) { | |
463 | fSlider.setThumb(Math.max(1, Math.min(fTableRows, fFullyVisibleRows))); | |
464 | } | |
9ccc6d01 FC |
465 | } |
466 | } | |
467 | ||
b21305c2 FC |
468 | public int getItemCount() { |
469 | return fTableItemCount; | |
470 | } | |
471 | ||
9ccc6d01 | 472 | public int getItemHeight() { |
b21305c2 FC |
473 | /* |
474 | * Bug in Linux. The method getItemHeight doesn't always return the correct value. | |
475 | */ | |
476 | if (fLinuxItemHeight >= 0 && System.getProperty("os.name").contains("Linux")) { //$NON-NLS-1$ //$NON-NLS-2$ | |
477 | if (fLinuxItemHeight != 0) { | |
478 | return fLinuxItemHeight; | |
479 | } | |
480 | if (fTable.getItemCount() > 1) { | |
481 | int itemHeight = fTable.getItem(1).getBounds().y - fTable.getItem(0).getBounds().y; | |
482 | if (itemHeight > 0) { | |
483 | fLinuxItemHeight = itemHeight; | |
484 | return fLinuxItemHeight; | |
485 | } | |
486 | } | |
487 | } else { | |
488 | fLinuxItemHeight = -1; // Not Linux, don't perform os.name check anymore | |
489 | } | |
9ccc6d01 FC |
490 | return fTable.getItemHeight(); |
491 | } | |
492 | ||
493 | public int getTopIndex() { | |
b21305c2 | 494 | return fTableTopEventRank + fFrozenRowCount; |
9ccc6d01 FC |
495 | } |
496 | ||
497 | public void setTopIndex(int i) { | |
b21305c2 FC |
498 | if (fTableItemCount > 0) { |
499 | i = Math.min(i, fTableItemCount - 1); | |
500 | i = Math.max(i, fFrozenRowCount); | |
501 | ||
502 | fTableTopEventRank = i - fFrozenRowCount; | |
503 | if (fFullyVisibleRows < fTableItemCount) { | |
504 | fSlider.setSelection(fTableTopEventRank); | |
505 | } | |
506 | ||
507 | refreshTable(); | |
508 | } | |
9ccc6d01 | 509 | } |
b21305c2 | 510 | |
9ccc6d01 | 511 | public int indexOf(TableItem ti) { |
b21305c2 FC |
512 | int index = fTable.indexOf(ti); |
513 | if (index < fFrozenRowCount) { | |
514 | return index; | |
515 | } else { | |
516 | return (index - fFrozenRowCount) + getTopIndex(); | |
517 | } | |
9ccc6d01 | 518 | } |
b21305c2 | 519 | |
9ccc6d01 FC |
520 | public TableColumn[] getColumns() { |
521 | return fTable.getColumns(); | |
522 | } | |
523 | ||
b21305c2 FC |
524 | public TableItem getItem(Point point) { |
525 | return fTable.getItem(point); | |
526 | } | |
527 | ||
9ccc6d01 | 528 | private void resize() { |
9ccc6d01 | 529 | // Compute the numbers of rows that fit the new area |
b21305c2 FC |
530 | int tableHeight = Math.max(0, getSize().y - fTable.getHeaderHeight()); |
531 | int itemHeight = getItemHeight(); | |
532 | fTableRows = Math.min((tableHeight + itemHeight - 1) / itemHeight, fTableItemCount); | |
533 | ||
534 | if (fTableTopEventRank + fFullyVisibleRows > fTableItemCount) { | |
535 | // If we are at the end, get elements before to populate | |
536 | fTableTopEventRank = Math.max(0, fTableItemCount - fFullyVisibleRows); | |
537 | refreshTable(); | |
538 | } else if (fTableRows > fTable.getItemCount() || fTableItemCount < fTable.getItemCount()) { | |
539 | // Only refresh if new table items are needed or if table items need to be deleted | |
540 | refreshTable(); | |
9ccc6d01 FC |
541 | } |
542 | ||
9ccc6d01 FC |
543 | } |
544 | ||
545 | // ------------------------------------------------------------------------ | |
546 | // Controls interactions | |
547 | // ------------------------------------------------------------------------ | |
548 | ||
549 | @Override | |
550 | public boolean setFocus() { | |
551 | boolean isVisible = isVisible(); | |
552 | if (isVisible) { | |
553 | fTable.setFocus(); | |
554 | } | |
555 | return isVisible; | |
556 | } | |
557 | ||
558 | public void refresh() { | |
b21305c2 FC |
559 | boolean done = refreshTable(); |
560 | if (fPendingSelection && done) { | |
561 | fPendingSelection = false; | |
562 | if (fTable.getSelection().length > 0) { | |
563 | Event e = new Event(); | |
564 | e.item = fTable.getSelection()[0]; | |
565 | fTable.notifyListeners(SWT.Selection, e); | |
566 | } | |
567 | } | |
9ccc6d01 FC |
568 | } |
569 | ||
570 | public void setColumnHeaders(ColumnData columnData[]) { | |
571 | for (int i = 0; i < columnData.length; i++) { | |
572 | TableColumn column = new TableColumn(fTable, columnData[i].alignment, i); | |
573 | column.setText(columnData[i].header); | |
574 | if (columnData[i].width > 0) { | |
575 | column.setWidth(columnData[i].width); | |
576 | } else { | |
577 | column.pack(); | |
578 | } | |
579 | } | |
b21305c2 | 580 | } |
9ccc6d01 FC |
581 | |
582 | public int removeAll() { | |
b21305c2 | 583 | setItemCount(0); |
9ccc6d01 FC |
584 | fSlider.setMaximum(0); |
585 | fTable.removeAll(); | |
b21305c2 | 586 | fSelectedEventRank = fFrozenRowCount; |
9ccc6d01 FC |
587 | return 0; |
588 | } | |
589 | ||
b21305c2 FC |
590 | private boolean refreshTable() { |
591 | boolean done = true; | |
886b12b1 | 592 | for (int i = 0; i < fTableRows; i++) { |
b21305c2 FC |
593 | if (i + fTableTopEventRank < fTableItemCount) { |
594 | TableItem tableItem; | |
595 | if (i < fTable.getItemCount()) { | |
596 | tableItem = fTable.getItem(i); | |
597 | } else { | |
598 | tableItem = new TableItem(fTable, SWT.NONE); | |
599 | } | |
600 | done &= setDataItem(i, tableItem); // false if table item not updated yet in this thread | |
601 | } else { | |
602 | if (fTable.getItemCount() > fTableItemCount - fTableTopEventRank) { | |
603 | fTable.remove(fTableItemCount - fTableTopEventRank); | |
604 | } | |
605 | } | |
886b12b1 | 606 | } |
886b12b1 | 607 | |
b21305c2 FC |
608 | int lastRowOffset = fTableTopEventRank + fTableRows - 1; |
609 | if ((fSelectedEventRank >= fTableTopEventRank + fFrozenRowCount) && (fSelectedEventRank <= lastRowOffset)) { | |
610 | int selectedRow = fSelectedEventRank - fTableTopEventRank; | |
611 | fTable.select(selectedRow); | |
612 | } else if (fSelectedEventRank < fFrozenRowCount) { | |
613 | fTable.select(fSelectedEventRank); | |
9ccc6d01 | 614 | } else { |
b21305c2 | 615 | fTable.deselectAll(); |
9ccc6d01 | 616 | } |
b21305c2 FC |
617 | return done; |
618 | } | |
9ccc6d01 FC |
619 | |
620 | public void setSelection(int i) { | |
b21305c2 FC |
621 | if (fTableItemCount > 0) { |
622 | i = Math.min(i, fTableItemCount - 1); | |
9ccc6d01 | 623 | i = Math.max(i, 0); |
2cc874d6 FC |
624 | |
625 | fSelectedEventRank = i; | |
b21305c2 FC |
626 | if ((i < fTableTopEventRank + fFrozenRowCount && i >= fFrozenRowCount) || |
627 | (i >= fTableTopEventRank + fFullyVisibleRows)) { | |
628 | fTableTopEventRank = Math.max(0, i - fFrozenRowCount - fFullyVisibleRows / 2); | |
89dcf304 | 629 | } |
b21305c2 FC |
630 | if (fFullyVisibleRows < fTableItemCount) { |
631 | fSlider.setSelection(fTableTopEventRank); | |
2cc874d6 | 632 | } |
b21305c2 | 633 | |
2cc874d6 | 634 | refreshTable(); |
b21305c2 FC |
635 | |
636 | } | |
637 | } | |
638 | ||
639 | public int getSelectionIndex() { | |
640 | int index = fTable.getSelectionIndex(); | |
2d55fd20 FC |
641 | if (index == -1) { |
642 | return fSelectedEventRank; | |
643 | } | |
b21305c2 FC |
644 | if (index < fFrozenRowCount) { |
645 | return index; | |
646 | } else { | |
647 | return (index - fFrozenRowCount) + getTopIndex(); | |
9ccc6d01 FC |
648 | } |
649 | } | |
b21305c2 FC |
650 | |
651 | public void setFrozenRowCount(int count) { | |
652 | fFrozenRowCount = count; | |
653 | refreshTable(); | |
654 | } | |
9ccc6d01 | 655 | |
b21305c2 FC |
656 | public TableEditor createTableEditor() { |
657 | return new TableEditor(fTable); | |
658 | } | |
659 | ||
660 | public Control createTableEditorControl(Class<? extends Control> control) { | |
661 | try { | |
662 | return (Control) control.getConstructor(Composite.class, int.class).newInstance(new Object[] {fTable, SWT.NONE}); | |
663 | } catch (Exception e) { | |
664 | e.printStackTrace(); | |
665 | } | |
666 | return null; | |
667 | } | |
9ccc6d01 | 668 | } |