1 /*******************************************************************************
2 * Copyright (c) 2010 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 ******************************************************************************/
14 package org
.eclipse
.linuxtools
.tmf
.ui
.widgets
;
16 import org
.eclipse
.linuxtools
.tmf
.event
.TmfTimestamp
;
17 import org
.eclipse
.linuxtools
.tmf
.signal
.TmfSignalManager
;
18 import org
.eclipse
.linuxtools
.tmf
.signal
.TmfTimeSynchSignal
;
19 import org
.eclipse
.swt
.SWT
;
20 import org
.eclipse
.swt
.events
.ControlAdapter
;
21 import org
.eclipse
.swt
.events
.ControlEvent
;
22 import org
.eclipse
.swt
.events
.KeyEvent
;
23 import org
.eclipse
.swt
.events
.KeyListener
;
24 import org
.eclipse
.swt
.events
.MouseEvent
;
25 import org
.eclipse
.swt
.events
.MouseWheelListener
;
26 import org
.eclipse
.swt
.events
.SelectionAdapter
;
27 import org
.eclipse
.swt
.events
.SelectionEvent
;
28 import org
.eclipse
.swt
.layout
.GridData
;
29 import org
.eclipse
.swt
.layout
.GridLayout
;
30 import org
.eclipse
.swt
.widgets
.Composite
;
31 import org
.eclipse
.swt
.widgets
.Event
;
32 import org
.eclipse
.swt
.widgets
.Listener
;
33 import org
.eclipse
.swt
.widgets
.Slider
;
34 import org
.eclipse
.swt
.widgets
.Table
;
35 import org
.eclipse
.swt
.widgets
.TableColumn
;
36 import org
.eclipse
.swt
.widgets
.TableItem
;
39 * <b><u>TmfVirtualTable</u></b>
41 * TmfVirtualTable allows for the tabular display of arbitrarily large data sets
42 * (well, up to Integer.MAX_VALUE or ~2G rows).
44 * It is essentially a Composite of Table and Slider, where the number of rows
45 * in the table is set to fill the table display area. The slider is rank-based.
47 * It differs from Table with the VIRTUAL style flag where an empty entry is
48 * created for each virtual row. This does not scale well for very large data sets.
50 public class TmfVirtualTable
extends Composite
{
54 private int fTableRows
= 0; // Number of table rows
55 private boolean fPartialRowVisible
= false; // Indicates that a row is partially displayed
56 private int fSelectedRow
= 0; // Currently selected row in the table
58 private int fTableTopEventRank
= 0; // Global rank of the first entry displayed
59 private int fSelectedEventRank
= 0; // Global rank of the selected event
61 private TableItem fSelectedItems
[] = new TableItem
[1];
62 private int fTableItemCount
= 0;
63 private TableItem fTableItems
[];
66 private Slider fSlider
;
68 // ------------------------------------------------------------------------
70 // ------------------------------------------------------------------------
76 public TmfVirtualTable(Composite parent
, int style
) {
77 super(parent
, style
| SWT
.BORDER
& (~SWT
.H_SCROLL
) & (~SWT
.V_SCROLL
));
79 // Create the controls
84 GridLayout gridLayout
= new GridLayout();
85 gridLayout
.numColumns
= 2;
86 gridLayout
.horizontalSpacing
= 0;
87 gridLayout
.verticalSpacing
= 0;
88 gridLayout
.marginWidth
= 0;
89 gridLayout
.marginHeight
= 0;
90 setLayout(gridLayout
);
92 GridData tableGridData
= new GridData(SWT
.FILL
, SWT
.FILL
, true, true);
93 fTable
.setLayoutData(tableGridData
);
95 GridData sliderGridData
= new GridData(SWT
.FILL
, SWT
.FILL
, false, true);
96 fSlider
.setLayoutData(sliderGridData
);
99 fTable
.addMouseWheelListener(new MouseWheelListener() {
101 public void mouseScrolled(MouseEvent event
) {
102 fTableTopEventRank
-= event
.count
;
103 if (fTableTopEventRank
< 0) {
104 fTableTopEventRank
= 0;
106 int latestFirstRowOffset
= fTableItemCount
- fTableRows
;
107 if (fTableTopEventRank
> latestFirstRowOffset
) {
108 fTableTopEventRank
= latestFirstRowOffset
;
111 fSlider
.setSelection(fTableTopEventRank
);
116 addControlListener(new ControlAdapter() {
118 public void controlResized(ControlEvent event
) {
127 // ------------------------------------------------------------------------
129 // ------------------------------------------------------------------------
132 * Create the table and add listeners
134 private void createTable(int style
) {
136 // int tableStyle = SWT.NO_SCROLL | SWT.H_SCROLL | SWT.SINGLE | SWT.FULL_SELECTION;
137 int tableStyle
= SWT
.NO_SCROLL
| SWT
.SINGLE
| SWT
.FULL_SELECTION
;
138 fTable
= new Table(this, tableStyle
);
140 fTable
.addSelectionListener(new SelectionAdapter() {
142 public void widgetSelected(SelectionEvent event
) {
143 handleTableSelection();
147 fTable
.addKeyListener(new KeyListener() {
149 public void keyPressed(KeyEvent event
) {
150 handleTableKeyEvent(event
);
153 public void keyReleased(KeyEvent event
) {
159 * Update the rows and selected item
161 private void handleTableSelection() {
162 fSelectedRow
= fTable
.getSelectionIndices()[0];
163 fSelectedEventRank
= fTableTopEventRank
+ fSelectedRow
;
164 fSelectedItems
[0] = fTable
.getSelection()[0];
168 * Handle key-based navigation in table.
172 private void handleTableKeyEvent(KeyEvent event
) {
174 int lastEventRank
= fTableItemCount
- 1;
175 int lastPageTopEntryRank
= fTableItemCount
- fTableRows
;
177 int lastRowIndex
= ((fTableItemCount
< fTableRows
) ? fTableItemCount
: fTableRows
) - 1;
178 int numberOfFullyVisibleRows
= fTableRows
- ((fPartialRowVisible
) ?
1 : 0);
180 boolean needsRefresh
= false;
182 // We are handling things...
185 // In all case, perform the following steps:
186 // - Update the selected entry rank (within valid range)
187 // - Update the selected row
188 // - Update the page's top entry if necessary (which also adjusts the selected row)
189 // - If the top displayed entry was changed, table refresh is needed
190 switch (event
.keyCode
) {
192 case SWT
.ARROW_DOWN
: {
193 if (fSelectedEventRank
< lastEventRank
) {
194 fSelectedEventRank
++;
195 fSelectedRow
= fSelectedEventRank
- fTableTopEventRank
;
196 if (fSelectedRow
> lastRowIndex
) {
197 fTableTopEventRank
++;
198 fSelectedRow
= lastRowIndex
;
206 if (fSelectedEventRank
> 0) {
207 fSelectedEventRank
--;
208 fSelectedRow
= fSelectedEventRank
- fTableTopEventRank
;
209 if (fSelectedRow
< 0) {
210 fTableTopEventRank
--;
219 fTableTopEventRank
= lastPageTopEntryRank
;
220 fSelectedEventRank
= lastEventRank
;
221 fSelectedRow
= lastRowIndex
;
227 fSelectedEventRank
= 0;
229 fTableTopEventRank
= 0;
234 case SWT
.PAGE_DOWN
: {
235 if (fSelectedEventRank
< lastEventRank
) {
236 fSelectedEventRank
+= numberOfFullyVisibleRows
;
237 if (fSelectedEventRank
> lastEventRank
) {
238 fSelectedEventRank
= lastEventRank
;
240 fSelectedRow
= fSelectedEventRank
- fTableTopEventRank
;
241 if (fSelectedRow
> numberOfFullyVisibleRows
- 1) {
242 fTableTopEventRank
+= numberOfFullyVisibleRows
;
243 if (fTableTopEventRank
> lastPageTopEntryRank
) {
244 fTableTopEventRank
= lastPageTopEntryRank
;
246 fSelectedRow
= fSelectedEventRank
- fTableTopEventRank
;
254 if (fSelectedEventRank
> 0) {
255 fSelectedEventRank
-= numberOfFullyVisibleRows
;
256 if (fSelectedEventRank
< 0) {
257 fSelectedEventRank
= 0;
259 fSelectedRow
= fSelectedEventRank
- fTableTopEventRank
;
260 if (fSelectedRow
< 0) {
262 fTableTopEventRank
-= numberOfFullyVisibleRows
;
263 if (fTableTopEventRank
< 0) {
264 fTableTopEventRank
= 0;
266 fSelectedRow
= fSelectedEventRank
- fTableTopEventRank
;
275 for (int i
= 0; i
< fTableItems
.length
; i
++) {
276 setDataItem(i
, fTableItems
[i
]);
279 // Notify about changed selection
280 notifyUpdatedSelection();
283 private void setDataItem(int index
, TableItem item
) {
285 Event event
= new Event();
287 event
.index
= index
+ fTableTopEventRank
;
289 notifyListeners(SWT
.SetData
, event
);
294 * Updates the selection in the table and broadcasts a signal to notify all signal
295 * handlers about the updated selection.
297 public void notifyUpdatedSelection() {
298 notifyUpdatedSelection(null);
302 * Updates the selection in the table and broadcasts a signal to notify all signal
303 * handlers about the updated selection.
305 * @param broadcastTimestamp - timestamp to broadcast if other than from the table selection.
306 * use null to broadcast selected time
308 public void notifyUpdatedSelection(TmfTimestamp broadcastTimestamp
) {
309 fSlider
.setSelection(fTableTopEventRank
+ fSelectedRow
);
310 setSelectedRowVisibility();
311 TableItem
[] tableSelection
= fTable
.getSelection();
312 if (tableSelection
.length
> 0 && tableSelection
[0] != null) {
313 fSelectedItems
[0] = tableSelection
[0];
314 TmfTimestamp ts
= (broadcastTimestamp
!= null) ? broadcastTimestamp
: (TmfTimestamp
) fSelectedItems
[0].getData();
315 TmfSignalManager
.dispatchSignal(new TmfTimeSynchSignal(this, ts
));
319 // ------------------------------------------------------------------------
321 // ------------------------------------------------------------------------
323 private void createSlider() {
324 fSlider
= new Slider(this, SWT
.VERTICAL
);
325 fSlider
.setMinimum(0);
326 fSlider
.setMaximum(0);
328 fSlider
.addListener(SWT
.Selection
, new Listener() {
330 public void handleEvent(Event event
) {
331 switch (event
.detail
) {
339 fTableTopEventRank
= fSlider
.getSelection();
348 // ------------------------------------------------------------------------
349 // Simulated Table API
350 // ------------------------------------------------------------------------
352 public void setHeaderVisible(boolean b
) {
353 fTable
.setHeaderVisible(b
);
356 public void setLinesVisible(boolean b
) {
357 fTable
.setLinesVisible(b
);
360 public TableItem
[] getSelection() {
361 return fSelectedItems
;
364 public void addSelectionListener(SelectionAdapter sa
) {
365 fTable
.addSelectionListener(sa
);
368 public void setItemCount(int nbItems
) {
369 nbItems
= Math
.max(0, nbItems
);
370 if (nbItems
!= fTableItemCount
) {
371 fTableItemCount
= nbItems
;
372 fSlider
.setMaximum(nbItems
);
377 public int getItemHeight() {
378 return fTable
.getItemHeight();
381 public int getTopIndex() {
382 return fTableTopEventRank
;
385 public void setTopIndex(int i
) {
386 fSlider
.setSelection(i
);
389 public int indexOf(TableItem ti
) {
390 return fTable
.indexOf(ti
) + getTopIndex();
393 public TableColumn
[] getColumns() {
394 return fTable
.getColumns();
397 private void resize() {
399 // Compute the numbers of rows that fit the new area
400 int tableHeight
= fTable
.getClientArea().height
- fTable
.getHeaderHeight();
402 if (tableHeight
< 0) tableHeight
= 0;
403 int itemHeight
= fTable
.getItemHeight();
404 fTableRows
= tableHeight
/ itemHeight
;
405 fPartialRowVisible
= false;
406 if (fTableRows
* itemHeight
< tableHeight
) {
407 fTableRows
++; // For partial rows
408 fPartialRowVisible
= true;
410 if (fTableRows
> fTableItemCount
) {
411 fTableRows
= fTableItemCount
;
414 // If we are at the end, get elements before to populate
415 if (fTableTopEventRank
+ fTableRows
>= fTableItemCount
) {
416 fTableTopEventRank
= fTableItemCount
- fTableRows
;
419 // Set the slider thumb size
420 if (fTableItemCount
> 0) {
421 fSlider
.setThumb(fTableRows
);
424 // Re-size and re-create the virtual table if needed
425 int delta
= fTable
.getItemCount() - fTableRows
;
428 if (fTableItems
!= null) {
429 for (int i
= 0; i
< fTableItems
.length
; i
++) {
430 if (fTableItems
[i
] != null) {
431 fTableItems
[i
].dispose();
433 fTableItems
[i
] = null;
436 fTableItems
= new TableItem
[fTableRows
];
437 for (int i
= 0; i
< fTableItems
.length
; i
++) {
438 fTableItems
[i
] = new TableItem(fTable
, i
);
445 // ------------------------------------------------------------------------
446 // Controls interactions
447 // ------------------------------------------------------------------------
450 public boolean setFocus() {
451 boolean isVisible
= isVisible();
458 public void refresh() {
462 public void setColumnHeaders(ColumnData columnData
[]) {
463 for (int i
= 0; i
< columnData
.length
; i
++) {
464 TableColumn column
= new TableColumn(fTable
, columnData
[i
].alignment
, i
);
465 column
.setText(columnData
[i
].header
);
466 if (columnData
[i
].width
> 0) {
467 column
.setWidth(columnData
[i
].width
);
474 public int removeAll() {
475 fSlider
.setMaximum(0);
480 private void refreshTable() {
481 setSelectedRowVisibility();
483 for (int i
= 0; i
< fTableRows
; i
++) {
484 setDataItem(i
, fTableItems
[i
]);
488 private void setSelectedRowVisibility() {
489 int lastRowOffset
= fTableTopEventRank
+ fTableRows
- 1;
490 if ((fSelectedEventRank
>= fTableTopEventRank
) && (fSelectedEventRank
<= lastRowOffset
)) {
491 fSelectedRow
= fSelectedEventRank
- fTableTopEventRank
;
492 fTable
.setSelection(fSelectedRow
);
494 fTable
.deselect(fSelectedRow
);
498 public void setSelection(int i
) {
499 if (fTableItems
!= null) {
500 i
= Math
.min(i
, fTableItemCount
);
502 fSlider
.setSelection(i
);
504 fSelectedEventRank
= i
;
506 // Check if enough events are available to display selected event
507 // in the middle of table
508 if (i
< (fTableItemCount
- fTableRows
/2)) {
509 fTableTopEventRank
= i
- (fTableRows
/ 2);
511 fTableTopEventRank
= fTableItemCount
- fTableRows
;
515 if (fTableTopEventRank
< 0) {
516 fTableTopEventRank
= 0;
518 fSelectedRow
= fSelectedEventRank
- fTableTopEventRank
;