1 /*******************************************************************************
2 * Copyright (c) 2010, 2016 Ericsson and others.
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
.tracecompass
.tmf
.ui
.widgets
.virtualtable
;
18 import org
.eclipse
.swt
.SWT
;
19 import org
.eclipse
.swt
.SWTException
;
20 import org
.eclipse
.swt
.custom
.TableEditor
;
21 import org
.eclipse
.swt
.events
.ControlAdapter
;
22 import org
.eclipse
.swt
.events
.ControlEvent
;
23 import org
.eclipse
.swt
.events
.FocusAdapter
;
24 import org
.eclipse
.swt
.events
.FocusEvent
;
25 import org
.eclipse
.swt
.events
.KeyAdapter
;
26 import org
.eclipse
.swt
.events
.KeyEvent
;
27 import org
.eclipse
.swt
.events
.KeyListener
;
28 import org
.eclipse
.swt
.events
.MouseAdapter
;
29 import org
.eclipse
.swt
.events
.MouseEvent
;
30 import org
.eclipse
.swt
.events
.MouseListener
;
31 import org
.eclipse
.swt
.events
.MouseWheelListener
;
32 import org
.eclipse
.swt
.events
.PaintEvent
;
33 import org
.eclipse
.swt
.events
.PaintListener
;
34 import org
.eclipse
.swt
.events
.SelectionAdapter
;
35 import org
.eclipse
.swt
.events
.SelectionEvent
;
36 import org
.eclipse
.swt
.events
.SelectionListener
;
37 import org
.eclipse
.swt
.graphics
.Point
;
38 import org
.eclipse
.swt
.graphics
.Rectangle
;
39 import org
.eclipse
.swt
.layout
.FillLayout
;
40 import org
.eclipse
.swt
.layout
.GridData
;
41 import org
.eclipse
.swt
.layout
.GridLayout
;
42 import org
.eclipse
.swt
.widgets
.Composite
;
43 import org
.eclipse
.swt
.widgets
.Control
;
44 import org
.eclipse
.swt
.widgets
.Display
;
45 import org
.eclipse
.swt
.widgets
.Event
;
46 import org
.eclipse
.swt
.widgets
.Label
;
47 import org
.eclipse
.swt
.widgets
.Listener
;
48 import org
.eclipse
.swt
.widgets
.Menu
;
49 import org
.eclipse
.swt
.widgets
.Shell
;
50 import org
.eclipse
.swt
.widgets
.Slider
;
51 import org
.eclipse
.swt
.widgets
.Table
;
52 import org
.eclipse
.swt
.widgets
.TableColumn
;
53 import org
.eclipse
.swt
.widgets
.TableItem
;
54 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.Activator
;
55 import org
.eclipse
.ui
.PlatformUI
;
58 * <b><u>TmfVirtualTable</u></b>
60 * TmfVirtualTable allows for the tabular display of arbitrarily large data sets
61 * (well, up to Integer.MAX_VALUE or ~2G rows).
63 * It is essentially a Composite of Table and Slider, where the number of rows
64 * in the table is set to fill the table display area. The slider is rank-based.
66 * It differs from Table with the VIRTUAL style flag where an empty entry is
67 * created for each virtual row. This does not scale well for very large data sets.
70 * H_SCROLL, V_SCROLL, SINGLE, MULTI, CHECK, FULL_SELECTION, HIDE_SELECTION, NO_SCROLL
71 * @author Matthew Khouzam, Francois Chouinard, Patrick Tasse, Xavier Raynaud
72 * @version $Revision: 1.0
74 public class TmfVirtualTable
extends Composite
{
78 private int fTableRows
= 0; // Number of table rows
79 private int fFullyVisibleRows
= 0; // Number of fully visible table rows
80 private int fFrozenRowCount
= 0; // Number of frozen table rows at top of table
82 private int fTableTopEventRank
= 0; // Global rank of the first entry displayed
83 private int fSelectedEventRank
= -1; // Global rank of the selected event
84 private int fSelectedBeginRank
= -1; // Global rank of the selected begin event
85 private boolean fPendingSelection
= false; // Pending selection update
87 private int fTableItemCount
= 0;
90 private Slider fSlider
;
91 private SliderThrottler fSliderThrottler
;
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 // ------------------------------------------------------------------------
104 private class SliderThrottler
extends Thread
{
105 private static final long DELAY
= 400L;
106 private static final long POLLING_INTERVAL
= 10L;
110 final long startTime
= System
.currentTimeMillis();
111 while ((System
.currentTimeMillis() - startTime
) < DELAY
) {
113 Thread
.sleep(POLLING_INTERVAL
);
114 } catch (InterruptedException e
) {
117 Display
.getDefault().asyncExec(new Runnable() {
120 if (fSliderThrottler
!= SliderThrottler
.this) {
123 fSliderThrottler
= null;
124 if (SliderThrottler
.this.isInterrupted() || fTable
.isDisposed()) {
133 // ------------------------------------------------------------------------
135 // ------------------------------------------------------------------------
138 * Standard constructor
141 * The parent composite object
145 public TmfVirtualTable(Composite parent
, int style
) {
146 super(parent
, style
& (~SWT
.H_SCROLL
) & (~SWT
.V_SCROLL
) & (~SWT
.SINGLE
) & (~SWT
.MULTI
) & (~SWT
.FULL_SELECTION
) & (~SWT
.HIDE_SELECTION
) & (~SWT
.CHECK
));
148 // Create the controls
149 createTable(style
& (SWT
.H_SCROLL
| SWT
.SINGLE
| SWT
.MULTI
| SWT
.FULL_SELECTION
| SWT
.HIDE_SELECTION
| SWT
.CHECK
));
150 createSlider(style
& SWT
.V_SCROLL
);
152 // Prevent the slider from being traversed
153 setTabList(new Control
[] { fTable
});
156 GridLayout gridLayout
= new GridLayout();
157 gridLayout
.numColumns
= 2;
158 gridLayout
.horizontalSpacing
= 0;
159 gridLayout
.verticalSpacing
= 0;
160 gridLayout
.marginWidth
= 0;
161 gridLayout
.marginHeight
= 0;
162 setLayout(gridLayout
);
164 GridData tableGridData
= new GridData(SWT
.FILL
, SWT
.FILL
, true, true);
165 fTable
.setLayoutData(tableGridData
);
167 GridData sliderGridData
= new GridData(SWT
.FILL
, SWT
.FILL
, false, true);
168 fSlider
.setLayoutData(sliderGridData
);
171 fTable
.addMouseWheelListener(new MouseWheelListener() {
173 public void mouseScrolled(MouseEvent event
) {
174 if (fTableItemCount
<= fFullyVisibleRows
|| event
.count
== 0) {
177 fTableTopEventRank
-= event
.count
;
178 if (fTableTopEventRank
< 0) {
179 fTableTopEventRank
= 0;
181 int latestFirstRowOffset
= fTableItemCount
- fFullyVisibleRows
;
182 if (fTableTopEventRank
> latestFirstRowOffset
) {
183 fTableTopEventRank
= latestFirstRowOffset
;
186 fSlider
.setSelection(fTableTopEventRank
);
191 fTable
.addListener(SWT
.MouseWheel
, new Listener() {
192 // disable mouse scroll of horizontal scroll bar
194 public void handleEvent(Event event
) {
199 fResizeListener
= new ControlAdapter() {
201 public void controlResized(ControlEvent event
) {
202 int tableHeight
= Math
.max(0, fTable
.getClientArea().height
- fTable
.getHeaderHeight());
203 fFullyVisibleRows
= tableHeight
/ getItemHeight();
204 if (fTableItemCount
> 0) {
205 fSlider
.setThumb(Math
.max(1, Math
.min(fTableRows
, fFullyVisibleRows
)));
209 fTable
.addControlListener(fResizeListener
);
211 // Implement a "fake" tooltip
212 final String TOOLTIP_DATA_KEY
= "_TABLEITEM"; //$NON-NLS-1$
213 final Listener labelListener
= new Listener() {
215 public void handleEvent (Event event
) {
216 Label label
= (Label
) event
.widget
;
217 Shell shell
= label
.getShell();
218 switch (event
.type
) {
220 Event e
= new Event();
221 e
.item
= (TableItem
) label
.getData(TOOLTIP_DATA_KEY
);
222 // Assuming table is single select, set the selection as if
223 // the mouse down event went through to the table
224 fTable
.setSelection(new TableItem
[] {(TableItem
) e
.item
});
225 fTable
.notifyListeners(SWT
.Selection
, e
);
239 Listener tableListener
= new Listener() {
243 public void handleEvent(Event event
) {
244 switch (event
.type
) {
247 case SWT
.MouseMove
: {
256 case SWT
.MouseHover
: {
257 TableItem item
= fTable
.getItem(new Point(event
.x
, event
.y
));
259 for (int i
= 0; i
< fTable
.getColumnCount(); i
++) {
260 Rectangle bounds
= item
.getBounds(i
);
261 if (bounds
.contains(event
.x
, event
.y
)) {
262 if (tip
!= null && !tip
.isDisposed()) {
265 if (tooltipProvider
== null) {
268 String tooltipText
= tooltipProvider
.getTooltip(i
, item
.getData());
269 if (tooltipText
== null) {
272 tip
= new Shell(fTable
.getShell(), SWT
.ON_TOP
| SWT
.NO_FOCUS
| SWT
.TOOL
);
273 tip
.setBackground(PlatformUI
.getWorkbench().getDisplay().getSystemColor(SWT
.COLOR_INFO_BACKGROUND
));
274 FillLayout layout
= new FillLayout();
275 layout
.marginWidth
= 2;
276 tip
.setLayout(layout
);
277 label
= new Label(tip
, SWT
.WRAP
);
278 label
.setForeground(PlatformUI
.getWorkbench().getDisplay().getSystemColor(SWT
.COLOR_INFO_FOREGROUND
));
279 label
.setBackground(PlatformUI
.getWorkbench().getDisplay().getSystemColor(SWT
.COLOR_INFO_BACKGROUND
));
280 label
.setData(TOOLTIP_DATA_KEY
, item
);
281 label
.setText(tooltipText
);
283 label
.addListener(SWT
.MouseExit
, labelListener
);
284 label
.addListener(SWT
.MouseDown
, labelListener
);
285 label
.addListener(SWT
.MouseWheel
, labelListener
);
286 Point size
= tip
.computeSize(SWT
.DEFAULT
, SWT
.DEFAULT
);
287 Point pt
= fTable
.toDisplay(bounds
.x
, bounds
.y
);
288 tip
.setBounds(pt
.x
, pt
.y
, size
.x
, size
.y
);
289 tip
.setVisible(true);
291 // Item found, leave loop.
303 fTable
.addListener(SWT
.Dispose
, tableListener
);
304 fTable
.addListener(SWT
.KeyDown
, tableListener
);
305 fTable
.addListener(SWT
.MouseMove
, tableListener
);
306 fTable
.addListener(SWT
.MouseHover
, tableListener
);
307 addControlListener(new ControlAdapter() {
309 public void controlResized(ControlEvent event
) {
311 if (fTableItemCount
> 0) {
312 fSlider
.setThumb(Math
.max(1, Math
.min(fTableRows
, fFullyVisibleRows
)));
321 // ------------------------------------------------------------------------
323 // ------------------------------------------------------------------------
326 * Create the table and add listeners
327 * @param style int can be H_SCROLL, SINGLE, MULTI, FULL_SELECTION, HIDE_SELECTION, CHECK
329 private void createTable(int style
) {
330 fTable
= new Table(this, style
| SWT
.NO_SCROLL
);
332 fTable
.addSelectionListener(new SelectionAdapter() {
334 public void widgetSelected(SelectionEvent event
) {
335 if (event
.item
== null) {
336 // Override table selection from Select All action
342 fTable
.addMouseListener(new MouseAdapter() {
344 public void mouseDown(MouseEvent e
) {
345 handleTableMouseEvent(e
);
349 fTable
.addKeyListener(new KeyAdapter() {
351 public void keyPressed(KeyEvent event
) {
352 handleTableKeyEvent(event
);
357 SWT
.MouseDoubleClick
, new Listener() {
359 public void handleEvent(Event event
) {
360 if (doubleClickListener
!= null) {
361 TableItem item
= fTable
.getItem(new Point (event
.x
, event
.y
));
363 for (int i
= 0; i
< fTable
.getColumnCount(); i
++){
364 Rectangle bounds
= item
.getBounds(i
);
365 if (bounds
.contains(event
.x
, event
.y
)){
366 doubleClickListener
.handleDoubleClick(TmfVirtualTable
.this, item
, i
);
377 * Feature in Windows. When a partially visible table item is selected,
378 * after ~500 ms the top index is changed to ensure the selected item is
379 * fully visible. This leaves a blank space at the bottom of the virtual
380 * table. The workaround is to reset the top index to 0 if it is not 0.
381 * Also reset the top index to 0 if indicated by the flag that was set
382 * at table selection of a partially visible table item.
384 fTable
.addPaintListener(new PaintListener() {
386 public void paintControl(PaintEvent e
) {
387 if (fTable
.getTopIndex() != 0 || fResetTopIndex
) {
388 fTable
.setTopIndex(0);
390 fResetTopIndex
= false;
396 * Handle mouse-based selection in table.
398 * @param event the mouse event
400 private void handleTableMouseEvent(MouseEvent event
) {
401 TableItem item
= fTable
.getItem(new Point(event
.x
, event
.y
));
405 int selectedRow
= indexOf(item
);
406 if (event
.button
== 1 || (event
.button
== 3 &&
407 (selectedRow
< Math
.min(fSelectedBeginRank
, fSelectedEventRank
) ||
408 selectedRow
> Math
.max(fSelectedBeginRank
, fSelectedEventRank
)))) {
409 if (selectedRow
>= 0) {
410 fSelectedEventRank
= selectedRow
;
412 fSelectedEventRank
= -1;
414 if ((event
.stateMask
& SWT
.SHIFT
) == 0 || (fTable
.getStyle() & SWT
.MULTI
) == 0 || fSelectedBeginRank
== -1) {
415 fSelectedBeginRank
= fSelectedEventRank
;
421 * Feature in Linux. When a partially visible table item is selected,
422 * the origin is changed to ensure the selected item is fully visible.
423 * This makes the first row partially visible. The solution is to force
424 * reset the origin by setting the top index to 0. This should happen
425 * only once at the next redraw by the paint listener.
427 if (selectedRow
>= fFullyVisibleRows
) {
428 fResetTopIndex
= true;
433 * Handle key-based navigation in table.
435 * @param event the key event
437 private void handleTableKeyEvent(KeyEvent event
) {
439 int lastEventRank
= fTableItemCount
- 1;
440 int lastPageTopEntryRank
= Math
.max(0, fTableItemCount
- fFullyVisibleRows
);
442 int previousSelectedEventRank
= fSelectedEventRank
;
443 int previousSelectedBeginRank
= fSelectedBeginRank
;
444 boolean needsRefresh
= false;
446 // In all case, perform the following steps:
447 // - Update the selected entry rank (within valid range)
448 // - Update the selected row
449 // - Update the page's top entry if necessary (which also adjusts the selected row)
450 // - If the top displayed entry was changed, table refresh is needed
451 switch (event
.keyCode
) {
453 case SWT
.ARROW_DOWN
: {
455 if (fSelectedEventRank
< lastEventRank
) {
456 fSelectedEventRank
++;
457 int selectedRow
= fSelectedEventRank
- fTableTopEventRank
;
458 if (selectedRow
== fFullyVisibleRows
) {
459 fTableTopEventRank
++;
461 } else if (selectedRow
< fFrozenRowCount
|| selectedRow
> fFullyVisibleRows
) {
462 fTableTopEventRank
= Math
.max(0, Math
.min(fSelectedEventRank
- fFrozenRowCount
, lastPageTopEntryRank
));
471 if (fSelectedEventRank
> 0) {
472 fSelectedEventRank
--;
473 int selectedRow
= fSelectedEventRank
- fTableTopEventRank
;
474 if (selectedRow
== fFrozenRowCount
- 1 && fTableTopEventRank
> 0) {
475 fTableTopEventRank
--;
477 } else if (selectedRow
< fFrozenRowCount
|| selectedRow
> fFullyVisibleRows
) {
478 fTableTopEventRank
= Math
.max(0, Math
.min(fSelectedEventRank
- fFrozenRowCount
, lastPageTopEntryRank
));
487 fTableTopEventRank
= lastPageTopEntryRank
;
488 fSelectedEventRank
= lastEventRank
;
495 fSelectedEventRank
= fFrozenRowCount
;
496 fTableTopEventRank
= 0;
501 case SWT
.PAGE_DOWN
: {
503 if (fSelectedEventRank
< lastEventRank
) {
504 fSelectedEventRank
+= fFullyVisibleRows
;
505 if (fSelectedEventRank
> lastEventRank
) {
506 fSelectedEventRank
= lastEventRank
;
508 int selectedRow
= fSelectedEventRank
- fTableTopEventRank
;
509 if (selectedRow
> fFullyVisibleRows
+ fFrozenRowCount
- 1 && selectedRow
< 2 * fFullyVisibleRows
) {
510 fTableTopEventRank
+= fFullyVisibleRows
;
511 if (fTableTopEventRank
> lastPageTopEntryRank
) {
512 fTableTopEventRank
= lastPageTopEntryRank
;
515 } else if (selectedRow
< fFrozenRowCount
|| selectedRow
>= 2 * fFullyVisibleRows
) {
516 fTableTopEventRank
= Math
.max(0, Math
.min(fSelectedEventRank
- fFrozenRowCount
, lastPageTopEntryRank
));
525 if (fSelectedEventRank
> 0) {
526 fSelectedEventRank
-= fFullyVisibleRows
;
527 if (fSelectedEventRank
< fFrozenRowCount
) {
528 fSelectedEventRank
= fFrozenRowCount
;
530 int selectedRow
= fSelectedEventRank
- fTableTopEventRank
;
531 if (selectedRow
< fFrozenRowCount
&& selectedRow
> -fFullyVisibleRows
) {
532 fTableTopEventRank
-= fFullyVisibleRows
;
533 if (fTableTopEventRank
< 0) {
534 fTableTopEventRank
= 0;
537 } else if (selectedRow
<= -fFullyVisibleRows
|| selectedRow
>= fFullyVisibleRows
) {
538 fTableTopEventRank
= Math
.max(0, Math
.min(fSelectedEventRank
- fFrozenRowCount
, lastPageTopEntryRank
));
549 if ((event
.stateMask
& SWT
.SHIFT
) == 0 || (fTable
.getStyle() & SWT
.MULTI
) == 0 || fSelectedBeginRank
== -1) {
550 fSelectedBeginRank
= fSelectedEventRank
;
555 done
= refreshTable(); // false if table items not updated yet in this thread
560 if (fFullyVisibleRows
< fTableItemCount
) {
561 fSlider
.setSelection(fTableTopEventRank
);
564 if (fSelectedEventRank
!= previousSelectedEventRank
|| fSelectedBeginRank
!= previousSelectedBeginRank
) {
566 Event e
= new Event();
567 e
.item
= fTable
.getItem(fSelectedEventRank
- fTableTopEventRank
);
568 fTable
.notifyListeners(SWT
.Selection
, e
);
570 fPendingSelection
= true;
576 * Method setDataItem.
578 * @param item TableItem
581 private boolean setDataItem(int index
, TableItem item
) {
583 Event event
= new Event();
585 if (index
< fFrozenRowCount
) {
588 event
.index
= index
+ fTableTopEventRank
;
591 fTable
.notifyListeners(SWT
.SetData
, event
);
592 return event
.doit
; // false if table item not updated yet in this thread
597 // ------------------------------------------------------------------------
599 // ------------------------------------------------------------------------
602 * Method createSlider.
605 private void createSlider(int style
) {
606 fSlider
= new Slider(this, SWT
.VERTICAL
| SWT
.NO_FOCUS
);
607 fSlider
.setMinimum(0);
608 fSlider
.setMaximum(0);
609 if ((style
& SWT
.V_SCROLL
) == 0) {
610 fSlider
.setVisible(false);
613 fSlider
.addListener(SWT
.Selection
, new Listener() {
615 public void handleEvent(Event event
) {
616 switch (event
.detail
) {
623 fTableTopEventRank
= fSlider
.getSelection();
630 * While the slider thumb is being dragged, only perform the
631 * refresh periodically. The event detail during the drag is
632 * SWT.DRAG on Windows and SWT.NONE on Linux.
634 fTableTopEventRank
= fSlider
.getSelection();
635 if (fSliderThrottler
== null) {
636 fSliderThrottler
= new SliderThrottler();
637 fSliderThrottler
.start();
647 * When the mouse button is released, perform the refresh immediately
648 * and interrupt and discard the slider throttler.
650 fSlider
.addMouseListener(new MouseAdapter() {
652 public void mouseUp(MouseEvent e
) {
653 if (fSliderThrottler
!= null) {
654 fSliderThrottler
.interrupt();
655 fSliderThrottler
= null;
657 fTableTopEventRank
= fSlider
.getSelection();
663 * The SWT.NO_FOCUS style is only a hint and is not always respected. If
664 * the slider gains focus, give it back to the table.
666 fSlider
.addFocusListener(new FocusAdapter() {
668 public void focusGained(FocusEvent e
) {
674 // ------------------------------------------------------------------------
675 // Simulated Table API
676 // ------------------------------------------------------------------------
679 * Constructs a new TableColumn instance given a style value describing its
680 * alignment behavior. The column is added to the end of the columns
681 * maintained by the table.
684 * the alignment style
685 * @return the new TableColumn
691 public TableColumn
newTableColumn(int style
) {
692 TableColumn column
= new TableColumn(fTable
, style
);
695 * In Linux the table does not receive a control resized event when
696 * a table column resize causes the horizontal scroll bar to become
697 * visible or invisible, so a resize listener must be added to every
698 * table column to properly update the number of fully visible rows.
700 column
.addControlListener(fResizeListener
);
706 * Method setHeaderVisible.
709 public void setHeaderVisible(boolean b
) {
710 fTable
.setHeaderVisible(b
);
714 * Method setLinesVisible.
717 public void setLinesVisible(boolean b
) {
718 fTable
.setLinesVisible(b
);
722 * Returns an array of <code>TableItem</code>s that are currently selected
723 * in the receiver. The order of the items is unspecified. An empty array
724 * indicates that no items are selected.
726 * Note: This array only contains the visible selected items in the virtual
727 * table. To get information about the full selection range, use
728 * {@link #getSelectionIndices()}.
731 * @return an array representing the selection
733 * @exception SWTException <ul>
734 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
735 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
738 public TableItem
[] getSelection() {
739 return fTable
.getSelection();
743 * Method addListener.
744 * @param eventType int
745 * @param listener Listener
748 public void addListener(int eventType
, Listener listener
) {
749 fTable
.addListener(eventType
, listener
);
753 * Method addKeyListener.
754 * @param listener KeyListener
757 public void addKeyListener(KeyListener listener
) {
758 fTable
.addKeyListener(listener
);
762 * Method addMouseListener.
763 * @param listener MouseListener
766 public void addMouseListener(MouseListener listener
) {
767 fTable
.addMouseListener(listener
);
771 * Method addSelectionListener.
772 * @param listener SelectionListener
774 public void addSelectionListener(SelectionListener listener
) {
775 fTable
.addSelectionListener(listener
);
779 * Method setMenu sets the menu
780 * @param menu Menu the menu
783 public void setMenu(Menu menu
) {
784 fTable
.setMenu(menu
);
788 * Gets the menu of this table
792 public Menu
getMenu() {
793 return fTable
.getMenu();
797 * Method clearAll empties a table.
799 public void clearAll() {
804 * Method setItemCount
805 * @param nbItems int the number of items in the table
808 public void setItemCount(int nbItems
) {
809 final int nb
= Math
.max(0, nbItems
);
811 if (nb
!= fTableItemCount
) {
812 fTableItemCount
= nb
;
813 fTable
.remove(fTableItemCount
, fTable
.getItemCount() - 1);
814 fSlider
.setMaximum(nb
);
816 int tableHeight
= Math
.max(0, fTable
.getClientArea().height
- fTable
.getHeaderHeight());
817 fFullyVisibleRows
= tableHeight
/ getItemHeight();
818 if (fTableItemCount
> 0) {
819 fSlider
.setThumb(Math
.max(1, Math
.min(fTableRows
, fFullyVisibleRows
)));
825 * Method getItemCount.
826 * @return int the number of items in the table
828 public int getItemCount() {
829 return fTableItemCount
;
833 * Method getItemHeight.
834 * @return int the height of a table item in pixels. (may vary from one os to another)
836 public int getItemHeight() {
838 * Bug in Linux. The method getItemHeight doesn't always return the correct value.
840 if (fLinuxItemHeight
>= 0 && System
.getProperty("os.name").contains("Linux")) { //$NON-NLS-1$ //$NON-NLS-2$
841 if (fLinuxItemHeight
!= 0) {
842 return fLinuxItemHeight
;
844 if (fTable
.getItemCount() > 1) {
845 int itemHeight
= fTable
.getItem(1).getBounds().y
- fTable
.getItem(0).getBounds().y
;
846 if (itemHeight
> 0) {
847 fLinuxItemHeight
= itemHeight
;
848 return fLinuxItemHeight
;
852 fLinuxItemHeight
= -1; // Not Linux, don't perform os.name check anymore
854 return fTable
.getItemHeight();
858 * Method getHeaderHeight.
859 * @return int get the height of the header in pixels.
861 public int getHeaderHeight() {
862 return fTable
.getHeaderHeight();
866 * Method getTopIndex.
867 * @return int get the first data item index, if you have a header it is offset.
869 public int getTopIndex() {
870 return fTableTopEventRank
+ fFrozenRowCount
;
874 * Method setTopIndex.
875 * @param index int suggested top index for the table.
877 public void setTopIndex(int index
) {
878 if (fTableItemCount
> 0) {
879 int i
= Math
.min(index
, fTableItemCount
- 1);
880 i
= Math
.max(i
, fFrozenRowCount
);
882 fTableTopEventRank
= i
- fFrozenRowCount
;
883 if (fFullyVisibleRows
< fTableItemCount
) {
884 fSlider
.setSelection(fTableTopEventRank
);
892 * Method indexOf. Return the index of a table item
893 * @param ti TableItem the table item to search for in the table
894 * @return int the index of the first match. (there should only be one match)
896 public int indexOf(TableItem ti
) {
897 int index
= fTable
.indexOf(ti
);
898 if (index
< fFrozenRowCount
) {
901 return (index
- fFrozenRowCount
) + getTopIndex();
906 * @return TableColumn[] the table columns
908 public TableColumn
[] getColumns() {
909 return fTable
.getColumns();
913 * Returns an array of zero-relative integers that map
914 * the creation order of the receiver's columns to the
915 * order in which they are currently being displayed.
917 * Specifically, the indices of the returned array represent
918 * the current visual order of the columns, and the contents
919 * of the array represent the creation order of the columns.
921 * @return the current visual order of the receiver's columns
923 public int[] getColumnOrder() {
924 return fTable
.getColumnOrder();
928 * Sets the order that the columns in the receiver should
929 * be displayed in to the given argument which is described
930 * in terms of the zero-relative ordering of when the columns
933 * Specifically, the contents of the array represent the
934 * original position of each column at the time its creation.
936 * @param order the new order to display the columns
938 public void setColumnOrder(int[] order
) {
939 fTable
.setColumnOrder(order
);
944 * @param point Point the coordinates in the table
945 * @return TableItem the corresponding table item
947 public TableItem
getItem(Point point
) {
948 return fTable
.getItem(point
);
954 private void resize() {
955 // Compute the numbers of rows that fit the new area
956 int tableHeight
= Math
.max(0, getSize().y
- fTable
.getHeaderHeight());
957 int itemHeight
= getItemHeight();
958 fTableRows
= Math
.min((tableHeight
+ itemHeight
- 1) / itemHeight
, fTableItemCount
);
960 if (fTableTopEventRank
+ fFullyVisibleRows
> fTableItemCount
) {
961 // If we are at the end, get elements before to populate
962 fTableTopEventRank
= Math
.max(0, fTableItemCount
- fFullyVisibleRows
);
964 } else if (fTableRows
> fTable
.getItemCount() || fTableItemCount
< fTable
.getItemCount()) {
965 // Only refresh if new table items are needed or if table items need to be deleted
971 // ------------------------------------------------------------------------
972 // Controls interactions
973 // ------------------------------------------------------------------------
977 * @return boolean is this visible?
980 public boolean setFocus() {
981 boolean isVisible
= isVisible();
991 public void refresh() {
992 boolean done
= refreshTable();
996 if (fPendingSelection
) {
997 fPendingSelection
= false;
998 TableItem item
= null;
999 if (fSelectedEventRank
>= 0 && fSelectedEventRank
< fFrozenRowCount
) {
1000 item
= fTable
.getItem(fSelectedEventRank
);
1001 } else if (fSelectedEventRank
>= fTableTopEventRank
+ fFrozenRowCount
&& fSelectedEventRank
- fTableTopEventRank
< fTable
.getItemCount()) {
1002 item
= fTable
.getItem(fSelectedEventRank
- fTableTopEventRank
);
1005 Event e
= new Event();
1007 fTable
.notifyListeners(SWT
.Selection
, e
);
1013 * Method setColumnHeaders.
1016 * ColumnData[] the columndata array.
1019 public void setColumnHeaders(ColumnData columnData
[]) {
1025 * @return int 0 the number of elements in the table
1027 public int removeAll() {
1029 fSlider
.setMaximum(0);
1031 fSelectedEventRank
= -1;
1032 fSelectedBeginRank
= fSelectedEventRank
;
1037 * Method refreshTable.
1038 * @return true if all table items have been refreshed, false otherwise
1040 private boolean refreshTable() {
1041 boolean done
= true;
1042 for (int i
= 0; i
< fTableRows
; i
++) {
1043 if (i
+ fTableTopEventRank
< fTableItemCount
) {
1044 TableItem tableItem
;
1045 if (i
< fTable
.getItemCount()) {
1046 tableItem
= fTable
.getItem(i
);
1048 tableItem
= new TableItem(fTable
, SWT
.NONE
);
1050 done
&= setDataItem(i
, tableItem
); // false if table item not updated yet in this thread
1052 if (fTable
.getItemCount() > fTableItemCount
- fTableTopEventRank
) {
1053 fTable
.remove(fTableItemCount
- fTableTopEventRank
);
1060 fTable
.deselectAll();
1065 private void refreshSelection() {
1066 int lastRowOffset
= fTableTopEventRank
+ fTableRows
- 1;
1067 int startRank
= Math
.min(fSelectedBeginRank
, fSelectedEventRank
);
1068 int endRank
= Math
.max(fSelectedBeginRank
, fSelectedEventRank
);
1069 int start
= Integer
.MAX_VALUE
;
1070 int end
= Integer
.MIN_VALUE
;
1071 if (startRank
< fFrozenRowCount
) {
1073 } else if (startRank
< fTableTopEventRank
+ fFrozenRowCount
) {
1074 start
= fFrozenRowCount
;
1075 } else if (startRank
<= lastRowOffset
) {
1076 start
= startRank
- fTableTopEventRank
;
1078 if (endRank
< fFrozenRowCount
) {
1080 } else if (endRank
< fTableTopEventRank
+ fFrozenRowCount
) {
1081 end
= fFrozenRowCount
- 1;
1082 } else if (endRank
<= lastRowOffset
) {
1083 end
= endRank
- fTableTopEventRank
;
1085 end
= fTableRows
- 1;
1088 fTable
.setSelection(start
, end
);
1089 /* Reset origin in case partially visible item selected */
1090 fTable
.setTopIndex(0);
1091 if (startRank
== fSelectedEventRank
) {
1092 fTable
.select(start
);
1098 * In GTK2, when the table is given focus, one table item is
1099 * highlighted even if there is no selection. In that case the
1100 * highlighted item is the last selected item. Make that last
1101 * selected item the top or bottom item depending on if the
1102 * out-of-range selection is above or below the visible items.
1104 if (SWT
.getPlatform().equals("gtk") && fTableRows
> 0) { //$NON-NLS-1$
1105 fTable
.setRedraw(false);
1106 if (start
< Integer
.MAX_VALUE
) {
1107 fTable
.setSelection(0);
1109 fTable
.setSelection(fTableRows
- 1);
1110 fTable
.setTopIndex(0);
1112 fTable
.setRedraw(true);
1114 fTable
.deselectAll();
1119 * Selects the item at the given zero-relative index in the receiver.
1120 * The current selection is first cleared, then the new item is selected,
1121 * and if necessary the receiver is scrolled to make the new selection visible.
1123 * @param index the index of the item to select
1125 * @exception SWTException <ul>
1126 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1127 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1130 public void setSelection(int index
) {
1131 setSelectionRange(index
, index
);
1135 * Selects a range of items starting at the given zero-relative index in the
1136 * receiver. The current selection is first cleared, then the new items are
1137 * selected, and if necessary the receiver is scrolled to make the new
1138 * selection visible.
1141 * The index of the first item in the selection range
1143 * The index of the last item in the selection range
1145 * @exception SWTException
1147 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been
1149 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
1150 * thread that created the receiver</li>
1154 public void setSelectionRange(int beginIndex
, int endIndex
) {
1155 if (fTableItemCount
> 0) {
1156 int begin
= Math
.max(Math
.min(beginIndex
, fTableItemCount
- 1), 0);
1157 int end
= Math
.max(Math
.min(endIndex
, fTableItemCount
- 1), 0);
1158 int selection
= fSelectedBeginRank
!= begin ? begin
: end
;
1160 fSelectedBeginRank
= begin
;
1161 fSelectedEventRank
= end
;
1162 if ((selection
< fTableTopEventRank
+ fFrozenRowCount
&& selection
>= fFrozenRowCount
) ||
1163 (selection
>= fTableTopEventRank
+ fFullyVisibleRows
)) {
1164 int lastPageTopEntryRank
= Math
.max(0, fTableItemCount
- fFullyVisibleRows
);
1165 fTableTopEventRank
= Math
.max(0, Math
.min(lastPageTopEntryRank
, selection
- fFrozenRowCount
- fFullyVisibleRows
/ 2));
1167 if (fFullyVisibleRows
< fTableItemCount
) {
1168 fSlider
.setSelection(fTableTopEventRank
);
1175 * Returns the zero-relative index of the item which is currently
1176 * selected in the receiver, or -1 if no item is selected.
1178 * @return the index of the selected item
1180 public int getSelectionIndex() {
1181 return fSelectedEventRank
;
1185 * Returns an index array representing the selection range. If there is a
1186 * single item selected the array holds one index. If there is a selected
1187 * range the first item in the array is the start index of the selection and
1188 * the second item is the end index of the selection, which is the item most
1189 * recently selected. The array is empty if no items are selected.
1191 * @return the array of indices of the selected items
1193 public int[] getSelectionIndices() {
1194 if (fSelectedEventRank
< 0 || fSelectedBeginRank
< 0) {
1195 return new int[] {};
1196 } else if (fSelectedEventRank
== fSelectedBeginRank
) {
1197 return new int[] { fSelectedEventRank
};
1199 return new int[] { fSelectedBeginRank
, fSelectedEventRank
};
1203 * Method setFrozenRowCount.
1204 * @param count int the number of rows to freeze from the top row
1206 public void setFrozenRowCount(int count
) {
1207 fFrozenRowCount
= count
;
1212 * Method createTableEditor.
1213 * @return a TableEditor of the table
1215 public TableEditor
createTableEditor() {
1216 return new TableEditor(fTable
);
1220 * Method createTableEditorControl.
1221 * @param control Class<? extends Control>
1224 public Control
createTableEditorControl(Class
<?
extends Control
> control
) {
1226 return control
.getConstructor(Composite
.class, int.class).newInstance(new Object
[] {fTable
, SWT
.NONE
});
1227 } catch (Exception e
) {
1228 Activator
.getDefault().logError("Error creating table editor control", e
); //$NON-NLS-1$
1234 * @return the tooltipProvider
1236 public TooltipProvider
getTooltipProvider() {
1237 return tooltipProvider
;
1241 * @param tooltipProvider the tooltipProvider to set
1243 public void setTooltipProvider(TooltipProvider tooltipProvider
) {
1244 this.tooltipProvider
= tooltipProvider
;
1248 * @return the doubleClickListener
1250 public IDoubleClickListener
getDoubleClickListener() {
1251 return doubleClickListener
;
1255 * @param doubleClickListener the doubleClickListener to set
1257 public void setDoubleClickListener(IDoubleClickListener doubleClickListener
) {
1258 this.doubleClickListener
= doubleClickListener
;