94800631e9183209f9d6f067868880fb34084eac
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.ui / src / org / eclipse / linuxtools / tmf / ui / widgets / virtualtable / TmfVirtualTable.java
1 /*******************************************************************************
2 * Copyright (c) 2010, 2013 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
12 * Patrick Tasse - Improvements and bug fixing
13 * Xavier Raynaud - Improvements
14 ******************************************************************************/
15
16 package org.eclipse.linuxtools.tmf.ui.widgets.virtualtable;
17
18 import java.util.List;
19
20 import org.eclipse.linuxtools.internal.tmf.ui.Activator;
21 import org.eclipse.linuxtools.tmf.ui.viewers.events.TmfEventsTable.Key;
22 import org.eclipse.linuxtools.tmf.ui.viewers.events.columns.TmfEventTableColumn;
23 import org.eclipse.swt.SWT;
24 import org.eclipse.swt.SWTException;
25 import org.eclipse.swt.custom.TableEditor;
26 import org.eclipse.swt.events.ControlAdapter;
27 import org.eclipse.swt.events.ControlEvent;
28 import org.eclipse.swt.events.KeyAdapter;
29 import org.eclipse.swt.events.KeyEvent;
30 import org.eclipse.swt.events.KeyListener;
31 import org.eclipse.swt.events.MouseAdapter;
32 import org.eclipse.swt.events.MouseEvent;
33 import org.eclipse.swt.events.MouseListener;
34 import org.eclipse.swt.events.MouseWheelListener;
35 import org.eclipse.swt.events.PaintEvent;
36 import org.eclipse.swt.events.PaintListener;
37 import org.eclipse.swt.events.SelectionAdapter;
38 import org.eclipse.swt.events.SelectionEvent;
39 import org.eclipse.swt.events.SelectionListener;
40 import org.eclipse.swt.graphics.Point;
41 import org.eclipse.swt.graphics.Rectangle;
42 import org.eclipse.swt.layout.FillLayout;
43 import org.eclipse.swt.layout.GridData;
44 import org.eclipse.swt.layout.GridLayout;
45 import org.eclipse.swt.widgets.Composite;
46 import org.eclipse.swt.widgets.Control;
47 import org.eclipse.swt.widgets.Event;
48 import org.eclipse.swt.widgets.Label;
49 import org.eclipse.swt.widgets.Listener;
50 import org.eclipse.swt.widgets.Menu;
51 import org.eclipse.swt.widgets.Shell;
52 import org.eclipse.swt.widgets.Slider;
53 import org.eclipse.swt.widgets.Table;
54 import org.eclipse.swt.widgets.TableColumn;
55 import org.eclipse.swt.widgets.TableItem;
56 import org.eclipse.ui.PlatformUI;
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).
63 *
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.
66 *
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.
69 *
70 * Styles:
71 * H_SCROLL, V_SCROLL, SINGLE, MULTI, CHECK, FULL_SELECTION, HIDE_SELECTION, NO_SCROLL
72 * @author Matthew Khouzam, Francois Chouinard, Patrick Tasse, Xavier Raynaud
73 * @version $Revision: 1.0
74 */
75 public class TmfVirtualTable extends Composite {
76
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
84 private int fSelectedEventRank = -1; // Global rank of the selected event
85 private int fSelectedBeginRank = -1; // Global rank of the selected begin event
86 private boolean fPendingSelection = false; // Pending selection update
87
88 private int fTableItemCount = 0;
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
97 private boolean fResetTopIndex = false; // Flag to trigger reset of top index
98 private ControlAdapter fResizeListener; // Resize listener to update visible rows
99
100 // ------------------------------------------------------------------------
101 // Constructor
102 // ------------------------------------------------------------------------
103
104 /**
105 * Standard constructor
106 *
107 * @param parent
108 * The parent composite object
109 * @param style
110 * The style to use
111 */
112 public TmfVirtualTable(Composite parent, int style) {
113 super(parent, style & (~SWT.H_SCROLL) & (~SWT.V_SCROLL) & (~SWT.SINGLE) & (~SWT.MULTI) & (~SWT.FULL_SELECTION) & (~SWT.HIDE_SELECTION) & (~SWT.CHECK));
114
115 // Create the controls
116 createTable(style & (SWT.H_SCROLL | SWT.SINGLE | SWT.MULTI | SWT.FULL_SELECTION | SWT.HIDE_SELECTION | SWT.CHECK));
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
161 public void handleEvent(Event event) {
162 event.doit = false;
163 }
164 });
165
166 fResizeListener = new ControlAdapter() {
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 }
175 };
176 fTable.addControlListener(fResizeListener);
177
178 // Implement a "fake" tooltip
179 final String TOOLTIP_DATA_KEY = "_TABLEITEM"; //$NON-NLS-1$
180 final Listener labelListener = new Listener() {
181 @Override
182 public void handleEvent (Event event) {
183 Label label = (Label) event.widget;
184 Shell shell = label.getShell();
185 switch (event.type) {
186 case SWT.MouseDown:
187 Event e = new Event();
188 e.item = (TableItem) label.getData(TOOLTIP_DATA_KEY);
189 // Assuming table is single select, set the selection as if
190 // the mouse down event went through to the table
191 fTable.setSelection(new TableItem [] {(TableItem) e.item});
192 fTable.notifyListeners(SWT.Selection, e);
193 shell.dispose();
194 fTable.setFocus();
195 break;
196 case SWT.MouseExit:
197 case SWT.MouseWheel:
198 shell.dispose();
199 break;
200 default:
201 break;
202 }
203 }
204 };
205
206 Listener tableListener = new Listener() {
207 Shell tip = null;
208 Label label = null;
209 @Override
210 public void handleEvent(Event event) {
211 switch (event.type) {
212 case SWT.Dispose:
213 case SWT.KeyDown:
214 case SWT.MouseMove: {
215 if (tip == null) {
216 break;
217 }
218 tip.dispose();
219 tip = null;
220 label = null;
221 break;
222 }
223 case SWT.MouseHover: {
224 TableItem item = fTable.getItem(new Point(event.x, event.y));
225 if (item != null) {
226 for (int i = 0; i < fTable.getColumnCount(); i++) {
227 Rectangle bounds = item.getBounds(i);
228 if (bounds.contains(event.x, event.y)) {
229 if (tip != null && !tip.isDisposed()) {
230 tip.dispose();
231 }
232 if (tooltipProvider == null) {
233 return;
234 }
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);
257
258 // Item found, leave loop.
259 break;
260 }
261 }
262 }
263 break;
264 }
265 default:
266 break;
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();
278 if (fTableItemCount > 0) {
279 fSlider.setThumb(Math.max(1, Math.min(fTableRows, fFullyVisibleRows)));
280 }
281 }
282 });
283
284 // And display
285 refresh();
286 }
287
288 // ------------------------------------------------------------------------
289 // Table handling
290 // ------------------------------------------------------------------------
291
292 /**
293 * Create the table and add listeners
294 * @param style int can be H_SCROLL, SINGLE, MULTI, FULL_SELECTION, HIDE_SELECTION, CHECK
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) {
302 if (event.item == null) {
303 // Override table selection from Select All action
304 refreshSelection();
305 }
306 }
307 });
308
309 fTable.addMouseListener(new MouseAdapter() {
310 @Override
311 public void mouseDown(MouseEvent e) {
312 handleTableMouseEvent(e);
313 }
314 });
315
316 fTable.addKeyListener(new KeyAdapter() {
317 @Override
318 public void keyPressed(KeyEvent event) {
319 handleTableKeyEvent(event);
320 }
321 });
322
323 fTable.addListener(
324 SWT.MouseDoubleClick, new Listener() {
325 @Override
326 public void handleEvent(Event event) {
327 if (doubleClickListener != null) {
328 TableItem item = fTable.getItem(new Point (event.x, event.y));
329 if (item != null) {
330 for (int i = 0; i < fTable.getColumnCount(); i++){
331 Rectangle bounds = item.getBounds(i);
332 if (bounds.contains(event.x, event.y)){
333 doubleClickListener.handleDoubleClick(TmfVirtualTable.this, item, i);
334 break;
335 }
336 }
337 }
338 }
339 }
340 }
341 );
342
343 /*
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.
350 */
351 fTable.addPaintListener(new PaintListener() {
352 @Override
353 public void paintControl(PaintEvent e) {
354 if (fTable.getTopIndex() != 0 || fResetTopIndex) {
355 fTable.setTopIndex(0);
356 }
357 fResetTopIndex = false;
358 }
359 });
360 }
361
362 /**
363 * Handle mouse-based selection in table.
364 *
365 * @param event the mouse event
366 */
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);
373 if (event.button == 1 || (event.button == 3 &&
374 (selectedRow < Math.min(fSelectedBeginRank, fSelectedEventRank) ||
375 selectedRow > Math.max(fSelectedBeginRank, fSelectedEventRank)))) {
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 }
384 }
385 refreshSelection();
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 }
397 }
398
399 /**
400 * Handle key-based navigation in table.
401 *
402 * @param event the key event
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;
410 int previousSelectedBeginRank = fSelectedBeginRank;
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++;
424 int selectedRow = fSelectedEventRank - fTableTopEventRank;
425 if (selectedRow == fFullyVisibleRows) {
426 fTableTopEventRank++;
427 needsRefresh = true;
428 } else if (selectedRow < fFrozenRowCount || selectedRow > fFullyVisibleRows) {
429 fTableTopEventRank = Math.max(0, Math.min(fSelectedEventRank - fFrozenRowCount, lastPageTopEntryRank));
430 needsRefresh = true;
431 }
432 }
433 break;
434 }
435
436 case SWT.ARROW_UP: {
437 event.doit = false;
438 if (fSelectedEventRank > 0) {
439 fSelectedEventRank--;
440 int selectedRow = fSelectedEventRank - fTableTopEventRank;
441 if (selectedRow == fFrozenRowCount - 1 && fTableTopEventRank > 0) {
442 fTableTopEventRank--;
443 needsRefresh = true;
444 } else if (selectedRow < fFrozenRowCount || selectedRow > fFullyVisibleRows) {
445 fTableTopEventRank = Math.max(0, Math.min(fSelectedEventRank - fFrozenRowCount, lastPageTopEntryRank));
446 needsRefresh = true;
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 }
475 int selectedRow = fSelectedEventRank - fTableTopEventRank;
476 if (selectedRow > fFullyVisibleRows + fFrozenRowCount - 1 && selectedRow < 2 * fFullyVisibleRows) {
477 fTableTopEventRank += fFullyVisibleRows;
478 if (fTableTopEventRank > lastPageTopEntryRank) {
479 fTableTopEventRank = lastPageTopEntryRank;
480 }
481 needsRefresh = true;
482 } else if (selectedRow < fFrozenRowCount || selectedRow >= 2 * fFullyVisibleRows) {
483 fTableTopEventRank = Math.max(0, Math.min(fSelectedEventRank - fFrozenRowCount, lastPageTopEntryRank));
484 needsRefresh = true;
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 }
497 int selectedRow = fSelectedEventRank - fTableTopEventRank;
498 if (selectedRow < fFrozenRowCount && selectedRow > -fFullyVisibleRows) {
499 fTableTopEventRank -= fFullyVisibleRows;
500 if (fTableTopEventRank < 0) {
501 fTableTopEventRank = 0;
502 }
503 needsRefresh = true;
504 } else if (selectedRow <= -fFullyVisibleRows || selectedRow >= fFullyVisibleRows) {
505 fTableTopEventRank = Math.max(0, Math.min(fSelectedEventRank - fFrozenRowCount, lastPageTopEntryRank));
506 needsRefresh = true;
507 }
508 }
509 break;
510 }
511 default: {
512 return;
513 }
514 }
515
516 if ((event.stateMask & SWT.SHIFT) == 0 || (fTable.getStyle() & SWT.MULTI) == 0 || fSelectedBeginRank == -1) {
517 fSelectedBeginRank = fSelectedEventRank;
518 }
519
520 boolean done = true;
521 if (needsRefresh) {
522 done = refreshTable(); // false if table items not updated yet in this thread
523 } else {
524 refreshSelection();
525 }
526
527 if (fFullyVisibleRows < fTableItemCount) {
528 fSlider.setSelection(fTableTopEventRank);
529 }
530
531 if (fSelectedEventRank != previousSelectedEventRank || fSelectedBeginRank != previousSelectedBeginRank) {
532 if (done) {
533 Event e = new Event();
534 e.item = fTable.getItem(fSelectedEventRank - fTableTopEventRank);
535 fTable.notifyListeners(SWT.Selection, e);
536 } else {
537 fPendingSelection = true;
538 }
539 }
540 }
541
542 /**
543 * Method setDataItem.
544 * @param index int
545 * @param item TableItem
546 * @return boolean
547 */
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
568 /**
569 * Method createSlider.
570 * @param style int
571 */
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:
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 }
594 // Not handled because of bug on Linux described below.
595 case SWT.NONE:
596 default:
597 break;
598 }
599 }
600 });
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
615 }
616
617 // ------------------------------------------------------------------------
618 // Simulated Table API
619 // ------------------------------------------------------------------------
620
621 /**
622 * Method setHeaderVisible.
623 * @param b boolean
624 */
625 public void setHeaderVisible(boolean b) {
626 fTable.setHeaderVisible(b);
627 }
628
629 /**
630 * Method setLinesVisible.
631 * @param b boolean
632 */
633 public void setLinesVisible(boolean b) {
634 fTable.setLinesVisible(b);
635 }
636
637 /**
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>
653 */
654 public TableItem[] getSelection() {
655 return fTable.getSelection();
656 }
657
658 /**
659 * Method addListener.
660 * @param eventType int
661 * @param listener Listener
662 */
663 @Override
664 public void addListener(int eventType, Listener listener) {
665 fTable.addListener(eventType, listener);
666 }
667
668 /**
669 * Method addKeyListener.
670 * @param listener KeyListener
671 */
672 @Override
673 public void addKeyListener(KeyListener listener) {
674 fTable.addKeyListener(listener);
675 }
676
677 /**
678 * Method addMouseListener.
679 * @param listener MouseListener
680 */
681 @Override
682 public void addMouseListener(MouseListener listener) {
683 fTable.addMouseListener(listener);
684 }
685
686 /**
687 * Method addSelectionListener.
688 * @param listener SelectionListener
689 */
690 public void addSelectionListener(SelectionListener listener) {
691 fTable.addSelectionListener(listener);
692 }
693
694 /**
695 * Method setMenu sets the menu
696 * @param menu Menu the menu
697 */
698 @Override
699 public void setMenu(Menu menu) {
700 fTable.setMenu(menu);
701 }
702
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
712 /**
713 * Method clearAll empties a table.
714 */
715 public void clearAll() {
716 setItemCount(0);
717 }
718
719 /**
720 * Method setItemCount
721 * @param nbItems int the number of items in the table
722 *
723 */
724 public void setItemCount(int nbItems) {
725 final int nb = Math.max(0, nbItems);
726
727 if (nb != fTableItemCount) {
728 fTableItemCount = nb;
729 fTable.remove(fTableItemCount, fTable.getItemCount() - 1);
730 fSlider.setMaximum(nb);
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
740 /**
741 * Method getItemCount.
742 * @return int the number of items in the table
743 */
744 public int getItemCount() {
745 return fTableItemCount;
746 }
747
748 /**
749 * Method getItemHeight.
750 * @return int the height of a table item in pixels. (may vary from one os to another)
751 */
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
773 /**
774 * Method getHeaderHeight.
775 * @return int get the height of the header in pixels.
776 */
777 public int getHeaderHeight() {
778 return fTable.getHeaderHeight();
779 }
780
781 /**
782 * Method getTopIndex.
783 * @return int get the first data item index, if you have a header it is offset.
784 */
785 public int getTopIndex() {
786 return fTableTopEventRank + fFrozenRowCount;
787 }
788
789 /**
790 * Method setTopIndex.
791 * @param index int suggested top index for the table.
792 */
793 public void setTopIndex(int index) {
794 if (fTableItemCount > 0) {
795 int i = Math.min(index, fTableItemCount - 1);
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
807 /**
808 * Method indexOf. Return the index of a table item
809 * @param ti TableItem the table item to search for in the table
810 * @return int the index of the first match. (there should only be one match)
811 */
812 public int indexOf(TableItem ti) {
813 int index = fTable.indexOf(ti);
814 if (index < fFrozenRowCount) {
815 return index;
816 }
817 return (index - fFrozenRowCount) + getTopIndex();
818 }
819
820 /**
821 * Method getColumns.
822 * @return TableColumn[] the table columns
823 */
824 public TableColumn[] getColumns() {
825 return fTable.getColumns();
826 }
827
828 /**
829 * Method getItem.
830 * @param point Point the coordinates in the table
831 * @return TableItem the corresponding table item
832 */
833 public TableItem getItem(Point point) {
834 return fTable.getItem(point);
835 }
836
837 /**
838 * Method resize.
839 */
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
861 /**
862 * Method setFocus.
863 * @return boolean is this visible?
864 */
865 @Override
866 public boolean setFocus() {
867 boolean isVisible = isVisible();
868 if (isVisible) {
869 fTable.setFocus();
870 }
871 return isVisible;
872 }
873
874 /**
875 * Method refresh.
876 */
877 public void refresh() {
878 boolean done = refreshTable();
879 if (!done) {
880 return;
881 }
882 if (fPendingSelection) {
883 fPendingSelection = false;
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) {
891 Event e = new Event();
892 e.item = item;
893 fTable.notifyListeners(SWT.Selection, e);
894 }
895 }
896 }
897
898 /**
899 * Method setColumnHeaders.
900 *
901 * @param columnData
902 * ColumnData[] the columndata array.
903 */
904 @Deprecated
905 public void setColumnHeaders(ColumnData columnData[]) {
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
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 */
939 tableCol.addControlListener(fResizeListener);
940 tableCol.pack();
941 }
942 }
943
944 /**
945 * Method removeAll.
946 * @return int 0 the number of elements in the table
947 */
948 public int removeAll() {
949 setItemCount(0);
950 fSlider.setMaximum(0);
951 fTable.removeAll();
952 fSelectedEventRank = -1;
953 fSelectedBeginRank = fSelectedEventRank;
954 return 0;
955 }
956
957 /**
958 * Method refreshTable.
959 * @return true if all table items have been refreshed, false otherwise
960 */
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 }
978 if (done) {
979 refreshSelection();
980 } else {
981 fTable.deselectAll();
982 }
983 return done;
984 }
985
986 private void refreshSelection() {
987 int lastRowOffset = fTableTopEventRank + fTableRows - 1;
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 }
1015 } else {
1016 fTable.deselectAll();
1017 }
1018 }
1019
1020 /**
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>
1031 */
1032 public void setSelection(int index) {
1033 if (fTableItemCount > 0) {
1034 int i = Math.min(index, fTableItemCount - 1);
1035 i = Math.max(i, 0);
1036
1037 fSelectedEventRank = i;
1038 fSelectedBeginRank = fSelectedEventRank;
1039 if ((i < fTableTopEventRank + fFrozenRowCount && i >= fFrozenRowCount) ||
1040 (i >= fTableTopEventRank + fFullyVisibleRows)) {
1041 int lastPageTopEntryRank = Math.max(0, fTableItemCount - fFullyVisibleRows);
1042 fTableTopEventRank = Math.max(0, Math.min(lastPageTopEntryRank, i - fFrozenRowCount - fFullyVisibleRows / 2));
1043 }
1044 if (fFullyVisibleRows < fTableItemCount) {
1045 fSlider.setSelection(fTableTopEventRank);
1046 }
1047
1048 refreshTable();
1049 }
1050 }
1051
1052 /**
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
1057 */
1058 public int getSelectionIndex() {
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
1070 * @since 2.1
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 };
1077 }
1078 return new int[] { fSelectedBeginRank, fSelectedEventRank };
1079 }
1080
1081 /**
1082 * Method setFrozenRowCount.
1083 * @param count int the number of rows to freeze from the top row
1084 */
1085 public void setFrozenRowCount(int count) {
1086 fFrozenRowCount = count;
1087 refreshTable();
1088 }
1089
1090 /**
1091 * Method createTableEditor.
1092 * @return a TableEditor of the table
1093 */
1094 public TableEditor createTableEditor() {
1095 return new TableEditor(fTable);
1096 }
1097
1098 /**
1099 * Method createTableEditorControl.
1100 * @param control Class<? extends Control>
1101 * @return Control
1102 */
1103 public Control createTableEditorControl(Class<? extends Control> control) {
1104 try {
1105 return control.getConstructor(Composite.class, int.class).newInstance(new Object[] {fTable, SWT.NONE});
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
1114 */
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
1128 */
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
1140 }
This page took 0.057756 seconds and 5 git commands to generate.