1 /*******************************************************************************
2 * Copyright (c) 2010, 2013 Ericsson
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
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 ******************************************************************************/
16 package org
.eclipse
.linuxtools
.tmf
.ui
.widgets
.virtualtable
;
18 import java
.util
.List
;
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
;
59 * <b><u>TmfVirtualTable</u></b>
61 * TmfVirtualTable allows for the tabular display of arbitrarily large data sets
62 * (well, up to Integer.MAX_VALUE or ~2G rows).
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.
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.
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
75 public class TmfVirtualTable
extends Composite
{
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
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
88 private int fTableItemCount
= 0;
91 private Slider fSlider
;
93 private int fLinuxItemHeight
= 0; // Calculated item height for Linux workaround
94 private TooltipProvider tooltipProvider
= null;
95 private IDoubleClickListener doubleClickListener
= null;
97 private boolean fResetTopIndex
= false; // Flag to trigger reset of top index
98 private ControlAdapter fResizeListener
; // Resize listener to update visible rows
100 // ------------------------------------------------------------------------
102 // ------------------------------------------------------------------------
105 * Standard constructor
108 * The parent composite object
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
));
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
);
119 // Prevent the slider from being traversed
120 setTabList(new Control
[] { fTable
});
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
);
131 GridData tableGridData
= new GridData(SWT
.FILL
, SWT
.FILL
, true, true);
132 fTable
.setLayoutData(tableGridData
);
134 GridData sliderGridData
= new GridData(SWT
.FILL
, SWT
.FILL
, false, true);
135 fSlider
.setLayoutData(sliderGridData
);
138 fTable
.addMouseWheelListener(new MouseWheelListener() {
140 public void mouseScrolled(MouseEvent event
) {
141 if (fTableItemCount
<= fFullyVisibleRows
) {
144 fTableTopEventRank
-= event
.count
;
145 if (fTableTopEventRank
< 0) {
146 fTableTopEventRank
= 0;
148 int latestFirstRowOffset
= fTableItemCount
- fFullyVisibleRows
;
149 if (fTableTopEventRank
> latestFirstRowOffset
) {
150 fTableTopEventRank
= latestFirstRowOffset
;
153 fSlider
.setSelection(fTableTopEventRank
);
158 fTable
.addListener(SWT
.MouseWheel
, new Listener() {
159 // disable mouse scroll of horizontal scroll bar
161 public void handleEvent(Event event
) {
166 fResizeListener
= new ControlAdapter() {
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
)));
176 fTable
.addControlListener(fResizeListener
);
178 // Implement a "fake" tooltip
179 final String TOOLTIP_DATA_KEY
= "_TABLEITEM"; //$NON-NLS-1$
180 final Listener labelListener
= new Listener() {
182 public void handleEvent (Event event
) {
183 Label label
= (Label
) event
.widget
;
184 Shell shell
= label
.getShell();
185 switch (event
.type
) {
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
);
206 Listener tableListener
= new Listener() {
210 public void handleEvent(Event event
) {
211 switch (event
.type
) {
214 case SWT
.MouseMove
: {
223 case SWT
.MouseHover
: {
224 TableItem item
= fTable
.getItem(new Point(event
.x
, event
.y
));
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()) {
232 if (tooltipProvider
== null) {
235 String tooltipText
= tooltipProvider
.getTooltip(i
, item
.getData());
236 if (tooltipText
== null) {
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
);
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);
258 // Item found, leave loop.
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() {
276 public void controlResized(ControlEvent event
) {
278 if (fTableItemCount
> 0) {
279 fSlider
.setThumb(Math
.max(1, Math
.min(fTableRows
, fFullyVisibleRows
)));
288 // ------------------------------------------------------------------------
290 // ------------------------------------------------------------------------
293 * Create the table and add listeners
294 * @param style int can be H_SCROLL, SINGLE, MULTI, FULL_SELECTION, HIDE_SELECTION, CHECK
296 private void createTable(int style
) {
297 fTable
= new Table(this, style
| SWT
.NO_SCROLL
);
299 fTable
.addSelectionListener(new SelectionAdapter() {
301 public void widgetSelected(SelectionEvent event
) {
302 if (event
.item
== null) {
303 // Override table selection from Select All action
309 fTable
.addMouseListener(new MouseAdapter() {
311 public void mouseDown(MouseEvent e
) {
312 handleTableMouseEvent(e
);
316 fTable
.addKeyListener(new KeyAdapter() {
318 public void keyPressed(KeyEvent event
) {
319 handleTableKeyEvent(event
);
324 SWT
.MouseDoubleClick
, new Listener() {
326 public void handleEvent(Event event
) {
327 if (doubleClickListener
!= null) {
328 TableItem item
= fTable
.getItem(new Point (event
.x
, event
.y
));
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
);
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.
351 fTable
.addPaintListener(new PaintListener() {
353 public void paintControl(PaintEvent e
) {
354 if (fTable
.getTopIndex() != 0 || fResetTopIndex
) {
355 fTable
.setTopIndex(0);
357 fResetTopIndex
= false;
363 * Handle mouse-based selection in table.
365 * @param event the mouse event
367 private void handleTableMouseEvent(MouseEvent event
) {
368 TableItem item
= fTable
.getItem(new Point(event
.x
, event
.y
));
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
;
379 fSelectedEventRank
= -1;
381 if ((event
.stateMask
& SWT
.SHIFT
) == 0 || (fTable
.getStyle() & SWT
.MULTI
) == 0 || fSelectedBeginRank
== -1) {
382 fSelectedBeginRank
= fSelectedEventRank
;
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.
394 if (selectedRow
>= fFullyVisibleRows
) {
395 fResetTopIndex
= true;
400 * Handle key-based navigation in table.
402 * @param event the key event
404 private void handleTableKeyEvent(KeyEvent event
) {
406 int lastEventRank
= fTableItemCount
- 1;
407 int lastPageTopEntryRank
= Math
.max(0, fTableItemCount
- fFullyVisibleRows
);
409 int previousSelectedEventRank
= fSelectedEventRank
;
410 int previousSelectedBeginRank
= fSelectedBeginRank
;
411 boolean needsRefresh
= false;
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
) {
420 case SWT
.ARROW_DOWN
: {
422 if (fSelectedEventRank
< lastEventRank
) {
423 fSelectedEventRank
++;
424 int selectedRow
= fSelectedEventRank
- fTableTopEventRank
;
425 if (selectedRow
== fFullyVisibleRows
) {
426 fTableTopEventRank
++;
428 } else if (selectedRow
< fFrozenRowCount
|| selectedRow
> fFullyVisibleRows
) {
429 fTableTopEventRank
= Math
.max(0, Math
.min(fSelectedEventRank
- fFrozenRowCount
, lastPageTopEntryRank
));
438 if (fSelectedEventRank
> 0) {
439 fSelectedEventRank
--;
440 int selectedRow
= fSelectedEventRank
- fTableTopEventRank
;
441 if (selectedRow
== fFrozenRowCount
- 1 && fTableTopEventRank
> 0) {
442 fTableTopEventRank
--;
444 } else if (selectedRow
< fFrozenRowCount
|| selectedRow
> fFullyVisibleRows
) {
445 fTableTopEventRank
= Math
.max(0, Math
.min(fSelectedEventRank
- fFrozenRowCount
, lastPageTopEntryRank
));
454 fTableTopEventRank
= lastPageTopEntryRank
;
455 fSelectedEventRank
= lastEventRank
;
462 fSelectedEventRank
= fFrozenRowCount
;
463 fTableTopEventRank
= 0;
468 case SWT
.PAGE_DOWN
: {
470 if (fSelectedEventRank
< lastEventRank
) {
471 fSelectedEventRank
+= fFullyVisibleRows
;
472 if (fSelectedEventRank
> lastEventRank
) {
473 fSelectedEventRank
= lastEventRank
;
475 int selectedRow
= fSelectedEventRank
- fTableTopEventRank
;
476 if (selectedRow
> fFullyVisibleRows
+ fFrozenRowCount
- 1 && selectedRow
< 2 * fFullyVisibleRows
) {
477 fTableTopEventRank
+= fFullyVisibleRows
;
478 if (fTableTopEventRank
> lastPageTopEntryRank
) {
479 fTableTopEventRank
= lastPageTopEntryRank
;
482 } else if (selectedRow
< fFrozenRowCount
|| selectedRow
>= 2 * fFullyVisibleRows
) {
483 fTableTopEventRank
= Math
.max(0, Math
.min(fSelectedEventRank
- fFrozenRowCount
, lastPageTopEntryRank
));
492 if (fSelectedEventRank
> 0) {
493 fSelectedEventRank
-= fFullyVisibleRows
;
494 if (fSelectedEventRank
< fFrozenRowCount
) {
495 fSelectedEventRank
= fFrozenRowCount
;
497 int selectedRow
= fSelectedEventRank
- fTableTopEventRank
;
498 if (selectedRow
< fFrozenRowCount
&& selectedRow
> -fFullyVisibleRows
) {
499 fTableTopEventRank
-= fFullyVisibleRows
;
500 if (fTableTopEventRank
< 0) {
501 fTableTopEventRank
= 0;
504 } else if (selectedRow
<= -fFullyVisibleRows
|| selectedRow
>= fFullyVisibleRows
) {
505 fTableTopEventRank
= Math
.max(0, Math
.min(fSelectedEventRank
- fFrozenRowCount
, lastPageTopEntryRank
));
516 if ((event
.stateMask
& SWT
.SHIFT
) == 0 || (fTable
.getStyle() & SWT
.MULTI
) == 0 || fSelectedBeginRank
== -1) {
517 fSelectedBeginRank
= fSelectedEventRank
;
522 done
= refreshTable(); // false if table items not updated yet in this thread
527 if (fFullyVisibleRows
< fTableItemCount
) {
528 fSlider
.setSelection(fTableTopEventRank
);
531 if (fSelectedEventRank
!= previousSelectedEventRank
|| fSelectedBeginRank
!= previousSelectedBeginRank
) {
533 Event e
= new Event();
534 e
.item
= fTable
.getItem(fSelectedEventRank
- fTableTopEventRank
);
535 fTable
.notifyListeners(SWT
.Selection
, e
);
537 fPendingSelection
= true;
543 * Method setDataItem.
545 * @param item TableItem
548 private boolean setDataItem(int index
, TableItem item
) {
550 Event event
= new Event();
552 if (index
< fFrozenRowCount
) {
555 event
.index
= index
+ fTableTopEventRank
;
558 fTable
.notifyListeners(SWT
.SetData
, event
);
559 return event
.doit
; // false if table item not updated yet in this thread
564 // ------------------------------------------------------------------------
566 // ------------------------------------------------------------------------
569 * Method createSlider.
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);
580 fSlider
.addListener(SWT
.Selection
, new Listener() {
582 public void handleEvent(Event event
) {
583 switch (event
.detail
) {
590 fTableTopEventRank
= fSlider
.getSelection();
594 // Not handled because of bug on Linux described below.
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.
607 fSlider
.addMouseListener(new MouseAdapter() {
609 public void mouseUp(MouseEvent e
) {
610 fTableTopEventRank
= fSlider
.getSelection();
617 // ------------------------------------------------------------------------
618 // Simulated Table API
619 // ------------------------------------------------------------------------
622 * Method setHeaderVisible.
625 public void setHeaderVisible(boolean b
) {
626 fTable
.setHeaderVisible(b
);
630 * Method setLinesVisible.
633 public void setLinesVisible(boolean b
) {
634 fTable
.setLinesVisible(b
);
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.
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()}.
647 * @return an array representing the selection
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>
654 public TableItem
[] getSelection() {
655 return fTable
.getSelection();
659 * Method addListener.
660 * @param eventType int
661 * @param listener Listener
664 public void addListener(int eventType
, Listener listener
) {
665 fTable
.addListener(eventType
, listener
);
669 * Method addKeyListener.
670 * @param listener KeyListener
673 public void addKeyListener(KeyListener listener
) {
674 fTable
.addKeyListener(listener
);
678 * Method addMouseListener.
679 * @param listener MouseListener
682 public void addMouseListener(MouseListener listener
) {
683 fTable
.addMouseListener(listener
);
687 * Method addSelectionListener.
688 * @param listener SelectionListener
690 public void addSelectionListener(SelectionListener listener
) {
691 fTable
.addSelectionListener(listener
);
695 * Method setMenu sets the menu
696 * @param menu Menu the menu
699 public void setMenu(Menu menu
) {
700 fTable
.setMenu(menu
);
704 * Gets the menu of this table
708 public Menu
getMenu() {
709 return fTable
.getMenu();
713 * Method clearAll empties a table.
715 public void clearAll() {
720 * Method setItemCount
721 * @param nbItems int the number of items in the table
724 public void setItemCount(int nbItems
) {
725 final int nb
= Math
.max(0, nbItems
);
727 if (nb
!= fTableItemCount
) {
728 fTableItemCount
= nb
;
729 fTable
.remove(fTableItemCount
, fTable
.getItemCount() - 1);
730 fSlider
.setMaximum(nb
);
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
)));
741 * Method getItemCount.
742 * @return int the number of items in the table
744 public int getItemCount() {
745 return fTableItemCount
;
749 * Method getItemHeight.
750 * @return int the height of a table item in pixels. (may vary from one os to another)
752 public int getItemHeight() {
754 * Bug in Linux. The method getItemHeight doesn't always return the correct value.
756 if (fLinuxItemHeight
>= 0 && System
.getProperty("os.name").contains("Linux")) { //$NON-NLS-1$ //$NON-NLS-2$
757 if (fLinuxItemHeight
!= 0) {
758 return fLinuxItemHeight
;
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
;
768 fLinuxItemHeight
= -1; // Not Linux, don't perform os.name check anymore
770 return fTable
.getItemHeight();
774 * Method getHeaderHeight.
775 * @return int get the height of the header in pixels.
777 public int getHeaderHeight() {
778 return fTable
.getHeaderHeight();
782 * Method getTopIndex.
783 * @return int get the first data item index, if you have a header it is offset.
785 public int getTopIndex() {
786 return fTableTopEventRank
+ fFrozenRowCount
;
790 * Method setTopIndex.
791 * @param index int suggested top index for the table.
793 public void setTopIndex(int index
) {
794 if (fTableItemCount
> 0) {
795 int i
= Math
.min(index
, fTableItemCount
- 1);
796 i
= Math
.max(i
, fFrozenRowCount
);
798 fTableTopEventRank
= i
- fFrozenRowCount
;
799 if (fFullyVisibleRows
< fTableItemCount
) {
800 fSlider
.setSelection(fTableTopEventRank
);
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)
812 public int indexOf(TableItem ti
) {
813 int index
= fTable
.indexOf(ti
);
814 if (index
< fFrozenRowCount
) {
817 return (index
- fFrozenRowCount
) + getTopIndex();
822 * @return TableColumn[] the table columns
824 public TableColumn
[] getColumns() {
825 return fTable
.getColumns();
830 * @param point Point the coordinates in the table
831 * @return TableItem the corresponding table item
833 public TableItem
getItem(Point point
) {
834 return fTable
.getItem(point
);
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
);
846 if (fTableTopEventRank
+ fFullyVisibleRows
> fTableItemCount
) {
847 // If we are at the end, get elements before to populate
848 fTableTopEventRank
= Math
.max(0, fTableItemCount
- fFullyVisibleRows
);
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
857 // ------------------------------------------------------------------------
858 // Controls interactions
859 // ------------------------------------------------------------------------
863 * @return boolean is this visible?
866 public boolean setFocus() {
867 boolean isVisible
= isVisible();
877 public void refresh() {
878 boolean done
= refreshTable();
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
);
891 Event e
= new Event();
893 fTable
.notifyListeners(SWT
.Selection
, e
);
899 * Method setColumnHeaders.
902 * ColumnData[] the columndata array.
905 public void setColumnHeaders(ColumnData columnData
[]) {
910 * Method setColumnHeaders.
913 * The configuration elements of every column, in the order they
914 * should be initially in the table.
917 public void createColumns(List
<TmfEventTableColumn
> columns
) {
918 for (TmfEventTableColumn column
: columns
) {
920 /* Set the column's header and properties */
921 TableColumn tableCol
= new TableColumn(fTable
, SWT
.LEFT
);
922 tableCol
.setText(column
.getHeaderName());
924 /* Set the column's tooltip, if any */
925 String tooltip
= column
.getHeaderTooltip();
926 if (tooltip
!= null) {
927 tableCol
.setToolTipText(tooltip
);
930 /* Set the column's Field ID (for filtering) */
931 tableCol
.setData(Key
.FIELD_ID
, column
.getFilterFieldId());
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.
939 tableCol
.addControlListener(fResizeListener
);
946 * @return int 0 the number of elements in the table
948 public int removeAll() {
950 fSlider
.setMaximum(0);
952 fSelectedEventRank
= -1;
953 fSelectedBeginRank
= fSelectedEventRank
;
958 * Method refreshTable.
959 * @return true if all table items have been refreshed, false otherwise
961 private boolean refreshTable() {
963 for (int i
= 0; i
< fTableRows
; i
++) {
964 if (i
+ fTableTopEventRank
< fTableItemCount
) {
966 if (i
< fTable
.getItemCount()) {
967 tableItem
= fTable
.getItem(i
);
969 tableItem
= new TableItem(fTable
, SWT
.NONE
);
971 done
&= setDataItem(i
, tableItem
); // false if table item not updated yet in this thread
973 if (fTable
.getItemCount() > fTableItemCount
- fTableTopEventRank
) {
974 fTable
.remove(fTableItemCount
- fTableTopEventRank
);
981 fTable
.deselectAll();
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
) {
994 } else if (startRank
< fTableTopEventRank
+ fFrozenRowCount
) {
995 start
= fFrozenRowCount
;
996 } else if (startRank
<= lastRowOffset
) {
997 start
= startRank
- fTableTopEventRank
;
999 if (endRank
< fFrozenRowCount
) {
1001 } else if (endRank
< fTableTopEventRank
+ fFrozenRowCount
) {
1002 end
= fFrozenRowCount
- 1;
1003 } else if (endRank
<= lastRowOffset
) {
1004 end
= endRank
- fTableTopEventRank
;
1006 end
= fTableRows
- 1;
1009 fTable
.setSelection(start
, end
);
1010 if (startRank
== fSelectedEventRank
) {
1011 fTable
.select(start
);
1016 fTable
.deselectAll();
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.
1025 * @param index the index of the item to select
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>
1032 public void setSelection(int index
) {
1033 if (fTableItemCount
> 0) {
1034 int i
= Math
.min(index
, fTableItemCount
- 1);
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));
1044 if (fFullyVisibleRows
< fTableItemCount
) {
1045 fSlider
.setSelection(fTableTopEventRank
);
1053 * Returns the zero-relative index of the item which is currently
1054 * selected in the receiver, or -1 if no item is selected.
1056 * @return the index of the selected item
1058 public int getSelectionIndex() {
1059 return fSelectedEventRank
;
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.
1069 * @return the array of indices of the selected items
1072 public int[] getSelectionIndices() {
1073 if (fSelectedEventRank
< 0 || fSelectedBeginRank
< 0) {
1074 return new int[] {};
1075 } else if (fSelectedEventRank
== fSelectedBeginRank
) {
1076 return new int[] { fSelectedEventRank
};
1078 return new int[] { fSelectedBeginRank
, fSelectedEventRank
};
1082 * Method setFrozenRowCount.
1083 * @param count int the number of rows to freeze from the top row
1085 public void setFrozenRowCount(int count
) {
1086 fFrozenRowCount
= count
;
1091 * Method createTableEditor.
1092 * @return a TableEditor of the table
1094 public TableEditor
createTableEditor() {
1095 return new TableEditor(fTable
);
1099 * Method createTableEditorControl.
1100 * @param control Class<? extends Control>
1103 public Control
createTableEditorControl(Class
<?
extends Control
> control
) {
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$
1113 * @return the tooltipProvider
1115 public TooltipProvider
getTooltipProvider() {
1116 return tooltipProvider
;
1120 * @param tooltipProvider the tooltipProvider to set
1122 public void setTooltipProvider(TooltipProvider tooltipProvider
) {
1123 this.tooltipProvider
= tooltipProvider
;
1127 * @return the doubleClickListener
1129 public IDoubleClickListener
getDoubleClickListener() {
1130 return doubleClickListener
;
1134 * @param doubleClickListener the doubleClickListener to set
1136 public void setDoubleClickListener(IDoubleClickListener doubleClickListener
) {
1137 this.doubleClickListener
= doubleClickListener
;