2010-07-28 Francois Chouinard <fchouinard@gmail.com> Fix for Bug316349 + a bunch...
[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
16import org.eclipse.swt.SWT;
17import org.eclipse.swt.events.ControlAdapter;
18import org.eclipse.swt.events.ControlEvent;
19import org.eclipse.swt.events.KeyEvent;
20import org.eclipse.swt.events.KeyListener;
21import org.eclipse.swt.events.MouseEvent;
22import org.eclipse.swt.events.MouseWheelListener;
23import org.eclipse.swt.events.SelectionAdapter;
24import org.eclipse.swt.events.SelectionEvent;
25import org.eclipse.swt.graphics.Rectangle;
26import org.eclipse.swt.layout.GridData;
27import org.eclipse.swt.layout.GridLayout;
28import org.eclipse.swt.widgets.Composite;
29import org.eclipse.swt.widgets.Event;
30import org.eclipse.swt.widgets.Listener;
31import org.eclipse.swt.widgets.Slider;
32import org.eclipse.swt.widgets.Table;
33import org.eclipse.swt.widgets.TableColumn;
34import org.eclipse.swt.widgets.TableItem;
35
36/**
37 * <b><u>TmfVirtualTable</u></b>
38 * <p>
39 * TmfVirtualTable allows for the tabular display of arbitrarily large data sets
40 * (well, up to Integer.MAX_VALUE or ~2G rows).
41 *
42 * It is essentially a Composite of Table and Slider, where the number of rows
43 * in the table is set to fill the table display area. The slider is rank-based.
44 *
45 * It differs from Table with the VIRTUAL style flag where an empty entry is
46 * created for each virtual row. This does not scale well for very large data sets.
47 */
48public class TmfVirtualTable extends Composite {
49
50 // The table
51 private Table fTable;
52 private int fFirstRowOffset = 0;
53 private int fTableRow = 0;
54 private int fEffectiveRow = 0;
55
56 private TableItem fSelectedItems[] = null;
57 private int fTableItemCount = 0;
58 private int fRowsDisplayed;
59 private TableItem fTableItems[];
60
61 // The slider
62 private Slider fSlider;
63
64 // ------------------------------------------------------------------------
65 // Constructor
66 // ------------------------------------------------------------------------
67
68 /**
69 * @param parent
70 * @param style
71 */
72 public TmfVirtualTable(Composite parent, int style) {
73 super(parent, style | SWT.BORDER & (~SWT.H_SCROLL) & (~SWT.V_SCROLL));
74
75 // Create the controls
76 createTable();
77 createSlider();
78
79 // Set the layout
80 GridLayout gridLayout = new GridLayout();
81 gridLayout.numColumns = 2;
82 gridLayout.horizontalSpacing = 0;
83 gridLayout.verticalSpacing = 0;
84 gridLayout.marginWidth = 0;
85 setLayout(gridLayout);
86
87 GridData tableGridData = new GridData(SWT.FILL, SWT.FILL, true, true);
88 fTable.setLayoutData(tableGridData);
89
90 GridData sliderGridData = new GridData(SWT.FILL, SWT.FILL, false, true);
91 fSlider.setLayoutData(sliderGridData);
92
93 // Add the listeners
94 addMouseWheelListener(new MouseWheelListener() {
95 public void mouseScrolled(MouseEvent event) {
96 fFirstRowOffset -= event.count;
97 int lastFirstRowOffset = fTableItemCount - fRowsDisplayed - 1;
98 if (fFirstRowOffset > lastFirstRowOffset) {
99 fFirstRowOffset = lastFirstRowOffset;
100 } else if (fFirstRowOffset < 0) {
101 fFirstRowOffset = 0;
102 }
103 fSlider.setSelection(fFirstRowOffset);
104 setSelection();
105 }
106 });
107
108 addControlListener(new ControlAdapter() {
109 @Override
110 public void controlResized(ControlEvent event) {
111 resize();
112 }
113 });
114
115 // And display
116 refresh();
117 }
118
119 // ------------------------------------------------------------------------
120 // Table handling
121 // ------------------------------------------------------------------------
122
123 /**
124 * Create the table and add listeners
125 */
126 private void createTable() {
127
128 int tableStyle = SWT.NO_SCROLL | SWT.SINGLE | SWT.FULL_SELECTION;
129 fTable = new Table(this, tableStyle);
130
131 fTable.addSelectionListener(new SelectionAdapter() {
132 @Override
133 public void widgetSelected(SelectionEvent event) {
134 handleTableSelection();
135 }
136 });
137
138 fTable.addKeyListener(new KeyListener() {
139 public void keyPressed(KeyEvent event) {
140 handleTableKeyEvent(event);
141 }
142 public void keyReleased(KeyEvent event) {
143 }
144 });
145 }
146
147 /**
148 * Update the rows and selected item
149 */
150 private void handleTableSelection() {
151 fTableRow = fTable.getSelectionIndices()[0];
152 fEffectiveRow = fFirstRowOffset + fTableRow;
153 fSelectedItems = new TableItem[1];
154 fSelectedItems[0] = fTable.getSelection()[0];
155 }
156
157 /**
158 * Handle key-based navigation in table.
159 *
160 * The key variables are:
161 * - fFirstRowOffset: the absolute index (in the data set) of the first row displayed
162 * - fTableRow: the index of the selected event in the table window
163 * - fEffectiveRow: the absolute index of the selected event (in the data set)
164 *
165 * At all times, the following relation should hold true:
166 * fEffectiveRow = fFirstRowOffset + fTableRow
167 *
168 * @param event
169 */
170 private void handleTableKeyEvent(KeyEvent event) {
171
172 boolean needsUpdate = false;
173 final int lastTableRow = fTableItemCount - 1;
174 int lastRowDisplayed = ((fTableItemCount < fRowsDisplayed) ? fTableItemCount : fRowsDisplayed) - 1;
175
176 // We are handling things
177 event.doit = false;
178
179 switch (event.keyCode) {
180
181 case SWT.ARROW_DOWN: {
182 if (fEffectiveRow < lastTableRow) {
183 fEffectiveRow++;
184 if (fTableRow < lastRowDisplayed) {
185 fTableRow++;
186 } else if (fTableRow < fEffectiveRow) {
187 fFirstRowOffset++;
188 needsUpdate = true;
189 }
190 }
191 break;
192 }
193
194 case SWT.PAGE_DOWN: {
195 if (fEffectiveRow < lastTableRow) {
196 if ((lastTableRow - fEffectiveRow) >= fRowsDisplayed) {
197 fEffectiveRow += fRowsDisplayed;
198 fFirstRowOffset += fRowsDisplayed;
199 } else {
200 fEffectiveRow = lastTableRow;
201 fTableRow = lastRowDisplayed;
202 }
203 needsUpdate = true;
204 }
205 break;
206 }
207
208 case SWT.END: {
209 fEffectiveRow = lastTableRow;
210 fTableRow = lastRowDisplayed;
211 if (lastTableRow > lastRowDisplayed) {
212 fFirstRowOffset = fTableItemCount - fRowsDisplayed;
213 }
214 needsUpdate = true;
215 break;
216 }
217
218 case SWT.ARROW_UP: {
219 if (fEffectiveRow > 0) {
220 fEffectiveRow--;
221 if (fTableRow > 0) {
222 fTableRow--;
223 } else {
224 fFirstRowOffset--;
225 needsUpdate = true;
226 }
227 }
228 break;
229 }
230
231 case SWT.PAGE_UP: {
232 if (fEffectiveRow > 0) {
233 if (fEffectiveRow > fRowsDisplayed - 1) {
234 fEffectiveRow -= fRowsDisplayed;
235 fFirstRowOffset -= fRowsDisplayed;
236 } else {
237 fEffectiveRow = 0;
238 fTableRow = 0;
239 }
240 needsUpdate = true;
241 }
242 break;
243 }
244
245 case SWT.HOME: {
246 fEffectiveRow = 0;
247 fTableRow = 0;
248 fFirstRowOffset = 0;
249 needsUpdate = true;
250 break;
251 }
252 }
253
254 if (needsUpdate) {
255 for (int i = 0; i < fTableItems.length; i++) {
256 setDataItem(fTableItems[i]);
257 }
258 }
259
260 fTable.setSelection(fTableRow);
261 fSlider.setSelection(fEffectiveRow);
262
263// System.out.println("1st: " + fFirstRowOffset + ", TR: " + fTableRow + ", ER: " + fEffectiveRow +
264// ", Valid: " + ((fFirstRowOffset >= 0) && (fEffectiveRow == (fFirstRowOffset + fTableRow))));
265 }
266
267 private void setDataItem(TableItem item) {
268 int index = fTable.indexOf(item);
269 if( index != -1) {
270 Event event = new Event();
271 event.item = item;
272 event.index = index + fFirstRowOffset;
273 event.doit = true;
274 notifyListeners(SWT.SetData, event);
275 }
276 }
277
278 // ------------------------------------------------------------------------
279 // Slider handling
280 // ------------------------------------------------------------------------
281
282 private void createSlider() {
283 fSlider = new Slider(this, SWT.VERTICAL);
284 fSlider.setMinimum(0);
285 fSlider.setMaximum(0);
286
287 fSlider.addSelectionListener(new SelectionAdapter() {
288 @Override
289 public void widgetSelected(SelectionEvent event) {
290 setSelection();
291 }
292 });
293
294 fSlider.addListener(SWT.Selection, new Listener() {
295 public void handleEvent(Event event) {
296 switch (event.detail) {
297 case SWT.ARROW_DOWN:
298 case SWT.ARROW_UP:
299 case SWT.NONE:
300 case SWT.END:
301 case SWT.HOME:
302 case SWT.PAGE_DOWN:
303 case SWT.PAGE_UP: {
304 fFirstRowOffset = fSlider.getSelection();
305 setSelection();
306 break;
307 }
308 }
309 }
310 });
311 }
312
313 // ------------------------------------------------------------------------
314 // Simulated Table API
315 // ------------------------------------------------------------------------
316
317 public void setHeaderVisible(boolean b) {
318 fTable.setHeaderVisible(b);
319 }
320
321 public void setLinesVisible(boolean b) {
322 fTable.setLinesVisible(b);
323 }
324
325 public TableItem[] getSelection() {
326 return fSelectedItems;
327 }
328
329 public void addSelectionListener(SelectionAdapter sa) {
330 fTable.addSelectionListener(sa);
331 }
332
333 public void setItemCount(int nbItems) {
334 nbItems = Math.max(0, nbItems);
335 if (nbItems != fTableItemCount) {
336 fTableItemCount = nbItems;
337 fSlider.setMaximum(nbItems);
338 resize();
339 }
340 }
341
342 public int getItemHeight() {
343 return fTable.getItemHeight();
344 }
345
346 public int getTopIndex() {
347 return fFirstRowOffset;
348 }
349
350 public void setTopIndex(int i) {
351 fSlider.setSelection(i);
352 }
353
354 public int indexOf(TableItem ti) {
355 return fTable.indexOf(ti) + getTopIndex();
356 }
357
358 public TableColumn[] getColumns() {
359 return fTable.getColumns();
360 }
361
362 private void resize() {
363
364 // Compute the numbers of rows that fit the new area
365 Rectangle clientArea = getClientArea();
366 int tableHeight = clientArea.height - fTable.getHeaderHeight();
367 int itemHeight = fTable.getItemHeight();
368 fRowsDisplayed = tableHeight / itemHeight + 1; // For partial rows
369 if (fTableItemCount == 0) {
370 fRowsDisplayed = 0;
371 }
372
373 // Re-size and re-create the virtual table if needed
374 int delta = fTable.getItemCount() - fRowsDisplayed;
375 if (delta != 0) {
376 fTable.removeAll();
377 if (fTableItems != null) {
378 for (int i = 0; i < fTableItems.length; i++) {
379 if (fTableItems[i] != null) {
380 fTableItems[i].dispose();
381 }
382 fTableItems[i] = null;
383 }
384 }
385 fTableItems = new TableItem[fRowsDisplayed];
386 for (int i = 0; i < fTableItems.length; i++) {
387 fTableItems[i] = new TableItem(fTable, i);
388 }
389 }
390
391 refresh();
392 }
393
394 // ------------------------------------------------------------------------
395 // Controls interactions
396 // ------------------------------------------------------------------------
397
398 @Override
399 public boolean setFocus() {
400 boolean isVisible = isVisible();
401 if (isVisible) {
402 fTable.setFocus();
403 }
404 return isVisible;
405 }
406
407 public void refresh() {
408 setSelection();
409 }
410
411 public void setColumnHeaders(ColumnData columnData[]) {
412 for (int i = 0; i < columnData.length; i++) {
413 TableColumn column = new TableColumn(fTable, columnData[i].alignment, i);
414 column.setText(columnData[i].header);
415 if (columnData[i].width > 0) {
416 column.setWidth(columnData[i].width);
417 } else {
418 column.pack();
419 }
420 }
421 }
422
423 public int removeAll() {
424 fSlider.setMaximum(0);
425 fTable.removeAll();
426 return 0;
427 }
428
429 private void setSelection() {
430 if ((fEffectiveRow >= fFirstRowOffset) && (fEffectiveRow < (fFirstRowOffset + fRowsDisplayed))) {
431 fTableRow = fEffectiveRow - fFirstRowOffset;
432 fTable.setSelection(fTableRow);
433 } else {
434 fTable.deselect(fTableRow);
435 }
436
437 for (int i = 0; i < fRowsDisplayed; i++) {
438 setDataItem(fTableItems[i]);
439 }
440 }
441
442 public void setSelection(int i) {
443 if (fTableItems != null) {
444 i = Math.min(i, fTableItemCount);
445 i = Math.max(i, 0);
446 fSlider.setSelection(i);
447 setSelection();
448 }
449 }
450
451}
This page took 0.042928 seconds and 5 git commands to generate.