-/*******************************************************************************\r
- * Copyright (c) 2012 Ericsson\r
- *\r
- * All rights reserved. This program and the accompanying materials are\r
- * made available under the terms of the Eclipse Public License v1.0 which\r
- * accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- * Patrick Tasse - Initial API and implementation\r
- *******************************************************************************/\r
-\r
-package org.eclipse.linuxtools.tmf.ui.widgets.timegraph;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Arrays;\r
-import java.util.HashMap;\r
-\r
-import org.eclipse.jface.viewers.ILabelProviderListener;\r
-import org.eclipse.jface.viewers.ISelectionChangedListener;\r
-import org.eclipse.jface.viewers.IStructuredSelection;\r
-import org.eclipse.jface.viewers.ITableLabelProvider;\r
-import org.eclipse.jface.viewers.ITreeContentProvider;\r
-import org.eclipse.jface.viewers.ITreeViewerListener;\r
-import org.eclipse.jface.viewers.SelectionChangedEvent;\r
-import org.eclipse.jface.viewers.StructuredSelection;\r
-import org.eclipse.jface.viewers.TreeExpansionEvent;\r
-import org.eclipse.jface.viewers.TreeViewer;\r
-import org.eclipse.jface.viewers.Viewer;\r
-import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;\r
-import org.eclipse.swt.SWT;\r
-import org.eclipse.swt.custom.SashForm;\r
-import org.eclipse.swt.events.ControlAdapter;\r
-import org.eclipse.swt.events.ControlEvent;\r
-import org.eclipse.swt.events.MouseEvent;\r
-import org.eclipse.swt.events.MouseTrackAdapter;\r
-import org.eclipse.swt.events.MouseWheelListener;\r
-import org.eclipse.swt.events.PaintEvent;\r
-import org.eclipse.swt.events.PaintListener;\r
-import org.eclipse.swt.events.SelectionAdapter;\r
-import org.eclipse.swt.events.SelectionEvent;\r
-import org.eclipse.swt.graphics.Image;\r
-import org.eclipse.swt.graphics.Point;\r
-import org.eclipse.swt.layout.FillLayout;\r
-import org.eclipse.swt.widgets.Composite;\r
-import org.eclipse.swt.widgets.Display;\r
-import org.eclipse.swt.widgets.Event;\r
-import org.eclipse.swt.widgets.Listener;\r
-import org.eclipse.swt.widgets.Slider;\r
-import org.eclipse.swt.widgets.Tree;\r
-import org.eclipse.swt.widgets.TreeColumn;\r
-import org.eclipse.swt.widgets.TreeItem;\r
-\r
-/**\r
- * Time graph "combo" view (with the list/tree on the left and the gantt chart\r
- * on the right)\r
- *\r
- * @version 1.0\r
- * @author Patrick Tasse\r
- */\r
-public class TimeGraphCombo extends Composite {\r
-\r
- // ------------------------------------------------------------------------\r
- // Constants\r
- // ------------------------------------------------------------------------\r
-\r
- private static final Object FILLER = new Object();\r
-\r
- // ------------------------------------------------------------------------\r
- // Fields\r
- // ------------------------------------------------------------------------\r
-\r
- // The tree viewer\r
- private TreeViewer fTreeViewer;\r
-\r
- // The time viewer\r
- private TimeGraphViewer fTimeGraphViewer;\r
-\r
- // The selection listener map\r
- private final HashMap<ITimeGraphSelectionListener, SelectionListenerWrapper> fSelectionListenerMap = new HashMap<ITimeGraphSelectionListener, SelectionListenerWrapper>();\r
-\r
- // Flag to block the tree selection changed listener when triggered by the time graph combo\r
- private boolean fInhibitTreeSelection = false;\r
-\r
- // Number of filler rows used by the tree content provider\r
- private int fNumFillerRows;\r
-\r
- // Calculated item height for Linux workaround\r
- private int fLinuxItemHeight = 0;\r
-\r
- // ------------------------------------------------------------------------\r
- // Classes\r
- // ------------------------------------------------------------------------\r
-\r
- /**\r
- * The TreeContentProviderWrapper is used to insert filler items after\r
- * the elements of the tree's real content provider.\r
- */\r
- private class TreeContentProviderWrapper implements ITreeContentProvider {\r
- private final ITreeContentProvider contentProvider;\r
-\r
- public TreeContentProviderWrapper(ITreeContentProvider contentProvider) {\r
- this.contentProvider = contentProvider;\r
- }\r
-\r
- @Override\r
- public void dispose() {\r
- contentProvider.dispose();\r
- }\r
-\r
- @Override\r
- public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {\r
- contentProvider.inputChanged(viewer, oldInput, newInput);\r
- }\r
-\r
- @Override\r
- public Object[] getElements(Object inputElement) {\r
- Object[] elements = contentProvider.getElements(inputElement);\r
- // add filler elements to ensure alignment with time analysis viewer\r
- Object[] oElements = Arrays.copyOf(elements, elements.length + fNumFillerRows, new Object[0].getClass());\r
- for (int i = 0; i < fNumFillerRows; i++) {\r
- oElements[elements.length + i] = FILLER;\r
- }\r
- return oElements;\r
- }\r
-\r
- @Override\r
- public Object[] getChildren(Object parentElement) {\r
- if (parentElement instanceof ITimeGraphEntry) {\r
- return contentProvider.getChildren(parentElement);\r
- } else {\r
- return new Object[0];\r
- }\r
- }\r
-\r
- @Override\r
- public Object getParent(Object element) {\r
- if (element instanceof ITimeGraphEntry) {\r
- return contentProvider.getParent(element);\r
- } else {\r
- return null;\r
- }\r
- }\r
-\r
- @Override\r
- public boolean hasChildren(Object element) {\r
- if (element instanceof ITimeGraphEntry) {\r
- return contentProvider.hasChildren(element);\r
- } else {\r
- return false;\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * The TreeLabelProviderWrapper is used to intercept the filler items\r
- * from the calls to the tree's real label provider.\r
- */\r
- private class TreeLabelProviderWrapper implements ITableLabelProvider {\r
- private final ITableLabelProvider labelProvider;\r
-\r
- public TreeLabelProviderWrapper(ITableLabelProvider labelProvider) {\r
- this.labelProvider = labelProvider;\r
- }\r
-\r
- @Override\r
- public void addListener(ILabelProviderListener listener) {\r
- labelProvider.addListener(listener);\r
- }\r
-\r
- @Override\r
- public void dispose() {\r
- labelProvider.dispose();\r
- }\r
-\r
- @Override\r
- public boolean isLabelProperty(Object element, String property) {\r
- if (element instanceof ITimeGraphEntry) {\r
- return labelProvider.isLabelProperty(element, property);\r
- } else {\r
- return false;\r
- }\r
- }\r
-\r
- @Override\r
- public void removeListener(ILabelProviderListener listener) {\r
- labelProvider.removeListener(listener);\r
- }\r
-\r
- @Override\r
- public Image getColumnImage(Object element, int columnIndex) {\r
- if (element instanceof ITimeGraphEntry) {\r
- return labelProvider.getColumnImage(element, columnIndex);\r
- } else {\r
- return null;\r
- }\r
- }\r
-\r
- @Override\r
- public String getColumnText(Object element, int columnIndex) {\r
- if (element instanceof ITimeGraphEntry) {\r
- return labelProvider.getColumnText(element, columnIndex);\r
- } else {\r
- return null;\r
- }\r
- }\r
-\r
- }\r
-\r
- /**\r
- * The SelectionListenerWrapper is used to intercept the filler items from\r
- * the time graph combo's real selection listener, and to prevent double\r
- * notifications from being sent when selection changes in both tree and\r
- * time graph at the same time.\r
- */\r
- private class SelectionListenerWrapper implements ISelectionChangedListener, ITimeGraphSelectionListener {\r
- private final ITimeGraphSelectionListener listener;\r
- private ITimeGraphEntry selection = null;\r
-\r
- public SelectionListenerWrapper(ITimeGraphSelectionListener listener) {\r
- this.listener = listener;\r
- }\r
-\r
- @Override\r
- public void selectionChanged(SelectionChangedEvent event) {\r
- if (fInhibitTreeSelection) {\r
- return;\r
- }\r
- Object element = ((IStructuredSelection) event.getSelection()).getFirstElement();\r
- if (element instanceof ITimeGraphEntry) {\r
- ITimeGraphEntry entry = (ITimeGraphEntry) element;\r
- if (entry != selection) {\r
- selection = entry;\r
- listener.selectionChanged(new TimeGraphSelectionEvent(event.getSource(), selection));\r
- }\r
- }\r
- }\r
-\r
- @Override\r
- public void selectionChanged(TimeGraphSelectionEvent event) {\r
- ITimeGraphEntry entry = event.getSelection();\r
- if (entry != selection) {\r
- selection = entry;\r
- listener.selectionChanged(new TimeGraphSelectionEvent(event.getSource(), selection));\r
- }\r
- }\r
- }\r
-\r
- // ------------------------------------------------------------------------\r
- // Constructors\r
- // ------------------------------------------------------------------------\r
-\r
- /**\r
- * Constructs a new instance of this class given its parent\r
- * and a style value describing its behavior and appearance.\r
- *\r
- * @param parent a widget which will be the parent of the new instance (cannot be null)\r
- * @param style the style of widget to construct\r
- */\r
- public TimeGraphCombo(Composite parent, int style) {\r
- super(parent, style);\r
- setLayout(new FillLayout());\r
-\r
- final SashForm sash = new SashForm(this, SWT.NONE);\r
-\r
- fTreeViewer = new TreeViewer(sash, SWT.FULL_SELECTION | SWT.H_SCROLL);\r
- final Tree tree = fTreeViewer.getTree();\r
- tree.setHeaderVisible(true);\r
- tree.setLinesVisible(true);\r
-\r
- fTimeGraphViewer = new TimeGraphViewer(sash, SWT.NONE);\r
- fTimeGraphViewer.setItemHeight(getItemHeight(tree));\r
- fTimeGraphViewer.setHeaderHeight(tree.getHeaderHeight());\r
- fTimeGraphViewer.setBorderWidth(tree.getBorderWidth());\r
- fTimeGraphViewer.setNameWidthPref(0);\r
-\r
- // Feature in Windows. The tree vertical bar reappears when\r
- // the control is resized so we need to hide it again.\r
- // Bug in Linux. The tree header height is 0 in constructor,\r
- // so we need to reset it later when the control is resized.\r
- tree.addControlListener(new ControlAdapter() {\r
- int depth = 0;\r
- @Override\r
- public void controlResized(ControlEvent e) {\r
- if (depth == 0) {\r
- depth++;\r
- tree.getVerticalBar().setEnabled(false);\r
- // this can trigger controlResized recursively\r
- tree.getVerticalBar().setVisible(false);\r
- depth--;\r
- }\r
- fTimeGraphViewer.setHeaderHeight(tree.getHeaderHeight());\r
- }\r
- });\r
-\r
- // ensure synchronization of expanded items between tree and time graph\r
- fTreeViewer.addTreeListener(new ITreeViewerListener() {\r
- @Override\r
- public void treeCollapsed(TreeExpansionEvent event) {\r
- fTimeGraphViewer.setExpandedState((ITimeGraphEntry) event.getElement(), false);\r
- }\r
-\r
- @Override\r
- public void treeExpanded(TreeExpansionEvent event) {\r
- fTimeGraphViewer.setExpandedState((ITimeGraphEntry) event.getElement(), true);\r
- }\r
- });\r
-\r
- // ensure synchronization of expanded items between tree and time graph\r
- fTimeGraphViewer.addTreeListener(new ITimeGraphTreeListener() {\r
- @Override\r
- public void treeCollapsed(TimeGraphTreeExpansionEvent event) {\r
- fTreeViewer.setExpandedState(event.getEntry(), false);\r
- }\r
-\r
- @Override\r
- public void treeExpanded(TimeGraphTreeExpansionEvent event) {\r
- fTreeViewer.setExpandedState(event.getEntry(), true);\r
- }\r
- });\r
-\r
- // prevent mouse button from selecting a filler tree item\r
- tree.addListener(SWT.MouseDown, new Listener() {\r
- @Override\r
- public void handleEvent(Event event) {\r
- TreeItem treeItem = tree.getItem(new Point(event.x, event.y));\r
- if (treeItem == null || treeItem.getData() == FILLER) {\r
- event.doit = false;\r
- ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree);\r
- if (treeItems.size() == 0) {\r
- fTreeViewer.setSelection(new StructuredSelection());\r
- fTimeGraphViewer.setSelection(null);\r
- return;\r
- }\r
- // this prevents from scrolling up when selecting\r
- // the partially visible tree item at the bottom\r
- tree.select(treeItems.get(treeItems.size() - 1));\r
- fTreeViewer.setSelection(new StructuredSelection());\r
- fTimeGraphViewer.setSelection(null);\r
- }\r
- }\r
- });\r
-\r
- // prevent mouse wheel from scrolling down into filler tree items\r
- tree.addListener(SWT.MouseWheel, new Listener() {\r
- @Override\r
- public void handleEvent(Event event) {\r
- event.doit = false;\r
- Slider scrollBar = fTimeGraphViewer.getVerticalBar();\r
- fTimeGraphViewer.setTopIndex(scrollBar.getSelection() - event.count);\r
- ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree);\r
- if (treeItems.size() == 0) {\r
- return;\r
- }\r
- TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex());\r
- tree.setTopItem(treeItem);\r
- }\r
- });\r
-\r
- // prevent key stroke from selecting a filler tree item\r
- tree.addListener(SWT.KeyDown, new Listener() {\r
- @Override\r
- public void handleEvent(Event event) {\r
- ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree);\r
- if (treeItems.size() == 0) {\r
- fTreeViewer.setSelection(new StructuredSelection());\r
- event.doit = false;\r
- return;\r
- }\r
- if (event.keyCode == SWT.ARROW_DOWN) {\r
- int index = Math.min(fTimeGraphViewer.getSelectionIndex() + 1, treeItems.size() - 1);\r
- fTimeGraphViewer.setSelection((ITimeGraphEntry) treeItems.get(index).getData());\r
- event.doit = false;\r
- } else if (event.keyCode == SWT.PAGE_DOWN) {\r
- int height = tree.getSize().y - tree.getHeaderHeight() - tree.getHorizontalBar().getSize().y;\r
- int countPerPage = height / getItemHeight(tree);\r
- int index = Math.min(fTimeGraphViewer.getSelectionIndex() + countPerPage - 1, treeItems.size() - 1);\r
- fTimeGraphViewer.setSelection((ITimeGraphEntry) treeItems.get(index).getData());\r
- event.doit = false;\r
- } else if (event.keyCode == SWT.END) {\r
- fTimeGraphViewer.setSelection((ITimeGraphEntry) treeItems.get(treeItems.size() - 1).getData());\r
- event.doit = false;\r
- }\r
- TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex());\r
- tree.setTopItem(treeItem);\r
- if (fTimeGraphViewer.getSelectionIndex() >= 0) {\r
- fTreeViewer.setSelection(new StructuredSelection(fTimeGraphViewer.getSelection()));\r
- } else {\r
- fTreeViewer.setSelection(new StructuredSelection());\r
- }\r
- }\r
- });\r
-\r
- // ensure alignment of top item between tree and time graph\r
- fTimeGraphViewer.getTimeGraphControl().addControlListener(new ControlAdapter() {\r
- @Override\r
- public void controlResized(ControlEvent e) {\r
- ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree);\r
- if (treeItems.size() == 0) {\r
- return;\r
- }\r
- TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex());\r
- tree.setTopItem(treeItem);\r
- }\r
- });\r
-\r
- // ensure synchronization of selected item between tree and time graph\r
- fTreeViewer.addSelectionChangedListener(new ISelectionChangedListener() {\r
- @Override\r
- public void selectionChanged(SelectionChangedEvent event) {\r
- if (fInhibitTreeSelection) {\r
- return;\r
- }\r
- if (event.getSelection() instanceof IStructuredSelection) {\r
- Object selection = ((IStructuredSelection) event.getSelection()).getFirstElement();\r
- if (selection instanceof ITimeGraphEntry) {\r
- fTimeGraphViewer.setSelection((ITimeGraphEntry) selection);\r
- }\r
- ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree);\r
- if (treeItems.size() == 0) {\r
- return;\r
- }\r
- TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex());\r
- tree.setTopItem(treeItem);\r
- }\r
- }\r
- });\r
-\r
- // ensure synchronization of selected item between tree and time graph\r
- fTimeGraphViewer.addSelectionListener(new ITimeGraphSelectionListener() {\r
- @Override\r
- public void selectionChanged(TimeGraphSelectionEvent event) {\r
- ITimeGraphEntry entry = fTimeGraphViewer.getSelection();\r
- fInhibitTreeSelection = true; // block the tree selection changed listener\r
- if (entry != null) {\r
- StructuredSelection selection = new StructuredSelection(entry);\r
- fTreeViewer.setSelection(selection);\r
- } else {\r
- fTreeViewer.setSelection(new StructuredSelection());\r
- }\r
- fInhibitTreeSelection = false;\r
- ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree);\r
- if (treeItems.size() == 0) {\r
- return;\r
- }\r
- TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex());\r
- tree.setTopItem(treeItem);\r
- }\r
- });\r
-\r
- // ensure alignment of top item between tree and time graph\r
- fTimeGraphViewer.getVerticalBar().addSelectionListener(new SelectionAdapter() {\r
- @Override\r
- public void widgetSelected(SelectionEvent e) {\r
- ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree);\r
- if (treeItems.size() == 0) {\r
- return;\r
- }\r
- TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex());\r
- tree.setTopItem(treeItem);\r
- }\r
- });\r
-\r
- // ensure alignment of top item between tree and time graph\r
- fTimeGraphViewer.getTimeGraphControl().addMouseWheelListener(new MouseWheelListener() {\r
- @Override\r
- public void mouseScrolled(MouseEvent e) {\r
- ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree);\r
- if (treeItems.size() == 0) {\r
- return;\r
- }\r
- TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex());\r
- tree.setTopItem(treeItem);\r
- }\r
- });\r
-\r
- // ensure the tree has focus control when mouse is over it if the time graph had control\r
- fTreeViewer.getControl().addMouseTrackListener(new MouseTrackAdapter() {\r
- @Override\r
- public void mouseEnter(MouseEvent e) {\r
- if (fTimeGraphViewer.getTimeGraphControl().isFocusControl()) {\r
- fTreeViewer.getControl().setFocus();\r
- }\r
- }\r
- });\r
-\r
- // ensure the time graph has focus control when mouse is over it if the tree had control\r
- fTimeGraphViewer.getTimeGraphControl().addMouseTrackListener(new MouseTrackAdapter() {\r
- @Override\r
- public void mouseEnter(MouseEvent e) {\r
- if (fTreeViewer.getControl().isFocusControl()) {\r
- fTimeGraphViewer.getTimeGraphControl().setFocus();\r
- }\r
- }\r
- });\r
- fTimeGraphViewer.getTimeGraphScale().addMouseTrackListener(new MouseTrackAdapter() {\r
- @Override\r
- public void mouseEnter(MouseEvent e) {\r
- if (fTreeViewer.getControl().isFocusControl()) {\r
- fTimeGraphViewer.getTimeGraphControl().setFocus();\r
- }\r
- }\r
- });\r
-\r
- // The filler rows are required to ensure alignment when the tree does not have a\r
- // visible horizontal scroll bar. The tree does not allow its top item to be set\r
- // to a value that would cause blank space to be drawn at the bottom of the tree.\r
- fNumFillerRows = Display.getDefault().getBounds().height / getItemHeight(tree);\r
-\r
- sash.setWeights(new int[] { 1, 1 });\r
- }\r
-\r
- // ------------------------------------------------------------------------\r
- // Accessors\r
- // ------------------------------------------------------------------------\r
-\r
- /**\r
- * Returns this time graph combo's tree viewer.\r
- *\r
- * @return the tree viewer\r
- */\r
- public TreeViewer getTreeViewer() {\r
- return fTreeViewer;\r
- }\r
-\r
- /**\r
- * Returns this time graph combo's time graph viewer.\r
- *\r
- * @return the time graph viewer\r
- */\r
- public TimeGraphViewer getTimeGraphViewer() {\r
- return fTimeGraphViewer;\r
- }\r
-\r
- // ------------------------------------------------------------------------\r
- // Control\r
- // ------------------------------------------------------------------------\r
-\r
- /* (non-Javadoc)\r
- * @see org.eclipse.swt.widgets.Control#redraw()\r
- */\r
- @Override\r
- public void redraw() {\r
- fTimeGraphViewer.getControl().redraw();\r
- super.redraw();\r
- }\r
-\r
- // ------------------------------------------------------------------------\r
- // Operations\r
- // ------------------------------------------------------------------------\r
-\r
- /**\r
- * Sets the tree content provider used by this time graph combo.\r
- *\r
- * @param contentProvider the tree content provider\r
- */\r
- public void setTreeContentProvider(ITreeContentProvider contentProvider) {\r
- fTreeViewer.setContentProvider(new TreeContentProviderWrapper(contentProvider));\r
- }\r
-\r
- /**\r
- * Sets the tree label provider used by this time graph combo.\r
- *\r
- * @param labelProvider the tree label provider\r
- */\r
- public void setTreeLabelProvider(ITableLabelProvider labelProvider) {\r
- fTreeViewer.setLabelProvider(new TreeLabelProviderWrapper(labelProvider));\r
- }\r
-\r
- /**\r
- * Sets the tree columns for this time graph combo.\r
- *\r
- * @param columnNames the tree column names\r
- */\r
- public void setTreeColumns(String[] columnNames) {\r
- final Tree tree = fTreeViewer.getTree();\r
- for (String columnName : columnNames) {\r
- TreeColumn column = new TreeColumn(tree, SWT.LEFT);\r
- column.setText(columnName);\r
- column.pack();\r
- }\r
- }\r
-\r
-\r
- /**\r
- * Sets the time graph provider used by this time graph combo.\r
- *\r
- * @param timeGraphProvider the time graph provider\r
- */\r
- public void setTimeGraphProvider(ITimeGraphPresentationProvider timeGraphProvider) {\r
- fTimeGraphViewer.setTimeGraphProvider(timeGraphProvider);\r
- }\r
-\r
- /**\r
- * Sets or clears the input for this time graph combo.\r
- * The input array should only contain top-level elements.\r
- *\r
- * @param input the input of this time graph combo, or <code>null</code> if none\r
- */\r
- public void setInput(ITimeGraphEntry[] input) {\r
- fInhibitTreeSelection = true;\r
- fTreeViewer.setInput(input);\r
- fInhibitTreeSelection = false;\r
- fTreeViewer.expandAll();\r
- fTreeViewer.getTree().getVerticalBar().setEnabled(false);\r
- fTreeViewer.getTree().getVerticalBar().setVisible(false);\r
- fTimeGraphViewer.setItemHeight(getItemHeight(fTreeViewer.getTree()));\r
- fTimeGraphViewer.setInput(input);\r
- }\r
-\r
- /**\r
- * Refreshes this time graph completely with information freshly obtained from its model.\r
- */\r
- public void refresh() {\r
- fTreeViewer.refresh();\r
- fTimeGraphViewer.refresh();\r
- }\r
-\r
- /**\r
- * Adds a listener for selection changes in this time graph combo.\r
- *\r
- * @param listener a selection listener\r
- */\r
- public void addSelectionListener(ITimeGraphSelectionListener listener) {\r
- SelectionListenerWrapper listenerWrapper = new SelectionListenerWrapper(listener);\r
- fTreeViewer.addSelectionChangedListener(listenerWrapper);\r
- fSelectionListenerMap.put(listener, listenerWrapper);\r
- fTimeGraphViewer.addSelectionListener(listenerWrapper);\r
- }\r
-\r
- /**\r
- * Removes the given selection listener from this time graph combo.\r
- *\r
- * @param listener a selection changed listener\r
- */\r
- public void removeSelectionListener(ITimeGraphSelectionListener listener) {\r
- SelectionListenerWrapper listenerWrapper = fSelectionListenerMap.remove(listener);\r
- fTreeViewer.removeSelectionChangedListener(listenerWrapper);\r
- fTimeGraphViewer.removeSelectionListener(listenerWrapper);\r
- }\r
-\r
- /**\r
- * Sets the current selection for this time graph combo.\r
- *\r
- * @param selection the new selection\r
- */\r
- public void setSelection(ITimeGraphEntry selection) {\r
- fTimeGraphViewer.setSelection(selection);\r
- fInhibitTreeSelection = true; // block the tree selection changed listener\r
- if (selection != null) {\r
- StructuredSelection structuredSelection = new StructuredSelection(selection);\r
- fTreeViewer.setSelection(structuredSelection);\r
- } else {\r
- fTreeViewer.setSelection(new StructuredSelection());\r
- }\r
- fInhibitTreeSelection = false;\r
- ArrayList<TreeItem> treeItems = getVisibleExpandedItems(fTreeViewer.getTree());\r
- if (treeItems.size() == 0) {\r
- return;\r
- }\r
- TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex());\r
- fTreeViewer.getTree().setTopItem(treeItem);\r
- }\r
-\r
- // ------------------------------------------------------------------------\r
- // Internal\r
- // ------------------------------------------------------------------------\r
-\r
- private ArrayList<TreeItem> getVisibleExpandedItems(Tree tree) {\r
- ArrayList<TreeItem> items = new ArrayList<TreeItem>();\r
- for (TreeItem item : tree.getItems()) {\r
- if (item.getData() == FILLER) {\r
- break;\r
- }\r
- items.add(item);\r
- if (item.getExpanded()) {\r
- items.addAll(getVisibleExpandedItems(item));\r
- }\r
- }\r
- return items;\r
- }\r
-\r
- private ArrayList<TreeItem> getVisibleExpandedItems(TreeItem treeItem) {\r
- ArrayList<TreeItem> items = new ArrayList<TreeItem>();\r
- for (TreeItem item : treeItem.getItems()) {\r
- items.add(item);\r
- if (item.getExpanded()) {\r
- items.addAll(getVisibleExpandedItems(item));\r
- }\r
- }\r
- return items;\r
- }\r
-\r
- private int getItemHeight(final Tree tree) {\r
- /*\r
- * Bug in Linux. The method getItemHeight doesn't always return the correct value.\r
- */\r
- if (fLinuxItemHeight >= 0 && System.getProperty("os.name").contains("Linux")) { //$NON-NLS-1$ //$NON-NLS-2$\r
- if (fLinuxItemHeight != 0) {\r
- return fLinuxItemHeight;\r
- }\r
- ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree);\r
- if (treeItems.size() > 1) {\r
- final TreeItem treeItem0 = treeItems.get(0);\r
- final TreeItem treeItem1 = treeItems.get(1);\r
- PaintListener paintListener = new PaintListener() {\r
- @Override\r
- public void paintControl(PaintEvent e) {\r
- tree.removePaintListener(this);\r
- int y0 = treeItem0.getBounds().y;\r
- int y1 = treeItem1.getBounds().y;\r
- int itemHeight = y1 - y0;\r
- if (itemHeight > 0) {\r
- fLinuxItemHeight = itemHeight;\r
- fTimeGraphViewer.setItemHeight(itemHeight);\r
- }\r
- }\r
- };\r
- tree.addPaintListener(paintListener);\r
- }\r
- } else {\r
- fLinuxItemHeight = -1; // Not Linux, don't perform os.name check anymore\r
- }\r
- return tree.getItemHeight();\r
- }\r
-\r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2012, 2013 Ericsson
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Patrick Tasse - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.linuxtools.tmf.ui.widgets.timegraph;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.viewers.ILabelProviderListener;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.ITableLabelProvider;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.ITreeViewerListener;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TreeExpansionEvent;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.eclipse.linuxtools.internal.tmf.ui.Activator;
+import org.eclipse.linuxtools.internal.tmf.ui.ITmfImageConstants;
+import org.eclipse.linuxtools.internal.tmf.ui.Messages;
+import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.dialogs.TimeGraphFilterDialog;
+import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.SashForm;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseTrackAdapter;
+import org.eclipse.swt.events.MouseWheelListener;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Slider;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeColumn;
+import org.eclipse.swt.widgets.TreeItem;
+
+/**
+ * Time graph "combo" view (with the list/tree on the left and the gantt chart
+ * on the right)
+ *
+ * @version 1.0
+ * @author Patrick Tasse
+ */
+public class TimeGraphCombo extends Composite {
+
+ // ------------------------------------------------------------------------
+ // Constants
+ // ------------------------------------------------------------------------
+
+ private static final Object FILLER = new Object();
+
+ // ------------------------------------------------------------------------
+ // Fields
+ // ------------------------------------------------------------------------
+
+ // The tree viewer
+ private TreeViewer fTreeViewer;
+
+ // The time viewer
+ private TimeGraphViewer fTimeGraphViewer;
+
+ // The top-level input (children excluded)
+ private List<? extends ITimeGraphEntry> fTopInput;
+
+ // All the inputs (children included)
+ private List<? extends ITimeGraphEntry> fAllInput;
+
+ // The selection listener map
+ private final HashMap<ITimeGraphSelectionListener, SelectionListenerWrapper> fSelectionListenerMap = new HashMap<ITimeGraphSelectionListener, SelectionListenerWrapper>();
+
+ // The map of viewer filters
+ private final Map<ViewerFilter, ViewerFilter> fViewerFilterMap = new HashMap<ViewerFilter, ViewerFilter>();
+
+ // Flag to block the tree selection changed listener when triggered by the time graph combo
+ private boolean fInhibitTreeSelection = false;
+
+ // Number of filler rows used by the tree content provider
+ private int fNumFillerRows;
+
+ // Calculated item height for Linux workaround
+ private int fLinuxItemHeight = 0;
+
+ // The button that opens the filter dialog
+ private Action showFilterAction;
+
+ // The filter dialog
+ private TimeGraphFilterDialog fFilterDialog;
+
+ // The filter generated from the filter dialog
+ private RawViewerFilter fFilter;
+
+ // ------------------------------------------------------------------------
+ // Classes
+ // ------------------------------------------------------------------------
+
+ /**
+ * The TreeContentProviderWrapper is used to insert filler items after
+ * the elements of the tree's real content provider.
+ */
+ private class TreeContentProviderWrapper implements ITreeContentProvider {
+ private final ITreeContentProvider contentProvider;
+
+ public TreeContentProviderWrapper(ITreeContentProvider contentProvider) {
+ this.contentProvider = contentProvider;
+ }
+
+ @Override
+ public void dispose() {
+ contentProvider.dispose();
+ }
+
+ @Override
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ contentProvider.inputChanged(viewer, oldInput, newInput);
+ }
+
+ @Override
+ public Object[] getElements(Object inputElement) {
+ Object[] elements = contentProvider.getElements(inputElement);
+ // add filler elements to ensure alignment with time analysis viewer
+ Object[] oElements = Arrays.copyOf(elements, elements.length + fNumFillerRows, new Object[0].getClass());
+ for (int i = 0; i < fNumFillerRows; i++) {
+ oElements[elements.length + i] = FILLER;
+ }
+ return oElements;
+ }
+
+ @Override
+ public Object[] getChildren(Object parentElement) {
+ if (parentElement instanceof ITimeGraphEntry) {
+ return contentProvider.getChildren(parentElement);
+ }
+ return new Object[0];
+ }
+
+ @Override
+ public Object getParent(Object element) {
+ if (element instanceof ITimeGraphEntry) {
+ return contentProvider.getParent(element);
+ }
+ return null;
+ }
+
+ @Override
+ public boolean hasChildren(Object element) {
+ if (element instanceof ITimeGraphEntry) {
+ return contentProvider.hasChildren(element);
+ }
+ return false;
+ }
+ }
+
+ /**
+ * The TreeLabelProviderWrapper is used to intercept the filler items
+ * from the calls to the tree's real label provider.
+ */
+ private class TreeLabelProviderWrapper implements ITableLabelProvider {
+ private final ITableLabelProvider labelProvider;
+
+ public TreeLabelProviderWrapper(ITableLabelProvider labelProvider) {
+ this.labelProvider = labelProvider;
+ }
+
+ @Override
+ public void addListener(ILabelProviderListener listener) {
+ labelProvider.addListener(listener);
+ }
+
+ @Override
+ public void dispose() {
+ labelProvider.dispose();
+ }
+
+ @Override
+ public boolean isLabelProperty(Object element, String property) {
+ if (element instanceof ITimeGraphEntry) {
+ return labelProvider.isLabelProperty(element, property);
+ }
+ return false;
+ }
+
+ @Override
+ public void removeListener(ILabelProviderListener listener) {
+ labelProvider.removeListener(listener);
+ }
+
+ @Override
+ public Image getColumnImage(Object element, int columnIndex) {
+ if (element instanceof ITimeGraphEntry) {
+ return labelProvider.getColumnImage(element, columnIndex);
+ }
+ return null;
+ }
+
+ @Override
+ public String getColumnText(Object element, int columnIndex) {
+ if (element instanceof ITimeGraphEntry) {
+ return labelProvider.getColumnText(element, columnIndex);
+ }
+ return null;
+ }
+
+ }
+
+ /**
+ * The SelectionListenerWrapper is used to intercept the filler items from
+ * the time graph combo's real selection listener, and to prevent double
+ * notifications from being sent when selection changes in both tree and
+ * time graph at the same time.
+ */
+ private class SelectionListenerWrapper implements ISelectionChangedListener, ITimeGraphSelectionListener {
+ private final ITimeGraphSelectionListener listener;
+ private ITimeGraphEntry selection = null;
+
+ public SelectionListenerWrapper(ITimeGraphSelectionListener listener) {
+ this.listener = listener;
+ }
+
+ @Override
+ public void selectionChanged(SelectionChangedEvent event) {
+ if (fInhibitTreeSelection) {
+ return;
+ }
+ Object element = ((IStructuredSelection) event.getSelection()).getFirstElement();
+ if (element instanceof ITimeGraphEntry) {
+ ITimeGraphEntry entry = (ITimeGraphEntry) element;
+ if (entry != selection) {
+ selection = entry;
+ listener.selectionChanged(new TimeGraphSelectionEvent(event.getSource(), selection));
+ }
+ }
+ }
+
+ @Override
+ public void selectionChanged(TimeGraphSelectionEvent event) {
+ ITimeGraphEntry entry = event.getSelection();
+ if (entry != selection) {
+ selection = entry;
+ listener.selectionChanged(new TimeGraphSelectionEvent(event.getSource(), selection));
+ }
+ }
+ }
+
+ /**
+ * The ViewerFilterWrapper is used to intercept the filler items from
+ * the time graph combo's real ViewerFilters. These filler items should
+ * always be visible.
+ */
+ private class ViewerFilterWrapper extends ViewerFilter {
+
+ ViewerFilter fWrappedFilter;
+
+ ViewerFilterWrapper(ViewerFilter filter) {
+ super();
+ this.fWrappedFilter = filter;
+ }
+
+ @Override
+ public boolean select(Viewer viewer, Object parentElement, Object element) {
+ if (element instanceof ITimeGraphEntry) {
+ return fWrappedFilter.select(viewer, parentElement, element);
+ }
+ return true;
+ }
+
+ }
+
+ /**
+ * This filter simply keeps a list of elements that should be shown
+ * All the other elements will be filtered
+ */
+ private class RawViewerFilter extends ViewerFilter {
+
+ private List<Object> fNonFiltered = new ArrayList<Object>();
+
+ public void setNonFiltered(List<Object> objects) {
+ fNonFiltered = objects;
+ }
+
+ public List<Object> getNonFiltered() {
+ return fNonFiltered;
+ }
+
+ @Override
+ public boolean select(Viewer viewer, Object parentElement, Object element) {
+ return fNonFiltered.contains(element);
+ }
+ }
+
+ // ------------------------------------------------------------------------
+ // Constructors
+ // ------------------------------------------------------------------------
+
+ /**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ *
+ * @param parent a widget which will be the parent of the new instance (cannot be null)
+ * @param style the style of widget to construct
+ */
+ public TimeGraphCombo(Composite parent, int style) {
+ super(parent, style);
+ setLayout(new FillLayout());
+
+ final SashForm sash = new SashForm(this, SWT.NONE);
+
+ fTreeViewer = new TreeViewer(sash, SWT.FULL_SELECTION | SWT.H_SCROLL);
+ final Tree tree = fTreeViewer.getTree();
+ tree.setHeaderVisible(true);
+ tree.setLinesVisible(true);
+
+ fTimeGraphViewer = new TimeGraphViewer(sash, SWT.NONE);
+ fTimeGraphViewer.setItemHeight(getItemHeight(tree));
+ fTimeGraphViewer.setHeaderHeight(tree.getHeaderHeight());
+ fTimeGraphViewer.setBorderWidth(tree.getBorderWidth());
+ fTimeGraphViewer.setNameWidthPref(0);
+
+ fFilter = new RawViewerFilter();
+ addFilter(fFilter);
+
+ fFilterDialog = new TimeGraphFilterDialog(getShell());
+
+ // Feature in Windows. The tree vertical bar reappears when
+ // the control is resized so we need to hide it again.
+ // Bug in Linux. The tree header height is 0 in constructor,
+ // so we need to reset it later when the control is resized.
+ tree.addControlListener(new ControlAdapter() {
+ int depth = 0;
+ @Override
+ public void controlResized(ControlEvent e) {
+ if (depth == 0) {
+ depth++;
+ tree.getVerticalBar().setEnabled(false);
+ // this can trigger controlResized recursively
+ tree.getVerticalBar().setVisible(false);
+ depth--;
+ }
+ fTimeGraphViewer.setHeaderHeight(tree.getHeaderHeight());
+ }
+ });
+
+ // ensure synchronization of expanded items between tree and time graph
+ fTreeViewer.addTreeListener(new ITreeViewerListener() {
+ @Override
+ public void treeCollapsed(TreeExpansionEvent event) {
+ fTimeGraphViewer.setExpandedState((ITimeGraphEntry) event.getElement(), false);
+ ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree);
+ if (treeItems.size() == 0) {
+ return;
+ }
+ TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex());
+ tree.setTopItem(treeItem);
+ }
+
+ @Override
+ public void treeExpanded(TreeExpansionEvent event) {
+ fTimeGraphViewer.setExpandedState((ITimeGraphEntry) event.getElement(), true);
+ ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree);
+ if (treeItems.size() == 0) {
+ return;
+ }
+ final TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex());
+ // queue the top item update because the tree can change its top item
+ // autonomously immediately after the listeners have been notified
+ getDisplay().asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ tree.setTopItem(treeItem);
+ }});
+ }
+ });
+
+ // ensure synchronization of expanded items between tree and time graph
+ fTimeGraphViewer.addTreeListener(new ITimeGraphTreeListener() {
+ @Override
+ public void treeCollapsed(TimeGraphTreeExpansionEvent event) {
+ fTreeViewer.setExpandedState(event.getEntry(), false);
+ }
+
+ @Override
+ public void treeExpanded(TimeGraphTreeExpansionEvent event) {
+ fTreeViewer.setExpandedState(event.getEntry(), true);
+ }
+ });
+
+ // prevent mouse button from selecting a filler tree item
+ tree.addListener(SWT.MouseDown, new Listener() {
+ @Override
+ public void handleEvent(Event event) {
+ TreeItem treeItem = tree.getItem(new Point(event.x, event.y));
+ if (treeItem == null || treeItem.getData() == FILLER) {
+ event.doit = false;
+ ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree);
+ if (treeItems.size() == 0) {
+ fTreeViewer.setSelection(new StructuredSelection());
+ fTimeGraphViewer.setSelection(null);
+ return;
+ }
+ // this prevents from scrolling up when selecting
+ // the partially visible tree item at the bottom
+ tree.select(treeItems.get(treeItems.size() - 1));
+ fTreeViewer.setSelection(new StructuredSelection());
+ fTimeGraphViewer.setSelection(null);
+ }
+ }
+ });
+
+ // prevent mouse wheel from scrolling down into filler tree items
+ tree.addListener(SWT.MouseWheel, new Listener() {
+ @Override
+ public void handleEvent(Event event) {
+ event.doit = false;
+ Slider scrollBar = fTimeGraphViewer.getVerticalBar();
+ fTimeGraphViewer.setTopIndex(scrollBar.getSelection() - event.count);
+ ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree);
+ if (treeItems.size() == 0) {
+ return;
+ }
+ TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex());
+ tree.setTopItem(treeItem);
+ }
+ });
+
+ // prevent key stroke from selecting a filler tree item
+ tree.addListener(SWT.KeyDown, new Listener() {
+ @Override
+ public void handleEvent(Event event) {
+ ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree);
+ if (treeItems.size() == 0) {
+ fTreeViewer.setSelection(new StructuredSelection());
+ event.doit = false;
+ return;
+ }
+ if (event.keyCode == SWT.ARROW_DOWN) {
+ int index = Math.min(fTimeGraphViewer.getSelectionIndex() + 1, treeItems.size() - 1);
+ fTimeGraphViewer.setSelection((ITimeGraphEntry) treeItems.get(index).getData());
+ event.doit = false;
+ } else if (event.keyCode == SWT.PAGE_DOWN) {
+ int height = tree.getSize().y - tree.getHeaderHeight() - tree.getHorizontalBar().getSize().y;
+ int countPerPage = height / getItemHeight(tree);
+ int index = Math.min(fTimeGraphViewer.getSelectionIndex() + countPerPage - 1, treeItems.size() - 1);
+ fTimeGraphViewer.setSelection((ITimeGraphEntry) treeItems.get(index).getData());
+ event.doit = false;
+ } else if (event.keyCode == SWT.END) {
+ fTimeGraphViewer.setSelection((ITimeGraphEntry) treeItems.get(treeItems.size() - 1).getData());
+ event.doit = false;
+ }
+ TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex());
+ tree.setTopItem(treeItem);
+ if (fTimeGraphViewer.getSelectionIndex() >= 0) {
+ fTreeViewer.setSelection(new StructuredSelection(fTimeGraphViewer.getSelection()));
+ } else {
+ fTreeViewer.setSelection(new StructuredSelection());
+ }
+ }
+ });
+
+ // ensure alignment of top item between tree and time graph
+ fTimeGraphViewer.getTimeGraphControl().addControlListener(new ControlAdapter() {
+ @Override
+ public void controlResized(ControlEvent e) {
+ ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree);
+ if (treeItems.size() == 0) {
+ return;
+ }
+ TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex());
+ tree.setTopItem(treeItem);
+ }
+ });
+
+ // ensure synchronization of selected item between tree and time graph
+ fTreeViewer.addSelectionChangedListener(new ISelectionChangedListener() {
+ @Override
+ public void selectionChanged(SelectionChangedEvent event) {
+ if (fInhibitTreeSelection) {
+ return;
+ }
+ if (event.getSelection() instanceof IStructuredSelection) {
+ Object selection = ((IStructuredSelection) event.getSelection()).getFirstElement();
+ if (selection instanceof ITimeGraphEntry) {
+ fTimeGraphViewer.setSelection((ITimeGraphEntry) selection);
+ }
+ ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree);
+ if (treeItems.size() == 0) {
+ return;
+ }
+ TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex());
+ tree.setTopItem(treeItem);
+ }
+ }
+ });
+
+ // ensure synchronization of selected item between tree and time graph
+ fTimeGraphViewer.addSelectionListener(new ITimeGraphSelectionListener() {
+ @Override
+ public void selectionChanged(TimeGraphSelectionEvent event) {
+ ITimeGraphEntry entry = fTimeGraphViewer.getSelection();
+ fInhibitTreeSelection = true; // block the tree selection changed listener
+ if (entry != null) {
+ StructuredSelection selection = new StructuredSelection(entry);
+ fTreeViewer.setSelection(selection);
+ } else {
+ fTreeViewer.setSelection(new StructuredSelection());
+ }
+ fInhibitTreeSelection = false;
+ ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree);
+ if (treeItems.size() == 0) {
+ return;
+ }
+ TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex());
+ tree.setTopItem(treeItem);
+ }
+ });
+
+ // ensure alignment of top item between tree and time graph
+ fTimeGraphViewer.getVerticalBar().addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree);
+ if (treeItems.size() == 0) {
+ return;
+ }
+ TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex());
+ tree.setTopItem(treeItem);
+ }
+ });
+
+ // ensure alignment of top item between tree and time graph
+ fTimeGraphViewer.getTimeGraphControl().addMouseWheelListener(new MouseWheelListener() {
+ @Override
+ public void mouseScrolled(MouseEvent e) {
+ ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree);
+ if (treeItems.size() == 0) {
+ return;
+ }
+ TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex());
+ tree.setTopItem(treeItem);
+ }
+ });
+
+ // ensure the tree has focus control when mouse is over it if the time graph had control
+ fTreeViewer.getControl().addMouseTrackListener(new MouseTrackAdapter() {
+ @Override
+ public void mouseEnter(MouseEvent e) {
+ if (fTimeGraphViewer.getTimeGraphControl().isFocusControl()) {
+ fTreeViewer.getControl().setFocus();
+ }
+ }
+ });
+
+ // ensure the time graph has focus control when mouse is over it if the tree had control
+ fTimeGraphViewer.getTimeGraphControl().addMouseTrackListener(new MouseTrackAdapter() {
+ @Override
+ public void mouseEnter(MouseEvent e) {
+ if (fTreeViewer.getControl().isFocusControl()) {
+ fTimeGraphViewer.getTimeGraphControl().setFocus();
+ }
+ }
+ });
+ fTimeGraphViewer.getTimeGraphScale().addMouseTrackListener(new MouseTrackAdapter() {
+ @Override
+ public void mouseEnter(MouseEvent e) {
+ if (fTreeViewer.getControl().isFocusControl()) {
+ fTimeGraphViewer.getTimeGraphControl().setFocus();
+ }
+ }
+ });
+
+ // The filler rows are required to ensure alignment when the tree does not have a
+ // visible horizontal scroll bar. The tree does not allow its top item to be set
+ // to a value that would cause blank space to be drawn at the bottom of the tree.
+ fNumFillerRows = Display.getDefault().getBounds().height / getItemHeight(tree);
+
+ sash.setWeights(new int[] { 1, 1 });
+ }
+
+ // ------------------------------------------------------------------------
+ // Accessors
+ // ------------------------------------------------------------------------
+
+ /**
+ * Returns this time graph combo's tree viewer.
+ *
+ * @return the tree viewer
+ */
+ public TreeViewer getTreeViewer() {
+ return fTreeViewer;
+ }
+
+ /**
+ * Returns this time graph combo's time graph viewer.
+ *
+ * @return the time graph viewer
+ */
+ public TimeGraphViewer getTimeGraphViewer() {
+ return fTimeGraphViewer;
+ }
+
+ /**
+ * Callback for the show filter action
+ *
+ * @since 2.0
+ */
+ public void showFilterDialog() {
+ if(fTopInput != null) {
+ fFilterDialog.setInput(fTopInput.toArray(new ITimeGraphEntry[0]));
+ fFilterDialog.setTitle(Messages.TmfTimeFilterDialog_WINDOW_TITLE);
+ fFilterDialog.setMessage(Messages.TmfTimeFilterDialog_MESSAGE);
+ fFilterDialog.setInitialElementSelections(fFilter.getNonFiltered());
+ fFilterDialog.setExpandedElements(fAllInput.toArray());
+ fFilterDialog.create();
+ fFilterDialog.open();
+ // Process selected elements
+ if (fFilterDialog.getResult() != null) {
+ fInhibitTreeSelection = true;
+ fFilter.setNonFiltered(new ArrayList<Object>(Arrays.asList(fFilterDialog.getResult())));
+ fTreeViewer.refresh();
+ fTreeViewer.expandAll();
+ fTimeGraphViewer.refresh();
+ fInhibitTreeSelection = false;
+ // Reset selection to first entry
+ if (fFilterDialog.getResult().length > 0) {
+ setSelection((ITimeGraphEntry) fFilterDialog.getResult()[0]);
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the show filter action.
+ *
+ * @return The Action object
+ * @since 2.0
+ */
+ public Action getShowFilterAction() {
+ if (showFilterAction == null) {
+ // showFilter
+ showFilterAction = new Action() {
+ @Override
+ public void run() {
+ showFilterDialog();
+ }
+ };
+ showFilterAction.setText(Messages.TmfTimeGraphCombo_FilterActionNameText);
+ showFilterAction.setToolTipText(Messages.TmfTimeGraphCombo_FilterActionToolTipText);
+ // TODO find a nice, distinctive icon
+ showFilterAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_FILTERS));
+ }
+
+ return showFilterAction;
+ }
+
+ // ------------------------------------------------------------------------
+ // Control
+ // ------------------------------------------------------------------------
+
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.widgets.Control#redraw()
+ */
+ @Override
+ public void redraw() {
+ fTimeGraphViewer.getControl().redraw();
+ super.redraw();
+ }
+
+ // ------------------------------------------------------------------------
+ // Operations
+ // ------------------------------------------------------------------------
+
+ /**
+ * Sets the tree content provider used by this time graph combo.
+ *
+ * @param contentProvider the tree content provider
+ */
+ public void setTreeContentProvider(ITreeContentProvider contentProvider) {
+ fTreeViewer.setContentProvider(new TreeContentProviderWrapper(contentProvider));
+ }
+
+ /**
+ * Sets the tree label provider used by this time graph combo.
+ *
+ * @param labelProvider the tree label provider
+ */
+ public void setTreeLabelProvider(ITableLabelProvider labelProvider) {
+ fTreeViewer.setLabelProvider(new TreeLabelProviderWrapper(labelProvider));
+ }
+
+ /**
+ * Sets the tree content provider used by the filter dialog
+ *
+ * @param contentProvider the tree content provider
+ * @since 2.0
+ */
+ public void setFilterContentProvider(ITreeContentProvider contentProvider) {
+ fFilterDialog.setContentProvider(contentProvider);
+ }
+
+ /**
+ * Sets the tree label provider used by the filter dialog
+ *
+ * @param labelProvider the tree label provider
+ * @since 2.0
+ */
+ public void setFilterLabelProvider(ITableLabelProvider labelProvider) {
+ fFilterDialog.setLabelProvider(labelProvider);
+ }
+
+ /**
+ * Sets the tree columns for this time graph combo.
+ *
+ * @param columnNames the tree column names
+ */
+ public void setTreeColumns(String[] columnNames) {
+ final Tree tree = fTreeViewer.getTree();
+ for (String columnName : columnNames) {
+ TreeColumn column = new TreeColumn(tree, SWT.LEFT);
+ column.setText(columnName);
+ column.pack();
+ }
+ }
+
+ /**
+ * Sets the tree columns for this time graph combo's filter dialog.
+ *
+ * @param columnNames the tree column names
+ * @since 2.0
+ */
+ public void setFilterColumns(String[] columnNames) {
+ fFilterDialog.setColumnNames(columnNames);
+ }
+
+ /**
+ * Sets the time graph provider used by this time graph combo.
+ *
+ * @param timeGraphProvider the time graph provider
+ */
+ public void setTimeGraphProvider(ITimeGraphPresentationProvider timeGraphProvider) {
+ fTimeGraphViewer.setTimeGraphProvider(timeGraphProvider);
+ }
+
+ /**
+ * Sets or clears the input for this time graph combo.
+ * The input array should only contain top-level elements.
+ *
+ * @param input the input of this time graph combo, or <code>null</code> if none
+ */
+ public void setInput(ITimeGraphEntry[] input) {
+ fTopInput = new ArrayList<ITimeGraphEntry>(Arrays.asList(input));
+ fAllInput = listAllInputs(fTopInput);
+ fFilter.setNonFiltered(new ArrayList<Object>(fAllInput));
+ fInhibitTreeSelection = true;
+ fTreeViewer.setInput(input);
+ for (SelectionListenerWrapper listenerWrapper : fSelectionListenerMap.values()) {
+ listenerWrapper.selection = null;
+ }
+ fInhibitTreeSelection = false;
+ fTreeViewer.expandAll();
+ fTreeViewer.getTree().getVerticalBar().setEnabled(false);
+ fTreeViewer.getTree().getVerticalBar().setVisible(false);
+ fTimeGraphViewer.setItemHeight(getItemHeight(fTreeViewer.getTree()));
+ fTimeGraphViewer.setInput(input);
+ }
+
+ /**
+ * @param filter The filter object to be attached to the view
+ * @since 2.0
+ */
+ public void addFilter(ViewerFilter filter) {
+ ViewerFilter wrapper = new ViewerFilterWrapper(filter);
+ fTreeViewer.addFilter(wrapper);
+ fTimeGraphViewer.addFilter(wrapper);
+ fViewerFilterMap.put(filter, wrapper);
+ }
+
+ /**
+ * @param filter The filter object to be removed from the view
+ * @since 2.0
+ */
+ public void removeFilter(ViewerFilter filter) {
+ ViewerFilter wrapper = fViewerFilterMap.get(filter);
+ fTreeViewer.removeFilter(wrapper);
+ fTimeGraphViewer.removeFilter(wrapper);
+ fViewerFilterMap.remove(filter);
+ }
+
+ /**
+ * Refreshes this time graph completely with information freshly obtained from its model.
+ */
+ public void refresh() {
+ fInhibitTreeSelection = true;
+ fTreeViewer.refresh();
+ fTimeGraphViewer.refresh();
+ fInhibitTreeSelection = false;
+ }
+
+ /**
+ * Adds a listener for selection changes in this time graph combo.
+ *
+ * @param listener a selection listener
+ */
+ public void addSelectionListener(ITimeGraphSelectionListener listener) {
+ SelectionListenerWrapper listenerWrapper = new SelectionListenerWrapper(listener);
+ fTreeViewer.addSelectionChangedListener(listenerWrapper);
+ fSelectionListenerMap.put(listener, listenerWrapper);
+ fTimeGraphViewer.addSelectionListener(listenerWrapper);
+ }
+
+ /**
+ * Removes the given selection listener from this time graph combo.
+ *
+ * @param listener a selection changed listener
+ */
+ public void removeSelectionListener(ITimeGraphSelectionListener listener) {
+ SelectionListenerWrapper listenerWrapper = fSelectionListenerMap.remove(listener);
+ fTreeViewer.removeSelectionChangedListener(listenerWrapper);
+ fTimeGraphViewer.removeSelectionListener(listenerWrapper);
+ }
+
+ /**
+ * Sets the current selection for this time graph combo.
+ *
+ * @param selection the new selection
+ */
+ public void setSelection(ITimeGraphEntry selection) {
+ fTimeGraphViewer.setSelection(selection);
+ fInhibitTreeSelection = true; // block the tree selection changed listener
+ if (selection != null) {
+ StructuredSelection structuredSelection = new StructuredSelection(selection);
+ fTreeViewer.setSelection(structuredSelection);
+ } else {
+ fTreeViewer.setSelection(new StructuredSelection());
+ }
+ fInhibitTreeSelection = false;
+ ArrayList<TreeItem> treeItems = getVisibleExpandedItems(fTreeViewer.getTree());
+ if (treeItems.size() == 0) {
+ return;
+ }
+ TreeItem treeItem = treeItems.get(fTimeGraphViewer.getTopIndex());
+ fTreeViewer.getTree().setTopItem(treeItem);
+ }
+
+ /**
+ * Set the expanded state of an entry
+ *
+ * @param entry
+ * The entry to expand/collapse
+ * @param expanded
+ * True for expanded, false for collapsed
+ *
+ * @since 2.0
+ */
+ public void setExpandedState(ITimeGraphEntry entry, boolean expanded) {
+ fTimeGraphViewer.setExpandedState(entry, expanded);
+ fTreeViewer.setExpandedState(entry, expanded);
+ }
+
+ /**
+ * Collapses all nodes of the viewer's tree, starting with the root.
+ *
+ * @since 2.0
+ */
+ public void collapseAll() {
+ fTimeGraphViewer.collapseAll();
+ fTreeViewer.collapseAll();
+ }
+
+ /**
+ * Expands all nodes of the viewer's tree, starting with the root.
+ *
+ * @since 2.0
+ */
+ public void expandAll() {
+ fTimeGraphViewer.expandAll();
+ fTreeViewer.expandAll();
+ }
+
+ // ------------------------------------------------------------------------
+ // Internal
+ // ------------------------------------------------------------------------
+
+ private ArrayList<TreeItem> getVisibleExpandedItems(Tree tree) {
+ ArrayList<TreeItem> items = new ArrayList<TreeItem>();
+ for (TreeItem item : tree.getItems()) {
+ if (item.getData() == FILLER) {
+ break;
+ }
+ items.add(item);
+ if (item.getExpanded()) {
+ items.addAll(getVisibleExpandedItems(item));
+ }
+ }
+ return items;
+ }
+
+ private ArrayList<TreeItem> getVisibleExpandedItems(TreeItem treeItem) {
+ ArrayList<TreeItem> items = new ArrayList<TreeItem>();
+ for (TreeItem item : treeItem.getItems()) {
+ items.add(item);
+ if (item.getExpanded()) {
+ items.addAll(getVisibleExpandedItems(item));
+ }
+ }
+ return items;
+ }
+
+ /**
+ * Explores the list of top-level inputs and returns all the inputs
+ *
+ * @param inputs The top-level inputs
+ * @return All the inputs
+ */
+ private List<? extends ITimeGraphEntry> listAllInputs(List<? extends ITimeGraphEntry> inputs) {
+ ArrayList<ITimeGraphEntry> items = new ArrayList<ITimeGraphEntry>();
+ for (ITimeGraphEntry entry : inputs) {
+ items.add(entry);
+ if (entry.hasChildren()) {
+ items.addAll(listAllInputs(entry.getChildren()));
+ }
+ }
+ return items;
+ }
+
+ private int getItemHeight(final Tree tree) {
+ /*
+ * Bug in Linux. The method getItemHeight doesn't always return the correct value.
+ */
+ if (fLinuxItemHeight >= 0 && System.getProperty("os.name").contains("Linux")) { //$NON-NLS-1$ //$NON-NLS-2$
+ if (fLinuxItemHeight != 0) {
+ return fLinuxItemHeight;
+ }
+ ArrayList<TreeItem> treeItems = getVisibleExpandedItems(tree);
+ if (treeItems.size() > 1) {
+ final TreeItem treeItem0 = treeItems.get(0);
+ final TreeItem treeItem1 = treeItems.get(1);
+ PaintListener paintListener = new PaintListener() {
+ @Override
+ public void paintControl(PaintEvent e) {
+ tree.removePaintListener(this);
+ int y0 = treeItem0.getBounds().y;
+ int y1 = treeItem1.getBounds().y;
+ int itemHeight = y1 - y0;
+ if (itemHeight > 0) {
+ fLinuxItemHeight = itemHeight;
+ fTimeGraphViewer.setItemHeight(itemHeight);
+ }
+ }
+ };
+ tree.addPaintListener(paintListener);
+ }
+ } else {
+ fLinuxItemHeight = -1; // Not Linux, don't perform os.name check anymore
+ }
+ return tree.getItemHeight();
+ }
+
+}