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