2010-09-12 Francois Chouinard <fchouinard@gmail.com> Fix for Bug323611
[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() {
9ccc6d01 100 public void mouseScrolled(MouseEvent event) {
2cc874d6
FC
101 fTableTopEventRank -= event.count;
102 if (fTableTopEventRank < 0) {
103 fTableTopEventRank = 0;
104 }
105 int latestFirstRowOffset = fTableItemCount - fTableRows;
106 if (fTableTopEventRank > latestFirstRowOffset) {
107 fTableTopEventRank = latestFirstRowOffset;
9ccc6d01 108 }
2cc874d6
FC
109
110 fSlider.setSelection(fTableTopEventRank);
111 refreshTable();
9ccc6d01
FC
112 }
113 });
114
115 addControlListener(new ControlAdapter() {
116 @Override
117 public void controlResized(ControlEvent event) {
118 resize();
119 }
120 });
121
122 // And display
123 refresh();
124 }
125
126 // ------------------------------------------------------------------------
127 // Table handling
128 // ------------------------------------------------------------------------
129
130 /**
131 * Create the table and add listeners
132 */
2cc874d6 133 private void createTable(int style) {
9ccc6d01 134
2cc874d6 135// int tableStyle = SWT.NO_SCROLL | SWT.H_SCROLL | SWT.SINGLE | SWT.FULL_SELECTION;
9ccc6d01
FC
136 int tableStyle = SWT.NO_SCROLL | SWT.SINGLE | SWT.FULL_SELECTION;
137 fTable = new Table(this, tableStyle);
138
139 fTable.addSelectionListener(new SelectionAdapter() {
140 @Override
141 public void widgetSelected(SelectionEvent event) {
142 handleTableSelection();
143 }
144 });
145
2cc874d6 146 fTable.addKeyListener(new KeyListener() {
9ccc6d01
FC
147 public void keyPressed(KeyEvent event) {
148 handleTableKeyEvent(event);
149 }
150 public void keyReleased(KeyEvent event) {
151 }
152 });
153 }
154
155 /**
156 * Update the rows and selected item
157 */
158 private void handleTableSelection() {
2cc874d6
FC
159 fSelectedRow = fTable.getSelectionIndices()[0];
160 fSelectedEventRank = fTableTopEventRank + fSelectedRow;
161 fSelectedItems[0] = fTable.getSelection()[0];
9ccc6d01
FC
162 }
163
164 /**
165 * Handle key-based navigation in table.
166 *
9ccc6d01
FC
167 * @param event
168 */
169 private void handleTableKeyEvent(KeyEvent event) {
170
2cc874d6
FC
171 int lastEventRank = fTableItemCount - 1;
172 int lastPageTopEntryRank = fTableItemCount - fTableRows;
173
174 int lastRowIndex = ((fTableItemCount < fTableRows) ? fTableItemCount : fTableRows) - 1;
175 int numberOfFullyVisibleRows = fTableRows - ((fPartialRowVisible) ? 1 : 0);
9ccc6d01 176
2cc874d6
FC
177 boolean needsRefresh = false;
178
179 // We are handling things...
9ccc6d01
FC
180 event.doit = false;
181
2cc874d6
FC
182 // In all case, perform the following steps:
183 // - Update the selected entry rank (within valid range)
184 // - Update the selected row
185 // - Update the page's top entry if necessary (which also adjusts the selected row)
186 // - If the top displayed entry was changed, table refresh is needed
9ccc6d01
FC
187 switch (event.keyCode) {
188
189 case SWT.ARROW_DOWN: {
2cc874d6
FC
190 if (fSelectedEventRank < lastEventRank) {
191 fSelectedEventRank++;
192 fSelectedRow = fSelectedEventRank - fTableTopEventRank;
193 if (fSelectedRow > lastRowIndex) {
194 fTableTopEventRank++;
195 fSelectedRow = lastRowIndex;
196 needsRefresh = true;
9ccc6d01
FC
197 }
198 }
199 break;
200 }
201
2cc874d6
FC
202 case SWT.ARROW_UP: {
203 if (fSelectedEventRank > 0) {
204 fSelectedEventRank--;
205 fSelectedRow = fSelectedEventRank - fTableTopEventRank;
206 if (fSelectedRow < 0) {
207 fTableTopEventRank--;
208 fSelectedRow = 0;
209 needsRefresh = true;
9ccc6d01 210 }
9ccc6d01
FC
211 }
212 break;
213 }
214
215 case SWT.END: {
2cc874d6
FC
216 fTableTopEventRank = lastPageTopEntryRank;
217 fSelectedEventRank = lastEventRank;
218 fSelectedRow = lastRowIndex;
219 needsRefresh = true;
9ccc6d01
FC
220 break;
221 }
222
2cc874d6
FC
223 case SWT.HOME: {
224 fSelectedEventRank = 0;
225 fSelectedRow = 0;
226 fTableTopEventRank = 0;
227 needsRefresh = true;
9ccc6d01
FC
228 break;
229 }
230
2cc874d6
FC
231 case SWT.PAGE_DOWN: {
232 if (fSelectedEventRank < lastEventRank) {
233 fSelectedEventRank += numberOfFullyVisibleRows;
234 if (fSelectedEventRank > lastEventRank) {
235 fSelectedEventRank = lastEventRank;
236 }
237 fSelectedRow = fSelectedEventRank - fTableTopEventRank;
238 if (fSelectedRow > numberOfFullyVisibleRows - 1) {
239 fTableTopEventRank += numberOfFullyVisibleRows;
240 if (fTableTopEventRank > lastPageTopEntryRank) {
241 fTableTopEventRank = lastPageTopEntryRank;
242 }
243 fSelectedRow = fSelectedEventRank - fTableTopEventRank;
244 needsRefresh = true;
9ccc6d01 245 }
9ccc6d01
FC
246 }
247 break;
248 }
249
2cc874d6
FC
250 case SWT.PAGE_UP: {
251 if (fSelectedEventRank > 0) {
252 fSelectedEventRank -= numberOfFullyVisibleRows;
253 if (fSelectedEventRank < 0) {
254 fSelectedEventRank = 0;
255 }
256 fSelectedRow = fSelectedEventRank - fTableTopEventRank;
257 if (fSelectedRow < 0) {
258 fSelectedRow = 0;
259 fTableTopEventRank -= numberOfFullyVisibleRows;
260 if (fTableTopEventRank < 0) {
261 fTableTopEventRank = 0;
262 }
263 fSelectedRow = fSelectedEventRank - fTableTopEventRank;
264 needsRefresh = true;
265 }
266 }
9ccc6d01
FC
267 break;
268 }
269 }
270
2cc874d6 271 if (needsRefresh) {
9ccc6d01 272 for (int i = 0; i < fTableItems.length; i++) {
2cc874d6 273 setDataItem(i, fTableItems[i]);
9ccc6d01
FC
274 }
275 }
276
2cc874d6
FC
277 fSlider.setSelection(fSelectedEventRank);
278 fTable.setSelection(fSelectedRow);
279 fTable.showSelection();
280 fSelectedItems[0] = fTable.getSelection()[0];
9ccc6d01 281
2cc874d6
FC
282 TmfTimestamp ts = (TmfTimestamp) fSelectedItems[0].getData();
283 TmfSignalManager.dispatchSignal(new TmfTimeSynchSignal(this, ts));
9ccc6d01
FC
284 }
285
2cc874d6 286 private void setDataItem(int index, TableItem item) {
9ccc6d01
FC
287 if( index != -1) {
288 Event event = new Event();
289 event.item = item;
2cc874d6 290 event.index = index + fTableTopEventRank;
9ccc6d01
FC
291 event.doit = true;
292 notifyListeners(SWT.SetData, event);
293 }
294 }
295
296 // ------------------------------------------------------------------------
297 // Slider handling
298 // ------------------------------------------------------------------------
299
300 private void createSlider() {
301 fSlider = new Slider(this, SWT.VERTICAL);
302 fSlider.setMinimum(0);
303 fSlider.setMaximum(0);
304
9ccc6d01
FC
305 fSlider.addListener(SWT.Selection, new Listener() {
306 public void handleEvent(Event event) {
307 switch (event.detail) {
308 case SWT.ARROW_DOWN:
309 case SWT.ARROW_UP:
310 case SWT.NONE:
311 case SWT.END:
312 case SWT.HOME:
313 case SWT.PAGE_DOWN:
314 case SWT.PAGE_UP: {
2cc874d6
FC
315 fTableTopEventRank = fSlider.getSelection();
316 refreshTable();
9ccc6d01
FC
317 break;
318 }
319 }
320 }
321 });
322 }
323
324 // ------------------------------------------------------------------------
325 // Simulated Table API
326 // ------------------------------------------------------------------------
327
328 public void setHeaderVisible(boolean b) {
329 fTable.setHeaderVisible(b);
330 }
331
332 public void setLinesVisible(boolean b) {
333 fTable.setLinesVisible(b);
334 }
335
336 public TableItem[] getSelection() {
337 return fSelectedItems;
338 }
339
340 public void addSelectionListener(SelectionAdapter sa) {
341 fTable.addSelectionListener(sa);
342 }
343
344 public void setItemCount(int nbItems) {
345 nbItems = Math.max(0, nbItems);
346 if (nbItems != fTableItemCount) {
347 fTableItemCount = nbItems;
348 fSlider.setMaximum(nbItems);
349 resize();
350 }
351 }
352
353 public int getItemHeight() {
354 return fTable.getItemHeight();
355 }
356
357 public int getTopIndex() {
2cc874d6 358 return fTableTopEventRank;
9ccc6d01
FC
359 }
360
361 public void setTopIndex(int i) {
362 fSlider.setSelection(i);
363 }
364
365 public int indexOf(TableItem ti) {
366 return fTable.indexOf(ti) + getTopIndex();
367 }
368
369 public TableColumn[] getColumns() {
370 return fTable.getColumns();
371 }
372
373 private void resize() {
374
375 // Compute the numbers of rows that fit the new area
2cc874d6
FC
376 int tableHeight = fTable.getClientArea().height - fTable.getHeaderHeight();
377
378 if (tableHeight < 0) tableHeight = 0;
379 int itemHeight = fTable.getItemHeight();
380 fTableRows = tableHeight / itemHeight;
381 fPartialRowVisible = false;
382 if (fTableRows * itemHeight < tableHeight) {
383 fTableRows++; // For partial rows
384 fPartialRowVisible = true;
385 }
386 if (fTableRows > fTableItemCount) {
387 fTableRows = fTableItemCount;
9ccc6d01 388 }
86148c85
FC
389
390 // If we are at the end, get elements before to populate
2cc874d6
FC
391 if (fTableTopEventRank + fTableRows >= fTableItemCount) {
392 fTableTopEventRank = fTableItemCount - fTableRows;
86148c85
FC
393 }
394
395 // Set the slider thumb size
2cc874d6
FC
396 if (fTableItemCount > 0) {
397 fSlider.setThumb(fTableRows);
398 }
86148c85 399
9ccc6d01 400 // Re-size and re-create the virtual table if needed
2cc874d6 401 int delta = fTable.getItemCount() - fTableRows;
9ccc6d01
FC
402 if (delta != 0) {
403 fTable.removeAll();
404 if (fTableItems != null) {
405 for (int i = 0; i < fTableItems.length; i++) {
406 if (fTableItems[i] != null) {
407 fTableItems[i].dispose();
408 }
409 fTableItems[i] = null;
410 }
411 }
2cc874d6 412 fTableItems = new TableItem[fTableRows];
9ccc6d01
FC
413 for (int i = 0; i < fTableItems.length; i++) {
414 fTableItems[i] = new TableItem(fTable, i);
415 }
416 }
417
418 refresh();
419 }
420
421 // ------------------------------------------------------------------------
422 // Controls interactions
423 // ------------------------------------------------------------------------
424
425 @Override
426 public boolean setFocus() {
427 boolean isVisible = isVisible();
428 if (isVisible) {
429 fTable.setFocus();
430 }
431 return isVisible;
432 }
433
434 public void refresh() {
2cc874d6 435 refreshTable();
9ccc6d01
FC
436 }
437
438 public void setColumnHeaders(ColumnData columnData[]) {
439 for (int i = 0; i < columnData.length; i++) {
440 TableColumn column = new TableColumn(fTable, columnData[i].alignment, i);
441 column.setText(columnData[i].header);
442 if (columnData[i].width > 0) {
443 column.setWidth(columnData[i].width);
444 } else {
445 column.pack();
446 }
447 }
448 }
449
450 public int removeAll() {
451 fSlider.setMaximum(0);
452 fTable.removeAll();
453 return 0;
454 }
455
2cc874d6
FC
456 private void refreshTable() {
457 int lastRowOffset = fTableTopEventRank + fTableRows - 1;
458 if ((fSelectedEventRank >= fTableTopEventRank) && (fSelectedEventRank <= lastRowOffset)) {
459 fSelectedRow = fSelectedEventRank - fTableTopEventRank;
460 fTable.setSelection(fSelectedRow);
9ccc6d01 461 } else {
2cc874d6 462 fTable.deselect(fSelectedRow);
9ccc6d01
FC
463 }
464
2cc874d6
FC
465 for (int i = 0; i < fTableRows; i++) {
466 setDataItem(i, fTableItems[i]);
9ccc6d01
FC
467 }
468 }
469
470 public void setSelection(int i) {
471 if (fTableItems != null) {
472 i = Math.min(i, fTableItemCount);
473 i = Math.max(i, 0);
474 fSlider.setSelection(i);
2cc874d6
FC
475
476 fSelectedEventRank = i;
477 fTableTopEventRank = i - (fTableRows / 2);
478 if (fTableTopEventRank < 0) {
479 fTableTopEventRank = 0;
480 }
481 fSelectedRow = fSelectedEventRank - fTableTopEventRank;
482
483 refreshTable();
9ccc6d01
FC
484 }
485 }
486
487}
This page took 0.045622 seconds and 5 git commands to generate.