1 /*******************************************************************************
2 * Copyright (c) 2012, 2015 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 * Christian Mansky - Add check active / uncheck inactive buttons
14 *******************************************************************************/
16 package org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
;
18 import java
.util
.ArrayList
;
19 import java
.util
.Arrays
;
20 import java
.util
.HashMap
;
21 import java
.util
.HashSet
;
22 import java
.util
.List
;
26 import org
.eclipse
.jdt
.annotation
.NonNull
;
27 import org
.eclipse
.jface
.action
.Action
;
28 import org
.eclipse
.jface
.viewers
.AbstractTreeViewer
;
29 import org
.eclipse
.jface
.viewers
.ILabelProviderListener
;
30 import org
.eclipse
.jface
.viewers
.ISelectionChangedListener
;
31 import org
.eclipse
.jface
.viewers
.IStructuredSelection
;
32 import org
.eclipse
.jface
.viewers
.ITableLabelProvider
;
33 import org
.eclipse
.jface
.viewers
.ITreeContentProvider
;
34 import org
.eclipse
.jface
.viewers
.ITreeViewerListener
;
35 import org
.eclipse
.jface
.viewers
.SelectionChangedEvent
;
36 import org
.eclipse
.jface
.viewers
.StructuredSelection
;
37 import org
.eclipse
.jface
.viewers
.TreeExpansionEvent
;
38 import org
.eclipse
.jface
.viewers
.TreeViewer
;
39 import org
.eclipse
.jface
.viewers
.Viewer
;
40 import org
.eclipse
.jface
.viewers
.ViewerFilter
;
41 import org
.eclipse
.swt
.SWT
;
42 import org
.eclipse
.swt
.custom
.SashForm
;
43 import org
.eclipse
.swt
.events
.ControlAdapter
;
44 import org
.eclipse
.swt
.events
.ControlEvent
;
45 import org
.eclipse
.swt
.events
.MouseEvent
;
46 import org
.eclipse
.swt
.events
.MouseTrackAdapter
;
47 import org
.eclipse
.swt
.events
.MouseWheelListener
;
48 import org
.eclipse
.swt
.events
.PaintEvent
;
49 import org
.eclipse
.swt
.events
.PaintListener
;
50 import org
.eclipse
.swt
.events
.SelectionAdapter
;
51 import org
.eclipse
.swt
.events
.SelectionEvent
;
52 import org
.eclipse
.swt
.graphics
.Image
;
53 import org
.eclipse
.swt
.graphics
.Point
;
54 import org
.eclipse
.swt
.graphics
.Rectangle
;
55 import org
.eclipse
.swt
.layout
.FillLayout
;
56 import org
.eclipse
.swt
.layout
.GridLayout
;
57 import org
.eclipse
.swt
.widgets
.Composite
;
58 import org
.eclipse
.swt
.widgets
.Control
;
59 import org
.eclipse
.swt
.widgets
.Display
;
60 import org
.eclipse
.swt
.widgets
.Event
;
61 import org
.eclipse
.swt
.widgets
.Listener
;
62 import org
.eclipse
.swt
.widgets
.Sash
;
63 import org
.eclipse
.swt
.widgets
.Slider
;
64 import org
.eclipse
.swt
.widgets
.Tree
;
65 import org
.eclipse
.swt
.widgets
.TreeColumn
;
66 import org
.eclipse
.swt
.widgets
.TreeItem
;
67 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.Activator
;
68 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.ITmfImageConstants
;
69 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.Messages
;
70 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfSignalManager
;
71 import org
.eclipse
.tracecompass
.tmf
.ui
.signal
.TmfTimeViewAlignmentInfo
;
72 import org
.eclipse
.tracecompass
.tmf
.ui
.signal
.TmfTimeViewAlignmentSignal
;
73 import org
.eclipse
.tracecompass
.tmf
.ui
.views
.ITmfTimeAligned
;
74 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.dialogs
.ITimeGraphEntryActiveProvider
;
75 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.dialogs
.TimeGraphFilterDialog
;
76 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.model
.ILinkEvent
;
77 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.model
.ITimeGraphEntry
;
79 import com
.google
.common
.collect
.Iterables
;
82 * Time graph "combo" view (with the list/tree on the left and the gantt chart
85 * @author Patrick Tasse
87 public class TimeGraphCombo
extends Composite
{
89 // ------------------------------------------------------------------------
91 // ------------------------------------------------------------------------
93 /** Constant indicating that all levels of the time graph should be expanded */
94 public static final int ALL_LEVELS
= AbstractTreeViewer
.ALL_LEVELS
;
96 private static final Object FILLER
= new Object();
98 // ------------------------------------------------------------------------
100 // ------------------------------------------------------------------------
102 /** The tree viewer */
103 private TreeViewer fTreeViewer
;
105 /** The time viewer */
106 private @NonNull TimeGraphViewer fTimeGraphViewer
;
108 /** The selection listener map */
109 private final Map
<ITimeGraphSelectionListener
, SelectionListenerWrapper
> fSelectionListenerMap
= new HashMap
<>();
111 /** The map of viewer filters to viewer filter wrappers */
112 private final Map
<ViewerFilter
, ViewerFilter
> fViewerFilterMap
= new HashMap
<>();
115 * Flag to block the tree selection changed listener when triggered by the
118 private boolean fInhibitTreeSelection
= false;
120 /** Number of filler rows used by the tree content provider */
121 private int fNumFillerRows
;
123 /** Calculated item height for Linux workaround */
124 private int fLinuxItemHeight
= 0;
126 /** The button that opens the filter dialog */
127 private Action showFilterAction
;
129 /** The filter dialog */
130 private TimeGraphFilterDialog fFilterDialog
;
132 /** Default weight of each part of the sash */
133 private static final int[] DEFAULT_WEIGHTS
= { 1, 1 };
135 /** List of all expanded items whose parents are also expanded */
136 private List
<TreeItem
> fVisibleExpandedItems
= null;
138 private Listener fSashDragListener
;
139 private SashForm fSashForm
;
141 // ------------------------------------------------------------------------
143 // ------------------------------------------------------------------------
146 * The TreeContentProviderWrapper is used to insert filler items after
147 * the elements of the tree's real content provider.
149 private class TreeContentProviderWrapper
implements ITreeContentProvider
{
150 private final ITreeContentProvider contentProvider
;
152 public TreeContentProviderWrapper(ITreeContentProvider contentProvider
) {
153 this.contentProvider
= contentProvider
;
157 public void dispose() {
158 contentProvider
.dispose();
162 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
163 contentProvider
.inputChanged(viewer
, oldInput
, newInput
);
167 public Object
[] getElements(Object inputElement
) {
168 Object
[] elements
= contentProvider
.getElements(inputElement
);
169 // add filler elements to ensure alignment with time analysis viewer
170 Object
[] oElements
= Arrays
.copyOf(elements
, elements
.length
+ fNumFillerRows
, Object
[].class);
171 for (int i
= 0; i
< fNumFillerRows
; i
++) {
172 oElements
[elements
.length
+ i
] = FILLER
;
178 public Object
[] getChildren(Object parentElement
) {
179 if (parentElement
instanceof ITimeGraphEntry
) {
180 return contentProvider
.getChildren(parentElement
);
182 return new Object
[0];
186 public Object
getParent(Object element
) {
187 if (element
instanceof ITimeGraphEntry
) {
188 return contentProvider
.getParent(element
);
194 public boolean hasChildren(Object element
) {
195 if (element
instanceof ITimeGraphEntry
) {
196 return contentProvider
.hasChildren(element
);
203 * The TreeLabelProviderWrapper is used to intercept the filler items
204 * from the calls to the tree's real label provider.
206 private class TreeLabelProviderWrapper
implements ITableLabelProvider
{
207 private final ITableLabelProvider labelProvider
;
209 public TreeLabelProviderWrapper(ITableLabelProvider labelProvider
) {
210 this.labelProvider
= labelProvider
;
214 public void addListener(ILabelProviderListener listener
) {
215 labelProvider
.addListener(listener
);
219 public void dispose() {
220 labelProvider
.dispose();
224 public boolean isLabelProperty(Object element
, String property
) {
225 if (element
instanceof ITimeGraphEntry
) {
226 return labelProvider
.isLabelProperty(element
, property
);
232 public void removeListener(ILabelProviderListener listener
) {
233 labelProvider
.removeListener(listener
);
237 public Image
getColumnImage(Object element
, int columnIndex
) {
238 if (element
instanceof ITimeGraphEntry
) {
239 return labelProvider
.getColumnImage(element
, columnIndex
);
245 public String
getColumnText(Object element
, int columnIndex
) {
246 if (element
instanceof ITimeGraphEntry
) {
247 return labelProvider
.getColumnText(element
, columnIndex
);
255 * The SelectionListenerWrapper is used to intercept the filler items from
256 * the time graph combo's real selection listener, and to prevent double
257 * notifications from being sent when selection changes in both tree and
258 * time graph at the same time.
260 private class SelectionListenerWrapper
implements ISelectionChangedListener
, ITimeGraphSelectionListener
{
261 private final ITimeGraphSelectionListener listener
;
262 private ITimeGraphEntry selection
= null;
264 public SelectionListenerWrapper(ITimeGraphSelectionListener listener
) {
265 this.listener
= listener
;
269 public void selectionChanged(SelectionChangedEvent event
) {
270 if (fInhibitTreeSelection
) {
273 Object element
= ((IStructuredSelection
) event
.getSelection()).getFirstElement();
274 if (element
instanceof ITimeGraphEntry
) {
275 ITimeGraphEntry entry
= (ITimeGraphEntry
) element
;
276 if (entry
!= selection
) {
278 listener
.selectionChanged(new TimeGraphSelectionEvent(event
.getSource(), selection
));
284 public void selectionChanged(TimeGraphSelectionEvent event
) {
285 ITimeGraphEntry entry
= event
.getSelection();
286 if (entry
!= selection
) {
288 listener
.selectionChanged(new TimeGraphSelectionEvent(event
.getSource(), selection
));
294 * The ViewerFilterWrapper is used to intercept the filler items from
295 * the time graph combo's real ViewerFilters. These filler items should
298 private class ViewerFilterWrapper
extends ViewerFilter
{
300 private ViewerFilter fWrappedFilter
;
302 ViewerFilterWrapper(ViewerFilter filter
) {
304 this.fWrappedFilter
= filter
;
308 public boolean select(Viewer viewer
, Object parentElement
, Object element
) {
309 if (element
instanceof ITimeGraphEntry
) {
310 return fWrappedFilter
.select(viewer
, parentElement
, element
);
318 * This filter simply keeps a list of elements that should be filtered out.
319 * All the other elements will be shown.
320 * By default and when the list is set to null, all elements are shown.
322 private class RawViewerFilter
extends ViewerFilter
{
324 private List
<Object
> fFiltered
= null;
326 public void setFiltered(List
<Object
> objects
) {
330 public List
<Object
> getFiltered() {
335 public boolean select(Viewer viewer
, Object parentElement
, Object element
) {
336 if (fFiltered
== null) {
339 return !fFiltered
.contains(element
);
343 // ------------------------------------------------------------------------
345 // ------------------------------------------------------------------------
348 * Constructs a new instance of this class given its parent
349 * and a style value describing its behavior and appearance.
351 * @param parent a widget which will be the parent of the new instance (cannot be null)
352 * @param style the style of widget to construct
354 public TimeGraphCombo(Composite parent
, int style
) {
355 this(parent
, style
, DEFAULT_WEIGHTS
);
359 * Constructs a new instance of this class given its parent and a style
360 * value describing its behavior and appearance.
363 * a widget which will be the parent of the new instance (cannot
366 * the style of widget to construct
368 * The array (length 2) of relative weights of each side of the sash form
370 public TimeGraphCombo(Composite parent
, int style
, int[] weights
) {
371 super(parent
, style
);
372 setLayout(new FillLayout());
374 fSashForm
= new SashForm(this, SWT
.NONE
);
376 fTreeViewer
= new TreeViewer(fSashForm
, SWT
.FULL_SELECTION
| SWT
.H_SCROLL
);
377 fTreeViewer
.setAutoExpandLevel(AbstractTreeViewer
.ALL_LEVELS
);
378 final Tree tree
= fTreeViewer
.getTree();
379 tree
.setHeaderVisible(true);
380 tree
.setLinesVisible(true);
382 fTimeGraphViewer
= new TimeGraphViewer(fSashForm
, SWT
.NONE
);
383 fTimeGraphViewer
.setItemHeight(getItemHeight(tree
));
384 fTimeGraphViewer
.setHeaderHeight(tree
.getHeaderHeight());
385 fTimeGraphViewer
.setBorderWidth(tree
.getBorderWidth());
386 fTimeGraphViewer
.setNameWidthPref(0);
388 fFilterDialog
= new TimeGraphFilterDialog(getShell());
390 // Feature in Windows. The tree vertical bar reappears when
391 // the control is resized so we need to hide it again.
392 tree
.addControlListener(new ControlAdapter() {
393 private int depth
= 0;
395 public void controlResized(ControlEvent e
) {
398 tree
.getVerticalBar().setEnabled(false);
399 // this can trigger controlResized recursively
400 tree
.getVerticalBar().setVisible(false);
405 // Bug in Linux. The tree header height is 0 in constructor,
406 // so we need to reset it later when the control is painted.
407 // This work around used to be done on control resized but the header
408 // height was not initialized on the initial resize on GTK3.
409 tree
.addPaintListener(new PaintListener() {
411 public void paintControl(PaintEvent e
) {
412 int headerHeight
= tree
.getHeaderHeight();
413 if (headerHeight
> 0) {
414 fTimeGraphViewer
.setHeaderHeight(headerHeight
);
415 tree
.removePaintListener(this);
420 // ensure synchronization of expanded items between tree and time graph
421 fTreeViewer
.addTreeListener(new ITreeViewerListener() {
423 public void treeCollapsed(TreeExpansionEvent event
) {
424 fTimeGraphViewer
.setExpandedState((ITimeGraphEntry
) event
.getElement(), false);
425 // queue the alignment update because the tree items may only be
426 // actually collapsed after the listeners have been notified
427 fVisibleExpandedItems
= null; // invalidate the cache
428 getDisplay().asyncExec(new Runnable() {
431 alignTreeItems(true);
436 public void treeExpanded(TreeExpansionEvent event
) {
437 ITimeGraphEntry entry
= (ITimeGraphEntry
) event
.getElement();
438 fTimeGraphViewer
.setExpandedState(entry
, true);
439 Set
<Object
> expandedElements
= new HashSet
<>(Arrays
.asList(fTreeViewer
.getExpandedElements()));
440 for (ITimeGraphEntry child
: entry
.getChildren()) {
441 if (child
.hasChildren()) {
442 boolean expanded
= expandedElements
.contains(child
);
443 fTimeGraphViewer
.setExpandedState(child
, expanded
);
446 // queue the alignment update because the tree items may only be
447 // actually expanded after the listeners have been notified
448 fVisibleExpandedItems
= null; // invalidate the cache
449 getDisplay().asyncExec(new Runnable() {
452 alignTreeItems(true);
457 // ensure synchronization of expanded items between tree and time graph
458 fTimeGraphViewer
.addTreeListener(new ITimeGraphTreeListener() {
460 public void treeCollapsed(TimeGraphTreeExpansionEvent event
) {
461 fTreeViewer
.setExpandedState(event
.getEntry(), false);
462 alignTreeItems(true);
466 public void treeExpanded(TimeGraphTreeExpansionEvent event
) {
467 ITimeGraphEntry entry
= event
.getEntry();
468 fTreeViewer
.setExpandedState(entry
, true);
469 Set
<Object
> expandedElements
= new HashSet
<>(Arrays
.asList(fTreeViewer
.getExpandedElements()));
470 for (ITimeGraphEntry child
: entry
.getChildren()) {
471 if (child
.hasChildren()) {
472 boolean expanded
= expandedElements
.contains(child
);
473 fTimeGraphViewer
.setExpandedState(child
, expanded
);
476 alignTreeItems(true);
480 // prevent mouse button from selecting a filler tree item
481 tree
.addListener(SWT
.MouseDown
, new Listener() {
483 public void handleEvent(Event event
) {
484 TreeItem treeItem
= tree
.getItem(new Point(event
.x
, event
.y
));
485 if (treeItem
== null || treeItem
.getData() == FILLER
) {
487 List
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
, false);
488 if (treeItems
.size() == 0) {
489 fTreeViewer
.setSelection(new StructuredSelection());
490 fTimeGraphViewer
.setSelection(null);
493 // this prevents from scrolling up when selecting
494 // the partially visible tree item at the bottom
495 tree
.select(treeItems
.get(treeItems
.size() - 1));
496 fTreeViewer
.setSelection(new StructuredSelection());
497 fTimeGraphViewer
.setSelection(null);
502 // prevent mouse wheel from scrolling down into filler tree items
503 tree
.addListener(SWT
.MouseWheel
, new Listener() {
505 public void handleEvent(Event event
) {
507 Slider scrollBar
= fTimeGraphViewer
.getVerticalBar();
508 fTimeGraphViewer
.setTopIndex(scrollBar
.getSelection() - event
.count
);
509 alignTreeItems(false);
513 // prevent key stroke from selecting a filler tree item
514 tree
.addListener(SWT
.KeyDown
, new Listener() {
516 public void handleEvent(Event event
) {
517 List
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
, false);
518 if (treeItems
.size() == 0) {
519 fTreeViewer
.setSelection(new StructuredSelection());
523 if (event
.keyCode
== SWT
.ARROW_DOWN
) {
524 int index
= Math
.min(fTimeGraphViewer
.getSelectionIndex() + 1, treeItems
.size() - 1);
525 fTimeGraphViewer
.setSelection((ITimeGraphEntry
) treeItems
.get(index
).getData());
527 } else if (event
.keyCode
== SWT
.PAGE_DOWN
) {
528 int height
= tree
.getSize().y
- tree
.getHeaderHeight() - tree
.getHorizontalBar().getSize().y
;
529 int countPerPage
= height
/ getItemHeight(tree
);
530 int index
= Math
.min(fTimeGraphViewer
.getSelectionIndex() + countPerPage
- 1, treeItems
.size() - 1);
531 fTimeGraphViewer
.setSelection((ITimeGraphEntry
) treeItems
.get(index
).getData());
533 } else if (event
.keyCode
== SWT
.END
) {
534 fTimeGraphViewer
.setSelection((ITimeGraphEntry
) treeItems
.get(treeItems
.size() - 1).getData());
537 if (fTimeGraphViewer
.getSelectionIndex() >= 0) {
538 fTreeViewer
.setSelection(new StructuredSelection(fTimeGraphViewer
.getSelection()));
540 fTreeViewer
.setSelection(new StructuredSelection());
542 alignTreeItems(false);
546 // ensure alignment of top item between tree and time graph
547 fTimeGraphViewer
.getTimeGraphControl().addControlListener(new ControlAdapter() {
549 public void controlResized(ControlEvent e
) {
550 alignTreeItems(false);
554 // ensure synchronization of selected item between tree and time graph
555 fTreeViewer
.addSelectionChangedListener(new ISelectionChangedListener() {
557 public void selectionChanged(SelectionChangedEvent event
) {
558 if (fInhibitTreeSelection
) {
561 if (event
.getSelection() instanceof IStructuredSelection
) {
562 Object selection
= ((IStructuredSelection
) event
.getSelection()).getFirstElement();
563 if (selection
instanceof ITimeGraphEntry
) {
564 fTimeGraphViewer
.setSelection((ITimeGraphEntry
) selection
);
566 alignTreeItems(false);
571 // ensure synchronization of selected item between tree and time graph
572 fTimeGraphViewer
.addSelectionListener(new ITimeGraphSelectionListener() {
574 public void selectionChanged(TimeGraphSelectionEvent event
) {
575 ITimeGraphEntry entry
= fTimeGraphViewer
.getSelection();
576 fInhibitTreeSelection
= true; // block the tree selection changed listener
578 StructuredSelection selection
= new StructuredSelection(entry
);
579 fTreeViewer
.setSelection(selection
);
581 fTreeViewer
.setSelection(new StructuredSelection());
583 fInhibitTreeSelection
= false;
584 alignTreeItems(false);
588 // ensure alignment of top item between tree and time graph
589 fTimeGraphViewer
.getVerticalBar().addSelectionListener(new SelectionAdapter() {
591 public void widgetSelected(SelectionEvent e
) {
592 alignTreeItems(false);
596 // ensure alignment of top item between tree and time graph
597 fTimeGraphViewer
.getTimeGraphControl().addMouseWheelListener(new MouseWheelListener() {
599 public void mouseScrolled(MouseEvent e
) {
600 alignTreeItems(false);
604 // ensure the tree has focus control when mouse is over it if the time graph had control
605 fTreeViewer
.getControl().addMouseTrackListener(new MouseTrackAdapter() {
607 public void mouseEnter(MouseEvent e
) {
608 if (fTimeGraphViewer
.getTimeGraphControl().isFocusControl()) {
609 fTreeViewer
.getControl().setFocus();
614 // ensure the time graph has focus control when mouse is over it if the tree had control
615 fTimeGraphViewer
.getTimeGraphControl().addMouseTrackListener(new MouseTrackAdapter() {
617 public void mouseEnter(MouseEvent e
) {
618 if (fTreeViewer
.getControl().isFocusControl()) {
619 fTimeGraphViewer
.getTimeGraphControl().setFocus();
623 fTimeGraphViewer
.getTimeGraphScale().addMouseTrackListener(new MouseTrackAdapter() {
625 public void mouseEnter(MouseEvent e
) {
626 if (fTreeViewer
.getControl().isFocusControl()) {
627 fTimeGraphViewer
.getTimeGraphControl().setFocus();
632 // The filler rows are required to ensure alignment when the tree does not have a
633 // visible horizontal scroll bar. The tree does not allow its top item to be set
634 // to a value that would cause blank space to be drawn at the bottom of the tree.
635 fNumFillerRows
= Display
.getDefault().getBounds().height
/ getItemHeight(tree
);
637 fSashForm
.setWeights(weights
);
639 fTimeGraphViewer
.getTimeGraphControl().addPaintListener(new PaintListener() {
641 public void paintControl(PaintEvent e
) {
642 // Sashes in a SashForm are being created on layout so add the
643 // drag listener here
644 if (fSashDragListener
== null) {
645 for (Control control
: fSashForm
.getChildren()) {
646 if (control
instanceof Sash
) {
647 fSashDragListener
= new Listener() {
650 public void handleEvent(Event event
) {
651 sendTimeViewAlignmentChanged();
655 control
.removePaintListener(this);
656 control
.addListener(SWT
.Selection
, fSashDragListener
);
657 // There should be only one sash
666 private void sendTimeViewAlignmentChanged() {
667 TmfSignalManager
.dispatchSignal(new TmfTimeViewAlignmentSignal(fSashForm
, getTimeViewAlignmentInfo()));
670 // ------------------------------------------------------------------------
672 // ------------------------------------------------------------------------
675 * Returns this time graph combo's tree viewer.
677 * @return the tree viewer
679 public TreeViewer
getTreeViewer() {
684 * Returns this time graph combo's time graph viewer.
686 * @return the time graph viewer
688 public @NonNull TimeGraphViewer
getTimeGraphViewer() {
689 return fTimeGraphViewer
;
693 * Callback for the show filter action
695 public void showFilterDialog() {
696 ITimeGraphEntry
[] topInput
= fTimeGraphViewer
.getTimeGraphContentProvider().getElements(fTimeGraphViewer
.getInput());
697 if (topInput
!= null) {
698 List
<?
extends ITimeGraphEntry
> allElements
= listAllInputs(Arrays
.asList(topInput
));
699 fFilterDialog
.setInput(fTimeGraphViewer
.getInput());
700 fFilterDialog
.setTitle(Messages
.TmfTimeFilterDialog_WINDOW_TITLE
);
701 fFilterDialog
.setMessage(Messages
.TmfTimeFilterDialog_MESSAGE
);
702 fFilterDialog
.setExpandedElements(allElements
.toArray());
703 RawViewerFilter rawViewerFilter
= null;
704 for (ViewerFilter filter
: fTimeGraphViewer
.getFilters()) {
705 if (filter
instanceof RawViewerFilter
) {
706 rawViewerFilter
= (RawViewerFilter
) filter
;
709 if (rawViewerFilter
!= null && rawViewerFilter
.getFiltered() != null) {
710 ArrayList
<?
extends ITimeGraphEntry
> nonFilteredElements
= new ArrayList
<>(allElements
);
711 nonFilteredElements
.removeAll(rawViewerFilter
.getFiltered());
712 fFilterDialog
.setInitialElementSelections(nonFilteredElements
);
714 fFilterDialog
.setInitialElementSelections(allElements
);
716 fFilterDialog
.create();
717 fFilterDialog
.open();
718 // Process selected elements
719 if (fFilterDialog
.getResult() != null) {
720 fInhibitTreeSelection
= true;
721 if (fFilterDialog
.getResult().length
!= allElements
.size()) {
722 ArrayList
<Object
> filteredElements
= new ArrayList
<Object
>(allElements
);
723 filteredElements
.removeAll(Arrays
.asList(fFilterDialog
.getResult()));
724 if (rawViewerFilter
== null) {
725 rawViewerFilter
= new RawViewerFilter();
726 addFilter(rawViewerFilter
);
728 rawViewerFilter
.setFiltered(filteredElements
);
729 } else if (rawViewerFilter
!= null) {
730 removeFilter(rawViewerFilter
);
732 fTreeViewer
.refresh();
733 fTreeViewer
.expandAll();
734 fTimeGraphViewer
.refresh();
735 fTimeGraphViewer
.expandAll();
736 fInhibitTreeSelection
= false;
737 alignTreeItems(true);
739 if (fFilterDialog
.getResult().length
> 0) {
747 * Get the show filter action.
749 * @return The Action object
751 public Action
getShowFilterAction() {
752 if (showFilterAction
== null) {
754 showFilterAction
= new Action() {
760 showFilterAction
.setText(Messages
.TmfTimeGraphCombo_FilterActionNameText
);
761 showFilterAction
.setToolTipText(Messages
.TmfTimeGraphCombo_FilterActionToolTipText
);
762 // TODO find a nice, distinctive icon
763 showFilterAction
.setImageDescriptor(Activator
.getDefault().getImageDescripterFromPath(ITmfImageConstants
.IMG_UI_FILTERS
));
766 return showFilterAction
;
769 // ------------------------------------------------------------------------
771 // ------------------------------------------------------------------------
774 public void redraw() {
775 fTimeGraphViewer
.getControl().redraw();
779 // ------------------------------------------------------------------------
781 // ------------------------------------------------------------------------
784 * Sets the tree content provider used by this time graph combo.
786 * @param contentProvider the tree content provider
788 public void setTreeContentProvider(ITreeContentProvider contentProvider
) {
789 fTreeViewer
.setContentProvider(new TreeContentProviderWrapper(contentProvider
));
793 * Sets the tree label provider used by this time graph combo.
795 * @param labelProvider the tree label provider
797 public void setTreeLabelProvider(ITableLabelProvider labelProvider
) {
798 fTreeViewer
.setLabelProvider(new TreeLabelProviderWrapper(labelProvider
));
802 * Sets the tree content provider used by the filter dialog
804 * @param contentProvider the tree content provider
806 public void setFilterContentProvider(ITreeContentProvider contentProvider
) {
807 fFilterDialog
.setContentProvider(contentProvider
);
811 * Sets the tree label provider used by the filter dialog
813 * @param labelProvider the tree label provider
815 public void setFilterLabelProvider(ITableLabelProvider labelProvider
) {
816 fFilterDialog
.setLabelProvider(labelProvider
);
820 * Adds a "check active" button used by the filter dialog
822 * @param activeProvider
823 * Additional button info specific to a certain view.
826 public void addTimeGraphFilterCheckActiveButton(ITimeGraphEntryActiveProvider activeProvider
) {
827 fFilterDialog
.addTimeGraphFilterCheckActiveButton(activeProvider
);
831 * Adds an "uncheck inactive" button used by the filter dialog
833 * @param inactiveProvider
834 * Additional button info specific to a certain view.
837 public void addTimeGraphFilterUncheckInactiveButton(ITimeGraphEntryActiveProvider inactiveProvider
) {
838 fFilterDialog
.addTimeGraphFilterUncheckInactiveButton(inactiveProvider
);
842 * Sets the tree columns for this time graph combo.
844 * @param columnNames the tree column names
846 public void setTreeColumns(String
[] columnNames
) {
847 final Tree tree
= fTreeViewer
.getTree();
848 for (String columnName
: columnNames
) {
849 TreeColumn column
= new TreeColumn(tree
, SWT
.LEFT
);
850 column
.setText(columnName
);
856 * Sets the tree columns for this time graph combo's filter dialog.
858 * @param columnNames the tree column names
860 public void setFilterColumns(String
[] columnNames
) {
861 fFilterDialog
.setColumnNames(columnNames
);
865 * Sets the time graph content provider used by this time graph combo.
867 * @param timeGraphContentProvider
868 * the time graph content provider
870 public void setTimeGraphContentProvider(ITimeGraphContentProvider timeGraphContentProvider
) {
871 fTimeGraphViewer
.setTimeGraphContentProvider(timeGraphContentProvider
);
875 * Sets the time graph presentation provider used by this time graph combo.
877 * @param timeGraphProvider the time graph provider
879 public void setTimeGraphProvider(ITimeGraphPresentationProvider timeGraphProvider
) {
880 fTimeGraphViewer
.setTimeGraphProvider(timeGraphProvider
);
884 * Sets or clears the input for this time graph combo.
886 * @param input the input of this time graph combo, or <code>null</code> if none
888 public void setInput(Object input
) {
889 fInhibitTreeSelection
= true;
890 fTreeViewer
.setInput(input
);
891 for (SelectionListenerWrapper listenerWrapper
: fSelectionListenerMap
.values()) {
892 listenerWrapper
.selection
= null;
894 fInhibitTreeSelection
= false;
895 fTreeViewer
.getTree().getVerticalBar().setEnabled(false);
896 fTreeViewer
.getTree().getVerticalBar().setVisible(false);
897 fTimeGraphViewer
.setInput(input
);
898 fTimeGraphViewer
.setItemHeight(getItemHeight(fTreeViewer
.getTree()));
899 // queue the alignment update because in Linux the item bounds are not
900 // set properly until the tree has been painted at least once
901 fVisibleExpandedItems
= null; // invalidate the cache
902 getDisplay().asyncExec(new Runnable() {
905 alignTreeItems(true);
910 * Gets the input for this time graph combo.
912 * @return The input of this time graph combo, or <code>null</code> if none
914 public Object
getInput() {
915 return fTreeViewer
.getInput();
919 * Sets or clears the list of links to display on this combo
921 * @param links the links to display in this time graph combo
923 public void setLinks(List
<ILinkEvent
> links
) {
924 fTimeGraphViewer
.setLinks(links
);
928 * @param filter The filter object to be attached to the view
930 public void addFilter(ViewerFilter filter
) {
931 ViewerFilter wrapper
= new ViewerFilterWrapper(filter
);
932 fTreeViewer
.addFilter(wrapper
);
933 fTimeGraphViewer
.addFilter(filter
);
934 fViewerFilterMap
.put(filter
, wrapper
);
935 alignTreeItems(true);
939 * @param filter The filter object to be removed from the view
941 public void removeFilter(ViewerFilter filter
) {
942 ViewerFilter wrapper
= fViewerFilterMap
.get(filter
);
943 fTreeViewer
.removeFilter(wrapper
);
944 fTimeGraphViewer
.removeFilter(filter
);
945 fViewerFilterMap
.remove(filter
);
946 alignTreeItems(true);
950 * Returns this viewer's filters.
952 * @return an array of viewer filters
955 public ViewerFilter
[] getFilters() {
956 return fTimeGraphViewer
.getFilters();
960 * Sets the filters, replacing any previous filters, and triggers
961 * refiltering of the elements.
964 * an array of viewer filters, or null
967 public void setFilters(ViewerFilter
[] filters
) {
968 fViewerFilterMap
.clear();
969 if (filters
== null) {
970 fTreeViewer
.resetFilters();
972 for (ViewerFilter filter
: filters
) {
973 ViewerFilter wrapper
= new ViewerFilterWrapper(filter
);
974 fViewerFilterMap
.put(filter
, wrapper
);
976 ViewerFilter
[] wrappers
= Iterables
.toArray(fViewerFilterMap
.values(), ViewerFilter
.class);
977 fTreeViewer
.setFilters(wrappers
);
979 fTimeGraphViewer
.setFilters(filters
);
980 alignTreeItems(true);
984 * Refreshes this time graph completely with information freshly obtained from its model.
986 public void refresh() {
987 fInhibitTreeSelection
= true;
988 Tree tree
= fTreeViewer
.getTree();
990 tree
.setRedraw(false);
991 fTreeViewer
.refresh();
993 tree
.setRedraw(true);
995 fTimeGraphViewer
.refresh();
996 alignTreeItems(true);
997 fInhibitTreeSelection
= false;
1001 * Adds a listener for selection changes in this time graph combo.
1003 * @param listener a selection listener
1005 public void addSelectionListener(ITimeGraphSelectionListener listener
) {
1006 SelectionListenerWrapper listenerWrapper
= new SelectionListenerWrapper(listener
);
1007 fTreeViewer
.addSelectionChangedListener(listenerWrapper
);
1008 fSelectionListenerMap
.put(listener
, listenerWrapper
);
1009 fTimeGraphViewer
.addSelectionListener(listenerWrapper
);
1013 * Removes the given selection listener from this time graph combo.
1015 * @param listener a selection changed listener
1017 public void removeSelectionListener(ITimeGraphSelectionListener listener
) {
1018 SelectionListenerWrapper listenerWrapper
= fSelectionListenerMap
.remove(listener
);
1019 fTreeViewer
.removeSelectionChangedListener(listenerWrapper
);
1020 fTimeGraphViewer
.removeSelectionListener(listenerWrapper
);
1024 * Sets the current selection for this time graph combo.
1026 * @param selection the new selection
1028 public void setSelection(ITimeGraphEntry selection
) {
1029 fTimeGraphViewer
.setSelection(selection
);
1030 fInhibitTreeSelection
= true; // block the tree selection changed listener
1031 if (selection
!= null) {
1032 StructuredSelection structuredSelection
= new StructuredSelection(selection
);
1033 fTreeViewer
.setSelection(structuredSelection
);
1035 fTreeViewer
.setSelection(new StructuredSelection());
1037 fInhibitTreeSelection
= false;
1038 alignTreeItems(false);
1042 * Sets the auto-expand level to be used for new entries discovered when
1043 * calling {@link #setInput(Object)} or {@link #refresh()}. The value 0
1044 * means that there is no auto-expand; 1 means that top-level entries are
1045 * expanded, but not their children; 2 means that top-level entries are
1046 * expanded, and their children, but not grand-children; and so on.
1048 * The value {@link #ALL_LEVELS} means that all subtrees should be expanded.
1052 * non-negative level, or <code>ALL_LEVELS</code> to expand all
1053 * levels of the tree
1055 public void setAutoExpandLevel(int level
) {
1056 fTimeGraphViewer
.setAutoExpandLevel(level
);
1058 fTreeViewer
.setAutoExpandLevel(level
);
1060 fTreeViewer
.setAutoExpandLevel(level
+ 1);
1065 * Returns the auto-expand level.
1067 * @return non-negative level, or <code>ALL_LEVELS</code> if all levels of
1068 * the tree are expanded automatically
1069 * @see #setAutoExpandLevel
1071 public int getAutoExpandLevel() {
1072 return fTimeGraphViewer
.getAutoExpandLevel();
1076 * Set the expanded state of an entry
1079 * The entry to expand/collapse
1081 * True for expanded, false for collapsed
1083 public void setExpandedState(ITimeGraphEntry entry
, boolean expanded
) {
1084 fTimeGraphViewer
.setExpandedState(entry
, expanded
);
1085 fTreeViewer
.setExpandedState(entry
, expanded
);
1086 alignTreeItems(true);
1090 * Collapses all nodes of the viewer's tree, starting with the root.
1092 public void collapseAll() {
1093 fTimeGraphViewer
.collapseAll();
1094 fTreeViewer
.collapseAll();
1095 alignTreeItems(true);
1099 * Expands all nodes of the viewer's tree, starting with the root.
1101 public void expandAll() {
1102 fTimeGraphViewer
.expandAll();
1103 fTreeViewer
.expandAll();
1104 alignTreeItems(true);
1107 // ------------------------------------------------------------------------
1109 // ------------------------------------------------------------------------
1111 private List
<TreeItem
> getVisibleExpandedItems(Tree tree
, boolean refresh
) {
1112 if (fVisibleExpandedItems
== null || refresh
) {
1113 List
<TreeItem
> visibleExpandedItems
= new ArrayList
<>();
1114 addVisibleExpandedItems(visibleExpandedItems
, tree
.getItems());
1115 fVisibleExpandedItems
= visibleExpandedItems
;
1117 return fVisibleExpandedItems
;
1120 private void addVisibleExpandedItems(List
<TreeItem
> visibleExpandedItems
, TreeItem
[] items
) {
1121 for (TreeItem item
: items
) {
1122 Object data
= item
.getData();
1123 if (data
== FILLER
) {
1126 visibleExpandedItems
.add(item
);
1127 boolean expandedState
= fTimeGraphViewer
.getExpandedState((ITimeGraphEntry
) data
);
1128 if (item
.getExpanded() != expandedState
) {
1129 /* synchronize the expanded state of both viewers */
1130 fTreeViewer
.setExpandedState(data
, expandedState
);
1132 if (expandedState
) {
1133 addVisibleExpandedItems(visibleExpandedItems
, item
.getItems());
1139 * Explores the list of top-level inputs and returns all the inputs
1141 * @param inputs The top-level inputs
1142 * @return All the inputs
1144 private List
<?
extends ITimeGraphEntry
> listAllInputs(List
<?
extends ITimeGraphEntry
> inputs
) {
1145 ArrayList
<ITimeGraphEntry
> items
= new ArrayList
<>();
1146 for (ITimeGraphEntry entry
: inputs
) {
1148 if (entry
.hasChildren()) {
1149 items
.addAll(listAllInputs(entry
.getChildren()));
1155 private int getItemHeight(final Tree tree
) {
1157 * Bug in Linux. The method getItemHeight doesn't always return the correct value.
1159 if (fLinuxItemHeight
>= 0 && System
.getProperty("os.name").contains("Linux")) { //$NON-NLS-1$ //$NON-NLS-2$
1160 if (fLinuxItemHeight
!= 0) {
1161 return fLinuxItemHeight
;
1164 if (getVisibleExpandedItems(tree
, true).size() > 1) {
1165 PaintListener paintListener
= new PaintListener() {
1167 public void paintControl(PaintEvent e
) {
1168 // get the treeItems here to have all items
1169 List
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
, true);
1170 if (treeItems
.size() < 2) {
1173 final TreeItem treeItem0
= treeItems
.get(0);
1174 final TreeItem treeItem1
= treeItems
.get(1);
1175 tree
.removePaintListener(this);
1176 int y0
= treeItem0
.getBounds().y
;
1177 int y1
= treeItem1
.getBounds().y
;
1178 int itemHeight
= y1
- y0
;
1179 if (itemHeight
> 0) {
1180 fLinuxItemHeight
= itemHeight
;
1181 fTimeGraphViewer
.setItemHeight(itemHeight
);
1185 tree
.addPaintListener(paintListener
);
1188 fLinuxItemHeight
= -1; // Not Linux, don't perform os.name check anymore
1190 return tree
.getItemHeight();
1193 private void alignTreeItems(boolean refreshExpandedItems
) {
1194 // align the tree top item with the time graph top item
1195 Tree tree
= fTreeViewer
.getTree();
1196 List
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
, refreshExpandedItems
);
1197 int topIndex
= fTimeGraphViewer
.getTopIndex();
1198 if (topIndex
>= treeItems
.size()) {
1201 TreeItem item
= treeItems
.get(topIndex
);
1202 tree
.setTopItem(item
);
1204 // ensure the time graph item heights are equal to the tree item heights
1205 int treeHeight
= fTreeViewer
.getTree().getBounds().height
;
1206 int index
= topIndex
;
1207 Rectangle bounds
= item
.getBounds();
1208 while (index
< treeItems
.size() - 1) {
1209 if (bounds
.y
> treeHeight
) {
1213 * Bug in Linux. The method getBounds doesn't always return the correct height.
1214 * Use the difference of y position between items to calculate the height.
1216 TreeItem nextItem
= treeItems
.get(index
+ 1);
1217 Rectangle nextBounds
= nextItem
.getBounds();
1218 Integer itemHeight
= nextBounds
.y
- bounds
.y
;
1219 if (itemHeight
> 0) {
1220 ITimeGraphEntry entry
= (ITimeGraphEntry
) item
.getData();
1221 fTimeGraphViewer
.getTimeGraphControl().setItemHeight(entry
, itemHeight
);
1225 bounds
= nextBounds
;
1230 * Return the time alignment information
1232 * @return the time alignment information
1234 * @see ITmfTimeAligned
1238 public TmfTimeViewAlignmentInfo
getTimeViewAlignmentInfo() {
1239 Point location
= fSashForm
.toDisplay(0, 0);
1240 int timeAxisOffset
= fTreeViewer
.getControl().getSize().x
+ fSashForm
.getSashWidth();
1241 return new TmfTimeViewAlignmentInfo(fSashForm
.getShell(), location
, timeAxisOffset
);
1245 * Return the available width for the time-axis.
1247 * @see ITmfTimeAligned
1249 * @param requestedOffset
1250 * the requested offset
1251 * @return the available width for the time-axis
1255 public int getAvailableWidth(int requestedOffset
) {
1256 int vBarWidth
= ((fTimeGraphViewer
.getVerticalBar() != null) && (fTimeGraphViewer
.getVerticalBar().isVisible())) ? fTimeGraphViewer
.getVerticalBar().getSize().x
: 0;
1257 int totalWidth
= fSashForm
.getBounds().width
;
1258 return Math
.min(totalWidth
, Math
.max(0, totalWidth
- requestedOffset
- vBarWidth
));
1262 * Perform the alignment operation.
1265 * the alignment offset
1267 * the alignment width
1269 * @see ITmfTimeAligned
1273 public void performAlign(int offset
, int width
) {
1274 int total
= fSashForm
.getBounds().width
;
1275 int timeAxisOffset
= Math
.min(offset
, total
);
1276 int width1
= Math
.max(0, timeAxisOffset
- fSashForm
.getSashWidth());
1277 int width2
= total
- timeAxisOffset
;
1278 fSashForm
.setWeights(new int[] { width1
, width2
});
1281 Composite composite
= fTimeGraphViewer
.getTimeAlignedComposite();
1282 GridLayout layout
= (GridLayout
) composite
.getLayout();
1283 int timeBasedControlsWidth
= composite
.getSize().x
;
1284 int marginSize
= timeBasedControlsWidth
- width
;
1285 layout
.marginRight
= Math
.max(0, marginSize
);