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
.viewers
.AbstractTreeViewer
;
28 import org
.eclipse
.jface
.viewers
.ILabelProviderListener
;
29 import org
.eclipse
.jface
.viewers
.ISelectionChangedListener
;
30 import org
.eclipse
.jface
.viewers
.IStructuredSelection
;
31 import org
.eclipse
.jface
.viewers
.ITableLabelProvider
;
32 import org
.eclipse
.jface
.viewers
.ITreeContentProvider
;
33 import org
.eclipse
.jface
.viewers
.ITreeViewerListener
;
34 import org
.eclipse
.jface
.viewers
.SelectionChangedEvent
;
35 import org
.eclipse
.jface
.viewers
.StructuredSelection
;
36 import org
.eclipse
.jface
.viewers
.TreeExpansionEvent
;
37 import org
.eclipse
.jface
.viewers
.TreeViewer
;
38 import org
.eclipse
.jface
.viewers
.Viewer
;
39 import org
.eclipse
.jface
.viewers
.ViewerFilter
;
40 import org
.eclipse
.swt
.SWT
;
41 import org
.eclipse
.swt
.custom
.SashForm
;
42 import org
.eclipse
.swt
.events
.ControlAdapter
;
43 import org
.eclipse
.swt
.events
.ControlEvent
;
44 import org
.eclipse
.swt
.events
.MouseEvent
;
45 import org
.eclipse
.swt
.events
.MouseTrackAdapter
;
46 import org
.eclipse
.swt
.events
.MouseWheelListener
;
47 import org
.eclipse
.swt
.events
.PaintEvent
;
48 import org
.eclipse
.swt
.events
.PaintListener
;
49 import org
.eclipse
.swt
.events
.SelectionAdapter
;
50 import org
.eclipse
.swt
.events
.SelectionEvent
;
51 import org
.eclipse
.swt
.graphics
.Image
;
52 import org
.eclipse
.swt
.graphics
.Point
;
53 import org
.eclipse
.swt
.graphics
.Rectangle
;
54 import org
.eclipse
.swt
.layout
.FillLayout
;
55 import org
.eclipse
.swt
.layout
.GridLayout
;
56 import org
.eclipse
.swt
.widgets
.Composite
;
57 import org
.eclipse
.swt
.widgets
.Control
;
58 import org
.eclipse
.swt
.widgets
.Display
;
59 import org
.eclipse
.swt
.widgets
.Event
;
60 import org
.eclipse
.swt
.widgets
.Listener
;
61 import org
.eclipse
.swt
.widgets
.Sash
;
62 import org
.eclipse
.swt
.widgets
.Slider
;
63 import org
.eclipse
.swt
.widgets
.Tree
;
64 import org
.eclipse
.swt
.widgets
.TreeColumn
;
65 import org
.eclipse
.swt
.widgets
.TreeItem
;
66 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfSignalManager
;
67 import org
.eclipse
.tracecompass
.tmf
.ui
.signal
.TmfTimeViewAlignmentInfo
;
68 import org
.eclipse
.tracecompass
.tmf
.ui
.signal
.TmfTimeViewAlignmentSignal
;
69 import org
.eclipse
.tracecompass
.tmf
.ui
.views
.ITmfTimeAligned
;
70 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.dialogs
.ITimeGraphEntryActiveProvider
;
71 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.dialogs
.ShowFilterDialogAction
;
72 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.model
.ILinkEvent
;
73 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.model
.ITimeGraphEntry
;
75 import com
.google
.common
.collect
.Iterables
;
78 * Time graph "combo" view (with the list/tree on the left and the gantt chart
81 * @author Patrick Tasse
83 public class TimeGraphCombo
extends Composite
{
85 // ------------------------------------------------------------------------
87 // ------------------------------------------------------------------------
89 /** Constant indicating that all levels of the time graph should be expanded */
90 public static final int ALL_LEVELS
= AbstractTreeViewer
.ALL_LEVELS
;
92 private static final Object FILLER
= new Object();
94 // ------------------------------------------------------------------------
96 // ------------------------------------------------------------------------
98 /** The tree viewer */
99 private TreeViewer fTreeViewer
;
101 /** The time viewer */
102 private @NonNull TimeGraphViewer fTimeGraphViewer
;
104 /** The selection listener map */
105 private final Map
<ITimeGraphSelectionListener
, SelectionListenerWrapper
> fSelectionListenerMap
= new HashMap
<>();
107 /** The map of viewer filters to viewer filter wrappers */
108 private final Map
<ViewerFilter
, ViewerFilter
> fViewerFilterMap
= new HashMap
<>();
111 * Flag to block the tree selection changed listener when triggered by the
114 private boolean fInhibitTreeSelection
= false;
116 /** Number of filler rows used by the tree content provider */
117 private int fNumFillerRows
;
119 /** Calculated item height for Linux workaround */
120 private int fLinuxItemHeight
= 0;
122 /** The action that opens the filter dialog */
123 private ShowFilterDialogAction fShowFilterDialogAction
;
125 /** Default weight of each part of the sash */
126 private static final int[] DEFAULT_WEIGHTS
= { 1, 1 };
128 /** List of all expanded items whose parents are also expanded */
129 private List
<TreeItem
> fVisibleExpandedItems
= null;
131 private Listener fSashDragListener
;
132 private SashForm fSashForm
;
134 private final boolean fScrollBarsInTreeWorkaround
;
136 // ------------------------------------------------------------------------
138 // ------------------------------------------------------------------------
141 * The TreeContentProviderWrapper is used to insert filler items after
142 * the elements of the tree's real content provider.
144 private class TreeContentProviderWrapper
implements ITreeContentProvider
{
145 private final ITreeContentProvider contentProvider
;
147 public TreeContentProviderWrapper(ITreeContentProvider contentProvider
) {
148 this.contentProvider
= contentProvider
;
152 public void dispose() {
153 contentProvider
.dispose();
157 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
158 contentProvider
.inputChanged(viewer
, oldInput
, newInput
);
162 public Object
[] getElements(Object inputElement
) {
163 Object
[] elements
= contentProvider
.getElements(inputElement
);
164 // add filler elements to ensure alignment with time analysis viewer
165 Object
[] oElements
= Arrays
.copyOf(elements
, elements
.length
+ fNumFillerRows
, Object
[].class);
166 for (int i
= 0; i
< fNumFillerRows
; i
++) {
167 oElements
[elements
.length
+ i
] = FILLER
;
173 public Object
[] getChildren(Object parentElement
) {
174 if (parentElement
instanceof ITimeGraphEntry
) {
175 return contentProvider
.getChildren(parentElement
);
177 return new Object
[0];
181 public Object
getParent(Object element
) {
182 if (element
instanceof ITimeGraphEntry
) {
183 return contentProvider
.getParent(element
);
189 public boolean hasChildren(Object element
) {
190 if (element
instanceof ITimeGraphEntry
) {
191 return contentProvider
.hasChildren(element
);
198 * The TreeLabelProviderWrapper is used to intercept the filler items
199 * from the calls to the tree's real label provider.
201 private class TreeLabelProviderWrapper
implements ITableLabelProvider
{
202 private final ITableLabelProvider labelProvider
;
204 public TreeLabelProviderWrapper(ITableLabelProvider labelProvider
) {
205 this.labelProvider
= labelProvider
;
209 public void addListener(ILabelProviderListener listener
) {
210 labelProvider
.addListener(listener
);
214 public void dispose() {
215 labelProvider
.dispose();
219 public boolean isLabelProperty(Object element
, String property
) {
220 if (element
instanceof ITimeGraphEntry
) {
221 return labelProvider
.isLabelProperty(element
, property
);
227 public void removeListener(ILabelProviderListener listener
) {
228 labelProvider
.removeListener(listener
);
232 public Image
getColumnImage(Object element
, int columnIndex
) {
233 if (element
instanceof ITimeGraphEntry
) {
234 return labelProvider
.getColumnImage(element
, columnIndex
);
240 public String
getColumnText(Object element
, int columnIndex
) {
241 if (element
instanceof ITimeGraphEntry
) {
242 return labelProvider
.getColumnText(element
, columnIndex
);
250 * The SelectionListenerWrapper is used to intercept the filler items from
251 * the time graph combo's real selection listener, and to prevent double
252 * notifications from being sent when selection changes in both tree and
253 * time graph at the same time.
255 private class SelectionListenerWrapper
implements ISelectionChangedListener
, ITimeGraphSelectionListener
{
256 private final ITimeGraphSelectionListener listener
;
257 private ITimeGraphEntry selection
= null;
259 public SelectionListenerWrapper(ITimeGraphSelectionListener listener
) {
260 this.listener
= listener
;
264 public void selectionChanged(SelectionChangedEvent event
) {
265 if (fInhibitTreeSelection
) {
268 Object element
= ((IStructuredSelection
) event
.getSelection()).getFirstElement();
269 if (element
instanceof ITimeGraphEntry
) {
270 ITimeGraphEntry entry
= (ITimeGraphEntry
) element
;
271 if (entry
!= selection
) {
273 listener
.selectionChanged(new TimeGraphSelectionEvent(event
.getSource(), selection
));
279 public void selectionChanged(TimeGraphSelectionEvent event
) {
280 ITimeGraphEntry entry
= event
.getSelection();
281 if (entry
!= selection
) {
283 listener
.selectionChanged(new TimeGraphSelectionEvent(event
.getSource(), selection
));
289 * The ViewerFilterWrapper is used to intercept the filler items from
290 * the time graph combo's real ViewerFilters. These filler items should
293 private class ViewerFilterWrapper
extends ViewerFilter
{
295 private ViewerFilter fWrappedFilter
;
297 ViewerFilterWrapper(ViewerFilter filter
) {
299 this.fWrappedFilter
= filter
;
303 public boolean select(Viewer viewer
, Object parentElement
, Object element
) {
304 if (element
instanceof ITimeGraphEntry
) {
305 return fWrappedFilter
.select(viewer
, parentElement
, element
);
312 // ------------------------------------------------------------------------
314 // ------------------------------------------------------------------------
317 * Constructs a new instance of this class given its parent
318 * and a style value describing its behavior and appearance.
320 * @param parent a widget which will be the parent of the new instance (cannot be null)
321 * @param style the style of widget to construct
323 public TimeGraphCombo(Composite parent
, int style
) {
324 this(parent
, style
, DEFAULT_WEIGHTS
);
328 * Constructs a new instance of this class given its parent and a style
329 * value describing its behavior and appearance.
332 * a widget which will be the parent of the new instance (cannot
335 * the style of widget to construct
337 * The array (length 2) of relative weights of each side of the sash form
339 public TimeGraphCombo(Composite parent
, int style
, int[] weights
) {
340 super(parent
, style
);
341 setLayout(new FillLayout());
343 fSashForm
= new SashForm(this, SWT
.NONE
);
346 * In Windows, SWT.H_SCROLL | SWT.NO_SCROLL is not properly supported,
347 * both scroll bars are always created. See Tree.checkStyle: "Even when
348 * WS_HSCROLL or WS_VSCROLL is not specified, Windows creates trees and
349 * tables with scroll bars."
351 fScrollBarsInTreeWorkaround
= "win32".equals(SWT
.getPlatform()); //$NON-NLS-1$
353 int scrollBarStyle
= fScrollBarsInTreeWorkaround ? SWT
.H_SCROLL
: SWT
.H_SCROLL
| SWT
.NO_SCROLL
;
355 fTreeViewer
= new TreeViewer(fSashForm
, SWT
.FULL_SELECTION
| scrollBarStyle
);
356 fTreeViewer
.setAutoExpandLevel(AbstractTreeViewer
.ALL_LEVELS
);
357 final Tree tree
= fTreeViewer
.getTree();
358 tree
.setHeaderVisible(true);
359 tree
.setLinesVisible(true);
361 fTimeGraphViewer
= new TimeGraphViewer(fSashForm
, SWT
.NONE
);
362 fTimeGraphViewer
.setItemHeight(getItemHeight(tree
));
363 fTimeGraphViewer
.setHeaderHeight(tree
.getHeaderHeight());
364 fTimeGraphViewer
.setBorderWidth(tree
.getBorderWidth());
365 fTimeGraphViewer
.setNameWidthPref(0);
367 if (fScrollBarsInTreeWorkaround
) {
368 // Feature in Windows. The tree vertical bar reappears when
369 // the control is resized so we need to hide it again.
370 tree
.addControlListener(new ControlAdapter() {
371 private int depth
= 0;
374 public void controlResized(ControlEvent e
) {
377 tree
.getVerticalBar().setEnabled(false);
378 // this can trigger controlResized recursively
379 tree
.getVerticalBar().setVisible(false);
385 // Bug in Linux. The tree header height is 0 in constructor,
386 // so we need to reset it later when the control is painted.
387 // This work around used to be done on control resized but the header
388 // height was not initialized on the initial resize on GTK3.
389 tree
.addPaintListener(new PaintListener() {
391 public void paintControl(PaintEvent e
) {
392 int headerHeight
= tree
.getHeaderHeight();
393 if (headerHeight
> 0) {
394 fTimeGraphViewer
.setHeaderHeight(headerHeight
);
395 tree
.removePaintListener(this);
400 // ensure synchronization of expanded items between tree and time graph
401 fTreeViewer
.addTreeListener(new ITreeViewerListener() {
403 public void treeCollapsed(TreeExpansionEvent event
) {
404 fTimeGraphViewer
.setExpandedState((ITimeGraphEntry
) event
.getElement(), false);
405 // queue the alignment update because the tree items may only be
406 // actually collapsed after the listeners have been notified
407 fVisibleExpandedItems
= null; // invalidate the cache
408 getDisplay().asyncExec(new Runnable() {
411 alignTreeItems(true);
416 public void treeExpanded(TreeExpansionEvent event
) {
417 ITimeGraphEntry entry
= (ITimeGraphEntry
) event
.getElement();
418 fTimeGraphViewer
.setExpandedState(entry
, true);
419 Set
<Object
> expandedElements
= new HashSet
<>(Arrays
.asList(fTreeViewer
.getExpandedElements()));
420 for (ITimeGraphEntry child
: entry
.getChildren()) {
421 if (child
.hasChildren()) {
422 boolean expanded
= expandedElements
.contains(child
);
423 fTimeGraphViewer
.setExpandedState(child
, expanded
);
426 // queue the alignment update because the tree items may only be
427 // actually expanded after the listeners have been notified
428 fVisibleExpandedItems
= null; // invalidate the cache
429 getDisplay().asyncExec(new Runnable() {
432 alignTreeItems(true);
437 // ensure synchronization of expanded items between tree and time graph
438 fTimeGraphViewer
.addTreeListener(new ITimeGraphTreeListener() {
440 public void treeCollapsed(TimeGraphTreeExpansionEvent event
) {
441 fTreeViewer
.setExpandedState(event
.getEntry(), false);
442 alignTreeItems(true);
446 public void treeExpanded(TimeGraphTreeExpansionEvent event
) {
447 ITimeGraphEntry entry
= event
.getEntry();
448 fTreeViewer
.setExpandedState(entry
, true);
449 Set
<Object
> expandedElements
= new HashSet
<>(Arrays
.asList(fTreeViewer
.getExpandedElements()));
450 for (ITimeGraphEntry child
: entry
.getChildren()) {
451 if (child
.hasChildren()) {
452 boolean expanded
= expandedElements
.contains(child
);
453 fTimeGraphViewer
.setExpandedState(child
, expanded
);
456 alignTreeItems(true);
460 // prevent mouse button from selecting a filler tree item
461 tree
.addListener(SWT
.MouseDown
, new Listener() {
463 public void handleEvent(Event event
) {
464 TreeItem treeItem
= tree
.getItem(new Point(event
.x
, event
.y
));
465 if (treeItem
== null || treeItem
.getData() == FILLER
) {
467 List
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
, false);
468 if (treeItems
.size() == 0) {
469 fTreeViewer
.setSelection(new StructuredSelection());
470 fTimeGraphViewer
.setSelection(null);
473 // this prevents from scrolling up when selecting
474 // the partially visible tree item at the bottom
475 tree
.select(treeItems
.get(treeItems
.size() - 1));
476 fTreeViewer
.setSelection(new StructuredSelection());
477 fTimeGraphViewer
.setSelection(null);
482 // prevent mouse wheel from scrolling down into filler tree items
483 tree
.addListener(SWT
.MouseWheel
, new Listener() {
485 public void handleEvent(Event event
) {
487 Slider scrollBar
= fTimeGraphViewer
.getVerticalBar();
488 fTimeGraphViewer
.setTopIndex(scrollBar
.getSelection() - event
.count
);
489 alignTreeItems(false);
493 // prevent key stroke from selecting a filler tree item
494 tree
.addListener(SWT
.KeyDown
, new Listener() {
496 public void handleEvent(Event event
) {
497 List
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
, false);
498 if (treeItems
.size() == 0) {
499 fTreeViewer
.setSelection(new StructuredSelection());
503 if (event
.keyCode
== SWT
.ARROW_DOWN
) {
504 int index
= Math
.min(fTimeGraphViewer
.getSelectionIndex() + 1, treeItems
.size() - 1);
505 fTimeGraphViewer
.setSelection((ITimeGraphEntry
) treeItems
.get(index
).getData());
507 } else if (event
.keyCode
== SWT
.PAGE_DOWN
) {
508 int height
= tree
.getSize().y
- tree
.getHeaderHeight() - tree
.getHorizontalBar().getSize().y
;
509 int countPerPage
= height
/ getItemHeight(tree
);
510 int index
= Math
.min(fTimeGraphViewer
.getSelectionIndex() + countPerPage
- 1, treeItems
.size() - 1);
511 fTimeGraphViewer
.setSelection((ITimeGraphEntry
) treeItems
.get(index
).getData());
513 } else if (event
.keyCode
== SWT
.END
) {
514 fTimeGraphViewer
.setSelection((ITimeGraphEntry
) treeItems
.get(treeItems
.size() - 1).getData());
517 if (fTimeGraphViewer
.getSelectionIndex() >= 0) {
518 fTreeViewer
.setSelection(new StructuredSelection(fTimeGraphViewer
.getSelection()));
520 fTreeViewer
.setSelection(new StructuredSelection());
522 alignTreeItems(false);
526 // ensure alignment of top item between tree and time graph
527 fTimeGraphViewer
.getTimeGraphControl().addControlListener(new ControlAdapter() {
529 public void controlResized(ControlEvent e
) {
530 alignTreeItems(false);
534 // ensure synchronization of selected item between tree and time graph
535 fTreeViewer
.addSelectionChangedListener(new ISelectionChangedListener() {
537 public void selectionChanged(SelectionChangedEvent event
) {
538 if (fInhibitTreeSelection
) {
541 if (event
.getSelection() instanceof IStructuredSelection
) {
542 Object selection
= ((IStructuredSelection
) event
.getSelection()).getFirstElement();
543 if (selection
instanceof ITimeGraphEntry
) {
544 fTimeGraphViewer
.setSelection((ITimeGraphEntry
) selection
);
546 alignTreeItems(false);
551 // ensure synchronization of selected item between tree and time graph
552 fTimeGraphViewer
.addSelectionListener(new ITimeGraphSelectionListener() {
554 public void selectionChanged(TimeGraphSelectionEvent event
) {
555 ITimeGraphEntry entry
= fTimeGraphViewer
.getSelection();
556 fInhibitTreeSelection
= true; // block the tree selection changed listener
558 StructuredSelection selection
= new StructuredSelection(entry
);
559 fTreeViewer
.setSelection(selection
);
561 fTreeViewer
.setSelection(new StructuredSelection());
563 fInhibitTreeSelection
= false;
564 alignTreeItems(false);
568 // ensure alignment of top item between tree and time graph
569 fTimeGraphViewer
.getVerticalBar().addSelectionListener(new SelectionAdapter() {
571 public void widgetSelected(SelectionEvent e
) {
572 alignTreeItems(false);
576 // ensure alignment of top item between tree and time graph
577 fTimeGraphViewer
.getTimeGraphControl().addMouseWheelListener(new MouseWheelListener() {
579 public void mouseScrolled(MouseEvent e
) {
580 alignTreeItems(false);
584 // ensure the tree has focus control when mouse is over it if the time graph had control
585 fTreeViewer
.getControl().addMouseTrackListener(new MouseTrackAdapter() {
587 public void mouseEnter(MouseEvent e
) {
588 if (fTimeGraphViewer
.getTimeGraphControl().isFocusControl()) {
589 fTreeViewer
.getControl().setFocus();
594 // ensure the time graph has focus control when mouse is over it if the tree had control
595 fTimeGraphViewer
.getTimeGraphControl().addMouseTrackListener(new MouseTrackAdapter() {
597 public void mouseEnter(MouseEvent e
) {
598 if (fTreeViewer
.getControl().isFocusControl()) {
599 fTimeGraphViewer
.getTimeGraphControl().setFocus();
603 fTimeGraphViewer
.getTimeGraphScale().addMouseTrackListener(new MouseTrackAdapter() {
605 public void mouseEnter(MouseEvent e
) {
606 if (fTreeViewer
.getControl().isFocusControl()) {
607 fTimeGraphViewer
.getTimeGraphControl().setFocus();
612 // The filler rows are required to ensure alignment when the tree does not have a
613 // visible horizontal scroll bar. The tree does not allow its top item to be set
614 // to a value that would cause blank space to be drawn at the bottom of the tree.
615 fNumFillerRows
= Display
.getDefault().getBounds().height
/ getItemHeight(tree
);
617 fSashForm
.setWeights(weights
);
619 fTimeGraphViewer
.getTimeGraphControl().addPaintListener(new PaintListener() {
621 public void paintControl(PaintEvent e
) {
622 // Sashes in a SashForm are being created on layout so add the
623 // drag listener here
624 if (fSashDragListener
== null) {
625 for (Control control
: fSashForm
.getChildren()) {
626 if (control
instanceof Sash
) {
627 fSashDragListener
= new Listener() {
630 public void handleEvent(Event event
) {
631 sendTimeViewAlignmentChanged();
635 control
.removePaintListener(this);
636 control
.addListener(SWT
.Selection
, fSashDragListener
);
637 // There should be only one sash
646 private void sendTimeViewAlignmentChanged() {
647 TmfSignalManager
.dispatchSignal(new TmfTimeViewAlignmentSignal(fSashForm
, getTimeViewAlignmentInfo()));
650 // ------------------------------------------------------------------------
652 // ------------------------------------------------------------------------
655 * Returns this time graph combo's tree viewer.
657 * @return the tree viewer
659 public TreeViewer
getTreeViewer() {
664 * Returns this time graph combo's time graph viewer.
666 * @return the time graph viewer
668 public @NonNull TimeGraphViewer
getTimeGraphViewer() {
669 return fTimeGraphViewer
;
673 * Get the show filter dialog action.
675 * @return The Action object
678 public ShowFilterDialogAction
getShowFilterDialogAction() {
679 if (fShowFilterDialogAction
== null) {
680 fShowFilterDialogAction
= new ShowFilterDialogAction(fTimeGraphViewer
) {
682 protected void addFilter(ViewerFilter filter
) {
683 /* add filter to the combo instead of the viewer */
684 TimeGraphCombo
.this.addFilter(filter
);
688 protected void removeFilter(ViewerFilter filter
) {
689 /* remove filter from the combo instead of the viewer */
690 TimeGraphCombo
.this.removeFilter(filter
);
694 protected void refresh() {
695 /* refresh the combo instead of the viewer */
696 TimeGraphCombo
.this.refresh();
700 return fShowFilterDialogAction
;
703 // ------------------------------------------------------------------------
705 // ------------------------------------------------------------------------
708 public void redraw() {
709 fTimeGraphViewer
.getControl().redraw();
713 // ------------------------------------------------------------------------
715 // ------------------------------------------------------------------------
718 * Sets the tree content provider used by this time graph combo.
720 * @param contentProvider the tree content provider
722 public void setTreeContentProvider(ITreeContentProvider contentProvider
) {
723 fTreeViewer
.setContentProvider(new TreeContentProviderWrapper(contentProvider
));
727 * Sets the tree label provider used by this time graph combo.
729 * @param labelProvider the tree label provider
731 public void setTreeLabelProvider(ITableLabelProvider labelProvider
) {
732 fTreeViewer
.setLabelProvider(new TreeLabelProviderWrapper(labelProvider
));
736 * Sets the tree content provider used by the filter dialog
738 * @param contentProvider the tree content provider
740 public void setFilterContentProvider(ITreeContentProvider contentProvider
) {
741 getShowFilterDialogAction().getFilterDialog().setContentProvider(contentProvider
);
745 * Sets the tree label provider used by the filter dialog
747 * @param labelProvider the tree label provider
749 public void setFilterLabelProvider(ITableLabelProvider labelProvider
) {
750 getShowFilterDialogAction().getFilterDialog().setLabelProvider(labelProvider
);
754 * Adds a "check active" button used by the filter dialog
756 * @param activeProvider
757 * Additional button info specific to a certain view.
760 public void addTimeGraphFilterCheckActiveButton(ITimeGraphEntryActiveProvider activeProvider
) {
761 getShowFilterDialogAction().getFilterDialog().addTimeGraphFilterCheckActiveButton(activeProvider
);
765 * Adds an "uncheck inactive" button used by the filter dialog
767 * @param inactiveProvider
768 * Additional button info specific to a certain view.
771 public void addTimeGraphFilterUncheckInactiveButton(ITimeGraphEntryActiveProvider inactiveProvider
) {
772 getShowFilterDialogAction().getFilterDialog().addTimeGraphFilterUncheckInactiveButton(inactiveProvider
);
776 * Sets the tree columns for this time graph combo.
778 * @param columnNames the tree column names
780 public void setTreeColumns(String
[] columnNames
) {
781 final Tree tree
= fTreeViewer
.getTree();
782 for (String columnName
: columnNames
) {
783 TreeColumn column
= new TreeColumn(tree
, SWT
.LEFT
);
784 column
.setText(columnName
);
790 * Sets the tree columns for this time graph combo's filter dialog.
792 * @param columnNames the tree column names
794 public void setFilterColumns(String
[] columnNames
) {
795 getShowFilterDialogAction().getFilterDialog().setColumnNames(columnNames
);
799 * Sets the time graph content provider used by this time graph combo.
801 * @param timeGraphContentProvider
802 * the time graph content provider
804 public void setTimeGraphContentProvider(ITimeGraphContentProvider timeGraphContentProvider
) {
805 fTimeGraphViewer
.setTimeGraphContentProvider(timeGraphContentProvider
);
809 * Sets the time graph presentation provider used by this time graph combo.
811 * @param timeGraphProvider the time graph provider
813 public void setTimeGraphProvider(ITimeGraphPresentationProvider timeGraphProvider
) {
814 fTimeGraphViewer
.setTimeGraphProvider(timeGraphProvider
);
818 * Sets or clears the input for this time graph combo.
820 * @param input the input of this time graph combo, or <code>null</code> if none
822 public void setInput(Object input
) {
823 fInhibitTreeSelection
= true;
824 fTreeViewer
.setInput(input
);
825 for (SelectionListenerWrapper listenerWrapper
: fSelectionListenerMap
.values()) {
826 listenerWrapper
.selection
= null;
828 fInhibitTreeSelection
= false;
829 if (fScrollBarsInTreeWorkaround
) {
830 fTreeViewer
.getTree().getVerticalBar().setEnabled(false);
831 fTreeViewer
.getTree().getVerticalBar().setVisible(false);
833 fTimeGraphViewer
.setInput(input
);
834 fTimeGraphViewer
.setItemHeight(getItemHeight(fTreeViewer
.getTree()));
835 // queue the alignment update because in Linux the item bounds are not
836 // set properly until the tree has been painted at least once
837 fVisibleExpandedItems
= null; // invalidate the cache
838 getDisplay().asyncExec(new Runnable() {
841 alignTreeItems(true);
846 * Gets the input for this time graph combo.
848 * @return The input of this time graph combo, or <code>null</code> if none
850 public Object
getInput() {
851 return fTreeViewer
.getInput();
855 * Sets or clears the list of links to display on this combo
857 * @param links the links to display in this time graph combo
859 public void setLinks(List
<ILinkEvent
> links
) {
860 fTimeGraphViewer
.setLinks(links
);
864 * @param filter The filter object to be attached to the view
866 public void addFilter(ViewerFilter filter
) {
867 fInhibitTreeSelection
= true;
868 ViewerFilter wrapper
= new ViewerFilterWrapper(filter
);
869 fTreeViewer
.addFilter(wrapper
);
870 fTimeGraphViewer
.addFilter(filter
);
871 fViewerFilterMap
.put(filter
, wrapper
);
872 alignTreeItems(true);
873 fInhibitTreeSelection
= false;
877 * @param filter The filter object to be removed from the view
879 public void removeFilter(ViewerFilter filter
) {
880 fInhibitTreeSelection
= true;
881 ViewerFilter wrapper
= fViewerFilterMap
.get(filter
);
882 fTreeViewer
.removeFilter(wrapper
);
883 fTimeGraphViewer
.removeFilter(filter
);
884 fViewerFilterMap
.remove(filter
);
885 alignTreeItems(true);
886 fInhibitTreeSelection
= false;
890 * Returns this viewer's filters.
892 * @return an array of viewer filters
895 public ViewerFilter
[] getFilters() {
896 return fTimeGraphViewer
.getFilters();
900 * Sets the filters, replacing any previous filters, and triggers
901 * refiltering of the elements.
904 * an array of viewer filters, or null
907 public void setFilters(ViewerFilter
[] filters
) {
908 fInhibitTreeSelection
= true;
909 fViewerFilterMap
.clear();
910 if (filters
== null) {
911 fTreeViewer
.resetFilters();
913 for (ViewerFilter filter
: filters
) {
914 ViewerFilter wrapper
= new ViewerFilterWrapper(filter
);
915 fViewerFilterMap
.put(filter
, wrapper
);
917 ViewerFilter
[] wrappers
= Iterables
.toArray(fViewerFilterMap
.values(), ViewerFilter
.class);
918 fTreeViewer
.setFilters(wrappers
);
920 fTimeGraphViewer
.setFilters(filters
);
921 alignTreeItems(true);
922 fInhibitTreeSelection
= false;
926 * Refreshes this time graph completely with information freshly obtained from its model.
928 public void refresh() {
929 fInhibitTreeSelection
= true;
930 Tree tree
= fTreeViewer
.getTree();
932 tree
.setRedraw(false);
933 fTreeViewer
.refresh();
935 tree
.setRedraw(true);
937 fTimeGraphViewer
.refresh();
938 alignTreeItems(true);
939 fInhibitTreeSelection
= false;
943 * Adds a listener for selection changes in this time graph combo.
945 * @param listener a selection listener
947 public void addSelectionListener(ITimeGraphSelectionListener listener
) {
948 SelectionListenerWrapper listenerWrapper
= new SelectionListenerWrapper(listener
);
949 fTreeViewer
.addSelectionChangedListener(listenerWrapper
);
950 fSelectionListenerMap
.put(listener
, listenerWrapper
);
951 fTimeGraphViewer
.addSelectionListener(listenerWrapper
);
955 * Removes the given selection listener from this time graph combo.
957 * @param listener a selection changed listener
959 public void removeSelectionListener(ITimeGraphSelectionListener listener
) {
960 SelectionListenerWrapper listenerWrapper
= fSelectionListenerMap
.remove(listener
);
961 fTreeViewer
.removeSelectionChangedListener(listenerWrapper
);
962 fTimeGraphViewer
.removeSelectionListener(listenerWrapper
);
966 * Sets the current selection for this time graph combo.
968 * @param selection the new selection
970 public void setSelection(ITimeGraphEntry selection
) {
971 fTimeGraphViewer
.setSelection(selection
);
972 fInhibitTreeSelection
= true; // block the tree selection changed listener
973 if (selection
!= null) {
974 StructuredSelection structuredSelection
= new StructuredSelection(selection
);
975 fTreeViewer
.setSelection(structuredSelection
);
977 fTreeViewer
.setSelection(new StructuredSelection());
979 fInhibitTreeSelection
= false;
980 alignTreeItems(false);
984 * Sets the auto-expand level to be used for new entries discovered when
985 * calling {@link #setInput(Object)} or {@link #refresh()}. The value 0
986 * means that there is no auto-expand; 1 means that top-level entries are
987 * expanded, but not their children; 2 means that top-level entries are
988 * expanded, and their children, but not grand-children; and so on.
990 * The value {@link #ALL_LEVELS} means that all subtrees should be expanded.
994 * non-negative level, or <code>ALL_LEVELS</code> to expand all
997 public void setAutoExpandLevel(int level
) {
998 fTimeGraphViewer
.setAutoExpandLevel(level
);
1000 fTreeViewer
.setAutoExpandLevel(level
);
1002 fTreeViewer
.setAutoExpandLevel(level
+ 1);
1007 * Returns the auto-expand level.
1009 * @return non-negative level, or <code>ALL_LEVELS</code> if all levels of
1010 * the tree are expanded automatically
1011 * @see #setAutoExpandLevel
1013 public int getAutoExpandLevel() {
1014 return fTimeGraphViewer
.getAutoExpandLevel();
1018 * Set the expanded state of an entry
1021 * The entry to expand/collapse
1023 * True for expanded, false for collapsed
1025 public void setExpandedState(ITimeGraphEntry entry
, boolean expanded
) {
1026 fTimeGraphViewer
.setExpandedState(entry
, expanded
);
1027 fTreeViewer
.setExpandedState(entry
, expanded
);
1028 alignTreeItems(true);
1032 * Collapses all nodes of the viewer's tree, starting with the root.
1034 public void collapseAll() {
1035 fTimeGraphViewer
.collapseAll();
1036 fTreeViewer
.collapseAll();
1037 alignTreeItems(true);
1041 * Expands all nodes of the viewer's tree, starting with the root.
1043 public void expandAll() {
1044 fTimeGraphViewer
.expandAll();
1045 fTreeViewer
.expandAll();
1046 alignTreeItems(true);
1049 // ------------------------------------------------------------------------
1051 // ------------------------------------------------------------------------
1053 private List
<TreeItem
> getVisibleExpandedItems(Tree tree
, boolean refresh
) {
1054 if (fVisibleExpandedItems
== null || refresh
) {
1055 List
<TreeItem
> visibleExpandedItems
= new ArrayList
<>();
1056 addVisibleExpandedItems(visibleExpandedItems
, tree
.getItems());
1057 fVisibleExpandedItems
= visibleExpandedItems
;
1059 return fVisibleExpandedItems
;
1062 private void addVisibleExpandedItems(List
<TreeItem
> visibleExpandedItems
, TreeItem
[] items
) {
1063 for (TreeItem item
: items
) {
1064 Object data
= item
.getData();
1065 if (data
== FILLER
) {
1068 visibleExpandedItems
.add(item
);
1069 boolean expandedState
= fTimeGraphViewer
.getExpandedState((ITimeGraphEntry
) data
);
1070 if (item
.getExpanded() != expandedState
) {
1071 /* synchronize the expanded state of both viewers */
1072 fTreeViewer
.setExpandedState(data
, expandedState
);
1074 if (expandedState
) {
1075 addVisibleExpandedItems(visibleExpandedItems
, item
.getItems());
1080 private int getItemHeight(final Tree tree
) {
1082 * Bug in Linux. The method getItemHeight doesn't always return the correct value.
1084 if (fLinuxItemHeight
>= 0 && System
.getProperty("os.name").contains("Linux")) { //$NON-NLS-1$ //$NON-NLS-2$
1085 if (fLinuxItemHeight
!= 0) {
1086 return fLinuxItemHeight
;
1089 if (getVisibleExpandedItems(tree
, true).size() > 1) {
1090 PaintListener paintListener
= new PaintListener() {
1092 public void paintControl(PaintEvent e
) {
1093 // get the treeItems here to have all items
1094 List
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
, true);
1095 if (treeItems
.size() < 2) {
1098 final TreeItem treeItem0
= treeItems
.get(0);
1099 final TreeItem treeItem1
= treeItems
.get(1);
1100 tree
.removePaintListener(this);
1101 int y0
= treeItem0
.getBounds().y
;
1102 int y1
= treeItem1
.getBounds().y
;
1103 int itemHeight
= y1
- y0
;
1104 if (itemHeight
> 0) {
1105 fLinuxItemHeight
= itemHeight
;
1106 fTimeGraphViewer
.setItemHeight(itemHeight
);
1110 tree
.addPaintListener(paintListener
);
1113 fLinuxItemHeight
= -1; // Not Linux, don't perform os.name check anymore
1115 return tree
.getItemHeight();
1118 private void alignTreeItems(boolean refreshExpandedItems
) {
1119 // align the tree top item with the time graph top item
1120 Tree tree
= fTreeViewer
.getTree();
1121 List
<TreeItem
> treeItems
= getVisibleExpandedItems(tree
, refreshExpandedItems
);
1122 int topIndex
= fTimeGraphViewer
.getTopIndex();
1123 if (topIndex
>= treeItems
.size()) {
1126 TreeItem item
= treeItems
.get(topIndex
);
1127 tree
.setTopItem(item
);
1129 // ensure the time graph item heights are equal to the tree item heights
1130 int treeHeight
= fTreeViewer
.getTree().getBounds().height
;
1131 int index
= topIndex
;
1132 Rectangle bounds
= item
.getBounds();
1133 while (index
< treeItems
.size() - 1) {
1134 if (bounds
.y
> treeHeight
) {
1138 * Bug in Linux. The method getBounds doesn't always return the correct height.
1139 * Use the difference of y position between items to calculate the height.
1141 TreeItem nextItem
= treeItems
.get(index
+ 1);
1142 Rectangle nextBounds
= nextItem
.getBounds();
1143 Integer itemHeight
= nextBounds
.y
- bounds
.y
;
1144 if (itemHeight
> 0) {
1145 ITimeGraphEntry entry
= (ITimeGraphEntry
) item
.getData();
1146 fTimeGraphViewer
.getTimeGraphControl().setItemHeight(entry
, itemHeight
);
1150 bounds
= nextBounds
;
1155 * Return the time alignment information
1157 * @return the time alignment information
1159 * @see ITmfTimeAligned
1163 public TmfTimeViewAlignmentInfo
getTimeViewAlignmentInfo() {
1164 Point location
= fSashForm
.toDisplay(0, 0);
1165 int timeAxisOffset
= fTreeViewer
.getControl().getSize().x
+ fSashForm
.getSashWidth();
1166 return new TmfTimeViewAlignmentInfo(fSashForm
.getShell(), location
, timeAxisOffset
);
1170 * Return the available width for the time-axis.
1172 * @see ITmfTimeAligned
1174 * @param requestedOffset
1175 * the requested offset
1176 * @return the available width for the time-axis
1180 public int getAvailableWidth(int requestedOffset
) {
1181 int vBarWidth
= ((fTimeGraphViewer
.getVerticalBar() != null) && (fTimeGraphViewer
.getVerticalBar().isVisible())) ? fTimeGraphViewer
.getVerticalBar().getSize().x
: 0;
1182 int totalWidth
= fSashForm
.getBounds().width
;
1183 return Math
.min(totalWidth
, Math
.max(0, totalWidth
- requestedOffset
- vBarWidth
));
1187 * Perform the alignment operation.
1190 * the alignment offset
1192 * the alignment width
1194 * @see ITmfTimeAligned
1198 public void performAlign(int offset
, int width
) {
1199 int total
= fSashForm
.getBounds().width
;
1200 int timeAxisOffset
= Math
.min(offset
, total
);
1201 int width1
= Math
.max(0, timeAxisOffset
- fSashForm
.getSashWidth());
1202 int width2
= total
- timeAxisOffset
;
1203 fSashForm
.setWeights(new int[] { width1
, width2
});
1206 Composite composite
= fTimeGraphViewer
.getTimeAlignedComposite();
1207 GridLayout layout
= (GridLayout
) composite
.getLayout();
1208 int timeBasedControlsWidth
= composite
.getSize().x
;
1209 int marginSize
= timeBasedControlsWidth
- width
;
1210 layout
.marginRight
= Math
.max(0, marginSize
);