1 /*******************************************************************************
2 * Copyright (c) 2012, 2013 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
.List
;
23 import org
.eclipse
.jface
.action
.Action
;
24 import org
.eclipse
.jface
.viewers
.ILabelProviderListener
;
25 import org
.eclipse
.jface
.viewers
.ISelectionChangedListener
;
26 import org
.eclipse
.jface
.viewers
.IStructuredSelection
;
27 import org
.eclipse
.jface
.viewers
.ITableLabelProvider
;
28 import org
.eclipse
.jface
.viewers
.ITreeContentProvider
;
29 import org
.eclipse
.jface
.viewers
.ITreeViewerListener
;
30 import org
.eclipse
.jface
.viewers
.SelectionChangedEvent
;
31 import org
.eclipse
.jface
.viewers
.StructuredSelection
;
32 import org
.eclipse
.jface
.viewers
.TreeExpansionEvent
;
33 import org
.eclipse
.jface
.viewers
.TreeViewer
;
34 import org
.eclipse
.jface
.viewers
.Viewer
;
35 import org
.eclipse
.jface
.viewers
.ViewerFilter
;
36 import org
.eclipse
.linuxtools
.internal
.tmf
.ui
.Activator
;
37 import org
.eclipse
.linuxtools
.internal
.tmf
.ui
.ITmfImageConstants
;
38 import org
.eclipse
.linuxtools
.internal
.tmf
.ui
.Messages
;
39 import org
.eclipse
.linuxtools
.tmf
.ui
.widgets
.timegraph
.dialogs
.TimeGraphFilterDialog
;
40 import org
.eclipse
.linuxtools
.tmf
.ui
.widgets
.timegraph
.model
.ILinkEvent
;
41 import org
.eclipse
.linuxtools
.tmf
.ui
.widgets
.timegraph
.model
.ITimeGraphEntry
;
42 import org
.eclipse
.swt
.SWT
;
43 import org
.eclipse
.swt
.custom
.SashForm
;
44 import org
.eclipse
.swt
.events
.ControlAdapter
;
45 import org
.eclipse
.swt
.events
.ControlEvent
;
46 import org
.eclipse
.swt
.events
.MouseEvent
;
47 import org
.eclipse
.swt
.events
.MouseTrackAdapter
;
48 import org
.eclipse
.swt
.events
.MouseWheelListener
;
49 import org
.eclipse
.swt
.events
.PaintEvent
;
50 import org
.eclipse
.swt
.events
.PaintListener
;
51 import org
.eclipse
.swt
.events
.SelectionAdapter
;
52 import org
.eclipse
.swt
.events
.SelectionEvent
;
53 import org
.eclipse
.swt
.graphics
.Image
;
54 import org
.eclipse
.swt
.graphics
.Point
;
55 import org
.eclipse
.swt
.layout
.FillLayout
;
56 import org
.eclipse
.swt
.widgets
.Composite
;
57 import org
.eclipse
.swt
.widgets
.Display
;
58 import org
.eclipse
.swt
.widgets
.Event
;
59 import org
.eclipse
.swt
.widgets
.Listener
;
60 import org
.eclipse
.swt
.widgets
.Slider
;
61 import org
.eclipse
.swt
.widgets
.Tree
;
62 import org
.eclipse
.swt
.widgets
.TreeColumn
;
63 import org
.eclipse
.swt
.widgets
.TreeItem
;
66 * Time graph "combo" view (with the list/tree on the left and the gantt chart
70 * @author Patrick Tasse
72 public class TimeGraphCombo
extends Composite
{
74 // ------------------------------------------------------------------------
76 // ------------------------------------------------------------------------
78 private static final Object FILLER
= new Object();
80 private static final String ITEM_HEIGHT
= "$height$"; //$NON-NLS-1$
82 // ------------------------------------------------------------------------
84 // ------------------------------------------------------------------------
86 /** The tree viewer */
87 private TreeViewer fTreeViewer
;
89 /** The time viewer */
90 private TimeGraphViewer fTimeGraphViewer
;
92 /** The top-level input (children excluded) */
93 private List
<?
extends ITimeGraphEntry
> fTopInput
;
95 /** The selection listener map */
96 private final Map
<ITimeGraphSelectionListener
, SelectionListenerWrapper
> fSelectionListenerMap
= new HashMap
<>();
98 /** The map of viewer filters */
99 private final Map
<ViewerFilter
, ViewerFilter
> fViewerFilterMap
= new HashMap
<>();
102 * Flag to block the tree selection changed listener when triggered by the
105 private boolean fInhibitTreeSelection
= false;
107 /** Number of filler rows used by the tree content provider */
108 private int fNumFillerRows
;
110 /** Calculated item height for Linux workaround */
111 private int fLinuxItemHeight
= 0;
113 /** The button that opens the filter dialog */
114 private Action showFilterAction
;
116 /** The filter dialog */
117 private TimeGraphFilterDialog fFilterDialog
;
119 /** The filter generated from the filter dialog */
120 private RawViewerFilter fFilter
;
122 /** Default weight of each part of the sash */
123 private static final int[] DEFAULT_WEIGHTS
= { 1, 1 };
125 // ------------------------------------------------------------------------
127 // ------------------------------------------------------------------------
130 * The TreeContentProviderWrapper is used to insert filler items after
131 * the elements of the tree's real content provider.
133 private class TreeContentProviderWrapper
implements ITreeContentProvider
{
134 private final ITreeContentProvider contentProvider
;
136 public TreeContentProviderWrapper(ITreeContentProvider contentProvider
) {
137 this.contentProvider
= contentProvider
;
141 public void dispose() {
142 contentProvider
.dispose();
146 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
147 contentProvider
.inputChanged(viewer
, oldInput
, newInput
);
151 public Object
[] getElements(Object inputElement
) {
152 Object
[] elements
= contentProvider
.getElements(inputElement
);
153 // add filler elements to ensure alignment with time analysis viewer
154 Object
[] oElements
= Arrays
.copyOf(elements
, elements
.length
+ fNumFillerRows
, Object
[].class);
155 for (int i
= 0; i
< fNumFillerRows
; i
++) {
156 oElements
[elements
.length
+ i
] = FILLER
;
162 public Object
[] getChildren(Object parentElement
) {
163 if (parentElement
instanceof ITimeGraphEntry
) {
164 return contentProvider
.getChildren(parentElement
);
166 return new Object
[0];
170 public Object
getParent(Object element
) {
171 if (element
instanceof ITimeGraphEntry
) {
172 return contentProvider
.getParent(element
);
178 public boolean hasChildren(Object element
) {
179 if (element
instanceof ITimeGraphEntry
) {
180 return contentProvider
.hasChildren(element
);
187 * The TreeLabelProviderWrapper is used to intercept the filler items
188 * from the calls to the tree's real label provider.
190 private class TreeLabelProviderWrapper
implements ITableLabelProvider
{
191 private final ITableLabelProvider labelProvider
;
193 public TreeLabelProviderWrapper(ITableLabelProvider labelProvider
) {
194 this.labelProvider
= labelProvider
;
198 public void addListener(ILabelProviderListener listener
) {
199 labelProvider
.addListener(listener
);
203 public void dispose() {
204 labelProvider
.dispose();
208 public boolean isLabelProperty(Object element
, String property
) {
209 if (element
instanceof ITimeGraphEntry
) {
210 return labelProvider
.isLabelProperty(element
, property
);
216 public void removeListener(ILabelProviderListener listener
) {
217 labelProvider
.removeListener(listener
);
221 public Image
getColumnImage(Object element
, int columnIndex
) {
222 if (element
instanceof ITimeGraphEntry
) {
223 return labelProvider
.getColumnImage(element
, columnIndex
);
229 public String
getColumnText(Object element
, int columnIndex
) {
230 if (element
instanceof ITimeGraphEntry
) {
231 return labelProvider
.getColumnText(element
, columnIndex
);
239 * The SelectionListenerWrapper is used to intercept the filler items from
240 * the time graph combo's real selection listener, and to prevent double
241 * notifications from being sent when selection changes in both tree and
242 * time graph at the same time.
244 private class SelectionListenerWrapper
implements ISelectionChangedListener
, ITimeGraphSelectionListener
{
245 private final ITimeGraphSelectionListener listener
;
246 private ITimeGraphEntry selection
= null;
248 public SelectionListenerWrapper(ITimeGraphSelectionListener listener
) {
249 this.listener
= listener
;
253 public void selectionChanged(SelectionChangedEvent event
) {
254 if (fInhibitTreeSelection
) {
257 Object element
= ((IStructuredSelection
) event
.getSelection()).getFirstElement();
258 if (element
instanceof ITimeGraphEntry
) {
259 ITimeGraphEntry entry
= (ITimeGraphEntry
) element
;
260 if (entry
!= selection
) {
262 listener
.selectionChanged(new TimeGraphSelectionEvent(event
.getSource(), selection
));
268 public void selectionChanged(TimeGraphSelectionEvent event
) {
269 ITimeGraphEntry entry
= event
.getSelection();
270 if (entry
!= selection
) {
272 listener
.selectionChanged(new TimeGraphSelectionEvent(event
.getSource(), selection
));
278 * The ViewerFilterWrapper is used to intercept the filler items from
279 * the time graph combo's real ViewerFilters. These filler items should
282 private class ViewerFilterWrapper
extends ViewerFilter
{
284 private ViewerFilter fWrappedFilter
;
286 ViewerFilterWrapper(ViewerFilter filter
) {
288 this.fWrappedFilter
= filter
;
292 public boolean select(Viewer viewer
, Object parentElement
, Object element
) {
293 if (element
instanceof ITimeGraphEntry
) {
294 return fWrappedFilter
.select(viewer
, parentElement
, element
);
302 * This filter simply keeps a list of elements that should be filtered out.
303 * All the other elements will be shown.
304 * By default and when the list is set to null, all elements are shown.
306 private class RawViewerFilter
extends ViewerFilter
{
308 private List
<Object
> fFiltered
= null;
310 public void setFiltered(List
<Object
> objects
) {
314 public List
<Object
> getFiltered() {
319 public boolean select(Viewer viewer
, Object parentElement
, Object element
) {
320 if (fFiltered
== null) {
323 return !fFiltered
.contains(element
);
327 // ------------------------------------------------------------------------
329 // ------------------------------------------------------------------------
332 * Constructs a new instance of this class given its parent
333 * and a style value describing its behavior and appearance.
335 * @param parent a widget which will be the parent of the new instance (cannot be null)
336 * @param style the style of widget to construct
338 public TimeGraphCombo(Composite parent
, int style
) {
339 this(parent
, style
, DEFAULT_WEIGHTS
);
343 * Constructs a new instance of this class given its parent and a style
344 * value describing its behavior and appearance.
347 * a widget which will be the parent of the new instance (cannot
350 * the style of widget to construct
352 * The relative weights of each side of the sash form
355 public TimeGraphCombo(Composite parent
, int style
, int[] weights
) {
356 super(parent
, style
);
357 setLayout(new FillLayout());
359 final SashForm sash
= new SashForm(this, SWT
.NONE
);
361 fTreeViewer
= new TreeViewer(sash
, SWT
.FULL_SELECTION
| SWT
.H_SCROLL
);
362 final Tree tree
= fTreeViewer
.getTree();
363 tree
.setHeaderVisible(true);
364 tree
.setLinesVisible(true);
366 fTimeGraphViewer
= new TimeGraphViewer(sash
, SWT
.NONE
);
367 fTimeGraphViewer
.setItemHeight(getItemHeight(tree
));
368 fTimeGraphViewer
.setHeaderHeight(tree
.getHeaderHeight());
369 fTimeGraphViewer
.setBorderWidth(tree
.getBorderWidth());
370 fTimeGraphViewer
.setNameWidthPref(0);
372 fFilter
= new RawViewerFilter();
375 fFilterDialog
= new TimeGraphFilterDialog(getShell());
377 // Feature in Windows. The tree vertical bar reappears when
378 // the control is resized so we need to hide it again.
379 // Bug in Linux. The tree header height is 0 in constructor,
380 // so we need to reset it later when the control is resized.
381 tree
.addControlListener(new ControlAdapter() {
382 private int depth
= 0;
384 public void controlResized(ControlEvent e
) {
387 tree
.getVerticalBar().setEnabled(false);
388 // this can trigger controlResized recursively
389 tree
.getVerticalBar().setVisible(false);
392 fTimeGraphViewer
.setHeaderHeight(tree
.getHeaderHeight());
396 // ensure synchronization of expanded items between tree and time graph
397 fTreeViewer
.addTreeListener(new ITreeViewerListener() {
399 public void treeCollapsed(TreeExpansionEvent event
) {
400 fTimeGraphViewer
.setExpandedState((ITimeGraphEntry
) event
.getElement(), false);
401 List
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
);
402 int topIndex
= fTimeGraphViewer
.getTopIndex();
403 if (topIndex
>= treeItems
.size()) {
406 TreeItem treeItem
= treeItems
.get(topIndex
);
407 tree
.setTopItem(treeItem
);
411 public void treeExpanded(TreeExpansionEvent event
) {
412 ITimeGraphEntry entry
= (ITimeGraphEntry
) event
.getElement();
413 fTimeGraphViewer
.setExpandedState(entry
, true);
414 for (ITimeGraphEntry child
: entry
.getChildren()) {
415 boolean expanded
= fTreeViewer
.getExpandedState(child
);
416 fTimeGraphViewer
.setExpandedState(child
, expanded
);
418 List
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
);
419 int topIndex
= fTimeGraphViewer
.getTopIndex();
420 if (topIndex
>= treeItems
.size()) {
423 final TreeItem treeItem
= treeItems
.get(topIndex
);
424 // queue the top item update because the tree can change its top item
425 // autonomously immediately after the listeners have been notified
426 getDisplay().asyncExec(new Runnable() {
429 tree
.setTopItem(treeItem
);
434 // ensure synchronization of expanded items between tree and time graph
435 fTimeGraphViewer
.addTreeListener(new ITimeGraphTreeListener() {
437 public void treeCollapsed(TimeGraphTreeExpansionEvent event
) {
438 fTreeViewer
.setExpandedState(event
.getEntry(), false);
442 public void treeExpanded(TimeGraphTreeExpansionEvent event
) {
443 ITimeGraphEntry entry
= event
.getEntry();
444 fTreeViewer
.setExpandedState(entry
, true);
445 for (ITimeGraphEntry child
: entry
.getChildren()) {
446 boolean expanded
= fTreeViewer
.getExpandedState(child
);
447 fTimeGraphViewer
.setExpandedState(child
, expanded
);
452 // prevent mouse button from selecting a filler tree item
453 tree
.addListener(SWT
.MouseDown
, new Listener() {
455 public void handleEvent(Event event
) {
456 TreeItem treeItem
= tree
.getItem(new Point(event
.x
, event
.y
));
457 if (treeItem
== null || treeItem
.getData() == FILLER
) {
459 List
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
);
460 if (treeItems
.size() == 0) {
461 fTreeViewer
.setSelection(new StructuredSelection());
462 fTimeGraphViewer
.setSelection(null);
465 // this prevents from scrolling up when selecting
466 // the partially visible tree item at the bottom
467 tree
.select(treeItems
.get(treeItems
.size() - 1));
468 fTreeViewer
.setSelection(new StructuredSelection());
469 fTimeGraphViewer
.setSelection(null);
474 // prevent mouse wheel from scrolling down into filler tree items
475 tree
.addListener(SWT
.MouseWheel
, new Listener() {
477 public void handleEvent(Event event
) {
479 Slider scrollBar
= fTimeGraphViewer
.getVerticalBar();
480 fTimeGraphViewer
.setTopIndex(scrollBar
.getSelection() - event
.count
);
481 List
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
);
482 int topIndex
= fTimeGraphViewer
.getTopIndex();
483 if (topIndex
>= treeItems
.size()) {
486 TreeItem treeItem
= treeItems
.get(topIndex
);
487 tree
.setTopItem(treeItem
);
491 // prevent key stroke from selecting a filler tree item
492 tree
.addListener(SWT
.KeyDown
, new Listener() {
494 public void handleEvent(Event event
) {
495 List
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
);
496 if (treeItems
.size() == 0) {
497 fTreeViewer
.setSelection(new StructuredSelection());
501 if (event
.keyCode
== SWT
.ARROW_DOWN
) {
502 int index
= Math
.min(fTimeGraphViewer
.getSelectionIndex() + 1, treeItems
.size() - 1);
503 fTimeGraphViewer
.setSelection((ITimeGraphEntry
) treeItems
.get(index
).getData());
505 } else if (event
.keyCode
== SWT
.PAGE_DOWN
) {
506 int height
= tree
.getSize().y
- tree
.getHeaderHeight() - tree
.getHorizontalBar().getSize().y
;
507 int countPerPage
= height
/ getItemHeight(tree
);
508 int index
= Math
.min(fTimeGraphViewer
.getSelectionIndex() + countPerPage
- 1, treeItems
.size() - 1);
509 fTimeGraphViewer
.setSelection((ITimeGraphEntry
) treeItems
.get(index
).getData());
511 } else if (event
.keyCode
== SWT
.END
) {
512 fTimeGraphViewer
.setSelection((ITimeGraphEntry
) treeItems
.get(treeItems
.size() - 1).getData());
515 TreeItem treeItem
= treeItems
.get(fTimeGraphViewer
.getTopIndex());
516 tree
.setTopItem(treeItem
);
517 if (fTimeGraphViewer
.getSelectionIndex() >= 0) {
518 fTreeViewer
.setSelection(new StructuredSelection(fTimeGraphViewer
.getSelection()));
520 fTreeViewer
.setSelection(new StructuredSelection());
525 // ensure alignment of top item between tree and time graph
526 fTimeGraphViewer
.getTimeGraphControl().addControlListener(new ControlAdapter() {
528 public void controlResized(ControlEvent e
) {
529 List
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
);
530 int topIndex
= fTimeGraphViewer
.getTopIndex();
531 if (topIndex
>= treeItems
.size()) {
534 TreeItem treeItem
= treeItems
.get(topIndex
);
535 tree
.setTopItem(treeItem
);
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 List
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
);
552 int topIndex
= fTimeGraphViewer
.getTopIndex();
553 if (topIndex
>= treeItems
.size()) {
556 TreeItem treeItem
= treeItems
.get(topIndex
);
557 tree
.setTopItem(treeItem
);
562 // ensure synchronization of selected item between tree and time graph
563 fTimeGraphViewer
.addSelectionListener(new ITimeGraphSelectionListener() {
565 public void selectionChanged(TimeGraphSelectionEvent event
) {
566 ITimeGraphEntry entry
= fTimeGraphViewer
.getSelection();
567 fInhibitTreeSelection
= true; // block the tree selection changed listener
569 StructuredSelection selection
= new StructuredSelection(entry
);
570 fTreeViewer
.setSelection(selection
);
572 fTreeViewer
.setSelection(new StructuredSelection());
574 fInhibitTreeSelection
= false;
575 List
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
);
576 int topIndex
= fTimeGraphViewer
.getTopIndex();
577 if (topIndex
>= treeItems
.size()) {
580 TreeItem treeItem
= treeItems
.get(topIndex
);
581 tree
.setTopItem(treeItem
);
585 // ensure alignment of top item between tree and time graph
586 fTimeGraphViewer
.getVerticalBar().addSelectionListener(new SelectionAdapter() {
588 public void widgetSelected(SelectionEvent e
) {
589 List
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
);
590 int topIndex
= fTimeGraphViewer
.getTopIndex();
591 if (topIndex
>= treeItems
.size()) {
594 TreeItem treeItem
= treeItems
.get(topIndex
);
595 tree
.setTopItem(treeItem
);
599 // ensure alignment of top item between tree and time graph
600 fTimeGraphViewer
.getTimeGraphControl().addMouseWheelListener(new MouseWheelListener() {
602 public void mouseScrolled(MouseEvent e
) {
603 List
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
);
604 int topIndex
= fTimeGraphViewer
.getTopIndex();
605 if (topIndex
>= treeItems
.size()) {
608 TreeItem treeItem
= treeItems
.get(topIndex
);
609 tree
.setTopItem(treeItem
);
613 // ensure the tree has focus control when mouse is over it if the time graph had control
614 fTreeViewer
.getControl().addMouseTrackListener(new MouseTrackAdapter() {
616 public void mouseEnter(MouseEvent e
) {
617 if (fTimeGraphViewer
.getTimeGraphControl().isFocusControl()) {
618 fTreeViewer
.getControl().setFocus();
623 // ensure the time graph has focus control when mouse is over it if the tree had control
624 fTimeGraphViewer
.getTimeGraphControl().addMouseTrackListener(new MouseTrackAdapter() {
626 public void mouseEnter(MouseEvent e
) {
627 if (fTreeViewer
.getControl().isFocusControl()) {
628 fTimeGraphViewer
.getTimeGraphControl().setFocus();
632 fTimeGraphViewer
.getTimeGraphScale().addMouseTrackListener(new MouseTrackAdapter() {
634 public void mouseEnter(MouseEvent e
) {
635 if (fTreeViewer
.getControl().isFocusControl()) {
636 fTimeGraphViewer
.getTimeGraphControl().setFocus();
641 // ensure the time graph item heights are equal to the tree item heights
642 tree
.addPaintListener(new PaintListener() {
644 public void paintControl(PaintEvent e
) {
645 List
<TreeItem
> items
= getVisibleExpandedItems(tree
);
646 for (int i
= 0; i
< items
.size() - 1; i
++) {
647 TreeItem item
= items
.get(i
);
649 * Bug in Linux. The method getBounds doesn't always return the correct height.
650 * Use the difference of y position between items to calculate the height.
652 Integer itemHeight
= items
.get(i
+ 1).getBounds().y
- item
.getBounds().y
;
653 if (!itemHeight
.equals(item
.getData(ITEM_HEIGHT
))) {
654 ITimeGraphEntry entry
= (ITimeGraphEntry
) item
.getData();
655 if (fTimeGraphViewer
.getTimeGraphControl().setItemHeight(entry
, itemHeight
)) {
656 item
.setData(ITEM_HEIGHT
, itemHeight
);
663 // The filler rows are required to ensure alignment when the tree does not have a
664 // visible horizontal scroll bar. The tree does not allow its top item to be set
665 // to a value that would cause blank space to be drawn at the bottom of the tree.
666 fNumFillerRows
= Display
.getDefault().getBounds().height
/ getItemHeight(tree
);
668 sash
.setWeights(weights
);
671 // ------------------------------------------------------------------------
673 // ------------------------------------------------------------------------
676 * Returns this time graph combo's tree viewer.
678 * @return the tree viewer
680 public TreeViewer
getTreeViewer() {
685 * Returns this time graph combo's time graph viewer.
687 * @return the time graph viewer
689 public TimeGraphViewer
getTimeGraphViewer() {
690 return fTimeGraphViewer
;
694 * Callback for the show filter action
698 public void showFilterDialog() {
699 if(fTopInput
!= null) {
700 List
<?
extends ITimeGraphEntry
> allElements
= listAllInputs(fTopInput
);
701 fFilterDialog
.setInput(fTopInput
.toArray(new ITimeGraphEntry
[0]));
702 fFilterDialog
.setTitle(Messages
.TmfTimeFilterDialog_WINDOW_TITLE
);
703 fFilterDialog
.setMessage(Messages
.TmfTimeFilterDialog_MESSAGE
);
704 fFilterDialog
.setExpandedElements(allElements
.toArray());
705 if (fFilter
.getFiltered() != null) {
706 ArrayList
<?
extends ITimeGraphEntry
> nonFilteredElements
= new ArrayList
<>(allElements
);
707 nonFilteredElements
.removeAll(fFilter
.getFiltered());
708 fFilterDialog
.setInitialElementSelections(nonFilteredElements
);
710 fFilterDialog
.setInitialElementSelections(allElements
);
712 fFilterDialog
.create();
713 fFilterDialog
.open();
714 // Process selected elements
715 if (fFilterDialog
.getResult() != null) {
716 fInhibitTreeSelection
= true;
717 if (fFilterDialog
.getResult().length
!= allElements
.size()) {
718 ArrayList
<Object
> filteredElements
= new ArrayList
<Object
>(allElements
);
719 filteredElements
.removeAll(Arrays
.asList(fFilterDialog
.getResult()));
720 fFilter
.setFiltered(filteredElements
);
722 fFilter
.setFiltered(null);
724 fTreeViewer
.refresh();
725 fTreeViewer
.expandAll();
726 fTimeGraphViewer
.refresh();
727 fInhibitTreeSelection
= false;
728 // Reset selection to first entry
729 if (fFilterDialog
.getResult().length
> 0) {
730 setSelection((ITimeGraphEntry
) fFilterDialog
.getResult()[0]);
737 * Get the show filter action.
739 * @return The Action object
742 public Action
getShowFilterAction() {
743 if (showFilterAction
== null) {
745 showFilterAction
= new Action() {
751 showFilterAction
.setText(Messages
.TmfTimeGraphCombo_FilterActionNameText
);
752 showFilterAction
.setToolTipText(Messages
.TmfTimeGraphCombo_FilterActionToolTipText
);
753 // TODO find a nice, distinctive icon
754 showFilterAction
.setImageDescriptor(Activator
.getDefault().getImageDescripterFromPath(ITmfImageConstants
.IMG_UI_FILTERS
));
757 return showFilterAction
;
760 // ------------------------------------------------------------------------
762 // ------------------------------------------------------------------------
765 public void redraw() {
766 fTimeGraphViewer
.getControl().redraw();
770 // ------------------------------------------------------------------------
772 // ------------------------------------------------------------------------
775 * Sets the tree content provider used by this time graph combo.
777 * @param contentProvider the tree content provider
779 public void setTreeContentProvider(ITreeContentProvider contentProvider
) {
780 fTreeViewer
.setContentProvider(new TreeContentProviderWrapper(contentProvider
));
784 * Sets the tree label provider used by this time graph combo.
786 * @param labelProvider the tree label provider
788 public void setTreeLabelProvider(ITableLabelProvider labelProvider
) {
789 fTreeViewer
.setLabelProvider(new TreeLabelProviderWrapper(labelProvider
));
793 * Sets the tree content provider used by the filter dialog
795 * @param contentProvider the tree content provider
798 public void setFilterContentProvider(ITreeContentProvider contentProvider
) {
799 fFilterDialog
.setContentProvider(contentProvider
);
803 * Sets the tree label provider used by the filter dialog
805 * @param labelProvider the tree label provider
808 public void setFilterLabelProvider(ITableLabelProvider labelProvider
) {
809 fFilterDialog
.setLabelProvider(labelProvider
);
813 * Sets the tree columns for this time graph combo.
815 * @param columnNames the tree column names
817 public void setTreeColumns(String
[] columnNames
) {
818 final Tree tree
= fTreeViewer
.getTree();
819 for (String columnName
: columnNames
) {
820 TreeColumn column
= new TreeColumn(tree
, SWT
.LEFT
);
821 column
.setText(columnName
);
827 * Sets the tree columns for this time graph combo's filter dialog.
829 * @param columnNames the tree column names
832 public void setFilterColumns(String
[] columnNames
) {
833 fFilterDialog
.setColumnNames(columnNames
);
837 * Sets the time graph provider used by this time graph combo.
839 * @param timeGraphProvider the time graph provider
841 public void setTimeGraphProvider(ITimeGraphPresentationProvider timeGraphProvider
) {
842 fTimeGraphViewer
.setTimeGraphProvider(timeGraphProvider
);
846 * Sets or clears the input for this time graph combo.
847 * The input array should only contain top-level elements.
849 * @param input the input of this time graph combo, or <code>null</code> if none
851 public void setInput(ITimeGraphEntry
[] input
) {
852 fTopInput
= new ArrayList
<>(Arrays
.asList(input
));
853 fFilter
.setFiltered(null);
854 fInhibitTreeSelection
= true;
855 fTreeViewer
.setInput(input
);
856 for (SelectionListenerWrapper listenerWrapper
: fSelectionListenerMap
.values()) {
857 listenerWrapper
.selection
= null;
859 fInhibitTreeSelection
= false;
860 fTreeViewer
.expandAll();
861 fTreeViewer
.getTree().getVerticalBar().setEnabled(false);
862 fTreeViewer
.getTree().getVerticalBar().setVisible(false);
863 fTimeGraphViewer
.setItemHeight(getItemHeight(fTreeViewer
.getTree()));
864 fTimeGraphViewer
.setInput(input
);
868 * Sets or clears the list of links to display on this combo
870 * @param links the links to display in this time graph combo
873 public void setLinks(List
<ILinkEvent
> links
) {
874 fTimeGraphViewer
.setLinks(links
);
878 * @param filter The filter object to be attached to the view
881 public void addFilter(ViewerFilter filter
) {
882 ViewerFilter wrapper
= new ViewerFilterWrapper(filter
);
883 fTreeViewer
.addFilter(wrapper
);
884 fTimeGraphViewer
.addFilter(wrapper
);
885 fViewerFilterMap
.put(filter
, wrapper
);
889 * @param filter The filter object to be removed from the view
892 public void removeFilter(ViewerFilter filter
) {
893 ViewerFilter wrapper
= fViewerFilterMap
.get(filter
);
894 fTreeViewer
.removeFilter(wrapper
);
895 fTimeGraphViewer
.removeFilter(wrapper
);
896 fViewerFilterMap
.remove(filter
);
900 * Refreshes this time graph completely with information freshly obtained from its model.
902 public void refresh() {
903 fInhibitTreeSelection
= true;
904 fTreeViewer
.refresh();
905 fTimeGraphViewer
.refresh();
906 fInhibitTreeSelection
= false;
910 * Adds a listener for selection changes in this time graph combo.
912 * @param listener a selection listener
914 public void addSelectionListener(ITimeGraphSelectionListener listener
) {
915 SelectionListenerWrapper listenerWrapper
= new SelectionListenerWrapper(listener
);
916 fTreeViewer
.addSelectionChangedListener(listenerWrapper
);
917 fSelectionListenerMap
.put(listener
, listenerWrapper
);
918 fTimeGraphViewer
.addSelectionListener(listenerWrapper
);
922 * Removes the given selection listener from this time graph combo.
924 * @param listener a selection changed listener
926 public void removeSelectionListener(ITimeGraphSelectionListener listener
) {
927 SelectionListenerWrapper listenerWrapper
= fSelectionListenerMap
.remove(listener
);
928 fTreeViewer
.removeSelectionChangedListener(listenerWrapper
);
929 fTimeGraphViewer
.removeSelectionListener(listenerWrapper
);
933 * Sets the current selection for this time graph combo.
935 * @param selection the new selection
937 public void setSelection(ITimeGraphEntry selection
) {
938 fTimeGraphViewer
.setSelection(selection
);
939 fInhibitTreeSelection
= true; // block the tree selection changed listener
940 if (selection
!= null) {
941 StructuredSelection structuredSelection
= new StructuredSelection(selection
);
942 fTreeViewer
.setSelection(structuredSelection
);
944 fTreeViewer
.setSelection(new StructuredSelection());
946 fInhibitTreeSelection
= false;
947 List
<TreeItem
> treeItems
= getVisibleExpandedItems(fTreeViewer
.getTree());
948 int topIndex
= fTimeGraphViewer
.getTopIndex();
949 if (topIndex
>= treeItems
.size()) {
952 TreeItem treeItem
= treeItems
.get(topIndex
);
953 fTreeViewer
.getTree().setTopItem(treeItem
);
957 * Set the expanded state of an entry
960 * The entry to expand/collapse
962 * True for expanded, false for collapsed
966 public void setExpandedState(ITimeGraphEntry entry
, boolean expanded
) {
967 fTimeGraphViewer
.setExpandedState(entry
, expanded
);
968 fTreeViewer
.setExpandedState(entry
, expanded
);
972 * Collapses all nodes of the viewer's tree, starting with the root.
976 public void collapseAll() {
977 fTimeGraphViewer
.collapseAll();
978 fTreeViewer
.collapseAll();
982 * Expands all nodes of the viewer's tree, starting with the root.
986 public void expandAll() {
987 fTimeGraphViewer
.expandAll();
988 fTreeViewer
.expandAll();
991 // ------------------------------------------------------------------------
993 // ------------------------------------------------------------------------
995 private List
<TreeItem
> getVisibleExpandedItems(Tree tree
) {
996 ArrayList
<TreeItem
> items
= new ArrayList
<>();
997 for (TreeItem item
: tree
.getItems()) {
998 if (item
.getData() == FILLER
) {
1002 if (item
.getExpanded()) {
1003 items
.addAll(getVisibleExpandedItems(item
));
1009 private List
<TreeItem
> getVisibleExpandedItems(TreeItem treeItem
) {
1010 ArrayList
<TreeItem
> items
= new ArrayList
<>();
1011 for (TreeItem item
: treeItem
.getItems()) {
1013 if (item
.getExpanded()) {
1014 items
.addAll(getVisibleExpandedItems(item
));
1021 * Explores the list of top-level inputs and returns all the inputs
1023 * @param inputs The top-level inputs
1024 * @return All the inputs
1026 private List
<?
extends ITimeGraphEntry
> listAllInputs(List
<?
extends ITimeGraphEntry
> inputs
) {
1027 ArrayList
<ITimeGraphEntry
> items
= new ArrayList
<>();
1028 for (ITimeGraphEntry entry
: inputs
) {
1030 if (entry
.hasChildren()) {
1031 items
.addAll(listAllInputs(entry
.getChildren()));
1037 private int getItemHeight(final Tree tree
) {
1039 * Bug in Linux. The method getItemHeight doesn't always return the correct value.
1041 if (fLinuxItemHeight
>= 0 && System
.getProperty("os.name").contains("Linux")) { //$NON-NLS-1$ //$NON-NLS-2$
1042 if (fLinuxItemHeight
!= 0) {
1043 return fLinuxItemHeight
;
1045 List
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
);
1046 if (treeItems
.size() > 1) {
1047 final TreeItem treeItem0
= treeItems
.get(0);
1048 final TreeItem treeItem1
= treeItems
.get(1);
1049 PaintListener paintListener
= new PaintListener() {
1051 public void paintControl(PaintEvent e
) {
1052 tree
.removePaintListener(this);
1053 int y0
= treeItem0
.getBounds().y
;
1054 int y1
= treeItem1
.getBounds().y
;
1055 int itemHeight
= y1
- y0
;
1056 if (itemHeight
> 0) {
1057 fLinuxItemHeight
= itemHeight
;
1058 fTimeGraphViewer
.setItemHeight(itemHeight
);
1062 tree
.addPaintListener(paintListener
);
1065 fLinuxItemHeight
= -1; // Not Linux, don't perform os.name check anymore
1067 return tree
.getItemHeight();