1 /*******************************************************************************
2 * Copyright (c) 2012, 2014 Ericsson, others
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
10 * Patrick Tasse - Initial API and implementation
11 * François Rajotte - Filter implementation
12 * Geneviève Bastien - Add event links between entries
13 *******************************************************************************/
15 package org
.eclipse
.linuxtools
.tmf
.ui
.widgets
.timegraph
;
17 import java
.util
.ArrayList
;
18 import java
.util
.Arrays
;
19 import java
.util
.HashMap
;
20 import java
.util
.HashSet
;
21 import java
.util
.List
;
25 import org
.eclipse
.jface
.action
.Action
;
26 import org
.eclipse
.jface
.viewers
.AbstractTreeViewer
;
27 import org
.eclipse
.jface
.viewers
.ILabelProviderListener
;
28 import org
.eclipse
.jface
.viewers
.ISelectionChangedListener
;
29 import org
.eclipse
.jface
.viewers
.IStructuredSelection
;
30 import org
.eclipse
.jface
.viewers
.ITableLabelProvider
;
31 import org
.eclipse
.jface
.viewers
.ITreeContentProvider
;
32 import org
.eclipse
.jface
.viewers
.ITreeViewerListener
;
33 import org
.eclipse
.jface
.viewers
.SelectionChangedEvent
;
34 import org
.eclipse
.jface
.viewers
.StructuredSelection
;
35 import org
.eclipse
.jface
.viewers
.TreeExpansionEvent
;
36 import org
.eclipse
.jface
.viewers
.TreeViewer
;
37 import org
.eclipse
.jface
.viewers
.Viewer
;
38 import org
.eclipse
.jface
.viewers
.ViewerFilter
;
39 import org
.eclipse
.linuxtools
.internal
.tmf
.ui
.Activator
;
40 import org
.eclipse
.linuxtools
.internal
.tmf
.ui
.ITmfImageConstants
;
41 import org
.eclipse
.linuxtools
.internal
.tmf
.ui
.Messages
;
42 import org
.eclipse
.linuxtools
.tmf
.ui
.widgets
.timegraph
.dialogs
.TimeGraphFilterDialog
;
43 import org
.eclipse
.linuxtools
.tmf
.ui
.widgets
.timegraph
.model
.ILinkEvent
;
44 import org
.eclipse
.linuxtools
.tmf
.ui
.widgets
.timegraph
.model
.ITimeGraphEntry
;
45 import org
.eclipse
.swt
.SWT
;
46 import org
.eclipse
.swt
.custom
.SashForm
;
47 import org
.eclipse
.swt
.events
.ControlAdapter
;
48 import org
.eclipse
.swt
.events
.ControlEvent
;
49 import org
.eclipse
.swt
.events
.MouseEvent
;
50 import org
.eclipse
.swt
.events
.MouseTrackAdapter
;
51 import org
.eclipse
.swt
.events
.MouseWheelListener
;
52 import org
.eclipse
.swt
.events
.PaintEvent
;
53 import org
.eclipse
.swt
.events
.PaintListener
;
54 import org
.eclipse
.swt
.events
.SelectionAdapter
;
55 import org
.eclipse
.swt
.events
.SelectionEvent
;
56 import org
.eclipse
.swt
.graphics
.Image
;
57 import org
.eclipse
.swt
.graphics
.Point
;
58 import org
.eclipse
.swt
.graphics
.Rectangle
;
59 import org
.eclipse
.swt
.layout
.FillLayout
;
60 import org
.eclipse
.swt
.widgets
.Composite
;
61 import org
.eclipse
.swt
.widgets
.Display
;
62 import org
.eclipse
.swt
.widgets
.Event
;
63 import org
.eclipse
.swt
.widgets
.Listener
;
64 import org
.eclipse
.swt
.widgets
.Slider
;
65 import org
.eclipse
.swt
.widgets
.Tree
;
66 import org
.eclipse
.swt
.widgets
.TreeColumn
;
67 import org
.eclipse
.swt
.widgets
.TreeItem
;
70 * Time graph "combo" view (with the list/tree on the left and the gantt chart
74 * @author Patrick Tasse
76 public class TimeGraphCombo
extends Composite
{
78 // ------------------------------------------------------------------------
80 // ------------------------------------------------------------------------
82 /** Constant indicating that all levels of the time graph should be expanded
84 public static final int ALL_LEVELS
= AbstractTreeViewer
.ALL_LEVELS
;
86 private static final Object FILLER
= new Object();
88 private static final String ITEM_HEIGHT
= "$height$"; //$NON-NLS-1$
90 // ------------------------------------------------------------------------
92 // ------------------------------------------------------------------------
94 /** The tree viewer */
95 private TreeViewer fTreeViewer
;
97 /** The time viewer */
98 private TimeGraphViewer fTimeGraphViewer
;
100 /** The selection listener map */
101 private final Map
<ITimeGraphSelectionListener
, SelectionListenerWrapper
> fSelectionListenerMap
= new HashMap
<>();
103 /** The map of viewer filters */
104 private final Map
<ViewerFilter
, ViewerFilter
> fViewerFilterMap
= new HashMap
<>();
107 * Flag to block the tree selection changed listener when triggered by the
110 private boolean fInhibitTreeSelection
= false;
112 /** Number of filler rows used by the tree content provider */
113 private int fNumFillerRows
;
115 /** Calculated item height for Linux workaround */
116 private int fLinuxItemHeight
= 0;
118 /** The button that opens the filter dialog */
119 private Action showFilterAction
;
121 /** The filter dialog */
122 private TimeGraphFilterDialog fFilterDialog
;
124 /** The filter generated from the filter dialog */
125 private RawViewerFilter fFilter
;
127 /** Default weight of each part of the sash */
128 private static final int[] DEFAULT_WEIGHTS
= { 1, 1 };
130 /** List of all expanded items whose parents are also expanded */
131 private List
<TreeItem
> fVisibleExpandedItems
= null;
133 // ------------------------------------------------------------------------
135 // ------------------------------------------------------------------------
138 * The TreeContentProviderWrapper is used to insert filler items after
139 * the elements of the tree's real content provider.
141 private class TreeContentProviderWrapper
implements ITreeContentProvider
{
142 private final ITreeContentProvider contentProvider
;
144 public TreeContentProviderWrapper(ITreeContentProvider contentProvider
) {
145 this.contentProvider
= contentProvider
;
149 public void dispose() {
150 contentProvider
.dispose();
154 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
155 contentProvider
.inputChanged(viewer
, oldInput
, newInput
);
159 public Object
[] getElements(Object inputElement
) {
160 Object
[] elements
= contentProvider
.getElements(inputElement
);
161 // add filler elements to ensure alignment with time analysis viewer
162 Object
[] oElements
= Arrays
.copyOf(elements
, elements
.length
+ fNumFillerRows
, Object
[].class);
163 for (int i
= 0; i
< fNumFillerRows
; i
++) {
164 oElements
[elements
.length
+ i
] = FILLER
;
170 public Object
[] getChildren(Object parentElement
) {
171 if (parentElement
instanceof ITimeGraphEntry
) {
172 return contentProvider
.getChildren(parentElement
);
174 return new Object
[0];
178 public Object
getParent(Object element
) {
179 if (element
instanceof ITimeGraphEntry
) {
180 return contentProvider
.getParent(element
);
186 public boolean hasChildren(Object element
) {
187 if (element
instanceof ITimeGraphEntry
) {
188 return contentProvider
.hasChildren(element
);
195 * The TreeLabelProviderWrapper is used to intercept the filler items
196 * from the calls to the tree's real label provider.
198 private class TreeLabelProviderWrapper
implements ITableLabelProvider
{
199 private final ITableLabelProvider labelProvider
;
201 public TreeLabelProviderWrapper(ITableLabelProvider labelProvider
) {
202 this.labelProvider
= labelProvider
;
206 public void addListener(ILabelProviderListener listener
) {
207 labelProvider
.addListener(listener
);
211 public void dispose() {
212 labelProvider
.dispose();
216 public boolean isLabelProperty(Object element
, String property
) {
217 if (element
instanceof ITimeGraphEntry
) {
218 return labelProvider
.isLabelProperty(element
, property
);
224 public void removeListener(ILabelProviderListener listener
) {
225 labelProvider
.removeListener(listener
);
229 public Image
getColumnImage(Object element
, int columnIndex
) {
230 if (element
instanceof ITimeGraphEntry
) {
231 return labelProvider
.getColumnImage(element
, columnIndex
);
237 public String
getColumnText(Object element
, int columnIndex
) {
238 if (element
instanceof ITimeGraphEntry
) {
239 return labelProvider
.getColumnText(element
, columnIndex
);
247 * The SelectionListenerWrapper is used to intercept the filler items from
248 * the time graph combo's real selection listener, and to prevent double
249 * notifications from being sent when selection changes in both tree and
250 * time graph at the same time.
252 private class SelectionListenerWrapper
implements ISelectionChangedListener
, ITimeGraphSelectionListener
{
253 private final ITimeGraphSelectionListener listener
;
254 private ITimeGraphEntry selection
= null;
256 public SelectionListenerWrapper(ITimeGraphSelectionListener listener
) {
257 this.listener
= listener
;
261 public void selectionChanged(SelectionChangedEvent event
) {
262 if (fInhibitTreeSelection
) {
265 Object element
= ((IStructuredSelection
) event
.getSelection()).getFirstElement();
266 if (element
instanceof ITimeGraphEntry
) {
267 ITimeGraphEntry entry
= (ITimeGraphEntry
) element
;
268 if (entry
!= selection
) {
270 listener
.selectionChanged(new TimeGraphSelectionEvent(event
.getSource(), selection
));
276 public void selectionChanged(TimeGraphSelectionEvent event
) {
277 ITimeGraphEntry entry
= event
.getSelection();
278 if (entry
!= selection
) {
280 listener
.selectionChanged(new TimeGraphSelectionEvent(event
.getSource(), selection
));
286 * The ViewerFilterWrapper is used to intercept the filler items from
287 * the time graph combo's real ViewerFilters. These filler items should
290 private class ViewerFilterWrapper
extends ViewerFilter
{
292 private ViewerFilter fWrappedFilter
;
294 ViewerFilterWrapper(ViewerFilter filter
) {
296 this.fWrappedFilter
= filter
;
300 public boolean select(Viewer viewer
, Object parentElement
, Object element
) {
301 if (element
instanceof ITimeGraphEntry
) {
302 return fWrappedFilter
.select(viewer
, parentElement
, element
);
310 * This filter simply keeps a list of elements that should be filtered out.
311 * All the other elements will be shown.
312 * By default and when the list is set to null, all elements are shown.
314 private class RawViewerFilter
extends ViewerFilter
{
316 private List
<Object
> fFiltered
= null;
318 public void setFiltered(List
<Object
> objects
) {
322 public List
<Object
> getFiltered() {
327 public boolean select(Viewer viewer
, Object parentElement
, Object element
) {
328 if (fFiltered
== null) {
331 return !fFiltered
.contains(element
);
335 // ------------------------------------------------------------------------
337 // ------------------------------------------------------------------------
340 * Constructs a new instance of this class given its parent
341 * and a style value describing its behavior and appearance.
343 * @param parent a widget which will be the parent of the new instance (cannot be null)
344 * @param style the style of widget to construct
346 public TimeGraphCombo(Composite parent
, int style
) {
347 this(parent
, style
, DEFAULT_WEIGHTS
);
351 * Constructs a new instance of this class given its parent and a style
352 * value describing its behavior and appearance.
355 * a widget which will be the parent of the new instance (cannot
358 * the style of widget to construct
360 * The relative weights of each side of the sash form
363 public TimeGraphCombo(Composite parent
, int style
, int[] weights
) {
364 super(parent
, style
);
365 setLayout(new FillLayout());
367 final SashForm sash
= new SashForm(this, SWT
.NONE
);
369 fTreeViewer
= new TreeViewer(sash
, SWT
.FULL_SELECTION
| SWT
.H_SCROLL
);
370 fTreeViewer
.setAutoExpandLevel(AbstractTreeViewer
.ALL_LEVELS
);
371 final Tree tree
= fTreeViewer
.getTree();
372 tree
.setHeaderVisible(true);
373 tree
.setLinesVisible(true);
375 fTimeGraphViewer
= new TimeGraphViewer(sash
, SWT
.NONE
);
376 fTimeGraphViewer
.setItemHeight(getItemHeight(tree
));
377 fTimeGraphViewer
.setHeaderHeight(tree
.getHeaderHeight());
378 fTimeGraphViewer
.setBorderWidth(tree
.getBorderWidth());
379 fTimeGraphViewer
.setNameWidthPref(0);
381 fFilter
= new RawViewerFilter();
384 fFilterDialog
= new TimeGraphFilterDialog(getShell());
386 // Feature in Windows. The tree vertical bar reappears when
387 // the control is resized so we need to hide it again.
388 // Bug in Linux. The tree header height is 0 in constructor,
389 // so we need to reset it later when the control is resized.
390 tree
.addControlListener(new ControlAdapter() {
391 private int depth
= 0;
393 public void controlResized(ControlEvent e
) {
396 tree
.getVerticalBar().setEnabled(false);
397 // this can trigger controlResized recursively
398 tree
.getVerticalBar().setVisible(false);
401 fTimeGraphViewer
.setHeaderHeight(tree
.getHeaderHeight());
405 // ensure synchronization of expanded items between tree and time graph
406 fTreeViewer
.addTreeListener(new ITreeViewerListener() {
408 public void treeCollapsed(TreeExpansionEvent event
) {
409 fTimeGraphViewer
.setExpandedState((ITimeGraphEntry
) event
.getElement(), false);
410 // queue the alignment update because the tree items may only be
411 // actually collapsed after the listeners have been notified
412 fVisibleExpandedItems
= null; // invalidate the cache
413 getDisplay().asyncExec(new Runnable() {
416 alignTreeItems(true);
421 public void treeExpanded(TreeExpansionEvent event
) {
422 ITimeGraphEntry entry
= (ITimeGraphEntry
) event
.getElement();
423 fTimeGraphViewer
.setExpandedState(entry
, true);
424 Set
<Object
> expandedElements
= new HashSet
<>(Arrays
.asList(fTreeViewer
.getExpandedElements()));
425 for (ITimeGraphEntry child
: entry
.getChildren()) {
426 if (child
.hasChildren()) {
427 boolean expanded
= expandedElements
.contains(child
);
428 fTimeGraphViewer
.setExpandedState(child
, expanded
);
431 // queue the alignment update because the tree items may only be
432 // actually expanded after the listeners have been notified
433 fVisibleExpandedItems
= null; // invalidate the cache
434 getDisplay().asyncExec(new Runnable() {
437 alignTreeItems(true);
442 // ensure synchronization of expanded items between tree and time graph
443 fTimeGraphViewer
.addTreeListener(new ITimeGraphTreeListener() {
445 public void treeCollapsed(TimeGraphTreeExpansionEvent event
) {
446 fTreeViewer
.setExpandedState(event
.getEntry(), false);
447 alignTreeItems(true);
451 public void treeExpanded(TimeGraphTreeExpansionEvent event
) {
452 ITimeGraphEntry entry
= event
.getEntry();
453 fTreeViewer
.setExpandedState(entry
, true);
454 Set
<Object
> expandedElements
= new HashSet
<>(Arrays
.asList(fTreeViewer
.getExpandedElements()));
455 for (ITimeGraphEntry child
: entry
.getChildren()) {
456 if (child
.hasChildren()) {
457 boolean expanded
= expandedElements
.contains(child
);
458 fTimeGraphViewer
.setExpandedState(child
, expanded
);
461 alignTreeItems(true);
465 // prevent mouse button from selecting a filler tree item
466 tree
.addListener(SWT
.MouseDown
, new Listener() {
468 public void handleEvent(Event event
) {
469 TreeItem treeItem
= tree
.getItem(new Point(event
.x
, event
.y
));
470 if (treeItem
== null || treeItem
.getData() == FILLER
) {
472 List
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
, false);
473 if (treeItems
.size() == 0) {
474 fTreeViewer
.setSelection(new StructuredSelection());
475 fTimeGraphViewer
.setSelection(null);
478 // this prevents from scrolling up when selecting
479 // the partially visible tree item at the bottom
480 tree
.select(treeItems
.get(treeItems
.size() - 1));
481 fTreeViewer
.setSelection(new StructuredSelection());
482 fTimeGraphViewer
.setSelection(null);
487 // prevent mouse wheel from scrolling down into filler tree items
488 tree
.addListener(SWT
.MouseWheel
, new Listener() {
490 public void handleEvent(Event event
) {
492 Slider scrollBar
= fTimeGraphViewer
.getVerticalBar();
493 fTimeGraphViewer
.setTopIndex(scrollBar
.getSelection() - event
.count
);
494 alignTreeItems(false);
498 // prevent key stroke from selecting a filler tree item
499 tree
.addListener(SWT
.KeyDown
, new Listener() {
501 public void handleEvent(Event event
) {
502 List
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
, false);
503 if (treeItems
.size() == 0) {
504 fTreeViewer
.setSelection(new StructuredSelection());
508 if (event
.keyCode
== SWT
.ARROW_DOWN
) {
509 int index
= Math
.min(fTimeGraphViewer
.getSelectionIndex() + 1, treeItems
.size() - 1);
510 fTimeGraphViewer
.setSelection((ITimeGraphEntry
) treeItems
.get(index
).getData());
512 } else if (event
.keyCode
== SWT
.PAGE_DOWN
) {
513 int height
= tree
.getSize().y
- tree
.getHeaderHeight() - tree
.getHorizontalBar().getSize().y
;
514 int countPerPage
= height
/ getItemHeight(tree
);
515 int index
= Math
.min(fTimeGraphViewer
.getSelectionIndex() + countPerPage
- 1, treeItems
.size() - 1);
516 fTimeGraphViewer
.setSelection((ITimeGraphEntry
) treeItems
.get(index
).getData());
518 } else if (event
.keyCode
== SWT
.END
) {
519 fTimeGraphViewer
.setSelection((ITimeGraphEntry
) treeItems
.get(treeItems
.size() - 1).getData());
522 if (fTimeGraphViewer
.getSelectionIndex() >= 0) {
523 fTreeViewer
.setSelection(new StructuredSelection(fTimeGraphViewer
.getSelection()));
525 fTreeViewer
.setSelection(new StructuredSelection());
527 alignTreeItems(false);
531 // ensure alignment of top item between tree and time graph
532 fTimeGraphViewer
.getTimeGraphControl().addControlListener(new ControlAdapter() {
534 public void controlResized(ControlEvent e
) {
535 alignTreeItems(false);
539 // ensure synchronization of selected item between tree and time graph
540 fTreeViewer
.addSelectionChangedListener(new ISelectionChangedListener() {
542 public void selectionChanged(SelectionChangedEvent event
) {
543 if (fInhibitTreeSelection
) {
546 if (event
.getSelection() instanceof IStructuredSelection
) {
547 Object selection
= ((IStructuredSelection
) event
.getSelection()).getFirstElement();
548 if (selection
instanceof ITimeGraphEntry
) {
549 fTimeGraphViewer
.setSelection((ITimeGraphEntry
) selection
);
551 alignTreeItems(false);
556 // ensure synchronization of selected item between tree and time graph
557 fTimeGraphViewer
.addSelectionListener(new ITimeGraphSelectionListener() {
559 public void selectionChanged(TimeGraphSelectionEvent event
) {
560 ITimeGraphEntry entry
= fTimeGraphViewer
.getSelection();
561 fInhibitTreeSelection
= true; // block the tree selection changed listener
563 StructuredSelection selection
= new StructuredSelection(entry
);
564 fTreeViewer
.setSelection(selection
);
566 fTreeViewer
.setSelection(new StructuredSelection());
568 fInhibitTreeSelection
= false;
569 alignTreeItems(false);
573 // ensure alignment of top item between tree and time graph
574 fTimeGraphViewer
.getVerticalBar().addSelectionListener(new SelectionAdapter() {
576 public void widgetSelected(SelectionEvent e
) {
577 alignTreeItems(false);
581 // ensure alignment of top item between tree and time graph
582 fTimeGraphViewer
.getTimeGraphControl().addMouseWheelListener(new MouseWheelListener() {
584 public void mouseScrolled(MouseEvent e
) {
585 alignTreeItems(false);
589 // ensure the tree has focus control when mouse is over it if the time graph had control
590 fTreeViewer
.getControl().addMouseTrackListener(new MouseTrackAdapter() {
592 public void mouseEnter(MouseEvent e
) {
593 if (fTimeGraphViewer
.getTimeGraphControl().isFocusControl()) {
594 fTreeViewer
.getControl().setFocus();
599 // ensure the time graph has focus control when mouse is over it if the tree had control
600 fTimeGraphViewer
.getTimeGraphControl().addMouseTrackListener(new MouseTrackAdapter() {
602 public void mouseEnter(MouseEvent e
) {
603 if (fTreeViewer
.getControl().isFocusControl()) {
604 fTimeGraphViewer
.getTimeGraphControl().setFocus();
608 fTimeGraphViewer
.getTimeGraphScale().addMouseTrackListener(new MouseTrackAdapter() {
610 public void mouseEnter(MouseEvent e
) {
611 if (fTreeViewer
.getControl().isFocusControl()) {
612 fTimeGraphViewer
.getTimeGraphControl().setFocus();
617 // The filler rows are required to ensure alignment when the tree does not have a
618 // visible horizontal scroll bar. The tree does not allow its top item to be set
619 // to a value that would cause blank space to be drawn at the bottom of the tree.
620 fNumFillerRows
= Display
.getDefault().getBounds().height
/ getItemHeight(tree
);
622 sash
.setWeights(weights
);
625 // ------------------------------------------------------------------------
627 // ------------------------------------------------------------------------
630 * Returns this time graph combo's tree viewer.
632 * @return the tree viewer
634 public TreeViewer
getTreeViewer() {
639 * Returns this time graph combo's time graph viewer.
641 * @return the time graph viewer
643 public TimeGraphViewer
getTimeGraphViewer() {
644 return fTimeGraphViewer
;
648 * Callback for the show filter action
652 public void showFilterDialog() {
653 ITimeGraphEntry
[] topInput
= fTimeGraphViewer
.getTimeGraphContentProvider().getElements(fTimeGraphViewer
.getInput());
654 if (topInput
!= null) {
655 List
<?
extends ITimeGraphEntry
> allElements
= listAllInputs(Arrays
.asList(topInput
));
656 fFilterDialog
.setInput(fTimeGraphViewer
.getInput());
657 fFilterDialog
.setTitle(Messages
.TmfTimeFilterDialog_WINDOW_TITLE
);
658 fFilterDialog
.setMessage(Messages
.TmfTimeFilterDialog_MESSAGE
);
659 fFilterDialog
.setExpandedElements(allElements
.toArray());
660 if (fFilter
.getFiltered() != null) {
661 ArrayList
<?
extends ITimeGraphEntry
> nonFilteredElements
= new ArrayList
<>(allElements
);
662 nonFilteredElements
.removeAll(fFilter
.getFiltered());
663 fFilterDialog
.setInitialElementSelections(nonFilteredElements
);
665 fFilterDialog
.setInitialElementSelections(allElements
);
667 fFilterDialog
.create();
668 fFilterDialog
.open();
669 // Process selected elements
670 if (fFilterDialog
.getResult() != null) {
671 fInhibitTreeSelection
= true;
672 if (fFilterDialog
.getResult().length
!= allElements
.size()) {
673 ArrayList
<Object
> filteredElements
= new ArrayList
<Object
>(allElements
);
674 filteredElements
.removeAll(Arrays
.asList(fFilterDialog
.getResult()));
675 fFilter
.setFiltered(filteredElements
);
677 fFilter
.setFiltered(null);
679 fTreeViewer
.refresh();
680 fTreeViewer
.expandAll();
681 fTimeGraphViewer
.refresh();
682 fInhibitTreeSelection
= false;
683 alignTreeItems(true);
685 if (fFilterDialog
.getResult().length
> 0) {
693 * Get the show filter action.
695 * @return The Action object
698 public Action
getShowFilterAction() {
699 if (showFilterAction
== null) {
701 showFilterAction
= new Action() {
707 showFilterAction
.setText(Messages
.TmfTimeGraphCombo_FilterActionNameText
);
708 showFilterAction
.setToolTipText(Messages
.TmfTimeGraphCombo_FilterActionToolTipText
);
709 // TODO find a nice, distinctive icon
710 showFilterAction
.setImageDescriptor(Activator
.getDefault().getImageDescripterFromPath(ITmfImageConstants
.IMG_UI_FILTERS
));
713 return showFilterAction
;
716 // ------------------------------------------------------------------------
718 // ------------------------------------------------------------------------
721 public void redraw() {
722 fTimeGraphViewer
.getControl().redraw();
726 // ------------------------------------------------------------------------
728 // ------------------------------------------------------------------------
731 * Sets the tree content provider used by this time graph combo.
733 * @param contentProvider the tree content provider
735 public void setTreeContentProvider(ITreeContentProvider contentProvider
) {
736 fTreeViewer
.setContentProvider(new TreeContentProviderWrapper(contentProvider
));
740 * Sets the tree label provider used by this time graph combo.
742 * @param labelProvider the tree label provider
744 public void setTreeLabelProvider(ITableLabelProvider labelProvider
) {
745 fTreeViewer
.setLabelProvider(new TreeLabelProviderWrapper(labelProvider
));
749 * Sets the tree content provider used by the filter dialog
751 * @param contentProvider the tree content provider
754 public void setFilterContentProvider(ITreeContentProvider contentProvider
) {
755 fFilterDialog
.setContentProvider(contentProvider
);
759 * Sets the tree label provider used by the filter dialog
761 * @param labelProvider the tree label provider
764 public void setFilterLabelProvider(ITableLabelProvider labelProvider
) {
765 fFilterDialog
.setLabelProvider(labelProvider
);
769 * Sets the tree columns for this time graph combo.
771 * @param columnNames the tree column names
773 public void setTreeColumns(String
[] columnNames
) {
774 final Tree tree
= fTreeViewer
.getTree();
775 for (String columnName
: columnNames
) {
776 TreeColumn column
= new TreeColumn(tree
, SWT
.LEFT
);
777 column
.setText(columnName
);
783 * Sets the tree columns for this time graph combo's filter dialog.
785 * @param columnNames the tree column names
788 public void setFilterColumns(String
[] columnNames
) {
789 fFilterDialog
.setColumnNames(columnNames
);
793 * Sets the time graph content provider used by this time graph combo.
795 * @param timeGraphContentProvider
796 * the time graph content provider
800 public void setTimeGraphContentProvider(ITimeGraphContentProvider timeGraphContentProvider
) {
801 fTimeGraphViewer
.setTimeGraphContentProvider(timeGraphContentProvider
);
805 * Sets the time graph presentation provider used by this time graph combo.
807 * @param timeGraphProvider the time graph provider
809 public void setTimeGraphProvider(ITimeGraphPresentationProvider timeGraphProvider
) {
810 fTimeGraphViewer
.setTimeGraphProvider(timeGraphProvider
);
814 * Sets or clears the input for this time graph combo.
816 * @param input the input of this time graph combo, or <code>null</code> if none
820 public void setInput(Object input
) {
821 fFilter
.setFiltered(null);
822 fInhibitTreeSelection
= true;
823 fTreeViewer
.setInput(input
);
824 for (SelectionListenerWrapper listenerWrapper
: fSelectionListenerMap
.values()) {
825 listenerWrapper
.selection
= null;
827 fInhibitTreeSelection
= false;
828 fTreeViewer
.getTree().getVerticalBar().setEnabled(false);
829 fTreeViewer
.getTree().getVerticalBar().setVisible(false);
830 fTimeGraphViewer
.setItemHeight(getItemHeight(fTreeViewer
.getTree()));
831 fTimeGraphViewer
.setInput(input
);
832 // queue the alignment update because in Linux the item bounds are not
833 // set properly until the tree has been painted at least once
834 fVisibleExpandedItems
= null; // invalidate the cache
835 getDisplay().asyncExec(new Runnable() {
838 alignTreeItems(true);
843 * Gets the input for this time graph combo.
845 * @return The input of this time graph combo, or <code>null</code> if none
849 public Object
getInput() {
850 return fTreeViewer
.getInput();
854 * Sets or clears the list of links to display on this combo
856 * @param links the links to display in this time graph combo
859 public void setLinks(List
<ILinkEvent
> links
) {
860 fTimeGraphViewer
.setLinks(links
);
864 * @param filter The filter object to be attached to the view
867 public void addFilter(ViewerFilter filter
) {
868 ViewerFilter wrapper
= new ViewerFilterWrapper(filter
);
869 fTreeViewer
.addFilter(wrapper
);
870 fTimeGraphViewer
.addFilter(wrapper
);
871 fViewerFilterMap
.put(filter
, wrapper
);
872 alignTreeItems(true);
876 * @param filter The filter object to be removed from the view
879 public void removeFilter(ViewerFilter filter
) {
880 ViewerFilter wrapper
= fViewerFilterMap
.get(filter
);
881 fTreeViewer
.removeFilter(wrapper
);
882 fTimeGraphViewer
.removeFilter(wrapper
);
883 fViewerFilterMap
.remove(filter
);
884 alignTreeItems(true);
888 * Refreshes this time graph completely with information freshly obtained from its model.
890 public void refresh() {
891 fInhibitTreeSelection
= true;
892 Tree tree
= fTreeViewer
.getTree();
893 tree
.setRedraw(false);
894 fTreeViewer
.refresh();
895 fTreeViewer
.expandAll();
896 tree
.setRedraw(true);
897 fTimeGraphViewer
.refresh();
898 alignTreeItems(true);
899 fInhibitTreeSelection
= false;
903 * Adds a listener for selection changes in this time graph combo.
905 * @param listener a selection listener
907 public void addSelectionListener(ITimeGraphSelectionListener listener
) {
908 SelectionListenerWrapper listenerWrapper
= new SelectionListenerWrapper(listener
);
909 fTreeViewer
.addSelectionChangedListener(listenerWrapper
);
910 fSelectionListenerMap
.put(listener
, listenerWrapper
);
911 fTimeGraphViewer
.addSelectionListener(listenerWrapper
);
915 * Removes the given selection listener from this time graph combo.
917 * @param listener a selection changed listener
919 public void removeSelectionListener(ITimeGraphSelectionListener listener
) {
920 SelectionListenerWrapper listenerWrapper
= fSelectionListenerMap
.remove(listener
);
921 fTreeViewer
.removeSelectionChangedListener(listenerWrapper
);
922 fTimeGraphViewer
.removeSelectionListener(listenerWrapper
);
926 * Sets the current selection for this time graph combo.
928 * @param selection the new selection
930 public void setSelection(ITimeGraphEntry selection
) {
931 fTimeGraphViewer
.setSelection(selection
);
932 fInhibitTreeSelection
= true; // block the tree selection changed listener
933 if (selection
!= null) {
934 StructuredSelection structuredSelection
= new StructuredSelection(selection
);
935 fTreeViewer
.setSelection(structuredSelection
);
937 fTreeViewer
.setSelection(new StructuredSelection());
939 fInhibitTreeSelection
= false;
940 alignTreeItems(false);
944 * Sets the auto-expand level to be used when the input of the viewer is set
945 * using {@link #setInput(Object)}. The value 0 means that there is no
946 * auto-expand; 1 means that top-level elements are expanded, but not their
947 * children; 2 means that top-level elements are expanded, and their
948 * children, but not grand-children; and so on.
950 * The value {@link #ALL_LEVELS} means that all subtrees should be expanded.
953 * non-negative level, or <code>ALL_LEVELS</code> to expand all
957 public void setAutoExpandLevel(int level
) {
958 fTimeGraphViewer
.setAutoExpandLevel(level
);
960 fTreeViewer
.setAutoExpandLevel(level
);
962 fTreeViewer
.setAutoExpandLevel(level
+ 1);
967 * Returns the auto-expand level.
969 * @return non-negative level, or <code>ALL_LEVELS</code> if all levels of
970 * the tree are expanded automatically
971 * @see #setAutoExpandLevel
974 public int getAutoExpandLevel() {
975 return fTimeGraphViewer
.getAutoExpandLevel();
979 * Set the expanded state of an entry
982 * The entry to expand/collapse
984 * True for expanded, false for collapsed
988 public void setExpandedState(ITimeGraphEntry entry
, boolean expanded
) {
989 fTimeGraphViewer
.setExpandedState(entry
, expanded
);
990 fTreeViewer
.setExpandedState(entry
, expanded
);
991 alignTreeItems(true);
995 * Collapses all nodes of the viewer's tree, starting with the root.
999 public void collapseAll() {
1000 fTimeGraphViewer
.collapseAll();
1001 fTreeViewer
.collapseAll();
1002 alignTreeItems(true);
1006 * Expands all nodes of the viewer's tree, starting with the root.
1010 public void expandAll() {
1011 fTimeGraphViewer
.expandAll();
1012 fTreeViewer
.expandAll();
1013 alignTreeItems(true);
1016 // ------------------------------------------------------------------------
1018 // ------------------------------------------------------------------------
1020 private List
<TreeItem
> getVisibleExpandedItems(Tree tree
, boolean refresh
) {
1021 if (fVisibleExpandedItems
== null || refresh
) {
1022 ArrayList
<TreeItem
> items
= new ArrayList
<>();
1023 for (TreeItem item
: tree
.getItems()) {
1024 if (item
.getData() == FILLER
) {
1028 if (item
.getExpanded()) {
1029 addVisibleExpandedItems(items
, item
);
1032 fVisibleExpandedItems
= items
;
1034 return fVisibleExpandedItems
;
1037 private void addVisibleExpandedItems(List
<TreeItem
> items
, TreeItem treeItem
) {
1038 for (TreeItem item
: treeItem
.getItems()) {
1040 if (item
.getExpanded()) {
1041 addVisibleExpandedItems(items
, item
);
1047 * Explores the list of top-level inputs and returns all the inputs
1049 * @param inputs The top-level inputs
1050 * @return All the inputs
1052 private List
<?
extends ITimeGraphEntry
> listAllInputs(List
<?
extends ITimeGraphEntry
> inputs
) {
1053 ArrayList
<ITimeGraphEntry
> items
= new ArrayList
<>();
1054 for (ITimeGraphEntry entry
: inputs
) {
1056 if (entry
.hasChildren()) {
1057 items
.addAll(listAllInputs(entry
.getChildren()));
1063 private int getItemHeight(final Tree tree
) {
1065 * Bug in Linux. The method getItemHeight doesn't always return the correct value.
1067 if (fLinuxItemHeight
>= 0 && System
.getProperty("os.name").contains("Linux")) { //$NON-NLS-1$ //$NON-NLS-2$
1068 if (fLinuxItemHeight
!= 0) {
1069 return fLinuxItemHeight
;
1071 List
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
, true);
1072 if (treeItems
.size() > 1) {
1073 final TreeItem treeItem0
= treeItems
.get(0);
1074 final TreeItem treeItem1
= treeItems
.get(1);
1075 PaintListener paintListener
= new PaintListener() {
1077 public void paintControl(PaintEvent e
) {
1078 tree
.removePaintListener(this);
1079 int y0
= treeItem0
.getBounds().y
;
1080 int y1
= treeItem1
.getBounds().y
;
1081 int itemHeight
= y1
- y0
;
1082 if (itemHeight
> 0) {
1083 fLinuxItemHeight
= itemHeight
;
1084 fTimeGraphViewer
.setItemHeight(itemHeight
);
1088 tree
.addPaintListener(paintListener
);
1091 fLinuxItemHeight
= -1; // Not Linux, don't perform os.name check anymore
1093 return tree
.getItemHeight();
1096 private void alignTreeItems(boolean refreshExpandedItems
) {
1097 // align the tree top item with the time graph top item
1098 Tree tree
= fTreeViewer
.getTree();
1099 List
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
, refreshExpandedItems
);
1100 int topIndex
= fTimeGraphViewer
.getTopIndex();
1101 if (topIndex
>= treeItems
.size()) {
1104 TreeItem item
= treeItems
.get(topIndex
);
1105 tree
.setTopItem(item
);
1107 // ensure the time graph item heights are equal to the tree item heights
1108 int treeHeight
= fTreeViewer
.getTree().getBounds().height
;
1109 int index
= topIndex
;
1110 Rectangle bounds
= item
.getBounds();
1111 while (index
< treeItems
.size() - 1) {
1112 if (bounds
.y
> treeHeight
) {
1116 * Bug in Linux. The method getBounds doesn't always return the correct height.
1117 * Use the difference of y position between items to calculate the height.
1119 TreeItem nextItem
= treeItems
.get(index
+ 1);
1120 Rectangle nextBounds
= nextItem
.getBounds();
1121 Integer itemHeight
= nextBounds
.y
- bounds
.y
;
1122 if (itemHeight
> 0 && !itemHeight
.equals(item
.getData(ITEM_HEIGHT
))) {
1123 ITimeGraphEntry entry
= (ITimeGraphEntry
) item
.getData();
1124 if (fTimeGraphViewer
.getTimeGraphControl().setItemHeight(entry
, itemHeight
)) {
1125 item
.setData(ITEM_HEIGHT
, itemHeight
);
1130 bounds
= nextBounds
;