tmf: Add unit tests for time offset
[deliverable/tracecompass.git] / org.eclipse.tracecompass.tmf.ui / src / org / eclipse / tracecompass / tmf / ui / widgets / virtualtable / TmfVirtualTable.java
CommitLineData
9ccc6d01 1/*******************************************************************************
c8422608 2 * Copyright (c) 2010, 2013 Ericsson
3934297e 3 *
9ccc6d01
FC
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
3934297e 8 *
9ccc6d01
FC
9 * Contributors:
10 * Matthew Khouzam - Initial API and implementation
3934297e 11 * Francois Chouinard - Refactoring, slider support, bug fixing
b21305c2 12 * Patrick Tasse - Improvements and bug fixing
7cc11afa 13 * Xavier Raynaud - Improvements
9ccc6d01
FC
14 ******************************************************************************/
15
2bdf0193 16package org.eclipse.tracecompass.tmf.ui.widgets.virtualtable;
9ccc6d01
FC
17
18import org.eclipse.swt.SWT;
3f43dc48 19import org.eclipse.swt.SWTException;
b21305c2 20import org.eclipse.swt.custom.TableEditor;
9ccc6d01
FC
21import org.eclipse.swt.events.ControlAdapter;
22import org.eclipse.swt.events.ControlEvent;
3f43dc48 23import org.eclipse.swt.events.KeyAdapter;
9ccc6d01
FC
24import org.eclipse.swt.events.KeyEvent;
25import org.eclipse.swt.events.KeyListener;
3f43dc48 26import org.eclipse.swt.events.MouseAdapter;
9ccc6d01 27import org.eclipse.swt.events.MouseEvent;
b21305c2 28import org.eclipse.swt.events.MouseListener;
9ccc6d01 29import org.eclipse.swt.events.MouseWheelListener;
dd03e2f1
PT
30import org.eclipse.swt.events.PaintEvent;
31import org.eclipse.swt.events.PaintListener;
9ccc6d01
FC
32import org.eclipse.swt.events.SelectionAdapter;
33import org.eclipse.swt.events.SelectionEvent;
b21305c2
FC
34import org.eclipse.swt.events.SelectionListener;
35import org.eclipse.swt.graphics.Point;
7800a42f
XR
36import org.eclipse.swt.graphics.Rectangle;
37import org.eclipse.swt.layout.FillLayout;
9ccc6d01
FC
38import org.eclipse.swt.layout.GridData;
39import org.eclipse.swt.layout.GridLayout;
40import org.eclipse.swt.widgets.Composite;
b21305c2 41import org.eclipse.swt.widgets.Control;
9ccc6d01 42import org.eclipse.swt.widgets.Event;
7800a42f 43import org.eclipse.swt.widgets.Label;
9ccc6d01 44import org.eclipse.swt.widgets.Listener;
b21305c2 45import org.eclipse.swt.widgets.Menu;
7800a42f 46import org.eclipse.swt.widgets.Shell;
9ccc6d01
FC
47import org.eclipse.swt.widgets.Slider;
48import org.eclipse.swt.widgets.Table;
49import org.eclipse.swt.widgets.TableColumn;
50import org.eclipse.swt.widgets.TableItem;
2bdf0193 51import org.eclipse.tracecompass.internal.tmf.ui.Activator;
7800a42f 52import org.eclipse.ui.PlatformUI;
9ccc6d01
FC
53
54/**
55 * <b><u>TmfVirtualTable</u></b>
56 * <p>
57 * TmfVirtualTable allows for the tabular display of arbitrarily large data sets
58 * (well, up to Integer.MAX_VALUE or ~2G rows).
3934297e 59 *
9ccc6d01
FC
60 * It is essentially a Composite of Table and Slider, where the number of rows
61 * in the table is set to fill the table display area. The slider is rank-based.
3934297e 62 *
9ccc6d01
FC
63 * It differs from Table with the VIRTUAL style flag where an empty entry is
64 * created for each virtual row. This does not scale well for very large data sets.
3934297e 65 *
b21305c2 66 * Styles:
3f43dc48 67 * H_SCROLL, V_SCROLL, SINGLE, MULTI, CHECK, FULL_SELECTION, HIDE_SELECTION, NO_SCROLL
02023181 68 * @author Matthew Khouzam, Francois Chouinard, Patrick Tasse, Xavier Raynaud
3934297e 69 * @version $Revision: 1.0
9ccc6d01
FC
70 */
71public class TmfVirtualTable extends Composite {
72
631d853f
PT
73 // The table
74 private Table fTable;
75 private int fTableRows = 0; // Number of table rows
76 private int fFullyVisibleRows = 0; // Number of fully visible table rows
77 private int fFrozenRowCount = 0; // Number of frozen table rows at top of table
78
79 private int fTableTopEventRank = 0; // Global rank of the first entry displayed
3f43dc48
PT
80 private int fSelectedEventRank = -1; // Global rank of the selected event
81 private int fSelectedBeginRank = -1; // Global rank of the selected begin event
631d853f
PT
82 private boolean fPendingSelection = false; // Pending selection update
83
dadf6e1a 84 private int fTableItemCount = 0;
631d853f
PT
85
86 // The slider
87 private Slider fSlider;
88
89 private int fLinuxItemHeight = 0; // Calculated item height for Linux workaround
90 private TooltipProvider tooltipProvider = null;
91 private IDoubleClickListener doubleClickListener = null;
92
b06f64b7
PT
93 private boolean fResetTopIndex = false; // Flag to trigger reset of top index
94 private ControlAdapter fResizeListener; // Resize listener to update visible rows
c9787691 95
631d853f
PT
96 // ------------------------------------------------------------------------
97 // Constructor
98 // ------------------------------------------------------------------------
99
100 /**
3934297e
AM
101 * Standard constructor
102 *
631d853f 103 * @param parent
3934297e 104 * The parent composite object
631d853f 105 * @param style
3934297e 106 * The style to use
631d853f
PT
107 */
108 public TmfVirtualTable(Composite parent, int style) {
3f43dc48 109 super(parent, style & (~SWT.H_SCROLL) & (~SWT.V_SCROLL) & (~SWT.SINGLE) & (~SWT.MULTI) & (~SWT.FULL_SELECTION) & (~SWT.HIDE_SELECTION) & (~SWT.CHECK));
631d853f
PT
110
111 // Create the controls
3f43dc48 112 createTable(style & (SWT.H_SCROLL | SWT.SINGLE | SWT.MULTI | SWT.FULL_SELECTION | SWT.HIDE_SELECTION | SWT.CHECK));
631d853f
PT
113 createSlider(style & SWT.V_SCROLL);
114
115 // Prevent the slider from being traversed
116 setTabList(new Control[] { fTable });
117
118 // Set the layout
119 GridLayout gridLayout = new GridLayout();
120 gridLayout.numColumns = 2;
121 gridLayout.horizontalSpacing = 0;
122 gridLayout.verticalSpacing = 0;
123 gridLayout.marginWidth = 0;
124 gridLayout.marginHeight = 0;
125 setLayout(gridLayout);
126
127 GridData tableGridData = new GridData(SWT.FILL, SWT.FILL, true, true);
128 fTable.setLayoutData(tableGridData);
129
130 GridData sliderGridData = new GridData(SWT.FILL, SWT.FILL, false, true);
131 fSlider.setLayoutData(sliderGridData);
132
133 // Add the listeners
134 fTable.addMouseWheelListener(new MouseWheelListener() {
135 @Override
136 public void mouseScrolled(MouseEvent event) {
137 if (fTableItemCount <= fFullyVisibleRows) {
138 return;
139 }
140 fTableTopEventRank -= event.count;
141 if (fTableTopEventRank < 0) {
142 fTableTopEventRank = 0;
143 }
144 int latestFirstRowOffset = fTableItemCount - fFullyVisibleRows;
145 if (fTableTopEventRank > latestFirstRowOffset) {
146 fTableTopEventRank = latestFirstRowOffset;
147 }
148
149 fSlider.setSelection(fTableTopEventRank);
150 refreshTable();
151 }
152 });
153
154 fTable.addListener(SWT.MouseWheel, new Listener() {
155 // disable mouse scroll of horizontal scroll bar
156 @Override
b21305c2 157 public void handleEvent(Event event) {
631d853f
PT
158 event.doit = false;
159 }
160 });
161
b06f64b7 162 fResizeListener = new ControlAdapter() {
631d853f
PT
163 @Override
164 public void controlResized(ControlEvent event) {
165 int tableHeight = Math.max(0, fTable.getClientArea().height - fTable.getHeaderHeight());
166 fFullyVisibleRows = tableHeight / getItemHeight();
167 if (fTableItemCount > 0) {
168 fSlider.setThumb(Math.max(1, Math.min(fTableRows, fFullyVisibleRows)));
169 }
170 }
b06f64b7
PT
171 };
172 fTable.addControlListener(fResizeListener);
173
631d853f
PT
174 // Implement a "fake" tooltip
175 final String TOOLTIP_DATA_KEY = "_TABLEITEM"; //$NON-NLS-1$
dadf6e1a 176 final Listener labelListener = new Listener() {
631d853f 177 @Override
f4c52cea 178 public void handleEvent (Event event) {
dadf6e1a
EB
179 Label label = (Label) event.widget;
180 Shell shell = label.getShell();
631d853f
PT
181 switch (event.type) {
182 case SWT.MouseDown:
dadf6e1a
EB
183 Event e = new Event();
184 e.item = (TableItem) label.getData(TOOLTIP_DATA_KEY);
631d853f
PT
185 // Assuming table is single select, set the selection as if
186 // the mouse down event went through to the table
dadf6e1a
EB
187 fTable.setSelection(new TableItem [] {(TableItem) e.item});
188 fTable.notifyListeners(SWT.Selection, e);
189 shell.dispose();
631d853f
PT
190 fTable.setFocus();
191 break;
192 case SWT.MouseExit:
193 case SWT.MouseWheel:
dadf6e1a 194 shell.dispose();
631d853f 195 break;
abbdd66a
AM
196 default:
197 break;
631d853f
PT
198 }
199 }
200 };
201
dadf6e1a 202 Listener tableListener = new Listener() {
631d853f
PT
203 Shell tip = null;
204 Label label = null;
205 @Override
dadf6e1a 206 public void handleEvent(Event event) {
631d853f
PT
207 switch (event.type) {
208 case SWT.Dispose:
209 case SWT.KeyDown:
210 case SWT.MouseMove: {
420bceb2
PT
211 if (tip == null) {
212 break;
213 }
dadf6e1a 214 tip.dispose();
631d853f
PT
215 tip = null;
216 label = null;
217 break;
218 }
219 case SWT.MouseHover: {
dadf6e1a 220 TableItem item = fTable.getItem(new Point(event.x, event.y));
631d853f 221 if (item != null) {
dadf6e1a 222 for (int i = 0; i < fTable.getColumnCount(); i++) {
631d853f 223 Rectangle bounds = item.getBounds(i);
dadf6e1a 224 if (bounds.contains(event.x, event.y)) {
420bceb2
PT
225 if (tip != null && !tip.isDisposed()) {
226 tip.dispose();
227 }
631d853f
PT
228 if (tooltipProvider == null) {
229 return;
631d853f 230 }
abbdd66a
AM
231 String tooltipText = tooltipProvider.getTooltip(i, item.getData());
232 if (tooltipText == null) {
233 return;
234 }
235 tip = new Shell(fTable.getShell(), SWT.ON_TOP | SWT.NO_FOCUS | SWT.TOOL);
236 tip.setBackground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));
237 FillLayout layout = new FillLayout();
238 layout.marginWidth = 2;
239 tip.setLayout(layout);
240 label = new Label(tip, SWT.WRAP);
241 label.setForeground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND));
242 label.setBackground(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));
243 label.setData(TOOLTIP_DATA_KEY, item);
244 label.setText(tooltipText);
245
246 label.addListener(SWT.MouseExit, labelListener);
247 label.addListener(SWT.MouseDown, labelListener);
248 label.addListener(SWT.MouseWheel, labelListener);
249 Point size = tip.computeSize(SWT.DEFAULT, SWT.DEFAULT);
250 Point pt = fTable.toDisplay(bounds.x, bounds.y);
251 tip.setBounds(pt.x, pt.y, size.x, size.y);
252 tip.setVisible(true);
dadf6e1a
EB
253
254 // Item found, leave loop.
255 break;
631d853f
PT
256 }
257 }
258 }
abbdd66a 259 break;
631d853f 260 }
abbdd66a
AM
261 default:
262 break;
631d853f
PT
263 }
264 }
265 };
266 fTable.addListener(SWT.Dispose, tableListener);
267 fTable.addListener(SWT.KeyDown, tableListener);
268 fTable.addListener(SWT.MouseMove, tableListener);
269 fTable.addListener(SWT.MouseHover, tableListener);
270 addControlListener(new ControlAdapter() {
271 @Override
272 public void controlResized(ControlEvent event) {
273 resize();
420bceb2
PT
274 if (fTableItemCount > 0) {
275 fSlider.setThumb(Math.max(1, Math.min(fTableRows, fFullyVisibleRows)));
276 }
631d853f
PT
277 }
278 });
279
280 // And display
281 refresh();
282 }
283
284 // ------------------------------------------------------------------------
285 // Table handling
286 // ------------------------------------------------------------------------
287
288 /**
289 * Create the table and add listeners
3f43dc48 290 * @param style int can be H_SCROLL, SINGLE, MULTI, FULL_SELECTION, HIDE_SELECTION, CHECK
631d853f
PT
291 */
292 private void createTable(int style) {
293 fTable = new Table(this, style | SWT.NO_SCROLL);
294
295 fTable.addSelectionListener(new SelectionAdapter() {
296 @Override
297 public void widgetSelected(SelectionEvent event) {
3f43dc48
PT
298 if (event.item == null) {
299 // Override table selection from Select All action
300 refreshSelection();
631d853f
PT
301 }
302 }
303 });
9ccc6d01 304
3f43dc48 305 fTable.addMouseListener(new MouseAdapter() {
631d853f 306 @Override
3f43dc48
PT
307 public void mouseDown(MouseEvent e) {
308 handleTableMouseEvent(e);
631d853f 309 }
3f43dc48
PT
310 });
311
312 fTable.addKeyListener(new KeyAdapter() {
631d853f 313 @Override
3f43dc48
PT
314 public void keyPressed(KeyEvent event) {
315 handleTableKeyEvent(event);
631d853f
PT
316 }
317 });
7800a42f
XR
318
319 fTable.addListener(
631d853f
PT
320 SWT.MouseDoubleClick, new Listener() {
321 @Override
322 public void handleEvent(Event event) {
323 if (doubleClickListener != null) {
dadf6e1a 324 TableItem item = fTable.getItem(new Point (event.x, event.y));
631d853f 325 if (item != null) {
dadf6e1a 326 for (int i = 0; i < fTable.getColumnCount(); i++){
631d853f 327 Rectangle bounds = item.getBounds(i);
dadf6e1a 328 if (bounds.contains(event.x, event.y)){
631d853f
PT
329 doubleClickListener.handleDoubleClick(TmfVirtualTable.this, item, i);
330 break;
331 }
332 }
333 }
334 }
335 }
336 }
337 );
dd03e2f1
PT
338
339 /*
c9787691
PT
340 * Feature in Windows. When a partially visible table item is selected,
341 * after ~500 ms the top index is changed to ensure the selected item is
342 * fully visible. This leaves a blank space at the bottom of the virtual
343 * table. The workaround is to reset the top index to 0 if it is not 0.
344 * Also reset the top index to 0 if indicated by the flag that was set
345 * at table selection of a partially visible table item.
dd03e2f1
PT
346 */
347 fTable.addPaintListener(new PaintListener() {
348 @Override
349 public void paintControl(PaintEvent e) {
c9787691
PT
350 if (fTable.getTopIndex() != 0 || fResetTopIndex) {
351 fTable.setTopIndex(0);
352 }
353 fResetTopIndex = false;
dd03e2f1
PT
354 }
355 });
631d853f
PT
356 }
357
358 /**
3f43dc48
PT
359 * Handle mouse-based selection in table.
360 *
361 * @param event the mouse event
631d853f 362 */
3f43dc48
PT
363 private void handleTableMouseEvent(MouseEvent event) {
364 TableItem item = fTable.getItem(new Point(event.x, event.y));
365 if (item == null) {
366 return;
367 }
368 int selectedRow = indexOf(item);
90328647
PT
369 if (event.button == 1 || (event.button == 3 &&
370 (selectedRow < Math.min(fSelectedBeginRank, fSelectedEventRank) ||
371 selectedRow > Math.max(fSelectedBeginRank, fSelectedEventRank)))) {
3f43dc48
PT
372 if (selectedRow >= 0) {
373 fSelectedEventRank = selectedRow;
374 } else {
375 fSelectedEventRank = -1;
376 }
377 if ((event.stateMask & SWT.SHIFT) == 0 || (fTable.getStyle() & SWT.MULTI) == 0 || fSelectedBeginRank == -1) {
378 fSelectedBeginRank = fSelectedEventRank;
379 }
631d853f 380 }
3f43dc48 381 refreshSelection();
c9787691
PT
382
383 /*
384 * Feature in Linux. When a partially visible table item is selected,
385 * the origin is changed to ensure the selected item is fully visible.
386 * This makes the first row partially visible. The solution is to force
387 * reset the origin by setting the top index to 0. This should happen
388 * only once at the next redraw by the paint listener.
389 */
390 if (selectedRow >= fFullyVisibleRows) {
391 fResetTopIndex = true;
392 }
631d853f
PT
393 }
394
395 /**
396 * Handle key-based navigation in table.
3934297e 397 *
3f43dc48 398 * @param event the key event
631d853f
PT
399 */
400 private void handleTableKeyEvent(KeyEvent event) {
401
402 int lastEventRank = fTableItemCount - 1;
403 int lastPageTopEntryRank = Math.max(0, fTableItemCount - fFullyVisibleRows);
404
405 int previousSelectedEventRank = fSelectedEventRank;
3f43dc48 406 int previousSelectedBeginRank = fSelectedBeginRank;
631d853f
PT
407 boolean needsRefresh = false;
408
409 // In all case, perform the following steps:
410 // - Update the selected entry rank (within valid range)
411 // - Update the selected row
412 // - Update the page's top entry if necessary (which also adjusts the selected row)
413 // - If the top displayed entry was changed, table refresh is needed
414 switch (event.keyCode) {
415
416 case SWT.ARROW_DOWN: {
417 event.doit = false;
418 if (fSelectedEventRank < lastEventRank) {
419 fSelectedEventRank++;
3f43dc48 420 int selectedRow = fSelectedEventRank - fTableTopEventRank;
75c271cf 421 if (selectedRow == fFullyVisibleRows) {
631d853f
PT
422 fTableTopEventRank++;
423 needsRefresh = true;
75c271cf
PT
424 } else if (selectedRow < fFrozenRowCount || selectedRow > fFullyVisibleRows) {
425 fTableTopEventRank = Math.max(0, Math.min(fSelectedEventRank - fFrozenRowCount, lastPageTopEntryRank));
426 needsRefresh = true;
631d853f
PT
427 }
428 }
429 break;
430 }
431
432 case SWT.ARROW_UP: {
433 event.doit = false;
434 if (fSelectedEventRank > 0) {
435 fSelectedEventRank--;
3f43dc48 436 int selectedRow = fSelectedEventRank - fTableTopEventRank;
75c271cf 437 if (selectedRow == fFrozenRowCount - 1 && fTableTopEventRank > 0) {
631d853f
PT
438 fTableTopEventRank--;
439 needsRefresh = true;
75c271cf
PT
440 } else if (selectedRow < fFrozenRowCount || selectedRow > fFullyVisibleRows) {
441 fTableTopEventRank = Math.max(0, Math.min(fSelectedEventRank - fFrozenRowCount, lastPageTopEntryRank));
442 needsRefresh = true;
631d853f
PT
443 }
444 }
445 break;
446 }
447
448 case SWT.END: {
449 event.doit = false;
450 fTableTopEventRank = lastPageTopEntryRank;
451 fSelectedEventRank = lastEventRank;
452 needsRefresh = true;
453 break;
454 }
455
456 case SWT.HOME: {
457 event.doit = false;
458 fSelectedEventRank = fFrozenRowCount;
459 fTableTopEventRank = 0;
460 needsRefresh = true;
461 break;
462 }
463
464 case SWT.PAGE_DOWN: {
465 event.doit = false;
466 if (fSelectedEventRank < lastEventRank) {
467 fSelectedEventRank += fFullyVisibleRows;
468 if (fSelectedEventRank > lastEventRank) {
469 fSelectedEventRank = lastEventRank;
470 }
3f43dc48 471 int selectedRow = fSelectedEventRank - fTableTopEventRank;
75c271cf 472 if (selectedRow > fFullyVisibleRows + fFrozenRowCount - 1 && selectedRow < 2 * fFullyVisibleRows) {
631d853f
PT
473 fTableTopEventRank += fFullyVisibleRows;
474 if (fTableTopEventRank > lastPageTopEntryRank) {
475 fTableTopEventRank = lastPageTopEntryRank;
476 }
477 needsRefresh = true;
75c271cf
PT
478 } else if (selectedRow < fFrozenRowCount || selectedRow >= 2 * fFullyVisibleRows) {
479 fTableTopEventRank = Math.max(0, Math.min(fSelectedEventRank - fFrozenRowCount, lastPageTopEntryRank));
480 needsRefresh = true;
631d853f
PT
481 }
482 }
483 break;
484 }
485
486 case SWT.PAGE_UP: {
487 event.doit = false;
488 if (fSelectedEventRank > 0) {
489 fSelectedEventRank -= fFullyVisibleRows;
490 if (fSelectedEventRank < fFrozenRowCount) {
491 fSelectedEventRank = fFrozenRowCount;
492 }
3f43dc48 493 int selectedRow = fSelectedEventRank - fTableTopEventRank;
75c271cf 494 if (selectedRow < fFrozenRowCount && selectedRow > -fFullyVisibleRows) {
631d853f
PT
495 fTableTopEventRank -= fFullyVisibleRows;
496 if (fTableTopEventRank < 0) {
497 fTableTopEventRank = 0;
498 }
499 needsRefresh = true;
75c271cf
PT
500 } else if (selectedRow <= -fFullyVisibleRows || selectedRow >= fFullyVisibleRows) {
501 fTableTopEventRank = Math.max(0, Math.min(fSelectedEventRank - fFrozenRowCount, lastPageTopEntryRank));
502 needsRefresh = true;
631d853f
PT
503 }
504 }
505 break;
506 }
507 default: {
508 return;
509 }
510 }
511
3f43dc48
PT
512 if ((event.stateMask & SWT.SHIFT) == 0 || (fTable.getStyle() & SWT.MULTI) == 0 || fSelectedBeginRank == -1) {
513 fSelectedBeginRank = fSelectedEventRank;
514 }
515
631d853f
PT
516 boolean done = true;
517 if (needsRefresh) {
518 done = refreshTable(); // false if table items not updated yet in this thread
519 } else {
3f43dc48 520 refreshSelection();
631d853f
PT
521 }
522
523 if (fFullyVisibleRows < fTableItemCount) {
524 fSlider.setSelection(fTableTopEventRank);
525 }
526
3f43dc48 527 if (fSelectedEventRank != previousSelectedEventRank || fSelectedBeginRank != previousSelectedBeginRank) {
631d853f
PT
528 if (done) {
529 Event e = new Event();
3f43dc48 530 e.item = fTable.getItem(fSelectedEventRank - fTableTopEventRank);
631d853f
PT
531 fTable.notifyListeners(SWT.Selection, e);
532 } else {
533 fPendingSelection = true;
534 }
535 }
536 }
537
02023181
MK
538 /**
539 * Method setDataItem.
540 * @param index int
541 * @param item TableItem
542 * @return boolean
543 */
631d853f
PT
544 private boolean setDataItem(int index, TableItem item) {
545 if (index != -1) {
546 Event event = new Event();
547 event.item = item;
548 if (index < fFrozenRowCount) {
549 event.index = index;
550 } else {
551 event.index = index + fTableTopEventRank;
552 }
553 event.doit = true;
554 fTable.notifyListeners(SWT.SetData, event);
555 return event.doit; // false if table item not updated yet in this thread
556 }
557 return true;
558 }
559
560 // ------------------------------------------------------------------------
561 // Slider handling
562 // ------------------------------------------------------------------------
563
02023181
MK
564 /**
565 * Method createSlider.
566 * @param style int
567 */
631d853f
PT
568 private void createSlider(int style) {
569 fSlider = new Slider(this, SWT.VERTICAL | SWT.NO_FOCUS);
570 fSlider.setMinimum(0);
571 fSlider.setMaximum(0);
572 if ((style & SWT.V_SCROLL) == 0) {
573 fSlider.setVisible(false);
574 }
575
576 fSlider.addListener(SWT.Selection, new Listener() {
577 @Override
578 public void handleEvent(Event event) {
579 switch (event.detail) {
580 case SWT.ARROW_DOWN:
581 case SWT.ARROW_UP:
631d853f
PT
582 case SWT.END:
583 case SWT.HOME:
584 case SWT.PAGE_DOWN:
585 case SWT.PAGE_UP: {
586 fTableTopEventRank = fSlider.getSelection();
587 refreshTable();
588 break;
589 }
874af45a
PT
590 // Not handled because of bug on Linux described below.
591 case SWT.NONE:
abbdd66a
AM
592 default:
593 break;
631d853f
PT
594 }
595 }
596 });
874af45a
PT
597
598 /*
599 * In Linux, the selection event above has event.detail set to SWT.NONE
600 * instead of SWT.DRAG during dragging of the thumb. To prevent refresh
601 * overflow, only update the table when the mouse button is released.
602 */
603 fSlider.addMouseListener(new MouseAdapter() {
604 @Override
605 public void mouseUp(MouseEvent e) {
606 fTableTopEventRank = fSlider.getSelection();
607 refreshTable();
608 }
609 });
610
631d853f
PT
611 }
612
613 // ------------------------------------------------------------------------
614 // Simulated Table API
615 // ------------------------------------------------------------------------
616
2fcd5eea
AM
617 /**
618 * Constructs a new TableColumn instance given a style value describing its
619 * alignment behavior. The column is added to the end of the columns
620 * maintained by the table.
621 *
622 * @param style
623 * the alignment style
624 * @return the new TableColumn
625 *
626 * @see SWT#LEFT
627 * @see SWT#RIGHT
628 * @see SWT#CENTER
629 *
630 * @since 3.1
631 */
632 public TableColumn newTableColumn(int style) {
633 TableColumn column = new TableColumn(fTable, style);
634
635 /*
636 * In Linux the table does not receive a control resized event when
637 * a table column resize causes the horizontal scroll bar to become
638 * visible or invisible, so a resize listener must be added to every
639 * table column to properly update the number of fully visible rows.
640 */
641 column.addControlListener(fResizeListener);
642
643 return column;
644 }
645
02023181
MK
646 /**
647 * Method setHeaderVisible.
648 * @param b boolean
649 */
631d853f
PT
650 public void setHeaderVisible(boolean b) {
651 fTable.setHeaderVisible(b);
652 }
653
02023181
MK
654 /**
655 * Method setLinesVisible.
656 * @param b boolean
657 */
631d853f
PT
658 public void setLinesVisible(boolean b) {
659 fTable.setLinesVisible(b);
660 }
661
02023181 662 /**
3f43dc48
PT
663 * Returns an array of <code>TableItem</code>s that are currently selected
664 * in the receiver. The order of the items is unspecified. An empty array
665 * indicates that no items are selected.
666 * <p>
667 * Note: This array only contains the visible selected items in the virtual
668 * table. To get information about the full selection range, use
669 * {@link #getSelectionIndices()}.
670 * </p>
671 *
672 * @return an array representing the selection
673 *
674 * @exception SWTException <ul>
675 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
676 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
677 * </ul>
02023181 678 */
631d853f
PT
679 public TableItem[] getSelection() {
680 return fTable.getSelection();
681 }
682
02023181
MK
683 /**
684 * Method addListener.
685 * @param eventType int
686 * @param listener Listener
687 */
631d853f 688 @Override
828e5592
PT
689 public void addListener(int eventType, Listener listener) {
690 fTable.addListener(eventType, listener);
691 }
631d853f 692
02023181
MK
693 /**
694 * Method addKeyListener.
695 * @param listener KeyListener
696 */
631d853f
PT
697 @Override
698 public void addKeyListener(KeyListener listener) {
699 fTable.addKeyListener(listener);
700 }
b21305c2 701
02023181
MK
702 /**
703 * Method addMouseListener.
704 * @param listener MouseListener
705 */
828e5592 706 @Override
631d853f
PT
707 public void addMouseListener(MouseListener listener) {
708 fTable.addMouseListener(listener);
709 }
710
02023181
MK
711 /**
712 * Method addSelectionListener.
713 * @param listener SelectionListener
714 */
631d853f
PT
715 public void addSelectionListener(SelectionListener listener) {
716 fTable.addSelectionListener(listener);
717 }
718
02023181
MK
719 /**
720 * Method setMenu sets the menu
721 * @param menu Menu the menu
722 */
631d853f
PT
723 @Override
724 public void setMenu(Menu menu) {
725 fTable.setMenu(menu);
726 }
727
48da630d
XR
728 /**
729 * Gets the menu of this table
730 * @return a Menu
731 */
732 @Override
733 public Menu getMenu() {
734 return fTable.getMenu();
735 }
736
02023181 737 /**
3934297e 738 * Method clearAll empties a table.
02023181 739 */
631d853f
PT
740 public void clearAll() {
741 setItemCount(0);
742 }
743
02023181
MK
744 /**
745 * Method setItemCount
746 * @param nbItems int the number of items in the table
d5efe032 747 *
02023181 748 */
631d853f 749 public void setItemCount(int nbItems) {
41b5c37f 750 final int nb = Math.max(0, nbItems);
631d853f 751
41b5c37f
AM
752 if (nb != fTableItemCount) {
753 fTableItemCount = nb;
631d853f 754 fTable.remove(fTableItemCount, fTable.getItemCount() - 1);
41b5c37f 755 fSlider.setMaximum(nb);
631d853f
PT
756 resize();
757 int tableHeight = Math.max(0, fTable.getClientArea().height - fTable.getHeaderHeight());
758 fFullyVisibleRows = tableHeight / getItemHeight();
759 if (fTableItemCount > 0) {
760 fSlider.setThumb(Math.max(1, Math.min(fTableRows, fFullyVisibleRows)));
761 }
762 }
763 }
764
02023181
MK
765 /**
766 * Method getItemCount.
767 * @return int the number of items in the table
768 */
631d853f
PT
769 public int getItemCount() {
770 return fTableItemCount;
771 }
772
02023181
MK
773 /**
774 * Method getItemHeight.
775 * @return int the height of a table item in pixels. (may vary from one os to another)
776 */
631d853f
PT
777 public int getItemHeight() {
778 /*
779 * Bug in Linux. The method getItemHeight doesn't always return the correct value.
780 */
781 if (fLinuxItemHeight >= 0 && System.getProperty("os.name").contains("Linux")) { //$NON-NLS-1$ //$NON-NLS-2$
782 if (fLinuxItemHeight != 0) {
783 return fLinuxItemHeight;
784 }
785 if (fTable.getItemCount() > 1) {
786 int itemHeight = fTable.getItem(1).getBounds().y - fTable.getItem(0).getBounds().y;
787 if (itemHeight > 0) {
788 fLinuxItemHeight = itemHeight;
789 return fLinuxItemHeight;
790 }
791 }
792 } else {
793 fLinuxItemHeight = -1; // Not Linux, don't perform os.name check anymore
794 }
795 return fTable.getItemHeight();
796 }
797
02023181
MK
798 /**
799 * Method getHeaderHeight.
800 * @return int get the height of the header in pixels.
801 */
ea08e8ed
PT
802 public int getHeaderHeight() {
803 return fTable.getHeaderHeight();
804 }
805
02023181
MK
806 /**
807 * Method getTopIndex.
808 * @return int get the first data item index, if you have a header it is offset.
809 */
631d853f
PT
810 public int getTopIndex() {
811 return fTableTopEventRank + fFrozenRowCount;
812 }
813
02023181
MK
814 /**
815 * Method setTopIndex.
41b5c37f 816 * @param index int suggested top index for the table.
02023181 817 */
41b5c37f 818 public void setTopIndex(int index) {
631d853f 819 if (fTableItemCount > 0) {
41b5c37f 820 int i = Math.min(index, fTableItemCount - 1);
631d853f
PT
821 i = Math.max(i, fFrozenRowCount);
822
823 fTableTopEventRank = i - fFrozenRowCount;
824 if (fFullyVisibleRows < fTableItemCount) {
825 fSlider.setSelection(fTableTopEventRank);
826 }
827
828 refreshTable();
829 }
830 }
831
02023181
MK
832 /**
833 * Method indexOf. Return the index of a table item
834 * @param ti TableItem the table item to search for in the table
3934297e 835 * @return int the index of the first match. (there should only be one match)
02023181 836 */
631d853f
PT
837 public int indexOf(TableItem ti) {
838 int index = fTable.indexOf(ti);
839 if (index < fFrozenRowCount) {
840 return index;
631d853f 841 }
abbdd66a 842 return (index - fFrozenRowCount) + getTopIndex();
631d853f
PT
843 }
844
02023181
MK
845 /**
846 * Method getColumns.
3934297e 847 * @return TableColumn[] the table columns
02023181 848 */
631d853f
PT
849 public TableColumn[] getColumns() {
850 return fTable.getColumns();
851 }
852
02023181
MK
853 /**
854 * Method getItem.
855 * @param point Point the coordinates in the table
3934297e 856 * @return TableItem the corresponding table item
02023181 857 */
631d853f
PT
858 public TableItem getItem(Point point) {
859 return fTable.getItem(point);
860 }
861
02023181
MK
862 /**
863 * Method resize.
864 */
631d853f
PT
865 private void resize() {
866 // Compute the numbers of rows that fit the new area
867 int tableHeight = Math.max(0, getSize().y - fTable.getHeaderHeight());
868 int itemHeight = getItemHeight();
869 fTableRows = Math.min((tableHeight + itemHeight - 1) / itemHeight, fTableItemCount);
870
871 if (fTableTopEventRank + fFullyVisibleRows > fTableItemCount) {
872 // If we are at the end, get elements before to populate
873 fTableTopEventRank = Math.max(0, fTableItemCount - fFullyVisibleRows);
874 refreshTable();
875 } else if (fTableRows > fTable.getItemCount() || fTableItemCount < fTable.getItemCount()) {
876 // Only refresh if new table items are needed or if table items need to be deleted
877 refreshTable();
878 }
879
880 }
881
882 // ------------------------------------------------------------------------
883 // Controls interactions
884 // ------------------------------------------------------------------------
885
02023181
MK
886 /**
887 * Method setFocus.
888 * @return boolean is this visible?
889 */
631d853f
PT
890 @Override
891 public boolean setFocus() {
892 boolean isVisible = isVisible();
893 if (isVisible) {
894 fTable.setFocus();
895 }
896 return isVisible;
897 }
898
02023181
MK
899 /**
900 * Method refresh.
901 */
631d853f
PT
902 public void refresh() {
903 boolean done = refreshTable();
3f43dc48
PT
904 if (!done) {
905 return;
906 }
dadf6e1a 907 if (fPendingSelection) {
631d853f 908 fPendingSelection = false;
3f43dc48
PT
909 TableItem item = null;
910 if (fSelectedEventRank >= 0 && fSelectedEventRank < fFrozenRowCount) {
911 item = fTable.getItem(fSelectedEventRank);
912 } else if (fSelectedEventRank >= fTableTopEventRank + fFrozenRowCount && fSelectedEventRank - fTableTopEventRank < fTable.getItemCount()) {
913 item = fTable.getItem(fSelectedEventRank - fTableTopEventRank);
914 }
915 if (item != null) {
631d853f 916 Event e = new Event();
3f43dc48 917 e.item = item;
631d853f
PT
918 fTable.notifyListeners(SWT.Selection, e);
919 }
920 }
921 }
922
02023181
MK
923 /**
924 * Method setColumnHeaders.
baafe54c
AM
925 *
926 * @param columnData
927 * ColumnData[] the columndata array.
02023181 928 */
baafe54c 929 @Deprecated
631d853f 930 public void setColumnHeaders(ColumnData columnData[]) {
baafe54c
AM
931 /* No-op */
932 }
933
02023181
MK
934 /**
935 * Method removeAll.
936 * @return int 0 the number of elements in the table
937 */
631d853f
PT
938 public int removeAll() {
939 setItemCount(0);
940 fSlider.setMaximum(0);
941 fTable.removeAll();
3f43dc48
PT
942 fSelectedEventRank = -1;
943 fSelectedBeginRank = fSelectedEventRank;
631d853f
PT
944 return 0;
945 }
946
02023181
MK
947 /**
948 * Method refreshTable.
3f43dc48 949 * @return true if all table items have been refreshed, false otherwise
02023181 950 */
631d853f
PT
951 private boolean refreshTable() {
952 boolean done = true;
953 for (int i = 0; i < fTableRows; i++) {
954 if (i + fTableTopEventRank < fTableItemCount) {
955 TableItem tableItem;
956 if (i < fTable.getItemCount()) {
957 tableItem = fTable.getItem(i);
958 } else {
959 tableItem = new TableItem(fTable, SWT.NONE);
960 }
961 done &= setDataItem(i, tableItem); // false if table item not updated yet in this thread
962 } else {
963 if (fTable.getItemCount() > fTableItemCount - fTableTopEventRank) {
964 fTable.remove(fTableItemCount - fTableTopEventRank);
965 }
966 }
967 }
3f43dc48
PT
968 if (done) {
969 refreshSelection();
970 } else {
971 fTable.deselectAll();
972 }
973 return done;
974 }
631d853f 975
3f43dc48 976 private void refreshSelection() {
631d853f 977 int lastRowOffset = fTableTopEventRank + fTableRows - 1;
3f43dc48
PT
978 int startRank = Math.min(fSelectedBeginRank, fSelectedEventRank);
979 int endRank = Math.max(fSelectedBeginRank, fSelectedEventRank);
980 int start = Integer.MAX_VALUE;
981 int end = Integer.MIN_VALUE;
982 if (startRank < fFrozenRowCount) {
983 start = startRank;
984 } else if (startRank < fTableTopEventRank + fFrozenRowCount) {
985 start = fFrozenRowCount;
986 } else if (startRank <= lastRowOffset) {
987 start = startRank - fTableTopEventRank;
988 }
989 if (endRank < fFrozenRowCount) {
990 end = endRank;
991 } else if (endRank < fTableTopEventRank + fFrozenRowCount) {
992 end = fFrozenRowCount - 1;
993 } else if (endRank <= lastRowOffset) {
994 end = endRank - fTableTopEventRank;
995 } else {
996 end = fTableRows - 1;
997 }
998 if (start <= end) {
999 fTable.setSelection(start, end);
1000 if (startRank == fSelectedEventRank) {
1001 fTable.select(start);
1002 } else {
1003 fTable.select(end);
1004 }
631d853f
PT
1005 } else {
1006 fTable.deselectAll();
1007 }
631d853f
PT
1008 }
1009
02023181 1010 /**
3f43dc48
PT
1011 * Selects the item at the given zero-relative index in the receiver.
1012 * The current selection is first cleared, then the new item is selected,
1013 * and if necessary the receiver is scrolled to make the new selection visible.
1014 *
1015 * @param index the index of the item to select
1016 *
1017 * @exception SWTException <ul>
1018 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1019 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1020 * </ul>
02023181 1021 */
41b5c37f 1022 public void setSelection(int index) {
631d853f 1023 if (fTableItemCount > 0) {
41b5c37f 1024 int i = Math.min(index, fTableItemCount - 1);
631d853f
PT
1025 i = Math.max(i, 0);
1026
1027 fSelectedEventRank = i;
3f43dc48 1028 fSelectedBeginRank = fSelectedEventRank;
631d853f
PT
1029 if ((i < fTableTopEventRank + fFrozenRowCount && i >= fFrozenRowCount) ||
1030 (i >= fTableTopEventRank + fFullyVisibleRows)) {
faa38350
PT
1031 int lastPageTopEntryRank = Math.max(0, fTableItemCount - fFullyVisibleRows);
1032 fTableTopEventRank = Math.max(0, Math.min(lastPageTopEntryRank, i - fFrozenRowCount - fFullyVisibleRows / 2));
631d853f
PT
1033 }
1034 if (fFullyVisibleRows < fTableItemCount) {
1035 fSlider.setSelection(fTableTopEventRank);
1036 }
1037
1038 refreshTable();
631d853f
PT
1039 }
1040 }
1041
02023181 1042 /**
3f43dc48
PT
1043 * Returns the zero-relative index of the item which is currently
1044 * selected in the receiver, or -1 if no item is selected.
1045 *
1046 * @return the index of the selected item
02023181 1047 */
631d853f 1048 public int getSelectionIndex() {
3f43dc48
PT
1049 return fSelectedEventRank;
1050 }
1051
1052 /**
1053 * Returns an index array representing the selection range. If there is a
1054 * single item selected the array holds one index. If there is a selected
1055 * range the first item in the array is the start index of the selection and
1056 * the second item is the end index of the selection, which is the item most
1057 * recently selected. The array is empty if no items are selected.
1058 * <p>
1059 * @return the array of indices of the selected items
4b121c48 1060 * @since 2.1
3f43dc48
PT
1061 */
1062 public int[] getSelectionIndices() {
1063 if (fSelectedEventRank < 0 || fSelectedBeginRank < 0) {
1064 return new int[] {};
1065 } else if (fSelectedEventRank == fSelectedBeginRank) {
1066 return new int[] { fSelectedEventRank };
631d853f 1067 }
3f43dc48 1068 return new int[] { fSelectedBeginRank, fSelectedEventRank };
631d853f
PT
1069 }
1070
02023181
MK
1071 /**
1072 * Method setFrozenRowCount.
1073 * @param count int the number of rows to freeze from the top row
1074 */
631d853f
PT
1075 public void setFrozenRowCount(int count) {
1076 fFrozenRowCount = count;
1077 refreshTable();
1078 }
1079
02023181
MK
1080 /**
1081 * Method createTableEditor.
1082 * @return a TableEditor of the table
1083 */
631d853f
PT
1084 public TableEditor createTableEditor() {
1085 return new TableEditor(fTable);
1086 }
1087
02023181
MK
1088 /**
1089 * Method createTableEditorControl.
1090 * @param control Class<? extends Control>
1091 * @return Control
1092 */
631d853f
PT
1093 public Control createTableEditorControl(Class<? extends Control> control) {
1094 try {
420bceb2 1095 return control.getConstructor(Composite.class, int.class).newInstance(new Object[] {fTable, SWT.NONE});
631d853f
PT
1096 } catch (Exception e) {
1097 Activator.getDefault().logError("Error creating table editor control", e); //$NON-NLS-1$
1098 }
1099 return null;
1100 }
1101
1102 /**
1103 * @return the tooltipProvider
d5efe032 1104 */
631d853f
PT
1105 public TooltipProvider getTooltipProvider() {
1106 return tooltipProvider;
1107 }
1108
1109 /**
1110 * @param tooltipProvider the tooltipProvider to set
1111 */
1112 public void setTooltipProvider(TooltipProvider tooltipProvider) {
1113 this.tooltipProvider = tooltipProvider;
1114 }
1115
1116 /**
1117 * @return the doubleClickListener
d5efe032 1118 */
631d853f
PT
1119 public IDoubleClickListener getDoubleClickListener() {
1120 return doubleClickListener;
1121 }
1122
1123 /**
1124 * @param doubleClickListener the doubleClickListener to set
1125 */
1126 public void setDoubleClickListener(IDoubleClickListener doubleClickListener) {
1127 this.doubleClickListener = doubleClickListener;
1128 }
1129
9ccc6d01 1130}
This page took 0.123103 seconds and 5 git commands to generate.