Commit | Line | Data |
---|---|---|
837a2f8c PT |
1 | /******************************************************************************* |
2 | * Copyright (c) 2012 Ericsson | |
3 | * | |
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 | |
8 | * | |
9 | * Contributors: | |
10 | * Patrick Tasse - Initial API and implementation | |
11 | *******************************************************************************/ | |
12 | ||
13 | package org.eclipse.linuxtools.tmf.ui.widgets.timegraph; | |
14 | ||
15 | import java.util.ArrayList; | |
16 | import java.util.Arrays; | |
17 | import java.util.HashMap; | |
18 | ||
19 | import org.eclipse.jface.viewers.ILabelProviderListener; | |
20 | import org.eclipse.jface.viewers.ISelectionChangedListener; | |
21 | import org.eclipse.jface.viewers.IStructuredSelection; | |
22 | import org.eclipse.jface.viewers.ITableLabelProvider; | |
23 | import org.eclipse.jface.viewers.ITreeContentProvider; | |
24 | import org.eclipse.jface.viewers.ITreeViewerListener; | |
25 | import org.eclipse.jface.viewers.SelectionChangedEvent; | |
26 | import org.eclipse.jface.viewers.StructuredSelection; | |
27 | import org.eclipse.jface.viewers.TreeExpansionEvent; | |
28 | import org.eclipse.jface.viewers.TreeViewer; | |
29 | import org.eclipse.jface.viewers.Viewer; | |
30 | import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.model.ITimeGraphEntry; | |
31 | import org.eclipse.swt.SWT; | |
32 | import org.eclipse.swt.custom.SashForm; | |
33 | import org.eclipse.swt.events.ControlAdapter; | |
34 | import org.eclipse.swt.events.ControlEvent; | |
35 | import org.eclipse.swt.events.MouseEvent; | |
36 | import org.eclipse.swt.events.MouseTrackAdapter; | |
37 | import org.eclipse.swt.events.MouseWheelListener; | |
38 | import org.eclipse.swt.events.PaintEvent; | |
39 | import org.eclipse.swt.events.PaintListener; | |
40 | import org.eclipse.swt.events.SelectionAdapter; | |
41 | import org.eclipse.swt.events.SelectionEvent; | |
42 | import org.eclipse.swt.graphics.Image; | |
43 | import org.eclipse.swt.graphics.Point; | |
44 | import org.eclipse.swt.layout.FillLayout; | |
45 | import org.eclipse.swt.widgets.Composite; | |
46 | import org.eclipse.swt.widgets.Display; | |
47 | import org.eclipse.swt.widgets.Event; | |
48 | import org.eclipse.swt.widgets.Listener; | |
49 | import org.eclipse.swt.widgets.Slider; | |
50 | import org.eclipse.swt.widgets.Tree; | |
51 | import org.eclipse.swt.widgets.TreeColumn; | |
52 | import org.eclipse.swt.widgets.TreeItem; | |
53 | ||
54 | /** | |
55 | * Time graph "combo" view (with the list/tree on the left and the gantt chart | |
56 | * on the right) | |
57 | * | |
58 | * @version 1.0 | |
59 | * @author Patrick Tasse | |
60 | */ | |
61 | public class TimeGraphCombo extends Composite { | |
62 | ||
63 | // ------------------------------------------------------------------------ | |
64 | // Constants | |
65 | // ------------------------------------------------------------------------ | |
66 | ||
67 | private static final Object FILLER = new Object(); | |
68 | ||
69 | // ------------------------------------------------------------------------ | |
70 | // Fields | |
71 | // ------------------------------------------------------------------------ | |
72 | ||
73 | // The tree viewer | |
74 | private TreeViewer fTreeViewer; | |
75 | ||
76 | // The time viewer | |
77 | private TimeGraphViewer fTimeGraphViewer; | |
78 | ||
79 | // The selection listener map | |
80 | private final HashMap<ITimeGraphSelectionListener, SelectionListenerWrapper> fSelectionListenerMap = new HashMap<ITimeGraphSelectionListener, SelectionListenerWrapper>(); | |
81 | ||
82 | // Flag to block the tree selection changed listener when triggered by the time graph combo | |
83 | private boolean fInhibitTreeSelection = false; | |
84 | ||
85 | // Number of filler rows used by the tree content provider | |
86 | private int fNumFillerRows; | |
87 | ||
88 | // Calculated item height for Linux workaround | |
89 | private int fLinuxItemHeight = 0; | |
90 | ||
91 | // ------------------------------------------------------------------------ | |
92 | // Classes | |
93 | // ------------------------------------------------------------------------ | |
94 | ||
95 | /** | |
96 | * The TreeContentProviderWrapper is used to insert filler items after | |
97 | * the elements of the tree's real content provider. | |
98 | */ | |
99 | private class TreeContentProviderWrapper implements ITreeContentProvider { | |
100 | private final ITreeContentProvider contentProvider; | |
101 | ||
102 | public TreeContentProviderWrapper(ITreeContentProvider contentProvider) { | |
103 | this.contentProvider = contentProvider; | |
104 | } | |
105 | ||
106 | @Override | |
107 | public void dispose() { | |
108 | contentProvider.dispose(); | |
109 | } | |
110 | ||
111 | @Override | |
112 | public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { | |
113 | contentProvider.inputChanged(viewer, oldInput, newInput); | |
114 | } | |
115 | ||
116 | @Override | |
117 | public Object[] getElements(Object inputElement) { | |
118 | Object[] elements = contentProvider.getElements(inputElement); | |
119 | // add filler elements to ensure alignment with time analysis viewer | |
120 | Object[] oElements = Arrays.copyOf(elements, elements.length + fNumFillerRows, new Object[0].getClass()); | |
121 | for (int i = 0; i < fNumFillerRows; i++) { | |
122 | oElements[elements.length + i] = FILLER; | |
123 | } | |
124 | return oElements; | |
125 | } | |
126 | ||
127 | @Override | |
128 | public Object[] getChildren(Object parentElement) { | |
129 | if (parentElement instanceof ITimeGraphEntry) { | |
130 | return contentProvider.getChildren(parentElement); | |
131 | } | |
132 | return new Object[0]; | |
133 | } | |
134 | ||
135 | @Override | |
136 | public Object getParent(Object element) { | |
137 | if (element instanceof ITimeGraphEntry) { | |
138 | return contentProvider.getParent(element); | |
139 | } | |
140 | return null; | |
141 | } | |
142 | ||
143 | @Override | |
144 | public boolean hasChildren(Object element) { | |
145 | if (element instanceof ITimeGraphEntry) { | |
146 | return contentProvider.hasChildren(element); | |
147 | } | |
148 | return false; | |
149 | } | |
150 | } | |
151 | ||
152 | /** | |
153 | * The TreeLabelProviderWrapper is used to intercept the filler items | |
154 | * from the calls to the tree's real label provider. | |
155 | */ | |
156 | private class TreeLabelProviderWrapper implements ITableLabelProvider { | |
157 | private final ITableLabelProvider labelProvider; | |
158 | ||
159 | public TreeLabelProviderWrapper(ITableLabelProvider labelProvider) { | |
160 | this.labelProvider = labelProvider; | |
161 | } | |
162 | ||
163 | @Override | |
164 | public void addListener(ILabelProviderListener listener) { | |
165 | labelProvider.addListener(listener); | |
166 | } | |
167 | ||
168 | @Override | |
169 | public void dispose() { | |
170 | labelProvider.dispose(); | |
171 | } | |
172 | ||
173 | @Override | |
174 | public boolean isLabelProperty(Object element, String property) { | |
175 | if (element instanceof ITimeGraphEntry) { | |
176 | return labelProvider.isLabelProperty(element, property); | |
177 | } | |
178 | return false; | |
179 | } | |
180 | ||
181 | @Override | |
182 | public void removeListener(ILabelProviderListener listener) { | |
183 | labelProvider.removeListener(listener); | |
184 | } | |
185 | ||
186 | @Override | |
187 | public Image getColumnImage(Object element, int columnIndex) { | |
188 | if (element instanceof ITimeGraphEntry) { | |
189 | return labelProvider.getColumnImage(element, columnIndex); | |
190 | } | |
191 | return null; | |
192 | } | |
193 | ||
194 | @Override | |
195 | public String getColumnText(Object element, int columnIndex) { | |
196 | if (element instanceof ITimeGraphEntry) { | |
197 | return labelProvider.getColumnText(element, columnIndex); | |
198 | } | |
199 | return null; | |
200 | } | |
201 | ||
202 | } | |
203 | ||
204 | /** | |
205 | * The SelectionListenerWrapper is used to intercept the filler items from | |
206 | * the time graph combo's real selection listener, and to prevent double | |
207 | * notifications from being sent when selection changes in both tree and | |
208 | * time graph at the same time. | |
209 | */ | |
210 | private class SelectionListenerWrapper implements ISelectionChangedListener, ITimeGraphSelectionListener { | |
211 | private final ITimeGraphSelectionListener listener; | |
212 | private ITimeGraphEntry selection = null; | |
213 | ||
214 | public SelectionListenerWrapper(ITimeGraphSelectionListener listener) { | |
215 | this.listener = listener; | |
216 | } | |
217 | ||
218 | @Override | |
219 | public void selectionChanged(SelectionChangedEvent event) { | |
220 | if (fInhibitTreeSelection) { | |
221 | return; | |
222 | } | |
223 | Object element = ((IStructuredSelection) event.getSelection()).getFirstElement(); | |
224 | if (element instanceof ITimeGraphEntry) { | |
225 | ITimeGraphEntry entry = (ITimeGraphEntry) element; | |
226 | if (entry != selection) { | |
227 | selection = entry; | |
228 | listener.selectionChanged(new TimeGraphSelectionEvent(event.getSource(), selection)); | |
229 | } | |
230 | } | |
231 | } | |
232 | ||
233 | @Override | |
234 | public void selectionChanged(TimeGraphSelectionEvent event) { | |
235 | ITimeGraphEntry entry = event.getSelection(); | |
236 | if (entry != selection) { | |
237 | selection = entry; | |
238 | listener.selectionChanged(new TimeGraphSelectionEvent(event.getSource(), selection)); | |
239 | } | |
240 | } | |
241 | } | |
242 | ||
243 | // ------------------------------------------------------------------------ | |
244 | // Constructors | |
245 | // ------------------------------------------------------------------------ | |
246 | ||
247 | /** | |
248 | * Constructs a new instance of this class given its parent | |
249 | * and a style value describing its behavior and appearance. | |
250 | * | |
251 | * @param parent a widget which will be the parent of the new instance (cannot be null) | |
252 | * @param style the style of widget to construct | |
253 | */ | |
254 | public TimeGraphCombo(Composite parent, int style) { | |
255 | super(parent, style); | |
256 | setLayout(new FillLayout()); | |
257 | ||
258 | final SashForm sash = new SashForm(this, SWT.NONE); | |
259 | ||
260 | fTreeViewer = new TreeViewer(sash, SWT.FULL_SELECTION | SWT.H_SCROLL); | |
261 | final Tree tree = fTreeViewer.getTree(); | |
262 | tree.setHeaderVisible(true); | |
263 | tree.setLinesVisible(true); | |
264 | ||
265 | fTimeGraphViewer = new TimeGraphViewer(sash, SWT.NONE); | |
266 | fTimeGraphViewer.setItemHeight(getItemHeight(tree)); | |
267 | fTimeGraphViewer.setHeaderHeight(tree.getHeaderHeight()); | |
268 | fTimeGraphViewer.setBorderWidth(tree.getBorderWidth()); | |
269 | fTimeGraphViewer.setNameWidthPref(0); | |
270 | ||
271 | // Feature in Windows. The tree vertical bar reappears when | |
272 | // the control is resized so we need to hide it again. | |
273 | // Bug in Linux. The tree header height is 0 in constructor, | |
274 | // so we need to reset it later when the control is resized. | |
275 | tree.addControlListener(new ControlAdapter() { | |
276 | int depth = 0; | |
277 | @Override | |
278 | public void controlResized(ControlEvent e) { | |
279 | if (depth == 0) { | |
280 | depth++; | |
281 | tree.getVerticalBar().setEnabled(false); | |
282 | // this can trigger controlResized recursively | |
283 | tree.getVerticalBar().setVisible(false); | |
284 | depth--; | |
285 | } | |
286 | fTimeGraphViewer.setHeaderHeight(tree.getHeaderHeight()); | |
287 | } | |
288 | }); | |
289 | ||
290 | // ensure synchronization of expanded items between tree and time graph | |
291 | fTreeViewer.addTreeListener(new ITreeViewerListener() { | |
292 | @Override | |
293 | public void treeCollapsed(TreeExpansionEvent event) { | |
294 | fTimeGraphViewer.setExpandedState((ITimeGraphEntry) event.getElement(), false); | |
295 | ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree); | |
296 | if (treeItems.size() == 0) { | |
297 | return; | |
298 | } | |
299 | TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex()); | |
300 | tree.setTopItem(treeItem); | |
301 | } | |
302 | ||
303 | @Override | |
304 | public void treeExpanded(TreeExpansionEvent event) { | |
305 | fTimeGraphViewer.setExpandedState((ITimeGraphEntry) event.getElement(), true); | |
306 | ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree); | |
307 | if (treeItems.size() == 0) { | |
308 | return; | |
309 | } | |
310 | final TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex()); | |
311 | // queue the top item update because the tree can change its top item | |
312 | // autonomously immediately after the listeners have been notified | |
313 | getDisplay().asyncExec(new Runnable() { | |
314 | @Override | |
315 | public void run() { | |
316 | tree.setTopItem(treeItem); | |
317 | }}); | |
318 | } | |
319 | }); | |
320 | ||
321 | // ensure synchronization of expanded items between tree and time graph | |
322 | fTimeGraphViewer.addTreeListener(new ITimeGraphTreeListener() { | |
323 | @Override | |
324 | public void treeCollapsed(TimeGraphTreeExpansionEvent event) { | |
325 | fTreeViewer.setExpandedState(event.getEntry(), false); | |
326 | } | |
327 | ||
328 | @Override | |
329 | public void treeExpanded(TimeGraphTreeExpansionEvent event) { | |
330 | fTreeViewer.setExpandedState(event.getEntry(), true); | |
331 | } | |
332 | }); | |
333 | ||
334 | // prevent mouse button from selecting a filler tree item | |
335 | tree.addListener(SWT.MouseDown, new Listener() { | |
336 | @Override | |
337 | public void handleEvent(Event event) { | |
338 | TreeItem treeItem = tree.getItem(new Point(event.x, event.y)); | |
339 | if (treeItem == null || treeItem.getData() == FILLER) { | |
340 | event.doit = false; | |
341 | ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree); | |
342 | if (treeItems.size() == 0) { | |
343 | fTreeViewer.setSelection(new StructuredSelection()); | |
344 | fTimeGraphViewer.setSelection(null); | |
345 | return; | |
346 | } | |
347 | // this prevents from scrolling up when selecting | |
348 | // the partially visible tree item at the bottom | |
349 | tree.select(treeItems.get(treeItems.size() - 1)); | |
350 | fTreeViewer.setSelection(new StructuredSelection()); | |
351 | fTimeGraphViewer.setSelection(null); | |
352 | } | |
353 | } | |
354 | }); | |
355 | ||
356 | // prevent mouse wheel from scrolling down into filler tree items | |
357 | tree.addListener(SWT.MouseWheel, new Listener() { | |
358 | @Override | |
359 | public void handleEvent(Event event) { | |
360 | event.doit = false; | |
361 | Slider scrollBar = fTimeGraphViewer.getVerticalBar(); | |
362 | fTimeGraphViewer.setTopIndex(scrollBar.getSelection() - event.count); | |
363 | ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree); | |
364 | if (treeItems.size() == 0) { | |
365 | return; | |
366 | } | |
367 | TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex()); | |
368 | tree.setTopItem(treeItem); | |
369 | } | |
370 | }); | |
371 | ||
372 | // prevent key stroke from selecting a filler tree item | |
373 | tree.addListener(SWT.KeyDown, new Listener() { | |
374 | @Override | |
375 | public void handleEvent(Event event) { | |
376 | ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree); | |
377 | if (treeItems.size() == 0) { | |
378 | fTreeViewer.setSelection(new StructuredSelection()); | |
379 | event.doit = false; | |
380 | return; | |
381 | } | |
382 | if (event.keyCode == SWT.ARROW_DOWN) { | |
383 | int index = Math.min(fTimeGraphViewer.getSelectionIndex() + 1, treeItems.size() - 1); | |
384 | fTimeGraphViewer.setSelection((ITimeGraphEntry) treeItems.get(index).getData()); | |
385 | event.doit = false; | |
386 | } else if (event.keyCode == SWT.PAGE_DOWN) { | |
387 | int height = tree.getSize().y - tree.getHeaderHeight() - tree.getHorizontalBar().getSize().y; | |
388 | int countPerPage = height / getItemHeight(tree); | |
389 | int index = Math.min(fTimeGraphViewer.getSelectionIndex() + countPerPage - 1, treeItems.size() - 1); | |
390 | fTimeGraphViewer.setSelection((ITimeGraphEntry) treeItems.get(index).getData()); | |
391 | event.doit = false; | |
392 | } else if (event.keyCode == SWT.END) { | |
393 | fTimeGraphViewer.setSelection((ITimeGraphEntry) treeItems.get(treeItems.size() - 1).getData()); | |
394 | event.doit = false; | |
395 | } | |
396 | TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex()); | |
397 | tree.setTopItem(treeItem); | |
398 | if (fTimeGraphViewer.getSelectionIndex() >= 0) { | |
399 | fTreeViewer.setSelection(new StructuredSelection(fTimeGraphViewer.getSelection())); | |
400 | } else { | |
401 | fTreeViewer.setSelection(new StructuredSelection()); | |
402 | } | |
403 | } | |
404 | }); | |
405 | ||
406 | // ensure alignment of top item between tree and time graph | |
407 | fTimeGraphViewer.getTimeGraphControl().addControlListener(new ControlAdapter() { | |
408 | @Override | |
409 | public void controlResized(ControlEvent e) { | |
410 | ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree); | |
411 | if (treeItems.size() == 0) { | |
412 | return; | |
413 | } | |
414 | TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex()); | |
415 | tree.setTopItem(treeItem); | |
416 | } | |
417 | }); | |
418 | ||
419 | // ensure synchronization of selected item between tree and time graph | |
420 | fTreeViewer.addSelectionChangedListener(new ISelectionChangedListener() { | |
421 | @Override | |
422 | public void selectionChanged(SelectionChangedEvent event) { | |
423 | if (fInhibitTreeSelection) { | |
424 | return; | |
425 | } | |
426 | if (event.getSelection() instanceof IStructuredSelection) { | |
427 | Object selection = ((IStructuredSelection) event.getSelection()).getFirstElement(); | |
428 | if (selection instanceof ITimeGraphEntry) { | |
429 | fTimeGraphViewer.setSelection((ITimeGraphEntry) selection); | |
430 | } | |
431 | ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree); | |
432 | if (treeItems.size() == 0) { | |
433 | return; | |
434 | } | |
435 | TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex()); | |
436 | tree.setTopItem(treeItem); | |
437 | } | |
438 | } | |
439 | }); | |
440 | ||
441 | // ensure synchronization of selected item between tree and time graph | |
442 | fTimeGraphViewer.addSelectionListener(new ITimeGraphSelectionListener() { | |
443 | @Override | |
444 | public void selectionChanged(TimeGraphSelectionEvent event) { | |
445 | ITimeGraphEntry entry = fTimeGraphViewer.getSelection(); | |
446 | fInhibitTreeSelection = true; // block the tree selection changed listener | |
447 | if (entry != null) { | |
448 | StructuredSelection selection = new StructuredSelection(entry); | |
449 | fTreeViewer.setSelection(selection); | |
450 | } else { | |
451 | fTreeViewer.setSelection(new StructuredSelection()); | |
452 | } | |
453 | fInhibitTreeSelection = false; | |
454 | ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree); | |
455 | if (treeItems.size() == 0) { | |
456 | return; | |
457 | } | |
458 | TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex()); | |
459 | tree.setTopItem(treeItem); | |
460 | } | |
461 | }); | |
462 | ||
463 | // ensure alignment of top item between tree and time graph | |
464 | fTimeGraphViewer.getVerticalBar().addSelectionListener(new SelectionAdapter() { | |
465 | @Override | |
466 | public void widgetSelected(SelectionEvent e) { | |
467 | ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree); | |
468 | if (treeItems.size() == 0) { | |
469 | return; | |
470 | } | |
471 | TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex()); | |
472 | tree.setTopItem(treeItem); | |
473 | } | |
474 | }); | |
475 | ||
476 | // ensure alignment of top item between tree and time graph | |
477 | fTimeGraphViewer.getTimeGraphControl().addMouseWheelListener(new MouseWheelListener() { | |
478 | @Override | |
479 | public void mouseScrolled(MouseEvent e) { | |
480 | ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree); | |
481 | if (treeItems.size() == 0) { | |
482 | return; | |
483 | } | |
484 | TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex()); | |
485 | tree.setTopItem(treeItem); | |
486 | } | |
487 | }); | |
488 | ||
489 | // ensure the tree has focus control when mouse is over it if the time graph had control | |
490 | fTreeViewer.getControl().addMouseTrackListener(new MouseTrackAdapter() { | |
491 | @Override | |
492 | public void mouseEnter(MouseEvent e) { | |
493 | if (fTimeGraphViewer.getTimeGraphControl().isFocusControl()) { | |
494 | fTreeViewer.getControl().setFocus(); | |
495 | } | |
496 | } | |
497 | }); | |
498 | ||
499 | // ensure the time graph has focus control when mouse is over it if the tree had control | |
500 | fTimeGraphViewer.getTimeGraphControl().addMouseTrackListener(new MouseTrackAdapter() { | |
501 | @Override | |
502 | public void mouseEnter(MouseEvent e) { | |
503 | if (fTreeViewer.getControl().isFocusControl()) { | |
504 | fTimeGraphViewer.getTimeGraphControl().setFocus(); | |
505 | } | |
506 | } | |
507 | }); | |
508 | fTimeGraphViewer.getTimeGraphScale().addMouseTrackListener(new MouseTrackAdapter() { | |
509 | @Override | |
510 | public void mouseEnter(MouseEvent e) { | |
511 | if (fTreeViewer.getControl().isFocusControl()) { | |
512 | fTimeGraphViewer.getTimeGraphControl().setFocus(); | |
513 | } | |
514 | } | |
515 | }); | |
516 | ||
517 | // The filler rows are required to ensure alignment when the tree does not have a | |
518 | // visible horizontal scroll bar. The tree does not allow its top item to be set | |
519 | // to a value that would cause blank space to be drawn at the bottom of the tree. | |
520 | fNumFillerRows = Display.getDefault().getBounds().height / getItemHeight(tree); | |
521 | ||
522 | sash.setWeights(new int[] { 1, 1 }); | |
523 | } | |
524 | ||
525 | // ------------------------------------------------------------------------ | |
526 | // Accessors | |
527 | // ------------------------------------------------------------------------ | |
528 | ||
529 | /** | |
530 | * Returns this time graph combo's tree viewer. | |
531 | * | |
532 | * @return the tree viewer | |
533 | */ | |
534 | public TreeViewer getTreeViewer() { | |
535 | return fTreeViewer; | |
536 | } | |
537 | ||
538 | /** | |
539 | * Returns this time graph combo's time graph viewer. | |
540 | * | |
541 | * @return the time graph viewer | |
542 | */ | |
543 | public TimeGraphViewer getTimeGraphViewer() { | |
544 | return fTimeGraphViewer; | |
545 | } | |
546 | ||
547 | // ------------------------------------------------------------------------ | |
548 | // Control | |
549 | // ------------------------------------------------------------------------ | |
550 | ||
551 | /* (non-Javadoc) | |
552 | * @see org.eclipse.swt.widgets.Control#redraw() | |
553 | */ | |
554 | @Override | |
555 | public void redraw() { | |
556 | fTimeGraphViewer.getControl().redraw(); | |
557 | super.redraw(); | |
558 | } | |
559 | ||
560 | // ------------------------------------------------------------------------ | |
561 | // Operations | |
562 | // ------------------------------------------------------------------------ | |
563 | ||
564 | /** | |
565 | * Sets the tree content provider used by this time graph combo. | |
566 | * | |
567 | * @param contentProvider the tree content provider | |
568 | */ | |
569 | public void setTreeContentProvider(ITreeContentProvider contentProvider) { | |
570 | fTreeViewer.setContentProvider(new TreeContentProviderWrapper(contentProvider)); | |
571 | } | |
572 | ||
573 | /** | |
574 | * Sets the tree label provider used by this time graph combo. | |
575 | * | |
576 | * @param labelProvider the tree label provider | |
577 | */ | |
578 | public void setTreeLabelProvider(ITableLabelProvider labelProvider) { | |
579 | fTreeViewer.setLabelProvider(new TreeLabelProviderWrapper(labelProvider)); | |
580 | } | |
581 | ||
582 | /** | |
583 | * Sets the tree columns for this time graph combo. | |
584 | * | |
585 | * @param columnNames the tree column names | |
586 | */ | |
587 | public void setTreeColumns(String[] columnNames) { | |
588 | final Tree tree = fTreeViewer.getTree(); | |
589 | for (String columnName : columnNames) { | |
590 | TreeColumn column = new TreeColumn(tree, SWT.LEFT); | |
591 | column.setText(columnName); | |
592 | column.pack(); | |
593 | } | |
594 | } | |
595 | ||
596 | /** | |
597 | * Sets the time graph provider used by this time graph combo. | |
598 | * | |
599 | * @param timeGraphProvider the time graph provider | |
600 | */ | |
601 | public void setTimeGraphProvider(ITimeGraphPresentationProvider timeGraphProvider) { | |
602 | fTimeGraphViewer.setTimeGraphProvider(timeGraphProvider); | |
603 | } | |
604 | ||
605 | /** | |
606 | * Sets or clears the input for this time graph combo. | |
607 | * The input array should only contain top-level elements. | |
608 | * | |
609 | * @param input the input of this time graph combo, or <code>null</code> if none | |
610 | */ | |
611 | public void setInput(ITimeGraphEntry[] input) { | |
612 | fInhibitTreeSelection = true; | |
613 | fTreeViewer.setInput(input); | |
614 | for (SelectionListenerWrapper listenerWrapper : fSelectionListenerMap.values()) { | |
615 | listenerWrapper.selection = null; | |
616 | } | |
617 | fInhibitTreeSelection = false; | |
618 | fTreeViewer.expandAll(); | |
619 | fTreeViewer.getTree().getVerticalBar().setEnabled(false); | |
620 | fTreeViewer.getTree().getVerticalBar().setVisible(false); | |
621 | fTimeGraphViewer.setItemHeight(getItemHeight(fTreeViewer.getTree())); | |
622 | fTimeGraphViewer.setInput(input); | |
623 | } | |
624 | ||
625 | /** | |
626 | * Refreshes this time graph completely with information freshly obtained from its model. | |
627 | */ | |
628 | public void refresh() { | |
629 | fInhibitTreeSelection = true; | |
630 | fTreeViewer.refresh(); | |
631 | fTimeGraphViewer.refresh(); | |
632 | fInhibitTreeSelection = false; | |
633 | } | |
634 | ||
635 | /** | |
636 | * Adds a listener for selection changes in this time graph combo. | |
637 | * | |
638 | * @param listener a selection listener | |
639 | */ | |
640 | public void addSelectionListener(ITimeGraphSelectionListener listener) { | |
641 | SelectionListenerWrapper listenerWrapper = new SelectionListenerWrapper(listener); | |
642 | fTreeViewer.addSelectionChangedListener(listenerWrapper); | |
643 | fSelectionListenerMap.put(listener, listenerWrapper); | |
644 | fTimeGraphViewer.addSelectionListener(listenerWrapper); | |
645 | } | |
646 | ||
647 | /** | |
648 | * Removes the given selection listener from this time graph combo. | |
649 | * | |
650 | * @param listener a selection changed listener | |
651 | */ | |
652 | public void removeSelectionListener(ITimeGraphSelectionListener listener) { | |
653 | SelectionListenerWrapper listenerWrapper = fSelectionListenerMap.remove(listener); | |
654 | fTreeViewer.removeSelectionChangedListener(listenerWrapper); | |
655 | fTimeGraphViewer.removeSelectionListener(listenerWrapper); | |
656 | } | |
657 | ||
658 | /** | |
659 | * Sets the current selection for this time graph combo. | |
660 | * | |
661 | * @param selection the new selection | |
662 | */ | |
663 | public void setSelection(ITimeGraphEntry selection) { | |
664 | fTimeGraphViewer.setSelection(selection); | |
665 | fInhibitTreeSelection = true; // block the tree selection changed listener | |
666 | if (selection != null) { | |
667 | StructuredSelection structuredSelection = new StructuredSelection(selection); | |
668 | fTreeViewer.setSelection(structuredSelection); | |
669 | } else { | |
670 | fTreeViewer.setSelection(new StructuredSelection()); | |
671 | } | |
672 | fInhibitTreeSelection = false; | |
673 | ArrayList<TreeItem> treeItems = getVisibleExpandedItems(fTreeViewer.getTree()); | |
674 | if (treeItems.size() == 0) { | |
675 | return; | |
676 | } | |
677 | TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex()); | |
678 | fTreeViewer.getTree().setTopItem(treeItem); | |
679 | } | |
680 | ||
681 | /** | |
682 | * Set the expanded state of an entry | |
683 | * | |
684 | * @param entry | |
685 | * The entry to expand/collapse | |
686 | * @param expanded | |
687 | * True for expanded, false for collapsed | |
688 | * | |
689 | * @since 2.0 | |
690 | */ | |
691 | public void setExpandedState(ITimeGraphEntry entry, boolean expanded) { | |
692 | fTimeGraphViewer.setExpandedState(entry, expanded); | |
693 | fTreeViewer.setExpandedState(entry, expanded); | |
694 | } | |
695 | ||
696 | /** | |
697 | * Collapses all nodes of the viewer's tree, starting with the root. | |
698 | * | |
699 | * @since 2.0 | |
700 | */ | |
701 | public void collapseAll() { | |
702 | fTimeGraphViewer.collapseAll(); | |
703 | fTreeViewer.collapseAll(); | |
704 | } | |
705 | ||
706 | /** | |
707 | * Expands all nodes of the viewer's tree, starting with the root. | |
708 | * | |
709 | * @since 2.0 | |
710 | */ | |
711 | public void expandAll() { | |
712 | fTimeGraphViewer.expandAll(); | |
713 | fTreeViewer.expandAll(); | |
714 | } | |
715 | ||
716 | // ------------------------------------------------------------------------ | |
717 | // Internal | |
718 | // ------------------------------------------------------------------------ | |
719 | ||
720 | private ArrayList<TreeItem> getVisibleExpandedItems(Tree tree) { | |
721 | ArrayList<TreeItem> items = new ArrayList<TreeItem>(); | |
722 | for (TreeItem item : tree.getItems()) { | |
723 | if (item.getData() == FILLER) { | |
724 | break; | |
725 | } | |
726 | items.add(item); | |
727 | if (item.getExpanded()) { | |
728 | items.addAll(getVisibleExpandedItems(item)); | |
729 | } | |
730 | } | |
731 | return items; | |
732 | } | |
733 | ||
734 | private ArrayList<TreeItem> getVisibleExpandedItems(TreeItem treeItem) { | |
735 | ArrayList<TreeItem> items = new ArrayList<TreeItem>(); | |
736 | for (TreeItem item : treeItem.getItems()) { | |
737 | items.add(item); | |
738 | if (item.getExpanded()) { | |
739 | items.addAll(getVisibleExpandedItems(item)); | |
740 | } | |
741 | } | |
742 | return items; | |
743 | } | |
744 | ||
745 | private int getItemHeight(final Tree tree) { | |
746 | /* | |
747 | * Bug in Linux. The method getItemHeight doesn't always return the correct value. | |
748 | */ | |
749 | if (fLinuxItemHeight >= 0 && System.getProperty("os.name").contains("Linux")) { //$NON-NLS-1$ //$NON-NLS-2$ | |
750 | if (fLinuxItemHeight != 0) { | |
751 | return fLinuxItemHeight; | |
752 | } | |
753 | ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree); | |
754 | if (treeItems.size() > 1) { | |
755 | final TreeItem treeItem0 = treeItems.get(0); | |
756 | final TreeItem treeItem1 = treeItems.get(1); | |
757 | PaintListener paintListener = new PaintListener() { | |
758 | @Override | |
759 | public void paintControl(PaintEvent e) { | |
760 | tree.removePaintListener(this); | |
761 | int y0 = treeItem0.getBounds().y; | |
762 | int y1 = treeItem1.getBounds().y; | |
763 | int itemHeight = y1 - y0; | |
764 | if (itemHeight > 0) { | |
765 | fLinuxItemHeight = itemHeight; | |
766 | fTimeGraphViewer.setItemHeight(itemHeight); | |
767 | } | |
768 | } | |
769 | }; | |
770 | tree.addPaintListener(paintListener); | |
771 | } | |
772 | } else { | |
773 | fLinuxItemHeight = -1; // Not Linux, don't perform os.name check anymore | |
774 | } | |
775 | return tree.getItemHeight(); | |
776 | } | |
777 | ||
778 | } |