2010-12-15 Francois Chouinard <fchouinard@gmail.com> Fix for Bug332590
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.ui / src / org / eclipse / linuxtools / tmf / ui / widgets / TmfVirtualTable.java
CommitLineData
9ccc6d01
FC
1/*******************************************************************************
2 * Copyright (c) 2010 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 ******************************************************************************/
13
14package org.eclipse.linuxtools.tmf.ui.widgets;
15
2cc874d6
FC
16import org.eclipse.linuxtools.tmf.event.TmfTimestamp;
17import org.eclipse.linuxtools.tmf.signal.TmfSignalManager;
18import org.eclipse.linuxtools.tmf.signal.TmfTimeSynchSignal;
9ccc6d01
FC
19import org.eclipse.swt.SWT;
20import org.eclipse.swt.events.ControlAdapter;
21import org.eclipse.swt.events.ControlEvent;
22import org.eclipse.swt.events.KeyEvent;
23import org.eclipse.swt.events.KeyListener;
24import org.eclipse.swt.events.MouseEvent;
25import org.eclipse.swt.events.MouseWheelListener;
26import org.eclipse.swt.events.SelectionAdapter;
27import org.eclipse.swt.events.SelectionEvent;
9ccc6d01
FC
28import org.eclipse.swt.layout.GridData;
29import org.eclipse.swt.layout.GridLayout;
30import org.eclipse.swt.widgets.Composite;
31import org.eclipse.swt.widgets.Event;
32import org.eclipse.swt.widgets.Listener;
33import org.eclipse.swt.widgets.Slider;
34import org.eclipse.swt.widgets.Table;
35import org.eclipse.swt.widgets.TableColumn;
36import org.eclipse.swt.widgets.TableItem;
37
38/**
39 * <b><u>TmfVirtualTable</u></b>
40 * <p>
41 * TmfVirtualTable allows for the tabular display of arbitrarily large data sets
42 * (well, up to Integer.MAX_VALUE or ~2G rows).
43 *
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.
46 *
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.
49 */
50public class TmfVirtualTable extends Composite {
51
52 // The table
2cc874d6
FC
53 private Table fTable;
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
9ccc6d01 57
2cc874d6
FC
58 private int fTableTopEventRank = 0; // Global rank of the first entry displayed
59 private int fSelectedEventRank = 0; // Global rank of the selected event
60
61 private TableItem fSelectedItems[] = new TableItem[1];
9ccc6d01 62 private int fTableItemCount = 0;
9ccc6d01
FC
63 private TableItem fTableItems[];
64
65 // The slider
2cc874d6 66 private Slider fSlider;
9ccc6d01
FC
67
68 // ------------------------------------------------------------------------
69 // Constructor
70 // ------------------------------------------------------------------------
71
72 /**
73 * @param parent
74 * @param style
75 */
76 public TmfVirtualTable(Composite parent, int style) {
77 super(parent, style | SWT.BORDER & (~SWT.H_SCROLL) & (~SWT.V_SCROLL));
78
79 // Create the controls
2cc874d6 80 createTable(style);
9ccc6d01
FC
81 createSlider();
82
83 // Set the layout
84 GridLayout gridLayout = new GridLayout();
85 gridLayout.numColumns = 2;
86 gridLayout.horizontalSpacing = 0;
2cc874d6
FC
87 gridLayout.verticalSpacing = 0;
88 gridLayout.marginWidth = 0;
89 gridLayout.marginHeight = 0;
9ccc6d01
FC
90 setLayout(gridLayout);
91
92 GridData tableGridData = new GridData(SWT.FILL, SWT.FILL, true, true);
93 fTable.setLayoutData(tableGridData);
94
95 GridData sliderGridData = new GridData(SWT.FILL, SWT.FILL, false, true);
96 fSlider.setLayoutData(sliderGridData);
97
98 // Add the listeners
2cc874d6 99 fTable.addMouseWheelListener(new MouseWheelListener() {
d4011df2 100 @Override
9ccc6d01 101 public void mouseScrolled(MouseEvent event) {
2cc874d6
FC
102 fTableTopEventRank -= event.count;
103 if (fTableTopEventRank < 0) {
104 fTableTopEventRank = 0;
105 }
106 int latestFirstRowOffset = fTableItemCount - fTableRows;
107 if (fTableTopEventRank > latestFirstRowOffset) {
108 fTableTopEventRank = latestFirstRowOffset;
9ccc6d01 109 }
2cc874d6
FC
110
111 fSlider.setSelection(fTableTopEventRank);
112 refreshTable();
9ccc6d01
FC
113 }
114 });
115
116 addControlListener(new ControlAdapter() {
117 @Override
118 public void controlResized(ControlEvent event) {
119 resize();
120 }
121 });
122
123 // And display
124 refresh();
125 }
126
127 // ------------------------------------------------------------------------
128 // Table handling
129 // ------------------------------------------------------------------------
130
131 /**
132 * Create the table and add listeners
133 */
2cc874d6 134 private void createTable(int style) {
9ccc6d01 135
2cc874d6 136// int tableStyle = SWT.NO_SCROLL | SWT.H_SCROLL | SWT.SINGLE | SWT.FULL_SELECTION;
9ccc6d01
FC
137 int tableStyle = SWT.NO_SCROLL | SWT.SINGLE | SWT.FULL_SELECTION;
138 fTable = new Table(this, tableStyle);
139
140 fTable.addSelectionListener(new SelectionAdapter() {
141 @Override
142 public void widgetSelected(SelectionEvent event) {
143 handleTableSelection();
144 }
145 });
146
2cc874d6 147 fTable.addKeyListener(new KeyListener() {
d4011df2 148 @Override
9ccc6d01
FC
149 public void keyPressed(KeyEvent event) {
150 handleTableKeyEvent(event);
151 }
d4011df2 152 @Override
9ccc6d01
FC
153 public void keyReleased(KeyEvent event) {
154 }
155 });
156 }
157
158 /**
159 * Update the rows and selected item
160 */
161 private void handleTableSelection() {
2cc874d6
FC
162 fSelectedRow = fTable.getSelectionIndices()[0];
163 fSelectedEventRank = fTableTopEventRank + fSelectedRow;
164 fSelectedItems[0] = fTable.getSelection()[0];
9ccc6d01
FC
165 }
166
167 /**
168 * Handle key-based navigation in table.
169 *
9ccc6d01
FC
170 * @param event
171 */
172 private void handleTableKeyEvent(KeyEvent event) {
173
2cc874d6
FC
174 int lastEventRank = fTableItemCount - 1;
175 int lastPageTopEntryRank = fTableItemCount - fTableRows;
176
177 int lastRowIndex = ((fTableItemCount < fTableRows) ? fTableItemCount : fTableRows) - 1;
178 int numberOfFullyVisibleRows = fTableRows - ((fPartialRowVisible) ? 1 : 0);
9ccc6d01 179
2cc874d6
FC
180 boolean needsRefresh = false;
181
182 // We are handling things...
9ccc6d01
FC
183 event.doit = false;
184
2cc874d6
FC
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
9ccc6d01
FC
190 switch (event.keyCode) {
191
192 case SWT.ARROW_DOWN: {
2cc874d6
FC
193 if (fSelectedEventRank < lastEventRank) {
194 fSelectedEventRank++;
195 fSelectedRow = fSelectedEventRank - fTableTopEventRank;
196 if (fSelectedRow > lastRowIndex) {
197 fTableTopEventRank++;
198 fSelectedRow = lastRowIndex;
199 needsRefresh = true;
9ccc6d01
FC
200 }
201 }
202 break;
203 }
204
2cc874d6
FC
205 case SWT.ARROW_UP: {
206 if (fSelectedEventRank > 0) {
207 fSelectedEventRank--;
208 fSelectedRow = fSelectedEventRank - fTableTopEventRank;
209 if (fSelectedRow < 0) {
210 fTableTopEventRank--;
211 fSelectedRow = 0;
212 needsRefresh = true;
9ccc6d01 213 }
9ccc6d01
FC
214 }
215 break;
216 }
217
218 case SWT.END: {
2cc874d6
FC
219 fTableTopEventRank = lastPageTopEntryRank;
220 fSelectedEventRank = lastEventRank;
221 fSelectedRow = lastRowIndex;
222 needsRefresh = true;
9ccc6d01
FC
223 break;
224 }
225
2cc874d6
FC
226 case SWT.HOME: {
227 fSelectedEventRank = 0;
228 fSelectedRow = 0;
229 fTableTopEventRank = 0;
230 needsRefresh = true;
9ccc6d01
FC
231 break;
232 }
233
2cc874d6
FC
234 case SWT.PAGE_DOWN: {
235 if (fSelectedEventRank < lastEventRank) {
236 fSelectedEventRank += numberOfFullyVisibleRows;
237 if (fSelectedEventRank > lastEventRank) {
238 fSelectedEventRank = lastEventRank;
239 }
240 fSelectedRow = fSelectedEventRank - fTableTopEventRank;
241 if (fSelectedRow > numberOfFullyVisibleRows - 1) {
242 fTableTopEventRank += numberOfFullyVisibleRows;
243 if (fTableTopEventRank > lastPageTopEntryRank) {
244 fTableTopEventRank = lastPageTopEntryRank;
245 }
246 fSelectedRow = fSelectedEventRank - fTableTopEventRank;
247 needsRefresh = true;
9ccc6d01 248 }
9ccc6d01
FC
249 }
250 break;
251 }
252
2cc874d6
FC
253 case SWT.PAGE_UP: {
254 if (fSelectedEventRank > 0) {
255 fSelectedEventRank -= numberOfFullyVisibleRows;
256 if (fSelectedEventRank < 0) {
257 fSelectedEventRank = 0;
258 }
259 fSelectedRow = fSelectedEventRank - fTableTopEventRank;
260 if (fSelectedRow < 0) {
261 fSelectedRow = 0;
262 fTableTopEventRank -= numberOfFullyVisibleRows;
263 if (fTableTopEventRank < 0) {
264 fTableTopEventRank = 0;
265 }
266 fSelectedRow = fSelectedEventRank - fTableTopEventRank;
267 needsRefresh = true;
268 }
269 }
9ccc6d01
FC
270 break;
271 }
272 }
273
2cc874d6 274 if (needsRefresh) {
9ccc6d01 275 for (int i = 0; i < fTableItems.length; i++) {
2cc874d6 276 setDataItem(i, fTableItems[i]);
9ccc6d01
FC
277 }
278 }
bbb3457d
FC
279 else {
280 notifyUpdatedSelection();
9ccc6d01
FC
281 }
282 }
283
bbb3457d
FC
284 private void setDataItem(int index, TableItem item) {
285 if( index != -1) {
286 Event event = new Event();
287 event.item = item;
288 event.index = index + fTableTopEventRank;
289 event.doit = true;
290 notifyListeners(SWT.SetData, event);
291 }
292 }
293
294 public void notifyUpdatedSelection() {
295 fSlider.setSelection(fSelectedEventRank);
296 fTable.setSelection(fSelectedRow);
297 fTable.showSelection();
298 TableItem[] tableSelection = fTable.getSelection();
299 if (tableSelection.length > 0 && tableSelection[0] != null) {
300 fSelectedItems[0] = tableSelection[0];
301 TmfTimestamp ts = (TmfTimestamp) fSelectedItems[0].getData();
302 TmfSignalManager.dispatchSignal(new TmfTimeSynchSignal(this, ts));
303 }
304 }
305
9ccc6d01
FC
306 // ------------------------------------------------------------------------
307 // Slider handling
308 // ------------------------------------------------------------------------
309
310 private void createSlider() {
311 fSlider = new Slider(this, SWT.VERTICAL);
312 fSlider.setMinimum(0);
313 fSlider.setMaximum(0);
314
9ccc6d01 315 fSlider.addListener(SWT.Selection, new Listener() {
d4011df2 316 @Override
9ccc6d01
FC
317 public void handleEvent(Event event) {
318 switch (event.detail) {
319 case SWT.ARROW_DOWN:
320 case SWT.ARROW_UP:
321 case SWT.NONE:
322 case SWT.END:
323 case SWT.HOME:
324 case SWT.PAGE_DOWN:
325 case SWT.PAGE_UP: {
2cc874d6
FC
326 fTableTopEventRank = fSlider.getSelection();
327 refreshTable();
9ccc6d01
FC
328 break;
329 }
330 }
331 }
332 });
333 }
334
335 // ------------------------------------------------------------------------
336 // Simulated Table API
337 // ------------------------------------------------------------------------
338
339 public void setHeaderVisible(boolean b) {
340 fTable.setHeaderVisible(b);
341 }
342
343 public void setLinesVisible(boolean b) {
344 fTable.setLinesVisible(b);
345 }
346
347 public TableItem[] getSelection() {
348 return fSelectedItems;
349 }
350
351 public void addSelectionListener(SelectionAdapter sa) {
352 fTable.addSelectionListener(sa);
353 }
354
355 public void setItemCount(int nbItems) {
356 nbItems = Math.max(0, nbItems);
357 if (nbItems != fTableItemCount) {
358 fTableItemCount = nbItems;
359 fSlider.setMaximum(nbItems);
360 resize();
361 }
362 }
363
364 public int getItemHeight() {
365 return fTable.getItemHeight();
366 }
367
368 public int getTopIndex() {
2cc874d6 369 return fTableTopEventRank;
9ccc6d01
FC
370 }
371
372 public void setTopIndex(int i) {
373 fSlider.setSelection(i);
374 }
375
376 public int indexOf(TableItem ti) {
377 return fTable.indexOf(ti) + getTopIndex();
378 }
379
380 public TableColumn[] getColumns() {
381 return fTable.getColumns();
382 }
383
384 private void resize() {
385
386 // Compute the numbers of rows that fit the new area
2cc874d6
FC
387 int tableHeight = fTable.getClientArea().height - fTable.getHeaderHeight();
388
389 if (tableHeight < 0) tableHeight = 0;
390 int itemHeight = fTable.getItemHeight();
391 fTableRows = tableHeight / itemHeight;
392 fPartialRowVisible = false;
393 if (fTableRows * itemHeight < tableHeight) {
394 fTableRows++; // For partial rows
395 fPartialRowVisible = true;
396 }
397 if (fTableRows > fTableItemCount) {
398 fTableRows = fTableItemCount;
9ccc6d01 399 }
86148c85
FC
400
401 // If we are at the end, get elements before to populate
2cc874d6
FC
402 if (fTableTopEventRank + fTableRows >= fTableItemCount) {
403 fTableTopEventRank = fTableItemCount - fTableRows;
86148c85
FC
404 }
405
406 // Set the slider thumb size
2cc874d6
FC
407 if (fTableItemCount > 0) {
408 fSlider.setThumb(fTableRows);
409 }
86148c85 410
9ccc6d01 411 // Re-size and re-create the virtual table if needed
2cc874d6 412 int delta = fTable.getItemCount() - fTableRows;
9ccc6d01
FC
413 if (delta != 0) {
414 fTable.removeAll();
415 if (fTableItems != null) {
416 for (int i = 0; i < fTableItems.length; i++) {
417 if (fTableItems[i] != null) {
418 fTableItems[i].dispose();
419 }
420 fTableItems[i] = null;
421 }
422 }
2cc874d6 423 fTableItems = new TableItem[fTableRows];
9ccc6d01
FC
424 for (int i = 0; i < fTableItems.length; i++) {
425 fTableItems[i] = new TableItem(fTable, i);
426 }
427 }
428
429 refresh();
430 }
431
432 // ------------------------------------------------------------------------
433 // Controls interactions
434 // ------------------------------------------------------------------------
435
436 @Override
437 public boolean setFocus() {
438 boolean isVisible = isVisible();
439 if (isVisible) {
440 fTable.setFocus();
441 }
442 return isVisible;
443 }
444
445 public void refresh() {
2cc874d6 446 refreshTable();
bbb3457d 447 notifyUpdatedSelection();
9ccc6d01
FC
448 }
449
450 public void setColumnHeaders(ColumnData columnData[]) {
451 for (int i = 0; i < columnData.length; i++) {
452 TableColumn column = new TableColumn(fTable, columnData[i].alignment, i);
453 column.setText(columnData[i].header);
454 if (columnData[i].width > 0) {
455 column.setWidth(columnData[i].width);
456 } else {
457 column.pack();
458 }
459 }
460 }
461
462 public int removeAll() {
463 fSlider.setMaximum(0);
464 fTable.removeAll();
465 return 0;
466 }
467
2cc874d6
FC
468 private void refreshTable() {
469 int lastRowOffset = fTableTopEventRank + fTableRows - 1;
470 if ((fSelectedEventRank >= fTableTopEventRank) && (fSelectedEventRank <= lastRowOffset)) {
471 fSelectedRow = fSelectedEventRank - fTableTopEventRank;
472 fTable.setSelection(fSelectedRow);
9ccc6d01 473 } else {
2cc874d6 474 fTable.deselect(fSelectedRow);
9ccc6d01
FC
475 }
476
2cc874d6
FC
477 for (int i = 0; i < fTableRows; i++) {
478 setDataItem(i, fTableItems[i]);
9ccc6d01
FC
479 }
480 }
481
482 public void setSelection(int i) {
483 if (fTableItems != null) {
484 i = Math.min(i, fTableItemCount);
485 i = Math.max(i, 0);
486 fSlider.setSelection(i);
2cc874d6
FC
487
488 fSelectedEventRank = i;
489 fTableTopEventRank = i - (fTableRows / 2);
490 if (fTableTopEventRank < 0) {
491 fTableTopEventRank = 0;
492 }
493 fSelectedRow = fSelectedEventRank - fTableTopEventRank;
494
495 refreshTable();
9ccc6d01
FC
496 }
497 }
498
499}
This page took 0.047736 seconds and 5 git commands to generate.