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