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
;
80 * Time graph "combo" view (with the list/tree on the left and the gantt chart
83 * @author Patrick Tasse
85 public class TimeGraphCombo
extends Composite
{
87 // ------------------------------------------------------------------------
89 // ------------------------------------------------------------------------
91 /** Constant indicating that all levels of the time graph should be expanded */
92 public static final int ALL_LEVELS
= AbstractTreeViewer
.ALL_LEVELS
;
94 private static final Object FILLER
= new Object();
96 private static final String ITEM_HEIGHT
= "$height$"; //$NON-NLS-1$
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 */
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 /** The filter generated from the filter dialog */
133 private RawViewerFilter fFilter
;
135 /** Default weight of each part of the sash */
136 private static final int[] DEFAULT_WEIGHTS
= { 1, 1 };
138 /** List of all expanded items whose parents are also expanded */
139 private List
<TreeItem
> fVisibleExpandedItems
= null;
141 private Listener fSashDragListener
;
142 private SashForm fSashForm
;
144 // ------------------------------------------------------------------------
146 // ------------------------------------------------------------------------
149 * The TreeContentProviderWrapper is used to insert filler items after
150 * the elements of the tree's real content provider.
152 private class TreeContentProviderWrapper
implements ITreeContentProvider
{
153 private final ITreeContentProvider contentProvider
;
155 public TreeContentProviderWrapper(ITreeContentProvider contentProvider
) {
156 this.contentProvider
= contentProvider
;
160 public void dispose() {
161 contentProvider
.dispose();
165 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
166 contentProvider
.inputChanged(viewer
, oldInput
, newInput
);
170 public Object
[] getElements(Object inputElement
) {
171 Object
[] elements
= contentProvider
.getElements(inputElement
);
172 // add filler elements to ensure alignment with time analysis viewer
173 Object
[] oElements
= Arrays
.copyOf(elements
, elements
.length
+ fNumFillerRows
, Object
[].class);
174 for (int i
= 0; i
< fNumFillerRows
; i
++) {
175 oElements
[elements
.length
+ i
] = FILLER
;
181 public Object
[] getChildren(Object parentElement
) {
182 if (parentElement
instanceof ITimeGraphEntry
) {
183 return contentProvider
.getChildren(parentElement
);
185 return new Object
[0];
189 public Object
getParent(Object element
) {
190 if (element
instanceof ITimeGraphEntry
) {
191 return contentProvider
.getParent(element
);
197 public boolean hasChildren(Object element
) {
198 if (element
instanceof ITimeGraphEntry
) {
199 return contentProvider
.hasChildren(element
);
206 * The TreeLabelProviderWrapper is used to intercept the filler items
207 * from the calls to the tree's real label provider.
209 private class TreeLabelProviderWrapper
implements ITableLabelProvider
{
210 private final ITableLabelProvider labelProvider
;
212 public TreeLabelProviderWrapper(ITableLabelProvider labelProvider
) {
213 this.labelProvider
= labelProvider
;
217 public void addListener(ILabelProviderListener listener
) {
218 labelProvider
.addListener(listener
);
222 public void dispose() {
223 labelProvider
.dispose();
227 public boolean isLabelProperty(Object element
, String property
) {
228 if (element
instanceof ITimeGraphEntry
) {
229 return labelProvider
.isLabelProperty(element
, property
);
235 public void removeListener(ILabelProviderListener listener
) {
236 labelProvider
.removeListener(listener
);
240 public Image
getColumnImage(Object element
, int columnIndex
) {
241 if (element
instanceof ITimeGraphEntry
) {
242 return labelProvider
.getColumnImage(element
, columnIndex
);
248 public String
getColumnText(Object element
, int columnIndex
) {
249 if (element
instanceof ITimeGraphEntry
) {
250 return labelProvider
.getColumnText(element
, columnIndex
);
258 * The SelectionListenerWrapper is used to intercept the filler items from
259 * the time graph combo's real selection listener, and to prevent double
260 * notifications from being sent when selection changes in both tree and
261 * time graph at the same time.
263 private class SelectionListenerWrapper
implements ISelectionChangedListener
, ITimeGraphSelectionListener
{
264 private final ITimeGraphSelectionListener listener
;
265 private ITimeGraphEntry selection
= null;
267 public SelectionListenerWrapper(ITimeGraphSelectionListener listener
) {
268 this.listener
= listener
;
272 public void selectionChanged(SelectionChangedEvent event
) {
273 if (fInhibitTreeSelection
) {
276 Object element
= ((IStructuredSelection
) event
.getSelection()).getFirstElement();
277 if (element
instanceof ITimeGraphEntry
) {
278 ITimeGraphEntry entry
= (ITimeGraphEntry
) element
;
279 if (entry
!= selection
) {
281 listener
.selectionChanged(new TimeGraphSelectionEvent(event
.getSource(), selection
));
287 public void selectionChanged(TimeGraphSelectionEvent event
) {
288 ITimeGraphEntry entry
= event
.getSelection();
289 if (entry
!= selection
) {
291 listener
.selectionChanged(new TimeGraphSelectionEvent(event
.getSource(), selection
));
297 * The ViewerFilterWrapper is used to intercept the filler items from
298 * the time graph combo's real ViewerFilters. These filler items should
301 private class ViewerFilterWrapper
extends ViewerFilter
{
303 private ViewerFilter fWrappedFilter
;
305 ViewerFilterWrapper(ViewerFilter filter
) {
307 this.fWrappedFilter
= filter
;
311 public boolean select(Viewer viewer
, Object parentElement
, Object element
) {
312 if (element
instanceof ITimeGraphEntry
) {
313 return fWrappedFilter
.select(viewer
, parentElement
, element
);
321 * This filter simply keeps a list of elements that should be filtered out.
322 * All the other elements will be shown.
323 * By default and when the list is set to null, all elements are shown.
325 private class RawViewerFilter
extends ViewerFilter
{
327 private List
<Object
> fFiltered
= null;
329 public void setFiltered(List
<Object
> objects
) {
333 public List
<Object
> getFiltered() {
338 public boolean select(Viewer viewer
, Object parentElement
, Object element
) {
339 if (fFiltered
== null) {
342 return !fFiltered
.contains(element
);
346 // ------------------------------------------------------------------------
348 // ------------------------------------------------------------------------
351 * Constructs a new instance of this class given its parent
352 * and a style value describing its behavior and appearance.
354 * @param parent a widget which will be the parent of the new instance (cannot be null)
355 * @param style the style of widget to construct
357 public TimeGraphCombo(Composite parent
, int style
) {
358 this(parent
, style
, DEFAULT_WEIGHTS
);
362 * Constructs a new instance of this class given its parent and a style
363 * value describing its behavior and appearance.
366 * a widget which will be the parent of the new instance (cannot
369 * the style of widget to construct
371 * The array (length 2) of relative weights of each side of the sash form
373 public TimeGraphCombo(Composite parent
, int style
, int[] weights
) {
374 super(parent
, style
);
375 setLayout(new FillLayout());
377 fSashForm
= new SashForm(this, SWT
.NONE
);
379 fTreeViewer
= new TreeViewer(fSashForm
, SWT
.FULL_SELECTION
| SWT
.H_SCROLL
);
380 fTreeViewer
.setAutoExpandLevel(AbstractTreeViewer
.ALL_LEVELS
);
381 final Tree tree
= fTreeViewer
.getTree();
382 tree
.setHeaderVisible(true);
383 tree
.setLinesVisible(true);
385 fTimeGraphViewer
= new TimeGraphViewer(fSashForm
, SWT
.NONE
);
386 fTimeGraphViewer
.setItemHeight(getItemHeight(tree
));
387 fTimeGraphViewer
.setHeaderHeight(tree
.getHeaderHeight());
388 fTimeGraphViewer
.setBorderWidth(tree
.getBorderWidth());
389 fTimeGraphViewer
.setNameWidthPref(0);
391 fFilter
= new RawViewerFilter();
394 fFilterDialog
= new TimeGraphFilterDialog(getShell());
396 // Feature in Windows. The tree vertical bar reappears when
397 // the control is resized so we need to hide it again.
398 // Bug in Linux. The tree header height is 0 in constructor,
399 // so we need to reset it later when the control is resized.
400 tree
.addControlListener(new ControlAdapter() {
401 private int depth
= 0;
403 public void controlResized(ControlEvent e
) {
406 tree
.getVerticalBar().setEnabled(false);
407 // this can trigger controlResized recursively
408 tree
.getVerticalBar().setVisible(false);
411 fTimeGraphViewer
.setHeaderHeight(tree
.getHeaderHeight());
415 // ensure synchronization of expanded items between tree and time graph
416 fTreeViewer
.addTreeListener(new ITreeViewerListener() {
418 public void treeCollapsed(TreeExpansionEvent event
) {
419 fTimeGraphViewer
.setExpandedState((ITimeGraphEntry
) event
.getElement(), false);
420 // queue the alignment update because the tree items may only be
421 // actually collapsed after the listeners have been notified
422 fVisibleExpandedItems
= null; // invalidate the cache
423 getDisplay().asyncExec(new Runnable() {
426 alignTreeItems(true);
431 public void treeExpanded(TreeExpansionEvent event
) {
432 ITimeGraphEntry entry
= (ITimeGraphEntry
) event
.getElement();
433 fTimeGraphViewer
.setExpandedState(entry
, true);
434 Set
<Object
> expandedElements
= new HashSet
<>(Arrays
.asList(fTreeViewer
.getExpandedElements()));
435 for (ITimeGraphEntry child
: entry
.getChildren()) {
436 if (child
.hasChildren()) {
437 boolean expanded
= expandedElements
.contains(child
);
438 fTimeGraphViewer
.setExpandedState(child
, expanded
);
441 // queue the alignment update because the tree items may only be
442 // actually expanded after the listeners have been notified
443 fVisibleExpandedItems
= null; // invalidate the cache
444 getDisplay().asyncExec(new Runnable() {
447 alignTreeItems(true);
452 // ensure synchronization of expanded items between tree and time graph
453 fTimeGraphViewer
.addTreeListener(new ITimeGraphTreeListener() {
455 public void treeCollapsed(TimeGraphTreeExpansionEvent event
) {
456 fTreeViewer
.setExpandedState(event
.getEntry(), false);
457 alignTreeItems(true);
461 public void treeExpanded(TimeGraphTreeExpansionEvent event
) {
462 ITimeGraphEntry entry
= event
.getEntry();
463 fTreeViewer
.setExpandedState(entry
, true);
464 Set
<Object
> expandedElements
= new HashSet
<>(Arrays
.asList(fTreeViewer
.getExpandedElements()));
465 for (ITimeGraphEntry child
: entry
.getChildren()) {
466 if (child
.hasChildren()) {
467 boolean expanded
= expandedElements
.contains(child
);
468 fTimeGraphViewer
.setExpandedState(child
, expanded
);
471 alignTreeItems(true);
475 // prevent mouse button from selecting a filler tree item
476 tree
.addListener(SWT
.MouseDown
, new Listener() {
478 public void handleEvent(Event event
) {
479 TreeItem treeItem
= tree
.getItem(new Point(event
.x
, event
.y
));
480 if (treeItem
== null || treeItem
.getData() == FILLER
) {
482 List
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
, false);
483 if (treeItems
.size() == 0) {
484 fTreeViewer
.setSelection(new StructuredSelection());
485 fTimeGraphViewer
.setSelection(null);
488 // this prevents from scrolling up when selecting
489 // the partially visible tree item at the bottom
490 tree
.select(treeItems
.get(treeItems
.size() - 1));
491 fTreeViewer
.setSelection(new StructuredSelection());
492 fTimeGraphViewer
.setSelection(null);
497 // prevent mouse wheel from scrolling down into filler tree items
498 tree
.addListener(SWT
.MouseWheel
, new Listener() {
500 public void handleEvent(Event event
) {
502 Slider scrollBar
= fTimeGraphViewer
.getVerticalBar();
503 fTimeGraphViewer
.setTopIndex(scrollBar
.getSelection() - event
.count
);
504 alignTreeItems(false);
508 // prevent key stroke from selecting a filler tree item
509 tree
.addListener(SWT
.KeyDown
, new Listener() {
511 public void handleEvent(Event event
) {
512 List
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
, false);
513 if (treeItems
.size() == 0) {
514 fTreeViewer
.setSelection(new StructuredSelection());
518 if (event
.keyCode
== SWT
.ARROW_DOWN
) {
519 int index
= Math
.min(fTimeGraphViewer
.getSelectionIndex() + 1, treeItems
.size() - 1);
520 fTimeGraphViewer
.setSelection((ITimeGraphEntry
) treeItems
.get(index
).getData());
522 } else if (event
.keyCode
== SWT
.PAGE_DOWN
) {
523 int height
= tree
.getSize().y
- tree
.getHeaderHeight() - tree
.getHorizontalBar().getSize().y
;
524 int countPerPage
= height
/ getItemHeight(tree
);
525 int index
= Math
.min(fTimeGraphViewer
.getSelectionIndex() + countPerPage
- 1, treeItems
.size() - 1);
526 fTimeGraphViewer
.setSelection((ITimeGraphEntry
) treeItems
.get(index
).getData());
528 } else if (event
.keyCode
== SWT
.END
) {
529 fTimeGraphViewer
.setSelection((ITimeGraphEntry
) treeItems
.get(treeItems
.size() - 1).getData());
532 if (fTimeGraphViewer
.getSelectionIndex() >= 0) {
533 fTreeViewer
.setSelection(new StructuredSelection(fTimeGraphViewer
.getSelection()));
535 fTreeViewer
.setSelection(new StructuredSelection());
537 alignTreeItems(false);
541 // ensure alignment of top item between tree and time graph
542 fTimeGraphViewer
.getTimeGraphControl().addControlListener(new ControlAdapter() {
544 public void controlResized(ControlEvent e
) {
545 alignTreeItems(false);
549 // ensure synchronization of selected item between tree and time graph
550 fTreeViewer
.addSelectionChangedListener(new ISelectionChangedListener() {
552 public void selectionChanged(SelectionChangedEvent event
) {
553 if (fInhibitTreeSelection
) {
556 if (event
.getSelection() instanceof IStructuredSelection
) {
557 Object selection
= ((IStructuredSelection
) event
.getSelection()).getFirstElement();
558 if (selection
instanceof ITimeGraphEntry
) {
559 fTimeGraphViewer
.setSelection((ITimeGraphEntry
) selection
);
561 alignTreeItems(false);
566 // ensure synchronization of selected item between tree and time graph
567 fTimeGraphViewer
.addSelectionListener(new ITimeGraphSelectionListener() {
569 public void selectionChanged(TimeGraphSelectionEvent event
) {
570 ITimeGraphEntry entry
= fTimeGraphViewer
.getSelection();
571 fInhibitTreeSelection
= true; // block the tree selection changed listener
573 StructuredSelection selection
= new StructuredSelection(entry
);
574 fTreeViewer
.setSelection(selection
);
576 fTreeViewer
.setSelection(new StructuredSelection());
578 fInhibitTreeSelection
= false;
579 alignTreeItems(false);
583 // ensure alignment of top item between tree and time graph
584 fTimeGraphViewer
.getVerticalBar().addSelectionListener(new SelectionAdapter() {
586 public void widgetSelected(SelectionEvent e
) {
587 alignTreeItems(false);
591 // ensure alignment of top item between tree and time graph
592 fTimeGraphViewer
.getTimeGraphControl().addMouseWheelListener(new MouseWheelListener() {
594 public void mouseScrolled(MouseEvent e
) {
595 alignTreeItems(false);
599 // ensure the tree has focus control when mouse is over it if the time graph had control
600 fTreeViewer
.getControl().addMouseTrackListener(new MouseTrackAdapter() {
602 public void mouseEnter(MouseEvent e
) {
603 if (fTimeGraphViewer
.getTimeGraphControl().isFocusControl()) {
604 fTreeViewer
.getControl().setFocus();
609 // ensure the time graph has focus control when mouse is over it if the tree had control
610 fTimeGraphViewer
.getTimeGraphControl().addMouseTrackListener(new MouseTrackAdapter() {
612 public void mouseEnter(MouseEvent e
) {
613 if (fTreeViewer
.getControl().isFocusControl()) {
614 fTimeGraphViewer
.getTimeGraphControl().setFocus();
618 fTimeGraphViewer
.getTimeGraphScale().addMouseTrackListener(new MouseTrackAdapter() {
620 public void mouseEnter(MouseEvent e
) {
621 if (fTreeViewer
.getControl().isFocusControl()) {
622 fTimeGraphViewer
.getTimeGraphControl().setFocus();
627 // The filler rows are required to ensure alignment when the tree does not have a
628 // visible horizontal scroll bar. The tree does not allow its top item to be set
629 // to a value that would cause blank space to be drawn at the bottom of the tree.
630 fNumFillerRows
= Display
.getDefault().getBounds().height
/ getItemHeight(tree
);
632 fSashForm
.setWeights(weights
);
634 fTimeGraphViewer
.getTimeGraphControl().addPaintListener(new PaintListener() {
636 public void paintControl(PaintEvent e
) {
637 // Sashes in a SashForm are being created on layout so add the
638 // drag listener here
639 if (fSashDragListener
== null) {
640 for (Control control
: fSashForm
.getChildren()) {
641 if (control
instanceof Sash
) {
642 fSashDragListener
= new Listener() {
645 public void handleEvent(Event event
) {
646 sendTimeViewAlignmentChanged();
650 control
.removePaintListener(this);
651 control
.addListener(SWT
.Selection
, fSashDragListener
);
652 // There should be only one sash
661 private void sendTimeViewAlignmentChanged() {
662 TmfSignalManager
.dispatchSignal(new TmfTimeViewAlignmentSignal(fSashForm
, getTimeViewAlignmentInfo()));
665 // ------------------------------------------------------------------------
667 // ------------------------------------------------------------------------
670 * Returns this time graph combo's tree viewer.
672 * @return the tree viewer
674 public TreeViewer
getTreeViewer() {
679 * Returns this time graph combo's time graph viewer.
681 * @return the time graph viewer
683 public @NonNull TimeGraphViewer
getTimeGraphViewer() {
684 return fTimeGraphViewer
;
688 * Callback for the show filter action
690 public void showFilterDialog() {
691 ITimeGraphEntry
[] topInput
= fTimeGraphViewer
.getTimeGraphContentProvider().getElements(fTimeGraphViewer
.getInput());
692 if (topInput
!= null) {
693 List
<?
extends ITimeGraphEntry
> allElements
= listAllInputs(Arrays
.asList(topInput
));
694 fFilterDialog
.setInput(fTimeGraphViewer
.getInput());
695 fFilterDialog
.setTitle(Messages
.TmfTimeFilterDialog_WINDOW_TITLE
);
696 fFilterDialog
.setMessage(Messages
.TmfTimeFilterDialog_MESSAGE
);
697 fFilterDialog
.setExpandedElements(allElements
.toArray());
698 if (fFilter
.getFiltered() != null) {
699 ArrayList
<?
extends ITimeGraphEntry
> nonFilteredElements
= new ArrayList
<>(allElements
);
700 nonFilteredElements
.removeAll(fFilter
.getFiltered());
701 fFilterDialog
.setInitialElementSelections(nonFilteredElements
);
703 fFilterDialog
.setInitialElementSelections(allElements
);
705 fFilterDialog
.create();
706 fFilterDialog
.open();
707 // Process selected elements
708 if (fFilterDialog
.getResult() != null) {
709 fInhibitTreeSelection
= true;
710 if (fFilterDialog
.getResult().length
!= allElements
.size()) {
711 ArrayList
<Object
> filteredElements
= new ArrayList
<Object
>(allElements
);
712 filteredElements
.removeAll(Arrays
.asList(fFilterDialog
.getResult()));
713 fFilter
.setFiltered(filteredElements
);
715 fFilter
.setFiltered(null);
717 fTreeViewer
.refresh();
718 fTreeViewer
.expandAll();
719 fTimeGraphViewer
.refresh();
720 fTimeGraphViewer
.expandAll();
721 fInhibitTreeSelection
= false;
722 alignTreeItems(true);
724 if (fFilterDialog
.getResult().length
> 0) {
732 * Get the show filter action.
734 * @return The Action object
736 public Action
getShowFilterAction() {
737 if (showFilterAction
== null) {
739 showFilterAction
= new Action() {
745 showFilterAction
.setText(Messages
.TmfTimeGraphCombo_FilterActionNameText
);
746 showFilterAction
.setToolTipText(Messages
.TmfTimeGraphCombo_FilterActionToolTipText
);
747 // TODO find a nice, distinctive icon
748 showFilterAction
.setImageDescriptor(Activator
.getDefault().getImageDescripterFromPath(ITmfImageConstants
.IMG_UI_FILTERS
));
751 return showFilterAction
;
754 // ------------------------------------------------------------------------
756 // ------------------------------------------------------------------------
759 public void redraw() {
760 fTimeGraphViewer
.getControl().redraw();
764 // ------------------------------------------------------------------------
766 // ------------------------------------------------------------------------
769 * Sets the tree content provider used by this time graph combo.
771 * @param contentProvider the tree content provider
773 public void setTreeContentProvider(ITreeContentProvider contentProvider
) {
774 fTreeViewer
.setContentProvider(new TreeContentProviderWrapper(contentProvider
));
778 * Sets the tree label provider used by this time graph combo.
780 * @param labelProvider the tree label provider
782 public void setTreeLabelProvider(ITableLabelProvider labelProvider
) {
783 fTreeViewer
.setLabelProvider(new TreeLabelProviderWrapper(labelProvider
));
787 * Sets the tree content provider used by the filter dialog
789 * @param contentProvider the tree content provider
791 public void setFilterContentProvider(ITreeContentProvider contentProvider
) {
792 fFilterDialog
.setContentProvider(contentProvider
);
796 * Sets the tree label provider used by the filter dialog
798 * @param labelProvider the tree label provider
800 public void setFilterLabelProvider(ITableLabelProvider labelProvider
) {
801 fFilterDialog
.setLabelProvider(labelProvider
);
805 * Adds a "check active" button used by the filter dialog
807 * @param activeProvider
808 * Additional button info specific to a certain view.
811 public void addTimeGraphFilterCheckActiveButton(ITimeGraphEntryActiveProvider activeProvider
) {
812 fFilterDialog
.addTimeGraphFilterCheckActiveButton(activeProvider
);
816 * Adds an "uncheck inactive" button used by the filter dialog
818 * @param inactiveProvider
819 * Additional button info specific to a certain view.
822 public void addTimeGraphFilterUncheckInactiveButton(ITimeGraphEntryActiveProvider inactiveProvider
) {
823 fFilterDialog
.addTimeGraphFilterUncheckInactiveButton(inactiveProvider
);
827 * Sets the tree columns for this time graph combo.
829 * @param columnNames the tree column names
831 public void setTreeColumns(String
[] columnNames
) {
832 final Tree tree
= fTreeViewer
.getTree();
833 for (String columnName
: columnNames
) {
834 TreeColumn column
= new TreeColumn(tree
, SWT
.LEFT
);
835 column
.setText(columnName
);
841 * Sets the tree columns for this time graph combo's filter dialog.
843 * @param columnNames the tree column names
845 public void setFilterColumns(String
[] columnNames
) {
846 fFilterDialog
.setColumnNames(columnNames
);
850 * Sets the time graph content provider used by this time graph combo.
852 * @param timeGraphContentProvider
853 * the time graph content provider
855 public void setTimeGraphContentProvider(ITimeGraphContentProvider timeGraphContentProvider
) {
856 fTimeGraphViewer
.setTimeGraphContentProvider(timeGraphContentProvider
);
860 * Sets the time graph presentation provider used by this time graph combo.
862 * @param timeGraphProvider the time graph provider
864 public void setTimeGraphProvider(ITimeGraphPresentationProvider timeGraphProvider
) {
865 fTimeGraphViewer
.setTimeGraphProvider(timeGraphProvider
);
869 * Sets or clears the input for this time graph combo.
871 * @param input the input of this time graph combo, or <code>null</code> if none
873 public void setInput(Object input
) {
874 fFilter
.setFiltered(null);
875 fInhibitTreeSelection
= true;
876 fTreeViewer
.setInput(input
);
877 for (SelectionListenerWrapper listenerWrapper
: fSelectionListenerMap
.values()) {
878 listenerWrapper
.selection
= null;
880 fInhibitTreeSelection
= false;
881 fTreeViewer
.getTree().getVerticalBar().setEnabled(false);
882 fTreeViewer
.getTree().getVerticalBar().setVisible(false);
883 fTimeGraphViewer
.setItemHeight(getItemHeight(fTreeViewer
.getTree()));
884 fTimeGraphViewer
.setInput(input
);
885 // queue the alignment update because in Linux the item bounds are not
886 // set properly until the tree has been painted at least once
887 fVisibleExpandedItems
= null; // invalidate the cache
888 getDisplay().asyncExec(new Runnable() {
891 alignTreeItems(true);
896 * Gets the input for this time graph combo.
898 * @return The input of this time graph combo, or <code>null</code> if none
900 public Object
getInput() {
901 return fTreeViewer
.getInput();
905 * Sets or clears the list of links to display on this combo
907 * @param links the links to display in this time graph combo
909 public void setLinks(List
<ILinkEvent
> links
) {
910 fTimeGraphViewer
.setLinks(links
);
914 * @param filter The filter object to be attached to the view
916 public void addFilter(ViewerFilter filter
) {
917 ViewerFilter wrapper
= new ViewerFilterWrapper(filter
);
918 fTreeViewer
.addFilter(wrapper
);
919 fTimeGraphViewer
.addFilter(wrapper
);
920 fViewerFilterMap
.put(filter
, wrapper
);
921 alignTreeItems(true);
925 * @param filter The filter object to be removed from the view
927 public void removeFilter(ViewerFilter filter
) {
928 ViewerFilter wrapper
= fViewerFilterMap
.get(filter
);
929 fTreeViewer
.removeFilter(wrapper
);
930 fTimeGraphViewer
.removeFilter(wrapper
);
931 fViewerFilterMap
.remove(filter
);
932 alignTreeItems(true);
936 * Refreshes this time graph completely with information freshly obtained from its model.
938 public void refresh() {
939 fInhibitTreeSelection
= true;
940 Tree tree
= fTreeViewer
.getTree();
942 tree
.setRedraw(false);
943 fTreeViewer
.refresh();
944 fTreeViewer
.expandToLevel(fTreeViewer
.getAutoExpandLevel());
946 tree
.setRedraw(true);
948 fTimeGraphViewer
.refresh();
949 alignTreeItems(true);
950 fInhibitTreeSelection
= false;
954 * Adds a listener for selection changes in this time graph combo.
956 * @param listener a selection listener
958 public void addSelectionListener(ITimeGraphSelectionListener listener
) {
959 SelectionListenerWrapper listenerWrapper
= new SelectionListenerWrapper(listener
);
960 fTreeViewer
.addSelectionChangedListener(listenerWrapper
);
961 fSelectionListenerMap
.put(listener
, listenerWrapper
);
962 fTimeGraphViewer
.addSelectionListener(listenerWrapper
);
966 * Removes the given selection listener from this time graph combo.
968 * @param listener a selection changed listener
970 public void removeSelectionListener(ITimeGraphSelectionListener listener
) {
971 SelectionListenerWrapper listenerWrapper
= fSelectionListenerMap
.remove(listener
);
972 fTreeViewer
.removeSelectionChangedListener(listenerWrapper
);
973 fTimeGraphViewer
.removeSelectionListener(listenerWrapper
);
977 * Sets the current selection for this time graph combo.
979 * @param selection the new selection
981 public void setSelection(ITimeGraphEntry selection
) {
982 fTimeGraphViewer
.setSelection(selection
);
983 fInhibitTreeSelection
= true; // block the tree selection changed listener
984 if (selection
!= null) {
985 StructuredSelection structuredSelection
= new StructuredSelection(selection
);
986 fTreeViewer
.setSelection(structuredSelection
);
988 fTreeViewer
.setSelection(new StructuredSelection());
990 fInhibitTreeSelection
= false;
991 alignTreeItems(false);
995 * Sets the auto-expand level to be used when the input of the viewer is set
996 * using {@link #setInput(Object)}. The value 0 means that there is no
997 * auto-expand; 1 means that top-level elements are expanded, but not their
998 * children; 2 means that top-level elements are expanded, and their
999 * children, but not grand-children; and so on.
1001 * The value {@link #ALL_LEVELS} means that all subtrees should be expanded.
1004 * non-negative level, or <code>ALL_LEVELS</code> to expand all
1005 * levels of the tree
1007 public void setAutoExpandLevel(int level
) {
1008 fTimeGraphViewer
.setAutoExpandLevel(level
);
1010 fTreeViewer
.setAutoExpandLevel(level
);
1012 fTreeViewer
.setAutoExpandLevel(level
+ 1);
1017 * Returns the auto-expand level.
1019 * @return non-negative level, or <code>ALL_LEVELS</code> if all levels of
1020 * the tree are expanded automatically
1021 * @see #setAutoExpandLevel
1023 public int getAutoExpandLevel() {
1024 return fTimeGraphViewer
.getAutoExpandLevel();
1028 * Set the expanded state of an entry
1031 * The entry to expand/collapse
1033 * True for expanded, false for collapsed
1035 public void setExpandedState(ITimeGraphEntry entry
, boolean expanded
) {
1036 fTimeGraphViewer
.setExpandedState(entry
, expanded
);
1037 fTreeViewer
.setExpandedState(entry
, expanded
);
1038 alignTreeItems(true);
1042 * Collapses all nodes of the viewer's tree, starting with the root.
1044 public void collapseAll() {
1045 fTimeGraphViewer
.collapseAll();
1046 fTreeViewer
.collapseAll();
1047 alignTreeItems(true);
1051 * Expands all nodes of the viewer's tree, starting with the root.
1053 public void expandAll() {
1054 fTimeGraphViewer
.expandAll();
1055 fTreeViewer
.expandAll();
1056 alignTreeItems(true);
1059 // ------------------------------------------------------------------------
1061 // ------------------------------------------------------------------------
1063 private List
<TreeItem
> getVisibleExpandedItems(Tree tree
, boolean refresh
) {
1064 if (fVisibleExpandedItems
== null || refresh
) {
1065 ArrayList
<TreeItem
> items
= new ArrayList
<>();
1066 for (TreeItem item
: tree
.getItems()) {
1067 if (item
.getData() == FILLER
) {
1071 if (item
.getExpanded()) {
1072 addVisibleExpandedItems(items
, item
);
1075 fVisibleExpandedItems
= items
;
1077 return fVisibleExpandedItems
;
1080 private void addVisibleExpandedItems(List
<TreeItem
> items
, TreeItem treeItem
) {
1081 for (TreeItem item
: treeItem
.getItems()) {
1083 if (item
.getExpanded()) {
1084 addVisibleExpandedItems(items
, item
);
1090 * Explores the list of top-level inputs and returns all the inputs
1092 * @param inputs The top-level inputs
1093 * @return All the inputs
1095 private List
<?
extends ITimeGraphEntry
> listAllInputs(List
<?
extends ITimeGraphEntry
> inputs
) {
1096 ArrayList
<ITimeGraphEntry
> items
= new ArrayList
<>();
1097 for (ITimeGraphEntry entry
: inputs
) {
1099 if (entry
.hasChildren()) {
1100 items
.addAll(listAllInputs(entry
.getChildren()));
1106 private int getItemHeight(final Tree tree
) {
1108 * Bug in Linux. The method getItemHeight doesn't always return the correct value.
1110 if (fLinuxItemHeight
>= 0 && System
.getProperty("os.name").contains("Linux")) { //$NON-NLS-1$ //$NON-NLS-2$
1111 if (fLinuxItemHeight
!= 0) {
1112 return fLinuxItemHeight
;
1114 List
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
, true);
1115 if (treeItems
.size() > 1) {
1116 final TreeItem treeItem0
= treeItems
.get(0);
1117 final TreeItem treeItem1
= treeItems
.get(1);
1118 PaintListener paintListener
= new PaintListener() {
1120 public void paintControl(PaintEvent e
) {
1121 tree
.removePaintListener(this);
1122 int y0
= treeItem0
.getBounds().y
;
1123 int y1
= treeItem1
.getBounds().y
;
1124 int itemHeight
= y1
- y0
;
1125 if (itemHeight
> 0) {
1126 fLinuxItemHeight
= itemHeight
;
1127 fTimeGraphViewer
.setItemHeight(itemHeight
);
1131 tree
.addPaintListener(paintListener
);
1134 fLinuxItemHeight
= -1; // Not Linux, don't perform os.name check anymore
1136 return tree
.getItemHeight();
1139 private void alignTreeItems(boolean refreshExpandedItems
) {
1140 // align the tree top item with the time graph top item
1141 Tree tree
= fTreeViewer
.getTree();
1142 List
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
, refreshExpandedItems
);
1143 int topIndex
= fTimeGraphViewer
.getTopIndex();
1144 if (topIndex
>= treeItems
.size()) {
1147 TreeItem item
= treeItems
.get(topIndex
);
1148 tree
.setTopItem(item
);
1150 // ensure the time graph item heights are equal to the tree item heights
1151 int treeHeight
= fTreeViewer
.getTree().getBounds().height
;
1152 int index
= topIndex
;
1153 Rectangle bounds
= item
.getBounds();
1154 while (index
< treeItems
.size() - 1) {
1155 if (bounds
.y
> treeHeight
) {
1159 * Bug in Linux. The method getBounds doesn't always return the correct height.
1160 * Use the difference of y position between items to calculate the height.
1162 TreeItem nextItem
= treeItems
.get(index
+ 1);
1163 Rectangle nextBounds
= nextItem
.getBounds();
1164 Integer itemHeight
= nextBounds
.y
- bounds
.y
;
1165 if (itemHeight
> 0 && !itemHeight
.equals(item
.getData(ITEM_HEIGHT
))) {
1166 ITimeGraphEntry entry
= (ITimeGraphEntry
) item
.getData();
1167 if (fTimeGraphViewer
.getTimeGraphControl().setItemHeight(entry
, itemHeight
)) {
1168 item
.setData(ITEM_HEIGHT
, itemHeight
);
1173 bounds
= nextBounds
;
1178 * Return the time alignment information
1180 * @return the time alignment information
1182 * @see ITmfTimeAligned
1186 public TmfTimeViewAlignmentInfo
getTimeViewAlignmentInfo() {
1187 int[] weights
= fSashForm
.getWeights();
1188 int leftWidth
= (int) (((float) weights
[0] / (weights
[0] + weights
[1])) * fSashForm
.getBounds().width
) + fSashForm
.getSashWidth();
1189 Point location
= fSashForm
.toDisplay(0, 0);
1190 return new TmfTimeViewAlignmentInfo(fSashForm
.getShell(), location
, leftWidth
);
1194 * Return the available width for the time-axis.
1196 * @see ITmfTimeAligned
1198 * @param requestedOffset
1199 * the requested offset
1200 * @return the available width for the time-axis
1204 public int getAvailableWidth(int requestedOffset
) {
1205 int totalWidth
= fSashForm
.getBounds().width
;
1206 int timeWidth
= totalWidth
- requestedOffset
;
1211 * Perform the alignment operation.
1214 * the alignment offset
1216 * the alignment width
1218 * @see ITmfTimeAligned
1222 public void performAlign(int offset
, int width
) {
1223 int total
= fSashForm
.getBounds().width
;
1224 int timeAxisOffset
= Math
.min(offset
, total
);
1225 int sash1Width
= (int) (timeAxisOffset
/ (float) total
* 1000);
1226 int sash2Width
= (int) ((total
- timeAxisOffset
) / (float) total
* 1000);
1227 fSashForm
.setWeights(new int[] { sash1Width
, sash2Width
});
1230 Composite composite
= fTimeGraphViewer
.getTimeAlignedComposite();
1231 GridLayout layout
= (GridLayout
) composite
.getLayout();
1232 int timeBasedControlsWidth
= composite
.getSize().x
;
1233 int marginSize
= timeBasedControlsWidth
- width
;
1234 layout
.marginRight
= Math
.max(0, marginSize
);