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