doc: update user guide for importing traces as experiment from control
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.ui / src / org / eclipse / tracecompass / tmf / ui / widgets / virtualtable / TmfVirtualTable.java
CommitLineData
9ccc6d01 1/*******************************************************************************
22ba2f65 2 * Copyright (c) 2010, 2015 Ericsson and others.
3934297e 3 *
9ccc6d01
FC
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
3934297e 8 *
9ccc6d01
FC
9 * Contributors:
10 * Matthew Khouzam - Initial API and implementation
3934297e 11 * Francois Chouinard - Refactoring, slider support, bug fixing
b21305c2 12 * Patrick Tasse - Improvements and bug fixing
7cc11afa 13 * Xavier Raynaud - Improvements
9ccc6d01
FC
14 ******************************************************************************/
15
2bdf0193 16package org.eclipse.tracecompass.tmf.ui.widgets.virtualtable;
9ccc6d01
FC
17
18import org.eclipse.swt.SWT;
3f43dc48 19import org.eclipse.swt.SWTException;
b21305c2 20import org.eclipse.swt.custom.TableEditor;
9ccc6d01
FC
21import org.eclipse.swt.events.ControlAdapter;
22import org.eclipse.swt.events.ControlEvent;
3f43dc48 23import org.eclipse.swt.events.KeyAdapter;
9ccc6d01
FC
24import org.eclipse.swt.events.KeyEvent;
25import org.eclipse.swt.events.KeyListener;
3f43dc48 26import org.eclipse.swt.events.MouseAdapter;
9ccc6d01 27import org.eclipse.swt.events.MouseEvent;
b21305c2 28import org.eclipse.swt.events.MouseListener;
9ccc6d01 29import org.eclipse.swt.events.MouseWheelListener;
dd03e2f1
PT
30import org.eclipse.swt.events.PaintEvent;
31import org.eclipse.swt.events.PaintListener;
9ccc6d01
FC
32import org.eclipse.swt.events.SelectionAdapter;
33import org.eclipse.swt.events.SelectionEvent;
b21305c2
FC
34import org.eclipse.swt.events.SelectionListener;
35import org.eclipse.swt.graphics.Point;
7800a42f
XR
36import org.eclipse.swt.graphics.Rectangle;
37import org.eclipse.swt.layout.FillLayout;
9ccc6d01
FC
38import org.eclipse.swt.layout.GridData;
39import org.eclipse.swt.layout.GridLayout;
40import org.eclipse.swt.widgets.Composite;
b21305c2 41import org.eclipse.swt.widgets.Control;
22ba2f65 42import org.eclipse.swt.widgets.Display;
9ccc6d01 43import org.eclipse.swt.widgets.Event;
7800a42f 44import org.eclipse.swt.widgets.Label;
9ccc6d01 45import org.eclipse.swt.widgets.Listener;
b21305c2 46import org.eclipse.swt.widgets.Menu;
7800a42f 47import org.eclipse.swt.widgets.Shell;
9ccc6d01
FC
48import org.eclipse.swt.widgets.Slider;
49import org.eclipse.swt.widgets.Table;
50import org.eclipse.swt.widgets.TableColumn;
51import org.eclipse.swt.widgets.TableItem;
2bdf0193 52import org.eclipse.tracecompass.internal.tmf.ui.Activator;
7800a42f 53import org.eclipse.ui.PlatformUI;
9ccc6d01
FC
54
55/**
56 * <b><u>TmfVirtualTable</u></b>
57 * <p>
58 * TmfVirtualTable allows for the tabular display of arbitrarily large data sets
59 * (well, up to Integer.MAX_VALUE or ~2G rows).
3934297e 60 *
9ccc6d01
FC
61 * It is essentially a Composite of Table and Slider, where the number of rows
62 * in the table is set to fill the table display area. The slider is rank-based.
3934297e 63 *
9ccc6d01
FC
64 * It differs from Table with the VIRTUAL style flag where an empty entry is
65 * created for each virtual row. This does not scale well for very large data sets.
3934297e 66 *
b21305c2 67 * Styles:
3f43dc48 68 * H_SCROLL, V_SCROLL, SINGLE, MULTI, CHECK, FULL_SELECTION, HIDE_SELECTION, NO_SCROLL
02023181 69 * @author Matthew Khouzam, Francois Chouinard, Patrick Tasse, Xavier Raynaud
3934297e 70 * @version $Revision: 1.0
9ccc6d01
FC
71 */
72public class TmfVirtualTable extends Composite {
73
631d853f
PT
74 // The table
75 private Table fTable;
76 private int fTableRows = 0; // Number of table rows
77 private int fFullyVisibleRows = 0; // Number of fully visible table rows
78 private int fFrozenRowCount = 0; // Number of frozen table rows at top of table
79
80 private int fTableTopEventRank = 0; // Global rank of the first entry displayed
3f43dc48
PT
81 private int fSelectedEventRank = -1; // Global rank of the selected event
82 private int fSelectedBeginRank = -1; // Global rank of the selected begin event
631d853f
PT
83 private boolean fPendingSelection = false; // Pending selection update
84
dadf6e1a 85 private int fTableItemCount = 0;
631d853f
PT
86
87 // The slider
88 private Slider fSlider;
22ba2f65 89 private SliderThrottler fSliderThrottler;
631d853f
PT
90
91 private int fLinuxItemHeight = 0; // Calculated item height for Linux workaround
92 private TooltipProvider tooltipProvider = null;
93 private IDoubleClickListener doubleClickListener = null;
94
b06f64b7
PT
95 private boolean fResetTopIndex = false; // Flag to trigger reset of top index
96 private ControlAdapter fResizeListener; // Resize listener to update visible rows
c9787691 97
22ba2f65
PT
98 // ------------------------------------------------------------------------
99 // Classes
100 // ------------------------------------------------------------------------
101
102 private class SliderThrottler extends Thread {
103 private static final long DELAY = 400L;
104 private static final long POLLING_INTERVAL = 10L;
105
106 @Override
107 public void run() {
108 final long startTime = System.currentTimeMillis();
109 while ((System.currentTimeMillis() - startTime) < DELAY) {
110 try {
111 Thread.sleep(POLLING_INTERVAL);
112 } catch (InterruptedException e) {
113 }
114 }
115 Display.getDefault().asyncExec(new Runnable() {
116 @Override
117 public void run() {
118 if (fSliderThrottler != SliderThrottler.this) {
119 return;
120 }
121 fSliderThrottler = null;
122 if (SliderThrottler.this.isInterrupted() || fTable.isDisposed()) {
123 return;
124 }
125 refreshTable();
126 }
127 });
128 }
129 }
130
631d853f
PT
131 // ------------------------------------------------------------------------
132 // Constructor
133 // ------------------------------------------------------------------------
134
135 /**
3934297e
AM
136 * Standard constructor
137 *
631d853f 138 * @param parent
3934297e 139 * The parent composite object
631d853f 140 * @param style
3934297e 141 * The style to use
631d853f
PT
142 */
143 public TmfVirtualTable(Composite parent, int style) {
3f43dc48 144 super(parent, style & (~SWT.H_SCROLL) & (~SWT.V_SCROLL) & (~SWT.SINGLE) & (~SWT.MULTI) & (~SWT.FULL_SELECTION) & (~SWT.HIDE_SELECTION) & (~SWT.CHECK));
631d853f
PT
145
146 // Create the controls
3f43dc48 147 createTable(style & (SWT.H_SCROLL | SWT.SINGLE | SWT.MULTI | SWT.FULL_SELECTION | SWT.HIDE_SELECTION | SWT.CHECK));
631d853f
PT
148 createSlider(style & SWT.V_SCROLL);
149
150 // Prevent the slider from being traversed
151 setTabList(new Control[] { fTable });
152
153 // Set the layout
154 GridLayout gridLayout = new GridLayout();
155 gridLayout.numColumns = 2;
156 gridLayout.horizontalSpacing = 0;
157 gridLayout.verticalSpacing = 0;
158 gridLayout.marginWidth = 0;
159 gridLayout.marginHeight = 0;
160 setLayout(gridLayout);
161
162 GridData tableGridData = new GridData(SWT.FILL, SWT.FILL, true, true);
163 fTable.setLayoutData(tableGridData);
164
165 GridData sliderGridData = new GridData(SWT.FILL, SWT.FILL, false, true);
166 fSlider.setLayoutData(sliderGridData);
167
168 // Add the listeners
169 fTable.addMouseWheelListener(new MouseWheelListener() {
170 @Override
171 public void mouseScrolled(MouseEvent event) {
172 if (fTableItemCount <= fFullyVisibleRows) {
173 return;
174 }
175 fTableTopEventRank -= event.count;
176 if (fTableTopEventRank < 0) {
177 fTableTopEventRank = 0;
178 }
179 int latestFirstRowOffset = fTableItemCount - fFullyVisibleRows;
180 if (fTableTopEventRank > latestFirstRowOffset) {
181 fTableTopEventRank = latestFirstRowOffset;
182 }
183
184 fSlider.setSelection(fTableTopEventRank);
185 refreshTable();
186 }
187 });
188
189 fTable.addListener(SWT.MouseWheel, new Listener() {
190 // disable mouse scroll of horizontal scroll bar
191 @Override
b21305c2 192 public void handleEvent(Event event) {
631d853f
PT
193 event.doit = false;
194 }
195 });
196
b06f64b7 197 fResizeListener = new ControlAdapter() {
631d853f
PT
198 @Override
199 public void controlResized(ControlEvent event) {
200 int tableHeight = Math.max(0, fTable.getClientArea().height - fTable.getHeaderHeight());
201 fFullyVisibleRows = tableHeight / getItemHeight();
202 if (fTableItemCount > 0) {
203 fSlider.setThumb(Math.max(1, Math.min(fTableRows, fFullyVisibleRows)));
204 }
205 }
b06f64b7
PT
206 };
207 fTable.addControlListener(fResizeListener);
208
631d853f
PT
209 // Implement a "fake" tooltip
210 final String TOOLTIP_DATA_KEY = "_TABLEITEM"; //$NON-NLS-1$
dadf6e1a 211 final Listener labelListener = new Listener() {
631d853f 212 @Override
f4c52cea 213 public void handleEvent (Event event) {
dadf6e1a
EB
214 Label label = (Label) event.widget;
215 Shell shell = label.getShell();
631d853f
PT
216 switch (event.type) {
217 case SWT.MouseDown:
dadf6e1a
EB
218 Event e = new Event();
219 e.item = (TableItem) label.getData(TOOLTIP_DATA_KEY);
631d853f
PT
220 // Assuming table is single select, set the selection as if
221 // the mouse down event went through to the table
dadf6e1a
EB
222 fTable.setSelection(new TableItem [] {(TableItem) e.item});
223 fTable.notifyListeners(SWT.Selection, e);
224 shell.dispose();
631d853f
PT
225 fTable.setFocus();
226 break;
227 case SWT.MouseExit:
228 case SWT.MouseWheel:
dadf6e1a 229 shell.dispose();
631d853f 230 break;
abbdd66a
AM
231 default:
232 break;
631d853f
PT
233 }
234 }
235 };
236
dadf6e1a 237 Listener tableListener = new Listener() {
631d853f
PT
238 Shell tip = null;
239 Label label = null;
240 @Override
dadf6e1a 241 public void handleEvent(Event event) {
631d853f
PT
242 switch (event.type) {
243 case SWT.Dispose:
244 case SWT.KeyDown:
245 case SWT.MouseMove: {
420bceb2
PT
246 if (tip == null) {
247 break;
248 }
dadf6e1a 249 tip.dispose();
631d853f
PT
250 tip = null;
251 label = null;
252 break;
253 }
254 case SWT.MouseHover: {
dadf6e1a 255 TableItem item = fTable.getItem(new Point(event.x, event.y));
631d853f 256 if (item != null) {
dadf6e1a 257 for (int i = 0; i < fTable.getColumnCount(); i++) {
631d853f 258 Rectangle bounds = item.getBounds(i);
dadf6e1a 259 if (bounds.contains(event.x, event.y)) {
420bceb2
PT
260 if (tip != null && !tip.isDisposed()) {
261 tip.dispose();
262 }
631d853f
PT
263 if (tooltipProvider == null) {
264 return;
631d853f 265 }
abbdd66a
AM
266 String tooltipText = tooltipProvider.getTooltip(i, item.getData());
267 if (tooltipText == null) {
268 return;
269 }
270 tip = new Shell(fTable.getShell(), SWT.ON_TOP | SWT.NO_FOCUS | SWT.TOOL);
271 tip.setBackground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));
272 FillLayout layout = new FillLayout();
273 layout.marginWidth = 2;
274 tip.setLayout(layout);
275 label = new Label(tip, SWT.WRAP);
276 label.setForeground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND));
277 label.setBackground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));
278 label.setData(TOOLTIP_DATA_KEY, item);
279 label.setText(tooltipText);
280
281 label.addListener(SWT.MouseExit, labelListener);
282 label.addListener(SWT.MouseDown, labelListener);
283 label.addListener(SWT.MouseWheel, labelListener);
284 Point size = tip.computeSize(SWT.DEFAULT, SWT.DEFAULT);
285 Point pt = fTable.toDisplay(bounds.x, bounds.y);
286 tip.setBounds(pt.x, pt.y, size.x, size.y);
287 tip.setVisible(true);
dadf6e1a
EB
288
289 // Item found, leave loop.
290 break;
631d853f
PT
291 }
292 }
293 }
abbdd66a 294 break;
631d853f 295 }
abbdd66a
AM
296 default:
297 break;
631d853f
PT
298 }
299 }
300 };
301 fTable.addListener(SWT.Dispose, tableListener);
302 fTable.addListener(SWT.KeyDown, tableListener);
303 fTable.addListener(SWT.MouseMove, tableListener);
304 fTable.addListener(SWT.MouseHover, tableListener);
305 addControlListener(new ControlAdapter() {
306 @Override
307 public void controlResized(ControlEvent event) {
308 resize();
420bceb2
PT
309 if (fTableItemCount > 0) {
310 fSlider.setThumb(Math.max(1, Math.min(fTableRows, fFullyVisibleRows)));
311 }
631d853f
PT
312 }
313 });
314
315 // And display
316 refresh();
317 }
318
319 // ------------------------------------------------------------------------
320 // Table handling
321 // ------------------------------------------------------------------------
322
323 /**
324 * Create the table and add listeners
3f43dc48 325 * @param style int can be H_SCROLL, SINGLE, MULTI, FULL_SELECTION, HIDE_SELECTION, CHECK
631d853f
PT
326 */
327 private void createTable(int style) {
328 fTable = new Table(this, style | SWT.NO_SCROLL);
329
330 fTable.addSelectionListener(new SelectionAdapter() {
331 @Override
332 public void widgetSelected(SelectionEvent event) {
3f43dc48
PT
333 if (event.item == null) {
334 // Override table selection from Select All action
335 refreshSelection();
631d853f
PT
336 }
337 }
338 });
9ccc6d01 339
3f43dc48 340 fTable.addMouseListener(new MouseAdapter() {
631d853f 341 @Override
3f43dc48
PT
342 public void mouseDown(MouseEvent e) {
343 handleTableMouseEvent(e);
631d853f 344 }
3f43dc48
PT
345 });
346
347 fTable.addKeyListener(new KeyAdapter() {
631d853f 348 @Override
3f43dc48
PT
349 public void keyPressed(KeyEvent event) {
350 handleTableKeyEvent(event);
631d853f
PT
351 }
352 });
7800a42f
XR
353
354 fTable.addListener(
631d853f
PT
355 SWT.MouseDoubleClick, new Listener() {
356 @Override
357 public void handleEvent(Event event) {
358 if (doubleClickListener != null) {
dadf6e1a 359 TableItem item = fTable.getItem(new Point (event.x, event.y));
631d853f 360 if (item != null) {
dadf6e1a 361 for (int i = 0; i < fTable.getColumnCount(); i++){
631d853f 362 Rectangle bounds = item.getBounds(i);
dadf6e1a 363 if (bounds.contains(event.x, event.y)){
631d853f
PT
364 doubleClickListener.handleDoubleClick(TmfVirtualTable.this, item, i);
365 break;
366 }
367 }
368 }
369 }
370 }
371 }
372 );
dd03e2f1
PT
373
374 /*
c9787691
PT
375 * Feature in Windows. When a partially visible table item is selected,
376 * after ~500 ms the top index is changed to ensure the selected item is
377 * fully visible. This leaves a blank space at the bottom of the virtual
378 * table. The workaround is to reset the top index to 0 if it is not 0.
379 * Also reset the top index to 0 if indicated by the flag that was set
380 * at table selection of a partially visible table item.
dd03e2f1
PT
381 */
382 fTable.addPaintListener(new PaintListener() {
383 @Override
384 public void paintControl(PaintEvent e) {
c9787691
PT
385 if (fTable.getTopIndex() != 0 || fResetTopIndex) {
386 fTable.setTopIndex(0);
387 }
388 fResetTopIndex = false;
dd03e2f1
PT
389 }
390 });
631d853f
PT
391 }
392
393 /**
3f43dc48
PT
394 * Handle mouse-based selection in table.
395 *
396 * @param event the mouse event
631d853f 397 */
3f43dc48
PT
398 private void handleTableMouseEvent(MouseEvent event) {
399 TableItem item = fTable.getItem(new Point(event.x, event.y));
400 if (item == null) {
401 return;
402 }
403 int selectedRow = indexOf(item);
90328647
PT
404 if (event.button == 1 || (event.button == 3 &&
405 (selectedRow < Math.min(fSelectedBeginRank, fSelectedEventRank) ||
406 selectedRow > Math.max(fSelectedBeginRank, fSelectedEventRank)))) {
3f43dc48
PT
407 if (selectedRow >= 0) {
408 fSelectedEventRank = selectedRow;
409 } else {
410 fSelectedEventRank = -1;
411 }
412 if ((event.stateMask & SWT.SHIFT) == 0 || (fTable.getStyle() & SWT.MULTI) == 0 || fSelectedBeginRank == -1) {
413 fSelectedBeginRank = fSelectedEventRank;
414 }
631d853f 415 }
3f43dc48 416 refreshSelection();
c9787691
PT
417
418 /*
419 * Feature in Linux. When a partially visible table item is selected,
420 * the origin is changed to ensure the selected item is fully visible.
421 * This makes the first row partially visible. The solution is to force
422 * reset the origin by setting the top index to 0. This should happen
423 * only once at the next redraw by the paint listener.
424 */
425 if (selectedRow >= fFullyVisibleRows) {
426 fResetTopIndex = true;
427 }
631d853f
PT
428 }
429
430 /**
431 * Handle key-based navigation in table.
3934297e 432 *
3f43dc48 433 * @param event the key event
631d853f
PT
434 */
435 private void handleTableKeyEvent(KeyEvent event) {
436
437 int lastEventRank = fTableItemCount - 1;
438 int lastPageTopEntryRank = Math.max(0, fTableItemCount - fFullyVisibleRows);
439
440 int previousSelectedEventRank = fSelectedEventRank;
3f43dc48 441 int previousSelectedBeginRank = fSelectedBeginRank;
631d853f
PT
442 boolean needsRefresh = false;
443
444 // In all case, perform the following steps:
445 // - Update the selected entry rank (within valid range)
446 // - Update the selected row
447 // - Update the page's top entry if necessary (which also adjusts the selected row)
448 // - If the top displayed entry was changed, table refresh is needed
449 switch (event.keyCode) {
450
451 case SWT.ARROW_DOWN: {
452 event.doit = false;
453 if (fSelectedEventRank < lastEventRank) {
454 fSelectedEventRank++;
3f43dc48 455 int selectedRow = fSelectedEventRank - fTableTopEventRank;
75c271cf 456 if (selectedRow == fFullyVisibleRows) {
631d853f
PT
457 fTableTopEventRank++;
458 needsRefresh = true;
75c271cf
PT
459 } else if (selectedRow < fFrozenRowCount || selectedRow > fFullyVisibleRows) {
460 fTableTopEventRank = Math.max(0, Math.min(fSelectedEventRank - fFrozenRowCount, lastPageTopEntryRank));
461 needsRefresh = true;
631d853f
PT
462 }
463 }
464 break;
465 }
466
467 case SWT.ARROW_UP: {
468 event.doit = false;
469 if (fSelectedEventRank > 0) {
470 fSelectedEventRank--;
3f43dc48 471 int selectedRow = fSelectedEventRank - fTableTopEventRank;
75c271cf 472 if (selectedRow == fFrozenRowCount - 1 && fTableTopEventRank > 0) {
631d853f
PT
473 fTableTopEventRank--;
474 needsRefresh = true;
75c271cf
PT
475 } else if (selectedRow < fFrozenRowCount || selectedRow > fFullyVisibleRows) {
476 fTableTopEventRank = Math.max(0, Math.min(fSelectedEventRank - fFrozenRowCount, lastPageTopEntryRank));
477 needsRefresh = true;
631d853f
PT
478 }
479 }
480 break;
481 }
482
483 case SWT.END: {
484 event.doit = false;
485 fTableTopEventRank = lastPageTopEntryRank;
486 fSelectedEventRank = lastEventRank;
487 needsRefresh = true;
488 break;
489 }
490
491 case SWT.HOME: {
492 event.doit = false;
493 fSelectedEventRank = fFrozenRowCount;
494 fTableTopEventRank = 0;
495 needsRefresh = true;
496 break;
497 }
498
499 case SWT.PAGE_DOWN: {
500 event.doit = false;
501 if (fSelectedEventRank < lastEventRank) {
502 fSelectedEventRank += fFullyVisibleRows;
503 if (fSelectedEventRank > lastEventRank) {
504 fSelectedEventRank = lastEventRank;
505 }
3f43dc48 506 int selectedRow = fSelectedEventRank - fTableTopEventRank;
75c271cf 507 if (selectedRow > fFullyVisibleRows + fFrozenRowCount - 1 && selectedRow < 2 * fFullyVisibleRows) {
631d853f
PT
508 fTableTopEventRank += fFullyVisibleRows;
509 if (fTableTopEventRank > lastPageTopEntryRank) {
510 fTableTopEventRank = lastPageTopEntryRank;
511 }
512 needsRefresh = true;
75c271cf
PT
513 } else if (selectedRow < fFrozenRowCount || selectedRow >= 2 * fFullyVisibleRows) {
514 fTableTopEventRank = Math.max(0, Math.min(fSelectedEventRank - fFrozenRowCount, lastPageTopEntryRank));
515 needsRefresh = true;
631d853f
PT
516 }
517 }
518 break;
519 }
520
521 case SWT.PAGE_UP: {
522 event.doit = false;
523 if (fSelectedEventRank > 0) {
524 fSelectedEventRank -= fFullyVisibleRows;
525 if (fSelectedEventRank < fFrozenRowCount) {
526 fSelectedEventRank = fFrozenRowCount;
527 }
3f43dc48 528 int selectedRow = fSelectedEventRank - fTableTopEventRank;
75c271cf 529 if (selectedRow < fFrozenRowCount && selectedRow > -fFullyVisibleRows) {
631d853f
PT
530 fTableTopEventRank -= fFullyVisibleRows;
531 if (fTableTopEventRank < 0) {
532 fTableTopEventRank = 0;
533 }
534 needsRefresh = true;
75c271cf
PT
535 } else if (selectedRow <= -fFullyVisibleRows || selectedRow >= fFullyVisibleRows) {
536 fTableTopEventRank = Math.max(0, Math.min(fSelectedEventRank - fFrozenRowCount, lastPageTopEntryRank));
537 needsRefresh = true;
631d853f
PT
538 }
539 }
540 break;
541 }
542 default: {
543 return;
544 }
545 }
546
3f43dc48
PT
547 if ((event.stateMask & SWT.SHIFT) == 0 || (fTable.getStyle() & SWT.MULTI) == 0 || fSelectedBeginRank == -1) {
548 fSelectedBeginRank = fSelectedEventRank;
549 }
550
631d853f
PT
551 boolean done = true;
552 if (needsRefresh) {
553 done = refreshTable(); // false if table items not updated yet in this thread
554 } else {
3f43dc48 555 refreshSelection();
631d853f
PT
556 }
557
558 if (fFullyVisibleRows < fTableItemCount) {
559 fSlider.setSelection(fTableTopEventRank);
560 }
561
3f43dc48 562 if (fSelectedEventRank != previousSelectedEventRank || fSelectedBeginRank != previousSelectedBeginRank) {
631d853f
PT
563 if (done) {
564 Event e = new Event();
3f43dc48 565 e.item = fTable.getItem(fSelectedEventRank - fTableTopEventRank);
631d853f
PT
566 fTable.notifyListeners(SWT.Selection, e);
567 } else {
568 fPendingSelection = true;
569 }
570 }
571 }
572
02023181
MK
573 /**
574 * Method setDataItem.
575 * @param index int
576 * @param item TableItem
577 * @return boolean
578 */
631d853f
PT
579 private boolean setDataItem(int index, TableItem item) {
580 if (index != -1) {
581 Event event = new Event();
582 event.item = item;
583 if (index < fFrozenRowCount) {
584 event.index = index;
585 } else {
586 event.index = index + fTableTopEventRank;
587 }
588 event.doit = true;
589 fTable.notifyListeners(SWT.SetData, event);
590 return event.doit; // false if table item not updated yet in this thread
591 }
592 return true;
593 }
594
595 // ------------------------------------------------------------------------
596 // Slider handling
597 // ------------------------------------------------------------------------
598
02023181
MK
599 /**
600 * Method createSlider.
601 * @param style int
602 */
631d853f
PT
603 private void createSlider(int style) {
604 fSlider = new Slider(this, SWT.VERTICAL | SWT.NO_FOCUS);
605 fSlider.setMinimum(0);
606 fSlider.setMaximum(0);
607 if ((style & SWT.V_SCROLL) == 0) {
608 fSlider.setVisible(false);
609 }
610
611 fSlider.addListener(SWT.Selection, new Listener() {
612 @Override
613 public void handleEvent(Event event) {
614 switch (event.detail) {
615 case SWT.ARROW_DOWN:
616 case SWT.ARROW_UP:
631d853f
PT
617 case SWT.END:
618 case SWT.HOME:
619 case SWT.PAGE_DOWN:
620 case SWT.PAGE_UP: {
621 fTableTopEventRank = fSlider.getSelection();
622 refreshTable();
623 break;
624 }
22ba2f65 625 case SWT.DRAG:
874af45a 626 case SWT.NONE:
22ba2f65
PT
627 /*
628 * While the slider thumb is being dragged, only perform the
629 * refresh periodically. The event detail during the drag is
630 * SWT.DRAG on Windows and SWT.NONE on Linux.
631 */
632 fTableTopEventRank = fSlider.getSelection();
633 if (fSliderThrottler == null) {
634 fSliderThrottler = new SliderThrottler();
635 fSliderThrottler.start();
636 }
637 break;
abbdd66a
AM
638 default:
639 break;
631d853f
PT
640 }
641 }
642 });
874af45a
PT
643
644 /*
22ba2f65
PT
645 * When the mouse button is released, perform the refresh immediately
646 * and interrupt and discard the slider throttler.
874af45a
PT
647 */
648 fSlider.addMouseListener(new MouseAdapter() {
649 @Override
650 public void mouseUp(MouseEvent e) {
22ba2f65
PT
651 if (fSliderThrottler != null) {
652 fSliderThrottler.interrupt();
653 fSliderThrottler = null;
654 }
874af45a
PT
655 fTableTopEventRank = fSlider.getSelection();
656 refreshTable();
657 }
658 });
659
631d853f
PT
660 }
661
662 // ------------------------------------------------------------------------
663 // Simulated Table API
664 // ------------------------------------------------------------------------
665
2fcd5eea
AM
666 /**
667 * Constructs a new TableColumn instance given a style value describing its
668 * alignment behavior. The column is added to the end of the columns
669 * maintained by the table.
670 *
671 * @param style
672 * the alignment style
673 * @return the new TableColumn
674 *
675 * @see SWT#LEFT
676 * @see SWT#RIGHT
677 * @see SWT#CENTER
2fcd5eea
AM
678 */
679 public TableColumn newTableColumn(int style) {
680 TableColumn column = new TableColumn(fTable, style);
681
682 /*
683 * In Linux the table does not receive a control resized event when
684 * a table column resize causes the horizontal scroll bar to become
685 * visible or invisible, so a resize listener must be added to every
686 * table column to properly update the number of fully visible rows.
687 */
688 column.addControlListener(fResizeListener);
689
690 return column;
691 }
692
02023181
MK
693 /**
694 * Method setHeaderVisible.
695 * @param b boolean
696 */
631d853f
PT
697 public void setHeaderVisible(boolean b) {
698 fTable.setHeaderVisible(b);
699 }
700
02023181
MK
701 /**
702 * Method setLinesVisible.
703 * @param b boolean
704 */
631d853f
PT
705 public void setLinesVisible(boolean b) {
706 fTable.setLinesVisible(b);
707 }
708
02023181 709 /**
3f43dc48
PT
710 * Returns an array of <code>TableItem</code>s that are currently selected
711 * in the receiver. The order of the items is unspecified. An empty array
712 * indicates that no items are selected.
713 * <p>
714 * Note: This array only contains the visible selected items in the virtual
715 * table. To get information about the full selection range, use
716 * {@link #getSelectionIndices()}.
717 * </p>
718 *
719 * @return an array representing the selection
720 *
721 * @exception SWTException <ul>
722 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
723 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
724 * </ul>
02023181 725 */
631d853f
PT
726 public TableItem[] getSelection() {
727 return fTable.getSelection();
728 }
729
02023181
MK
730 /**
731 * Method addListener.
732 * @param eventType int
733 * @param listener Listener
734 */
631d853f 735 @Override
828e5592
PT
736 public void addListener(int eventType, Listener listener) {
737 fTable.addListener(eventType, listener);
738 }
631d853f 739
02023181
MK
740 /**
741 * Method addKeyListener.
742 * @param listener KeyListener
743 */
631d853f
PT
744 @Override
745 public void addKeyListener(KeyListener listener) {
746 fTable.addKeyListener(listener);
747 }
b21305c2 748
02023181
MK
749 /**
750 * Method addMouseListener.
751 * @param listener MouseListener
752 */
828e5592 753 @Override
631d853f
PT
754 public void addMouseListener(MouseListener listener) {
755 fTable.addMouseListener(listener);
756 }
757
02023181
MK
758 /**
759 * Method addSelectionListener.
760 * @param listener SelectionListener
761 */
631d853f
PT
762 public void addSelectionListener(SelectionListener listener) {
763 fTable.addSelectionListener(listener);
764 }
765
02023181
MK
766 /**
767 * Method setMenu sets the menu
768 * @param menu Menu the menu
769 */
631d853f
PT
770 @Override
771 public void setMenu(Menu menu) {
772 fTable.setMenu(menu);
773 }
774
48da630d
XR
775 /**
776 * Gets the menu of this table
777 * @return a Menu
778 */
779 @Override
780 public Menu getMenu() {
781 return fTable.getMenu();
782 }
783
02023181 784 /**
3934297e 785 * Method clearAll empties a table.
02023181 786 */
631d853f
PT
787 public void clearAll() {
788 setItemCount(0);
789 }
790
02023181
MK
791 /**
792 * Method setItemCount
793 * @param nbItems int the number of items in the table
d5efe032 794 *
02023181 795 */
631d853f 796 public void setItemCount(int nbItems) {
41b5c37f 797 final int nb = Math.max(0, nbItems);
631d853f 798
41b5c37f
AM
799 if (nb != fTableItemCount) {
800 fTableItemCount = nb;
631d853f 801 fTable.remove(fTableItemCount, fTable.getItemCount() - 1);
41b5c37f 802 fSlider.setMaximum(nb);
631d853f
PT
803 resize();
804 int tableHeight = Math.max(0, fTable.getClientArea().height - fTable.getHeaderHeight());
805 fFullyVisibleRows = tableHeight / getItemHeight();
806 if (fTableItemCount > 0) {
807 fSlider.setThumb(Math.max(1, Math.min(fTableRows, fFullyVisibleRows)));
808 }
809 }
810 }
811
02023181
MK
812 /**
813 * Method getItemCount.
814 * @return int the number of items in the table
815 */
631d853f
PT
816 public int getItemCount() {
817 return fTableItemCount;
818 }
819
02023181
MK
820 /**
821 * Method getItemHeight.
822 * @return int the height of a table item in pixels. (may vary from one os to another)
823 */
631d853f
PT
824 public int getItemHeight() {
825 /*
826 * Bug in Linux. The method getItemHeight doesn't always return the correct value.
827 */
828 if (fLinuxItemHeight >= 0 && System.getProperty("os.name").contains("Linux")) { //$NON-NLS-1$ //$NON-NLS-2$
829 if (fLinuxItemHeight != 0) {
830 return fLinuxItemHeight;
831 }
832 if (fTable.getItemCount() > 1) {
833 int itemHeight = fTable.getItem(1).getBounds().y - fTable.getItem(0).getBounds().y;
834 if (itemHeight > 0) {
835 fLinuxItemHeight = itemHeight;
836 return fLinuxItemHeight;
837 }
838 }
839 } else {
840 fLinuxItemHeight = -1; // Not Linux, don't perform os.name check anymore
841 }
842 return fTable.getItemHeight();
843 }
844
02023181
MK
845 /**
846 * Method getHeaderHeight.
847 * @return int get the height of the header in pixels.
848 */
ea08e8ed
PT
849 public int getHeaderHeight() {
850 return fTable.getHeaderHeight();
851 }
852
02023181
MK
853 /**
854 * Method getTopIndex.
855 * @return int get the first data item index, if you have a header it is offset.
856 */
631d853f
PT
857 public int getTopIndex() {
858 return fTableTopEventRank + fFrozenRowCount;
859 }
860
02023181
MK
861 /**
862 * Method setTopIndex.
41b5c37f 863 * @param index int suggested top index for the table.
02023181 864 */
41b5c37f 865 public void setTopIndex(int index) {
631d853f 866 if (fTableItemCount > 0) {
41b5c37f 867 int i = Math.min(index, fTableItemCount - 1);
631d853f
PT
868 i = Math.max(i, fFrozenRowCount);
869
870 fTableTopEventRank = i - fFrozenRowCount;
871 if (fFullyVisibleRows < fTableItemCount) {
872 fSlider.setSelection(fTableTopEventRank);
873 }
874
875 refreshTable();
876 }
877 }
878
02023181
MK
879 /**
880 * Method indexOf. Return the index of a table item
881 * @param ti TableItem the table item to search for in the table
3934297e 882 * @return int the index of the first match. (there should only be one match)
02023181 883 */
631d853f
PT
884 public int indexOf(TableItem ti) {
885 int index = fTable.indexOf(ti);
886 if (index < fFrozenRowCount) {
887 return index;
631d853f 888 }
abbdd66a 889 return (index - fFrozenRowCount) + getTopIndex();
631d853f
PT
890 }
891
02023181
MK
892 /**
893 * Method getColumns.
3934297e 894 * @return TableColumn[] the table columns
02023181 895 */
631d853f
PT
896 public TableColumn[] getColumns() {
897 return fTable.getColumns();
898 }
899
6817308e
PT
900 /**
901 * Returns an array of zero-relative integers that map
902 * the creation order of the receiver's columns to the
903 * order in which they are currently being displayed.
904 * <p>
905 * Specifically, the indices of the returned array represent
906 * the current visual order of the columns, and the contents
907 * of the array represent the creation order of the columns.
908 *
909 * @return the current visual order of the receiver's columns
910 */
911 public int[] getColumnOrder() {
912 return fTable.getColumnOrder();
913 }
914
915 /**
916 * Sets the order that the columns in the receiver should
917 * be displayed in to the given argument which is described
918 * in terms of the zero-relative ordering of when the columns
919 * were added.
920 * <p>
921 * Specifically, the contents of the array represent the
922 * original position of each column at the time its creation.
923 *
924 * @param order the new order to display the columns
925 */
926 public void setColumnOrder(int[] order) {
927 fTable.setColumnOrder(order);
928 }
929
02023181
MK
930 /**
931 * Method getItem.
932 * @param point Point the coordinates in the table
3934297e 933 * @return TableItem the corresponding table item
02023181 934 */
631d853f
PT
935 public TableItem getItem(Point point) {
936 return fTable.getItem(point);
937 }
938
02023181
MK
939 /**
940 * Method resize.
941 */
631d853f
PT
942 private void resize() {
943 // Compute the numbers of rows that fit the new area
944 int tableHeight = Math.max(0, getSize().y - fTable.getHeaderHeight());
945 int itemHeight = getItemHeight();
946 fTableRows = Math.min((tableHeight + itemHeight - 1) / itemHeight, fTableItemCount);
947
948 if (fTableTopEventRank + fFullyVisibleRows > fTableItemCount) {
949 // If we are at the end, get elements before to populate
950 fTableTopEventRank = Math.max(0, fTableItemCount - fFullyVisibleRows);
951 refreshTable();
952 } else if (fTableRows > fTable.getItemCount() || fTableItemCount < fTable.getItemCount()) {
953 // Only refresh if new table items are needed or if table items need to be deleted
954 refreshTable();
955 }
956
957 }
958
959 // ------------------------------------------------------------------------
960 // Controls interactions
961 // ------------------------------------------------------------------------
962
02023181
MK
963 /**
964 * Method setFocus.
965 * @return boolean is this visible?
966 */
631d853f
PT
967 @Override
968 public boolean setFocus() {
969 boolean isVisible = isVisible();
970 if (isVisible) {
971 fTable.setFocus();
972 }
973 return isVisible;
974 }
975
02023181
MK
976 /**
977 * Method refresh.
978 */
631d853f
PT
979 public void refresh() {
980 boolean done = refreshTable();
3f43dc48
PT
981 if (!done) {
982 return;
983 }
dadf6e1a 984 if (fPendingSelection) {
631d853f 985 fPendingSelection = false;
3f43dc48
PT
986 TableItem item = null;
987 if (fSelectedEventRank >= 0 && fSelectedEventRank < fFrozenRowCount) {
988 item = fTable.getItem(fSelectedEventRank);
989 } else if (fSelectedEventRank >= fTableTopEventRank + fFrozenRowCount && fSelectedEventRank - fTableTopEventRank < fTable.getItemCount()) {
990 item = fTable.getItem(fSelectedEventRank - fTableTopEventRank);
991 }
992 if (item != null) {
631d853f 993 Event e = new Event();
3f43dc48 994 e.item = item;
631d853f
PT
995 fTable.notifyListeners(SWT.Selection, e);
996 }
997 }
998 }
999
02023181
MK
1000 /**
1001 * Method setColumnHeaders.
baafe54c
AM
1002 *
1003 * @param columnData
1004 * ColumnData[] the columndata array.
02023181 1005 */
baafe54c 1006 @Deprecated
631d853f 1007 public void setColumnHeaders(ColumnData columnData[]) {
baafe54c
AM
1008 /* No-op */
1009 }
1010
02023181
MK
1011 /**
1012 * Method removeAll.
1013 * @return int 0 the number of elements in the table
1014 */
631d853f
PT
1015 public int removeAll() {
1016 setItemCount(0);
1017 fSlider.setMaximum(0);
1018 fTable.removeAll();
3f43dc48
PT
1019 fSelectedEventRank = -1;
1020 fSelectedBeginRank = fSelectedEventRank;
631d853f
PT
1021 return 0;
1022 }
1023
02023181
MK
1024 /**
1025 * Method refreshTable.
3f43dc48 1026 * @return true if all table items have been refreshed, false otherwise
02023181 1027 */
631d853f
PT
1028 private boolean refreshTable() {
1029 boolean done = true;
1030 for (int i = 0; i < fTableRows; i++) {
1031 if (i + fTableTopEventRank < fTableItemCount) {
1032 TableItem tableItem;
1033 if (i < fTable.getItemCount()) {
1034 tableItem = fTable.getItem(i);
1035 } else {
1036 tableItem = new TableItem(fTable, SWT.NONE);
1037 }
1038 done &= setDataItem(i, tableItem); // false if table item not updated yet in this thread
1039 } else {
1040 if (fTable.getItemCount() > fTableItemCount - fTableTopEventRank) {
1041 fTable.remove(fTableItemCount - fTableTopEventRank);
1042 }
1043 }
1044 }
3f43dc48
PT
1045 if (done) {
1046 refreshSelection();
1047 } else {
1048 fTable.deselectAll();
1049 }
1050 return done;
1051 }
631d853f 1052
3f43dc48 1053 private void refreshSelection() {
631d853f 1054 int lastRowOffset = fTableTopEventRank + fTableRows - 1;
3f43dc48
PT
1055 int startRank = Math.min(fSelectedBeginRank, fSelectedEventRank);
1056 int endRank = Math.max(fSelectedBeginRank, fSelectedEventRank);
1057 int start = Integer.MAX_VALUE;
1058 int end = Integer.MIN_VALUE;
1059 if (startRank < fFrozenRowCount) {
1060 start = startRank;
1061 } else if (startRank < fTableTopEventRank + fFrozenRowCount) {
1062 start = fFrozenRowCount;
1063 } else if (startRank <= lastRowOffset) {
1064 start = startRank - fTableTopEventRank;
1065 }
1066 if (endRank < fFrozenRowCount) {
1067 end = endRank;
1068 } else if (endRank < fTableTopEventRank + fFrozenRowCount) {
1069 end = fFrozenRowCount - 1;
1070 } else if (endRank <= lastRowOffset) {
1071 end = endRank - fTableTopEventRank;
1072 } else {
1073 end = fTableRows - 1;
1074 }
1075 if (start <= end) {
1076 fTable.setSelection(start, end);
1077 if (startRank == fSelectedEventRank) {
1078 fTable.select(start);
1079 } else {
1080 fTable.select(end);
1081 }
631d853f
PT
1082 } else {
1083 fTable.deselectAll();
1084 }
631d853f
PT
1085 }
1086
02023181 1087 /**
3f43dc48
PT
1088 * Selects the item at the given zero-relative index in the receiver.
1089 * The current selection is first cleared, then the new item is selected,
1090 * and if necessary the receiver is scrolled to make the new selection visible.
1091 *
1092 * @param index the index of the item to select
1093 *
1094 * @exception SWTException <ul>
1095 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1096 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1097 * </ul>
02023181 1098 */
41b5c37f 1099 public void setSelection(int index) {
8b6aedef
JCK
1100 setSelectionRange(index, index);
1101 }
631d853f 1102
8b6aedef
JCK
1103 /**
1104 * Selects a range of items starting at the given zero-relative index in the
1105 * receiver. The current selection is first cleared, then the new items are
1106 * selected, and if necessary the receiver is scrolled to make the new
1107 * selection visible.
1108 *
1109 * @param beginIndex
1110 * The index of the first item in the selection range
1111 * @param endIndex
1112 * The index of the last item in the selection range
1113 *
1114 * @exception SWTException
1115 * <ul>
1116 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been
1117 * disposed</li>
1118 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
1119 * thread that created the receiver</li>
1120 * </ul>
1121 * @since 2.0
1122 */
1123 public void setSelectionRange(int beginIndex, int endIndex) {
1124 if (fTableItemCount > 0) {
1125 int begin = Math.max(Math.min(beginIndex, fTableItemCount - 1), 0);
1126 int end = Math.max(Math.min(endIndex, fTableItemCount - 1), 0);
1127 int selection = fSelectedBeginRank != begin ? begin : end;
1128
1129 fSelectedBeginRank = begin;
1130 fSelectedEventRank = end;
1131 if ((selection < fTableTopEventRank + fFrozenRowCount && selection >= fFrozenRowCount) ||
1132 (selection >= fTableTopEventRank + fFullyVisibleRows)) {
faa38350 1133 int lastPageTopEntryRank = Math.max(0, fTableItemCount - fFullyVisibleRows);
8b6aedef 1134 fTableTopEventRank = Math.max(0, Math.min(lastPageTopEntryRank, selection - fFrozenRowCount - fFullyVisibleRows / 2));
631d853f
PT
1135 }
1136 if (fFullyVisibleRows < fTableItemCount) {
1137 fSlider.setSelection(fTableTopEventRank);
1138 }
631d853f 1139 refreshTable();
631d853f
PT
1140 }
1141 }
1142
02023181 1143 /**
3f43dc48
PT
1144 * Returns the zero-relative index of the item which is currently
1145 * selected in the receiver, or -1 if no item is selected.
1146 *
1147 * @return the index of the selected item
02023181 1148 */
631d853f 1149 public int getSelectionIndex() {
3f43dc48
PT
1150 return fSelectedEventRank;
1151 }
1152
1153 /**
1154 * Returns an index array representing the selection range. If there is a
1155 * single item selected the array holds one index. If there is a selected
1156 * range the first item in the array is the start index of the selection and
1157 * the second item is the end index of the selection, which is the item most
1158 * recently selected. The array is empty if no items are selected.
1159 * <p>
1160 * @return the array of indices of the selected items
3f43dc48
PT
1161 */
1162 public int[] getSelectionIndices() {
1163 if (fSelectedEventRank < 0 || fSelectedBeginRank < 0) {
1164 return new int[] {};
1165 } else if (fSelectedEventRank == fSelectedBeginRank) {
1166 return new int[] { fSelectedEventRank };
631d853f 1167 }
3f43dc48 1168 return new int[] { fSelectedBeginRank, fSelectedEventRank };
631d853f
PT
1169 }
1170
02023181
MK
1171 /**
1172 * Method setFrozenRowCount.
1173 * @param count int the number of rows to freeze from the top row
1174 */
631d853f
PT
1175 public void setFrozenRowCount(int count) {
1176 fFrozenRowCount = count;
1177 refreshTable();
1178 }
1179
02023181
MK
1180 /**
1181 * Method createTableEditor.
1182 * @return a TableEditor of the table
1183 */
631d853f
PT
1184 public TableEditor createTableEditor() {
1185 return new TableEditor(fTable);
1186 }
1187
02023181
MK
1188 /**
1189 * Method createTableEditorControl.
1190 * @param control Class<? extends Control>
1191 * @return Control
1192 */
631d853f
PT
1193 public Control createTableEditorControl(Class<? extends Control> control) {
1194 try {
420bceb2 1195 return control.getConstructor(Composite.class, int.class).newInstance(new Object[] {fTable, SWT.NONE});
631d853f
PT
1196 } catch (Exception e) {
1197 Activator.getDefault().logError("Error creating table editor control", e); //$NON-NLS-1$
1198 }
1199 return null;
1200 }
1201
1202 /**
1203 * @return the tooltipProvider
d5efe032 1204 */
631d853f
PT
1205 public TooltipProvider getTooltipProvider() {
1206 return tooltipProvider;
1207 }
1208
1209 /**
1210 * @param tooltipProvider the tooltipProvider to set
1211 */
1212 public void setTooltipProvider(TooltipProvider tooltipProvider) {
1213 this.tooltipProvider = tooltipProvider;
1214 }
1215
1216 /**
1217 * @return the doubleClickListener
d5efe032 1218 */
631d853f
PT
1219 public IDoubleClickListener getDoubleClickListener() {
1220 return doubleClickListener;
1221 }
1222
1223 /**
1224 * @param doubleClickListener the doubleClickListener to set
1225 */
1226 public void setDoubleClickListener(IDoubleClickListener doubleClickListener) {
1227 this.doubleClickListener = doubleClickListener;
1228 }
1229
9ccc6d01 1230}
This page took 0.152407 seconds and 5 git commands to generate.