cc92e6bb7eebfa33841cf04e49016c62d1790a2f
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.ui / src / org / eclipse / linuxtools / tmf / ui / widgets / TmfVirtualTable.java
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
14 package org.eclipse.linuxtools.tmf.ui.widgets;
15
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;
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 */
50 public class TmfVirtualTable extends Composite {
51
52 // The table
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
57
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];
62 private int fTableItemCount = 0;
63 private TableItem fTableItems[];
64
65 // The slider
66 private Slider fSlider;
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
80 createTable(style);
81 createSlider();
82
83 // Set the layout
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);
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
99 fTable.addMouseWheelListener(new MouseWheelListener() {
100 @Override
101 public void mouseScrolled(MouseEvent event) {
102 fTableTopEventRank -= event.count;
103 if (fTableTopEventRank < 0) {
104 fTableTopEventRank = 0;
105 }
106 int latestFirstRowOffset = fTableItemCount - fTableRows;
107 if (fTableTopEventRank > latestFirstRowOffset) {
108 fTableTopEventRank = latestFirstRowOffset;
109 }
110
111 fSlider.setSelection(fTableTopEventRank);
112 refreshTable();
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 */
134 private void createTable(int style) {
135
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);
139
140 fTable.addSelectionListener(new SelectionAdapter() {
141 @Override
142 public void widgetSelected(SelectionEvent event) {
143 handleTableSelection();
144 }
145 });
146
147 fTable.addKeyListener(new KeyListener() {
148 @Override
149 public void keyPressed(KeyEvent event) {
150 handleTableKeyEvent(event);
151 }
152 @Override
153 public void keyReleased(KeyEvent event) {
154 }
155 });
156 }
157
158 /**
159 * Update the rows and selected item
160 */
161 private void handleTableSelection() {
162 fSelectedRow = fTable.getSelectionIndices()[0];
163 fSelectedEventRank = fTableTopEventRank + fSelectedRow;
164 fSelectedItems[0] = fTable.getSelection()[0];
165 }
166
167 /**
168 * Handle key-based navigation in table.
169 *
170 * @param event
171 */
172 private void handleTableKeyEvent(KeyEvent event) {
173
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);
179
180 boolean needsRefresh = false;
181
182 // We are handling things...
183 event.doit = false;
184
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) {
191
192 case SWT.ARROW_DOWN: {
193 if (fSelectedEventRank < lastEventRank) {
194 fSelectedEventRank++;
195 fSelectedRow = fSelectedEventRank - fTableTopEventRank;
196 if (fSelectedRow > lastRowIndex) {
197 fTableTopEventRank++;
198 fSelectedRow = lastRowIndex;
199 needsRefresh = true;
200 }
201 }
202 break;
203 }
204
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;
213 }
214 }
215 break;
216 }
217
218 case SWT.END: {
219 fTableTopEventRank = lastPageTopEntryRank;
220 fSelectedEventRank = lastEventRank;
221 fSelectedRow = lastRowIndex;
222 needsRefresh = true;
223 break;
224 }
225
226 case SWT.HOME: {
227 fSelectedEventRank = 0;
228 fSelectedRow = 0;
229 fTableTopEventRank = 0;
230 needsRefresh = true;
231 break;
232 }
233
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;
248 }
249 }
250 break;
251 }
252
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 }
270 break;
271 }
272 }
273
274 if (needsRefresh) {
275 for (int i = 0; i < fTableItems.length; i++) {
276 setDataItem(i, fTableItems[i]);
277 }
278 }
279
280 fSlider.setSelection(fSelectedEventRank);
281 fTable.setSelection(fSelectedRow);
282 fTable.showSelection();
283 fSelectedItems[0] = fTable.getSelection()[0];
284
285 TmfTimestamp ts = (TmfTimestamp) fSelectedItems[0].getData();
286 TmfSignalManager.dispatchSignal(new TmfTimeSynchSignal(this, ts));
287 }
288
289 private void setDataItem(int index, TableItem item) {
290 if( index != -1) {
291 Event event = new Event();
292 event.item = item;
293 event.index = index + fTableTopEventRank;
294 event.doit = true;
295 notifyListeners(SWT.SetData, event);
296 }
297 }
298
299 // ------------------------------------------------------------------------
300 // Slider handling
301 // ------------------------------------------------------------------------
302
303 private void createSlider() {
304 fSlider = new Slider(this, SWT.VERTICAL);
305 fSlider.setMinimum(0);
306 fSlider.setMaximum(0);
307
308 fSlider.addListener(SWT.Selection, new Listener() {
309 @Override
310 public void handleEvent(Event event) {
311 switch (event.detail) {
312 case SWT.ARROW_DOWN:
313 case SWT.ARROW_UP:
314 case SWT.NONE:
315 case SWT.END:
316 case SWT.HOME:
317 case SWT.PAGE_DOWN:
318 case SWT.PAGE_UP: {
319 fTableTopEventRank = fSlider.getSelection();
320 refreshTable();
321 break;
322 }
323 }
324 }
325 });
326 }
327
328 // ------------------------------------------------------------------------
329 // Simulated Table API
330 // ------------------------------------------------------------------------
331
332 public void setHeaderVisible(boolean b) {
333 fTable.setHeaderVisible(b);
334 }
335
336 public void setLinesVisible(boolean b) {
337 fTable.setLinesVisible(b);
338 }
339
340 public TableItem[] getSelection() {
341 return fSelectedItems;
342 }
343
344 public void addSelectionListener(SelectionAdapter sa) {
345 fTable.addSelectionListener(sa);
346 }
347
348 public void setItemCount(int nbItems) {
349 nbItems = Math.max(0, nbItems);
350 if (nbItems != fTableItemCount) {
351 fTableItemCount = nbItems;
352 fSlider.setMaximum(nbItems);
353 resize();
354 }
355 }
356
357 public int getItemHeight() {
358 return fTable.getItemHeight();
359 }
360
361 public int getTopIndex() {
362 return fTableTopEventRank;
363 }
364
365 public void setTopIndex(int i) {
366 fSlider.setSelection(i);
367 }
368
369 public int indexOf(TableItem ti) {
370 return fTable.indexOf(ti) + getTopIndex();
371 }
372
373 public TableColumn[] getColumns() {
374 return fTable.getColumns();
375 }
376
377 private void resize() {
378
379 // Compute the numbers of rows that fit the new area
380 int tableHeight = fTable.getClientArea().height - fTable.getHeaderHeight();
381
382 if (tableHeight < 0) tableHeight = 0;
383 int itemHeight = fTable.getItemHeight();
384 fTableRows = tableHeight / itemHeight;
385 fPartialRowVisible = false;
386 if (fTableRows * itemHeight < tableHeight) {
387 fTableRows++; // For partial rows
388 fPartialRowVisible = true;
389 }
390 if (fTableRows > fTableItemCount) {
391 fTableRows = fTableItemCount;
392 }
393
394 // If we are at the end, get elements before to populate
395 if (fTableTopEventRank + fTableRows >= fTableItemCount) {
396 fTableTopEventRank = fTableItemCount - fTableRows;
397 }
398
399 // Set the slider thumb size
400 if (fTableItemCount > 0) {
401 fSlider.setThumb(fTableRows);
402 }
403
404 // Re-size and re-create the virtual table if needed
405 int delta = fTable.getItemCount() - fTableRows;
406 if (delta != 0) {
407 fTable.removeAll();
408 if (fTableItems != null) {
409 for (int i = 0; i < fTableItems.length; i++) {
410 if (fTableItems[i] != null) {
411 fTableItems[i].dispose();
412 }
413 fTableItems[i] = null;
414 }
415 }
416 fTableItems = new TableItem[fTableRows];
417 for (int i = 0; i < fTableItems.length; i++) {
418 fTableItems[i] = new TableItem(fTable, i);
419 }
420 }
421
422 refresh();
423 }
424
425 // ------------------------------------------------------------------------
426 // Controls interactions
427 // ------------------------------------------------------------------------
428
429 @Override
430 public boolean setFocus() {
431 boolean isVisible = isVisible();
432 if (isVisible) {
433 fTable.setFocus();
434 }
435 return isVisible;
436 }
437
438 public void refresh() {
439 refreshTable();
440 }
441
442 public void setColumnHeaders(ColumnData columnData[]) {
443 for (int i = 0; i < columnData.length; i++) {
444 TableColumn column = new TableColumn(fTable, columnData[i].alignment, i);
445 column.setText(columnData[i].header);
446 if (columnData[i].width > 0) {
447 column.setWidth(columnData[i].width);
448 } else {
449 column.pack();
450 }
451 }
452 }
453
454 public int removeAll() {
455 fSlider.setMaximum(0);
456 fTable.removeAll();
457 return 0;
458 }
459
460 private void refreshTable() {
461 int lastRowOffset = fTableTopEventRank + fTableRows - 1;
462 if ((fSelectedEventRank >= fTableTopEventRank) && (fSelectedEventRank <= lastRowOffset)) {
463 fSelectedRow = fSelectedEventRank - fTableTopEventRank;
464 fTable.setSelection(fSelectedRow);
465 } else {
466 fTable.deselect(fSelectedRow);
467 }
468
469 for (int i = 0; i < fTableRows; i++) {
470 setDataItem(i, fTableItems[i]);
471 }
472 }
473
474 public void setSelection(int i) {
475 if (fTableItems != null) {
476 i = Math.min(i, fTableItemCount);
477 i = Math.max(i, 0);
478 fSlider.setSelection(i);
479
480 fSelectedEventRank = i;
481 fTableTopEventRank = i - (fTableRows / 2);
482 if (fTableTopEventRank < 0) {
483 fTableTopEventRank = 0;
484 }
485 fSelectedRow = fSelectedEventRank - fTableTopEventRank;
486
487 refreshTable();
488 }
489 }
490
491 }
This page took 0.041881 seconds and 4 git commands to generate.