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