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