Add tree handling APIs and fix tree synchronization in TimeGraphCombo.
authorPatrick Tasse <patrick.tasse@gmail.com>
Wed, 5 Sep 2012 17:49:20 +0000 (13:49 -0400)
committerPatrick Tasse <patrick.tasse@gmail.com>
Wed, 5 Sep 2012 21:49:31 +0000 (17:49 -0400)
Change-Id: Ic3f871a72eea0cdf97de146634075a42dd47f550
Reviewed-on: https://git.eclipse.org/r/7624
Tested-by: Hudson CI
Reviewed-by: Bernd Hufmann <bhufmann@gmail.com>
IP-Clean: Bernd Hufmann <bhufmann@gmail.com>

org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/tmf/ui/widgets/timegraph/TimeGraphCombo.java
org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/tmf/ui/widgets/timegraph/TimeGraphViewer.java
org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/tmf/ui/widgets/timegraph/widgets/TimeGraphControl.java

index f8039cd4c0f92b6add83d2ce988a1647a79a9783..023f47b3450c6bc282bfb0dcff4370f3d13ef058 100644 (file)
-/*******************************************************************************\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
-            }\r
-            return new Object[0];\r
-        }\r
-\r
-        @Override\r
-        public Object getParent(Object element) {\r
-            if (element instanceof ITimeGraphEntry) {\r
-                return contentProvider.getParent(element);\r
-            }\r
-            return null;\r
-        }\r
-\r
-        @Override\r
-        public boolean hasChildren(Object element) {\r
-            if (element instanceof ITimeGraphEntry) {\r
-                return contentProvider.hasChildren(element);\r
-            }\r
-            return false;\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
-            }\r
-            return false;\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
-            }\r
-            return null;\r
-        }\r
-\r
-        @Override\r
-        public String getColumnText(Object element, int columnIndex) {\r
-            if (element instanceof ITimeGraphEntry) {\r
-                return labelProvider.getColumnText(element, columnIndex);\r
-            }\r
-            return null;\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
-        for (SelectionListenerWrapper listenerWrapper : fSelectionListenerMap.values()) {\r
-            listenerWrapper.selection = null;\r
-        }\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 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 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.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 selection listener map
+    private final HashMap<ITimeGraphSelectionListener, SelectionListenerWrapper> fSelectionListenerMap = new HashMap<ITimeGraphSelectionListener, SelectionListenerWrapper>();
+
+    // 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;
+
+    // ------------------------------------------------------------------------
+    // 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));
+            }
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // 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);
+
+        // 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;
+    }
+
+    // ------------------------------------------------------------------------
+    // 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 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 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) {
+        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);
+    }
+
+    /**
+     * 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;
+    }
+
+    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();
+    }
+
+}
index ca3c629a53698a6acc83a7a1f5c724cfc6a3d2f0..d3e9151ce1dbf69c05def0d23ed27f05fcb2a490 100644 (file)
-/*****************************************************************************\r
- * Copyright (c) 2007, 2008 Intel Corporation, 2009, 2010, 2011, 2012 Ericsson.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- *   Intel Corporation - Initial API and implementation\r
- *   Ruslan A. Scherbakov, Intel - Initial API and implementation\r
- *   Alexander N. Alexeev, Intel - Add monitors statistics support\r
- *   Alvaro Sanchez-Leon - Adapted for TMF\r
- *   Patrick Tasse - Refactoring\r
- *\r
- *****************************************************************************/\r
-\r
-package org.eclipse.linuxtools.tmf.ui.widgets.timegraph;\r
-\r
-import java.util.ArrayList;\r
-\r
-import org.eclipse.jface.action.Action;\r
-import org.eclipse.jface.viewers.ISelectionProvider;\r
-import org.eclipse.linuxtools.internal.tmf.ui.Activator;\r
-import org.eclipse.linuxtools.internal.tmf.ui.ITmfImageConstants;\r
-import org.eclipse.linuxtools.internal.tmf.ui.Messages;\r
-import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.dialogs.TimeGraphLegend;\r
-import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.model.ITimeEvent;\r
-import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;\r
-import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.widgets.ITimeDataProvider;\r
-import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.widgets.TimeGraphColorScheme;\r
-import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.widgets.TimeGraphControl;\r
-import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.widgets.TimeGraphScale;\r
-import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.widgets.TimeGraphTooltipHandler;\r
-import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.widgets.Utils;\r
-import org.eclipse.swt.SWT;\r
-import org.eclipse.swt.events.ControlAdapter;\r
-import org.eclipse.swt.events.ControlEvent;\r
-import org.eclipse.swt.events.KeyAdapter;\r
-import org.eclipse.swt.events.KeyEvent;\r
-import org.eclipse.swt.events.MouseEvent;\r
-import org.eclipse.swt.events.MouseWheelListener;\r
-import org.eclipse.swt.events.SelectionAdapter;\r
-import org.eclipse.swt.events.SelectionEvent;\r
-import org.eclipse.swt.events.SelectionListener;\r
-import org.eclipse.swt.graphics.Rectangle;\r
-import org.eclipse.swt.layout.FillLayout;\r
-import org.eclipse.swt.layout.GridData;\r
-import org.eclipse.swt.layout.GridLayout;\r
-import org.eclipse.swt.widgets.Composite;\r
-import org.eclipse.swt.widgets.Control;\r
-import org.eclipse.swt.widgets.ScrollBar;\r
-import org.eclipse.swt.widgets.Slider;\r
-\r
-/**\r
- * Generic time graph viewer implementation\r
- *\r
- * @version 1.0\r
- * @author Patrick Tasse, and others\r
- */\r
-public class TimeGraphViewer implements ITimeDataProvider, SelectionListener {\r
-\r
-    /** vars */\r
-    private long _minTimeInterval;\r
-    private long _selectedTime;\r
-    private ITimeGraphEntry _selectedEntry;\r
-    private long _beginTime;\r
-    private long _endTime;\r
-    private long _time0;\r
-    private long _time1;\r
-    private long _time0_;\r
-    private long _time1_;\r
-    private long _time0_extSynch = 0;\r
-    private long _time1_extSynch = 0;\r
-    private boolean _timeRangeFixed;\r
-    private int _nameWidthPref = 200;\r
-    private int _minNameWidth = 6;\r
-    private int _nameWidth;\r
-    private Composite _dataViewer;\r
-\r
-    private TimeGraphControl _stateCtrl;\r
-    private TimeGraphScale _timeScaleCtrl;\r
-    private Slider _verticalScrollBar;\r
-    private TimeGraphTooltipHandler _threadTip;\r
-    private TimeGraphColorScheme _colors;\r
-    private ITimeGraphPresentationProvider fTimeGraphProvider;\r
-\r
-    ArrayList<ITimeGraphSelectionListener> fSelectionListeners = new ArrayList<ITimeGraphSelectionListener>();\r
-    ArrayList<ITimeGraphTimeListener> fTimeListeners = new ArrayList<ITimeGraphTimeListener>();\r
-    ArrayList<ITimeGraphRangeListener> fRangeListeners = new ArrayList<ITimeGraphRangeListener>();\r
-\r
-    // Calender Time format, using Epoch reference or Relative time\r
-    // format(default\r
-    private boolean calendarTimeFormat = false;\r
-    private int borderWidth = 0;\r
-    private int timeScaleHeight = 22;\r
-\r
-    private Action resetScale;\r
-    private Action showLegendAction;\r
-    private Action nextEventAction;\r
-    private Action prevEventAction;\r
-    private Action nextItemAction;\r
-    private Action previousItemAction;\r
-    private Action zoomInAction;\r
-    private Action zoomOutAction;\r
-\r
-    /**\r
-     * Standard constructor\r
-     *\r
-     * @param parent\r
-     *            The parent UI composite object\r
-     * @param style\r
-     *            The style to use\r
-     */\r
-    public TimeGraphViewer(Composite parent, int style) {\r
-        createDataViewer(parent, style);\r
-    }\r
-\r
-    /**\r
-     * Sets the timegraph provider used by this timegraph viewer.\r
-     *\r
-     * @param timeGraphProvider the timegraph provider\r
-     */\r
-    public void setTimeGraphProvider(ITimeGraphPresentationProvider timeGraphProvider) {\r
-        fTimeGraphProvider = timeGraphProvider;\r
-        _stateCtrl.setTimeGraphProvider(timeGraphProvider);\r
-        _threadTip = new TimeGraphTooltipHandler(_dataViewer.getShell(), fTimeGraphProvider, this);\r
-        _threadTip.activateHoverHelp(_stateCtrl);\r
-    }\r
-\r
-    /**\r
-     * Sets or clears the input for this time graph viewer.\r
-     * The input array should only contain top-level elements.\r
-     *\r
-     * @param input the input of this time graph viewer, or <code>null</code> if none\r
-     */\r
-    public void setInput(ITimeGraphEntry[] input) {\r
-        if (null != _stateCtrl) {\r
-            if (null == input) {\r
-                input = new ITimeGraphEntry[0];\r
-            }\r
-            setTimeRange(input);\r
-            _verticalScrollBar.setEnabled(true);\r
-            setTopIndex(0);\r
-            _selectedTime = 0;\r
-            _selectedEntry = null;\r
-            refreshAllData(input);\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Refresh the view\r
-     */\r
-    public void refresh() {\r
-        setInput(_stateCtrl.getTraces());\r
-    }\r
-\r
-    /**\r
-     * Callback for when the control is moved\r
-     *\r
-     * @param e\r
-     *            The caller event\r
-     */\r
-    public void controlMoved(ControlEvent e) {\r
-    }\r
-\r
-    /**\r
-     * Callback for when the control is resized\r
-     *\r
-     * @param e\r
-     *            The caller event\r
-     */\r
-    public void controlResized(ControlEvent e) {\r
-        resizeControls();\r
-    }\r
-\r
-    /**\r
-     * Handler for when the model is updated. Called from the display order in\r
-     * the API\r
-     *\r
-     * @param traces\r
-     *            The traces in the model\r
-     * @param start\r
-     *            The start time\r
-     * @param end\r
-     *            The end time\r
-     * @param updateTimeBounds\r
-     *            Should we updated the time bounds too\r
-     */\r
-    public void modelUpdate(ITimeGraphEntry[] traces, long start,\r
-            long end, boolean updateTimeBounds) {\r
-        if (null != _stateCtrl) {\r
-            //loadOptions();\r
-            updateInternalData(traces, start, end);\r
-            if (updateTimeBounds) {\r
-                _timeRangeFixed = true;\r
-                // set window to match limits\r
-                setStartFinishTime(_time0_, _time1_);\r
-            } else {\r
-                _stateCtrl.redraw();\r
-                _timeScaleCtrl.redraw();\r
-            }\r
-        }\r
-    }\r
-\r
-    protected String getViewTypeStr() {\r
-        return "viewoption.threads"; //$NON-NLS-1$\r
-    }\r
-\r
-    int getMarginWidth(int idx) {\r
-        return 0;\r
-    }\r
-\r
-    int getMarginHeight(int idx) {\r
-        return 0;\r
-    }\r
-\r
-    void loadOptions() {\r
-        _minTimeInterval = 1;\r
-        _selectedTime = -1;\r
-        _nameWidth = Utils.loadIntOption(getPreferenceString("namewidth"), //$NON-NLS-1$\r
-                _nameWidthPref, _minNameWidth, 1000);\r
-    }\r
-\r
-    void saveOptions() {\r
-        Utils.saveIntOption(getPreferenceString("namewidth"), _nameWidth); //$NON-NLS-1$\r
-    }\r
-\r
-    protected Control createDataViewer(Composite parent, int style) {\r
-        loadOptions();\r
-        _colors = new TimeGraphColorScheme();\r
-        _dataViewer = new Composite(parent, style) {\r
-            @Override\r
-            public void redraw() {\r
-                _timeScaleCtrl.redraw();\r
-                _stateCtrl.redraw();\r
-                super.redraw();\r
-            }\r
-        };\r
-        GridLayout gl = new GridLayout(2, false);\r
-        gl.marginHeight = borderWidth;\r
-        gl.marginWidth = 0;\r
-        gl.verticalSpacing = 0;\r
-        gl.horizontalSpacing = 0;\r
-        _dataViewer.setLayout(gl);\r
-\r
-        _timeScaleCtrl = new TimeGraphScale(_dataViewer, _colors);\r
-        _timeScaleCtrl.setTimeProvider(this);\r
-        _timeScaleCtrl.setLayoutData(new GridData(SWT.FILL, SWT.DEFAULT, true, false));\r
-        _timeScaleCtrl.setHeight(timeScaleHeight);\r
-\r
-        _verticalScrollBar = new Slider(_dataViewer, SWT.VERTICAL | SWT.NO_FOCUS);\r
-        _verticalScrollBar.setLayoutData(new GridData(SWT.DEFAULT, SWT.FILL, false, true, 1, 2));\r
-        _verticalScrollBar.addSelectionListener(new SelectionAdapter() {\r
-            @Override\r
-            public void widgetSelected(SelectionEvent e) {\r
-                setTopIndex(_verticalScrollBar.getSelection());\r
-            }\r
-        });\r
-        _verticalScrollBar.setEnabled(false);\r
-\r
-        _stateCtrl = createTimeGraphControl();\r
-\r
-        _stateCtrl.setTimeProvider(this);\r
-        _stateCtrl.addSelectionListener(this);\r
-        _stateCtrl.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 2));\r
-        _stateCtrl.addMouseWheelListener(new MouseWheelListener() {\r
-            @Override\r
-            public void mouseScrolled(MouseEvent e) {\r
-                adjustVerticalScrollBar();\r
-            }\r
-        });\r
-        _stateCtrl.addKeyListener(new KeyAdapter() {\r
-            @Override\r
-            public void keyPressed(KeyEvent e) {\r
-                adjustVerticalScrollBar();\r
-            }\r
-        });\r
-\r
-        Composite filler = new Composite(_dataViewer, SWT.NONE);\r
-        GridData gd = new GridData(SWT.DEFAULT, SWT.DEFAULT, false, false);\r
-        gd.heightHint = _stateCtrl.getHorizontalBar().getSize().y;\r
-        filler.setLayoutData(gd);\r
-        filler.setLayout(new FillLayout());\r
-\r
-        _stateCtrl.addControlListener(new ControlAdapter() {\r
-            @Override\r
-            public void controlResized(ControlEvent event) {\r
-                resizeControls();\r
-            }\r
-        });\r
-        resizeControls();\r
-        _dataViewer.update();\r
-        adjustVerticalScrollBar();\r
-        return _dataViewer;\r
-    }\r
-\r
-    /**\r
-     * Dispose the view.\r
-     */\r
-    public void dispose() {\r
-        saveOptions();\r
-        _stateCtrl.dispose();\r
-        _dataViewer.dispose();\r
-        _colors.dispose();\r
-    }\r
-\r
-    protected TimeGraphControl createTimeGraphControl() {\r
-        return new TimeGraphControl(_dataViewer, _colors);\r
-    }\r
-\r
-    /**\r
-     * Resize the controls\r
-     */\r
-    public void resizeControls() {\r
-        Rectangle r = _dataViewer.getClientArea();\r
-        if (r.isEmpty()) {\r
-            return;\r
-        }\r
-\r
-        int width = r.width;\r
-        if (_nameWidth > width - _minNameWidth) {\r
-            _nameWidth = width - _minNameWidth;\r
-        }\r
-        if (_nameWidth < _minNameWidth) {\r
-            _nameWidth = _minNameWidth;\r
-        }\r
-        adjustVerticalScrollBar();\r
-    }\r
-\r
-    /**\r
-     * Try to set most convenient time range for display.\r
-     *\r
-     * @param traces\r
-     *            The traces in the model\r
-     */\r
-    public void setTimeRange(ITimeGraphEntry traces[]) {\r
-        _endTime = 0;\r
-        _beginTime = -1;\r
-        for (int i = 0; i < traces.length; i++) {\r
-            ITimeGraphEntry entry = traces[i];\r
-            if (entry.getEndTime() >= entry.getStartTime() && entry.getEndTime() > 0) {\r
-                if (_beginTime < 0 || entry.getStartTime() < _beginTime) {\r
-                    _beginTime = entry.getStartTime();\r
-                }\r
-                if (entry.getEndTime() > _endTime) {\r
-                    _endTime = entry.getEndTime();\r
-                }\r
-            }\r
-        }\r
-\r
-        if (_beginTime < 0) {\r
-            _beginTime = 0;\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Recalculate the time bounds\r
-     */\r
-    public void setTimeBounds() {\r
-        //_time0_ = _beginTime - (long) ((_endTime - _beginTime) * 0.02);\r
-        _time0_ = _beginTime;\r
-        if (_time0_ < 0) {\r
-            _time0_ = 0;\r
-        }\r
-        // _time1_ = _time0_ + (_endTime - _time0_) * 1.05;\r
-        _time1_ = _endTime;\r
-        // _time0_ = Math.floor(_time0_);\r
-        // _time1_ = Math.ceil(_time1_);\r
-        if (!_timeRangeFixed) {\r
-            _time0 = _time0_;\r
-            _time1 = _time1_;\r
-        }\r
-        if (_time1 - _time0 < _minTimeInterval) {\r
-            _time1 = Math.min(_time1_, _time0 + _minTimeInterval);\r
-        }\r
-    }\r
-\r
-    /**\r
-     * @param traces\r
-     * @param start\r
-     * @param end\r
-     */\r
-    void updateInternalData(ITimeGraphEntry[] traces, long start, long end) {\r
-        if (null == traces) {\r
-            traces = new ITimeGraphEntry[0];\r
-        }\r
-        if ((start == 0 && end == 0) || start < 0 || end < 0) {\r
-            // Start and end time are unspecified and need to be determined from\r
-            // individual processes\r
-            setTimeRange(traces);\r
-        } else {\r
-            _beginTime = start;\r
-            _endTime = end;\r
-        }\r
-\r
-        refreshAllData(traces);\r
-    }\r
-\r
-    /**\r
-     * @param traces\r
-     */\r
-    private void refreshAllData(ITimeGraphEntry[] traces) {\r
-        setTimeBounds();\r
-        if (_selectedTime < _beginTime) {\r
-            _selectedTime = _beginTime;\r
-        } else if (_selectedTime > _endTime) {\r
-            _selectedTime = _endTime;\r
-        }\r
-        _stateCtrl.refreshData(traces);\r
-        _timeScaleCtrl.redraw();\r
-        adjustVerticalScrollBar();\r
-    }\r
-\r
-    /**\r
-     * Callback for when this view is focused\r
-     */\r
-    public void setFocus() {\r
-        if (null != _stateCtrl) {\r
-            _stateCtrl.setFocus();\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Get the current focus status of this view.\r
-     *\r
-     * @return If the view is currently focused, or not\r
-     */\r
-    public boolean isInFocus() {\r
-        return _stateCtrl.isInFocus();\r
-    }\r
-\r
-    /**\r
-     * Get the view's current selection\r
-     *\r
-     * @return The entry that is selected\r
-     */\r
-    public ITimeGraphEntry getSelection() {\r
-        return _stateCtrl.getSelectedTrace();\r
-    }\r
-\r
-    /**\r
-     * Get the index of the current selection\r
-     *\r
-     * @return The index\r
-     */\r
-    public int getSelectionIndex() {\r
-        return _stateCtrl.getSelectedIndex();\r
-    }\r
-\r
-    @Override\r
-    public long getTime0() {\r
-        return _time0;\r
-    }\r
-\r
-    @Override\r
-    public long getTime1() {\r
-        return _time1;\r
-    }\r
-\r
-    @Override\r
-    public long getMinTimeInterval() {\r
-        return _minTimeInterval;\r
-    }\r
-\r
-    @Override\r
-    public int getNameSpace() {\r
-        return _nameWidth;\r
-    }\r
-\r
-    @Override\r
-    public void setNameSpace(int width) {\r
-        _nameWidth = width;\r
-        width = _stateCtrl.getClientArea().width;\r
-        if (_nameWidth > width - 6) {\r
-            _nameWidth = width - 6;\r
-        }\r
-        if (_nameWidth < 6) {\r
-            _nameWidth = 6;\r
-        }\r
-        _stateCtrl.adjustScrolls();\r
-        _stateCtrl.redraw();\r
-        _timeScaleCtrl.redraw();\r
-    }\r
-\r
-    @Override\r
-    public int getTimeSpace() {\r
-        int w = _stateCtrl.getClientArea().width;\r
-        return w - _nameWidth;\r
-    }\r
-\r
-    @Override\r
-    public long getSelectedTime() {\r
-        return _selectedTime;\r
-    }\r
-\r
-    @Override\r
-    public long getBeginTime() {\r
-        return _beginTime;\r
-    }\r
-\r
-    @Override\r
-    public long getEndTime() {\r
-        return _endTime;\r
-    }\r
-\r
-    @Override\r
-    public long getMaxTime() {\r
-        return _time1_;\r
-    }\r
-\r
-    @Override\r
-    public long getMinTime() {\r
-        return _time0_;\r
-    }\r
-\r
-    /*\r
-     * (non-Javadoc)\r
-     *\r
-     * @see\r
-     * org.eclipse.linuxtools.tmf.ui.viewers.timeAnalysis.widgets.ITimeDataProvider\r
-     * #setStartFinishTimeNotify(long, long)\r
-     */\r
-    @Override\r
-    public void setStartFinishTimeNotify(long time0, long time1) {\r
-        setStartFinishTime(time0, time1);\r
-        notifyRangeListeners(time0, time1);\r
-    }\r
-\r
-\r
-    /* (non-Javadoc)\r
-     * @see org.eclipse.linuxtools.tmf.ui.viewers.timeAnalysis.widgets.ITimeDataProvider#notifyStartFinishTime()\r
-     */\r
-    @Override\r
-    public void notifyStartFinishTime() {\r
-        notifyRangeListeners(_time0, _time1);\r
-    }\r
-\r
-    /*\r
-     * (non-Javadoc)\r
-     *\r
-     * @see\r
-     * org.eclipse.linuxtools.tmf.ui.viewers.timeAnalysis.widgets.ITimeDataProvider\r
-     * #setStartFinishTime(long, long)\r
-     */\r
-    @Override\r
-    public void setStartFinishTime(long time0, long time1) {\r
-        _time0 = time0;\r
-        if (_time0 < _time0_) {\r
-            _time0 = _time0_;\r
-        }\r
-        if (_time0 > _time1_) {\r
-            _time0 = _time1_;\r
-        }\r
-        _time1 = time1;\r
-        if (_time1 < _time0_) {\r
-            _time1 = _time0_;\r
-        }\r
-        if (_time1 > _time1_) {\r
-            _time1 = _time1_;\r
-        }\r
-        if (_time1 - _time0 < _minTimeInterval) {\r
-            _time1 = Math.min(_time1_, _time0 + _minTimeInterval);\r
-        }\r
-        _timeRangeFixed = true;\r
-        _stateCtrl.adjustScrolls();\r
-        _stateCtrl.redraw();\r
-        _timeScaleCtrl.redraw();\r
-    }\r
-\r
-    /**\r
-     * Set the time bounds to the provided values\r
-     *\r
-     * @param beginTime\r
-     *            The start time of the window\r
-     * @param endTime\r
-     *            The end time\r
-     */\r
-    public void setTimeBounds(long beginTime, long endTime) {\r
-        _beginTime = beginTime;\r
-        _endTime = endTime;\r
-        _time0_ = beginTime;\r
-        _time1_ = endTime;\r
-        _stateCtrl.adjustScrolls();\r
-    }\r
-\r
-    @Override\r
-    public void resetStartFinishTime() {\r
-        setStartFinishTimeNotify(_time0_, _time1_);\r
-        _timeRangeFixed = false;\r
-    }\r
-\r
-    @Override\r
-    public void setSelectedTimeNotify(long time, boolean ensureVisible) {\r
-        setSelectedTimeInt(time, ensureVisible, true);\r
-    }\r
-\r
-    @Override\r
-    public void setSelectedTime(long time, boolean ensureVisible) {\r
-        setSelectedTimeInt(time, ensureVisible, false);\r
-    }\r
-\r
-    private void setSelectedTimeInt(long time, boolean ensureVisible, boolean doNotify) {\r
-        long time0 = _time0;\r
-        long time1 = _time1;\r
-        if (ensureVisible) {\r
-            long timeSpace = (long) ((_time1 - _time0) * .02);\r
-            long timeMid = (long) ((_time1 - _time0) * .5);\r
-            if (time < _time0 + timeSpace) {\r
-                long dt = _time0 - time + timeMid;\r
-                _time0 -= dt;\r
-                _time1 -= dt;\r
-            } else if (time > _time1 - timeSpace) {\r
-                long dt = time - _time1 + timeMid;\r
-                _time0 += dt;\r
-                _time1 += dt;\r
-            }\r
-            if (_time0 < _time0_) {\r
-                _time1 = Math.min(_time1_, _time1 + (_time0_ - _time0));\r
-                _time0 = _time0_;\r
-            } else if (_time1 > _time1_) {\r
-                _time0 = Math.max(_time0_, _time0 - (_time1 - _time1_));\r
-                _time1 = _time1_;\r
-            }\r
-        }\r
-        if (_time1 - _time0 < _minTimeInterval) {\r
-            _time1 = Math.min(_time1_, _time0 + _minTimeInterval);\r
-        }\r
-        _stateCtrl.adjustScrolls();\r
-        _stateCtrl.redraw();\r
-        _timeScaleCtrl.redraw();\r
-\r
-\r
-        boolean notifySelectedTime = (time != _selectedTime);\r
-        _selectedTime = time;\r
-\r
-        if (doNotify && ((time0 != _time0) || (time1 != _time1))) {\r
-            notifyRangeListeners(_time0, _time1);\r
-        }\r
-\r
-        if (doNotify && notifySelectedTime) {\r
-            notifyTimeListeners(_selectedTime);\r
-        }\r
-    }\r
-\r
-    @Override\r
-    public void widgetDefaultSelected(SelectionEvent e) {\r
-        if (_selectedEntry != getSelection()) {\r
-            _selectedEntry = getSelection();\r
-            notifySelectionListeners(_selectedEntry);\r
-        }\r
-    }\r
-\r
-    @Override\r
-    public void widgetSelected(SelectionEvent e) {\r
-        if (_selectedEntry != getSelection()) {\r
-            _selectedEntry = getSelection();\r
-            notifySelectionListeners(_selectedEntry);\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Callback for when the next event is selected\r
-     */\r
-    public void selectNextEvent() {\r
-        _stateCtrl.selectNextEvent();\r
-        adjustVerticalScrollBar();\r
-    }\r
-\r
-    /**\r
-     * Callback for when the previous event is selected\r
-     */\r
-    public void selectPrevEvent() {\r
-        _stateCtrl.selectPrevEvent();\r
-        adjustVerticalScrollBar();\r
-    }\r
-\r
-    /**\r
-     * Callback for when the next item is selected\r
-     */\r
-    public void selectNextItem() {\r
-        _stateCtrl.selectNextTrace();\r
-        adjustVerticalScrollBar();\r
-    }\r
-\r
-    /**\r
-     * Callback for when the previous item is selected\r
-     */\r
-    public void selectPrevItem() {\r
-        _stateCtrl.selectPrevTrace();\r
-        adjustVerticalScrollBar();\r
-    }\r
-\r
-    /**\r
-     * Callback for the show legend action\r
-     */\r
-    public void showLegend() {\r
-        if (_dataViewer == null || _dataViewer.isDisposed()) {\r
-            return;\r
-        }\r
-\r
-        TimeGraphLegend.open(_dataViewer.getShell(), fTimeGraphProvider);\r
-    }\r
-\r
-    /**\r
-     * Callback for the Zoom In action\r
-     */\r
-    public void zoomIn() {\r
-        _stateCtrl.zoomIn();\r
-    }\r
-\r
-    /**\r
-     * Callback for the Zoom Out action\r
-     */\r
-    public void zoomOut() {\r
-        _stateCtrl.zoomOut();\r
-    }\r
-\r
-    private String getPreferenceString(String string) {\r
-        return getViewTypeStr() + "." + string; //$NON-NLS-1$\r
-    }\r
-\r
-    /**\r
-     * Add a selection listener\r
-     *\r
-     * @param listener\r
-     *            The listener to add\r
-     */\r
-    public void addSelectionListener(ITimeGraphSelectionListener listener) {\r
-        fSelectionListeners.add(listener);\r
-    }\r
-\r
-    /**\r
-     * Remove a selection listener\r
-     *\r
-     * @param listener\r
-     *            The listener to remove\r
-     */\r
-    public void removeSelectionListener(ITimeGraphSelectionListener listener) {\r
-        fSelectionListeners.remove(listener);\r
-    }\r
-\r
-    private void notifySelectionListeners(ITimeGraphEntry selection) {\r
-        TimeGraphSelectionEvent event = new TimeGraphSelectionEvent(this, selection);\r
-\r
-        for (ITimeGraphSelectionListener listener : fSelectionListeners) {\r
-            listener.selectionChanged(event);\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Add a time listener\r
-     *\r
-     * @param listener\r
-     *            The listener to add\r
-     */\r
-    public void addTimeListener(ITimeGraphTimeListener listener) {\r
-        fTimeListeners.add(listener);\r
-    }\r
-\r
-    /**\r
-     * Remove a time listener\r
-     *\r
-     * @param listener\r
-     *            The listener to remove\r
-     */\r
-    public void removeTimeListener(ITimeGraphTimeListener listener) {\r
-        fTimeListeners.remove(listener);\r
-    }\r
-\r
-    private void notifyTimeListeners(long time) {\r
-        TimeGraphTimeEvent event = new TimeGraphTimeEvent(this, time);\r
-\r
-        for (ITimeGraphTimeListener listener : fTimeListeners) {\r
-            listener.timeSelected(event);\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Add a range listener\r
-     *\r
-     * @param listener\r
-     *            The listener to add\r
-     */\r
-    public void addRangeListener(ITimeGraphRangeListener listener) {\r
-        fRangeListeners.add(listener);\r
-    }\r
-\r
-    /**\r
-     * Remove a range listener\r
-     *\r
-     * @param listener\r
-     *            The listener to remove\r
-     */\r
-    public void removeRangeListener(ITimeGraphRangeListener listener) {\r
-        fRangeListeners.remove(listener);\r
-    }\r
-\r
-    private void notifyRangeListeners(long startTime, long endTime) {\r
-        // Check if the time has actually changed from last notification\r
-        if (startTime != _time0_extSynch || endTime != _time1_extSynch) {\r
-            // Notify Time Scale Selection Listeners\r
-            TimeGraphRangeUpdateEvent event = new TimeGraphRangeUpdateEvent(this, startTime, endTime);\r
-\r
-            for (ITimeGraphRangeListener listener : fRangeListeners) {\r
-                listener.timeRangeUpdated(event);\r
-            }\r
-\r
-            // update external synch timers\r
-            updateExtSynchTimers();\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Callback to set a selected event in the view\r
-     *\r
-     * @param event\r
-     *            The event that was selected\r
-     * @param source\r
-     *            The source of this selection event\r
-     */\r
-    public void setSelectedEvent(ITimeEvent event, Object source) {\r
-        if (event == null || source == this) {\r
-            return;\r
-        }\r
-        _selectedEntry = event.getEntry();\r
-        _stateCtrl.selectItem(_selectedEntry, false);\r
-\r
-        setSelectedTimeInt(event.getTime(), true, true);\r
-        adjustVerticalScrollBar();\r
-    }\r
-\r
-    /**\r
-     * Set the seeked time of a trace\r
-     *\r
-     * @param trace\r
-     *            The trace that was seeked\r
-     * @param time\r
-     *            The target time\r
-     * @param source\r
-     *            The source of this seek event\r
-     */\r
-    public void setSelectedTraceTime(ITimeGraphEntry trace, long time, Object source) {\r
-        if (trace == null || source == this) {\r
-            return;\r
-        }\r
-        _selectedEntry = trace;\r
-        _stateCtrl.selectItem(trace, false);\r
-\r
-        setSelectedTimeInt(time, true, true);\r
-    }\r
-\r
-    /**\r
-     * Callback for a trace selection\r
-     *\r
-     * @param trace\r
-     *            The trace that was selected\r
-     */\r
-    public void setSelection(ITimeGraphEntry trace) {\r
-        _selectedEntry = trace;\r
-        _stateCtrl.selectItem(trace, false);\r
-        adjustVerticalScrollBar();\r
-    }\r
-\r
-    /**\r
-     * Callback for a time window selection\r
-     *\r
-     * @param time0\r
-     *            Start time of the range\r
-     * @param time1\r
-     *            End time of the range\r
-     * @param source\r
-     *            Source of the event\r
-     */\r
-    public void setSelectVisTimeWindow(long time0, long time1, Object source) {\r
-        if (source == this) {\r
-            return;\r
-        }\r
-\r
-        setStartFinishTime(time0, time1);\r
-\r
-        // update notification time values since we are now in synch with the\r
-        // external application\r
-        updateExtSynchTimers();\r
-    }\r
-\r
-    /**\r
-     * update the cache timers used to identify the need to send a time window\r
-     * update to external registered listeners\r
-     */\r
-    private void updateExtSynchTimers() {\r
-        // last time notification cache\r
-        _time0_extSynch = _time0;\r
-        _time1_extSynch = _time1;\r
-    }\r
-\r
-    /**\r
-     * Set the calendar format\r
-     *\r
-     * @param toAbsoluteCaltime\r
-     *            True for absolute time, false for relative\r
-     */\r
-    public void setTimeCalendarFormat(boolean toAbsoluteCaltime) {\r
-        calendarTimeFormat = toAbsoluteCaltime;\r
-    }\r
-\r
-    @Override\r
-    public boolean isCalendarFormat() {\r
-        return calendarTimeFormat;\r
-    }\r
-\r
-    /**\r
-     * Retrieve the border width\r
-     *\r
-     * @return The width\r
-     */\r
-    public int getBorderWidth() {\r
-        return borderWidth;\r
-    }\r
-\r
-    /**\r
-     * Set the border width\r
-     *\r
-     * @param borderWidth\r
-     *            The width\r
-     */\r
-    public void setBorderWidth(int borderWidth) {\r
-        if (borderWidth > -1) {\r
-            this.borderWidth = borderWidth;\r
-            GridLayout gl = (GridLayout)_dataViewer.getLayout();\r
-            gl.marginHeight = borderWidth;\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Retrieve the height of the header\r
-     *\r
-     * @return The height\r
-     */\r
-    public int getHeaderHeight() {\r
-        return timeScaleHeight;\r
-    }\r
-\r
-    /**\r
-     * Set the height of the header\r
-     *\r
-     * @param headerHeight\r
-     *            The height to set\r
-     */\r
-    public void setHeaderHeight(int headerHeight) {\r
-        if (headerHeight > -1) {\r
-            this.timeScaleHeight = headerHeight;\r
-            _timeScaleCtrl.setHeight(headerHeight);\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Retrieve the height of an item row\r
-     *\r
-     * @return The height\r
-     */\r
-    public int getItemHeight() {\r
-        if (_stateCtrl != null) {\r
-            return _stateCtrl.getItemHeight();\r
-        }\r
-        return 0;\r
-    }\r
-\r
-    /**\r
-     * Set the height of an item row\r
-     *\r
-     * @param rowHeight\r
-     *            The height to set\r
-     */\r
-    public void setItemHeight(int rowHeight) {\r
-        if (_stateCtrl != null) {\r
-            _stateCtrl.setItemHeight(rowHeight);\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Set the minimum item width\r
-     *\r
-     * @param width\r
-     *            The min width\r
-     */\r
-    public void setMinimumItemWidth(int width) {\r
-        if (_stateCtrl != null) {\r
-            _stateCtrl.setMinimumItemWidth(width);\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Set the width for the name column\r
-     *\r
-     * @param width The width\r
-     */\r
-    public void setNameWidthPref(int width) {\r
-        _nameWidthPref = width;\r
-        if (width == 0) {\r
-            _minNameWidth = 0;\r
-            _nameWidth = 0;\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Retrieve the configure width for the name column\r
-     *\r
-     * @param width\r
-     *            Unused?\r
-     * @return The width\r
-     */\r
-    public int getNameWidthPref(int width) {\r
-        return _nameWidthPref;\r
-    }\r
-\r
-    /**\r
-     * Returns the primary control associated with this viewer.\r
-     *\r
-     * @return the SWT control which displays this viewer's content\r
-     */\r
-    public Control getControl() {\r
-        return _dataViewer;\r
-    }\r
-\r
-    /**\r
-     * Returns the time graph control associated with this viewer.\r
-     *\r
-     * @return the time graph control\r
-     */\r
-    TimeGraphControl getTimeGraphControl() {\r
-        return _stateCtrl;\r
-    }\r
-\r
-    /**\r
-     * Returns the time graph scale associated with this viewer.\r
-     *\r
-     * @return the time graph scale\r
-     */\r
-    TimeGraphScale getTimeGraphScale() {\r
-        return _timeScaleCtrl;\r
-    }\r
-\r
-    /**\r
-     * Get the selection provider\r
-     *\r
-     * @return the selection provider\r
-     */\r
-    public ISelectionProvider getSelectionProvider() {\r
-        return _stateCtrl;\r
-    }\r
-\r
-    /**\r
-     * Wait for the cursor\r
-     *\r
-     * @param waitInd\r
-     *            Wait indefinitely?\r
-     */\r
-    public void waitCursor(boolean waitInd) {\r
-        _stateCtrl.waitCursor(waitInd);\r
-    }\r
-\r
-    /**\r
-     * Get the horizontal scroll bar object\r
-     *\r
-     * @return The scroll bar\r
-     */\r
-    public ScrollBar getHorizontalBar() {\r
-        return _stateCtrl.getHorizontalBar();\r
-    }\r
-\r
-    /**\r
-     * Get the vertical scroll bar object\r
-     *\r
-     * @return The scroll bar\r
-     */\r
-    public Slider getVerticalBar() {\r
-        return _verticalScrollBar;\r
-    }\r
-\r
-    /**\r
-     * Set the given index as the top one\r
-     *\r
-     * @param index\r
-     *            The index that will go to the top\r
-     */\r
-    public void setTopIndex(int index) {\r
-        _stateCtrl.setTopIndex(index);\r
-        adjustVerticalScrollBar();\r
-    }\r
-\r
-    /**\r
-     * Retrieve the current top index\r
-     *\r
-     * @return The top index\r
-     */\r
-    public int getTopIndex() {\r
-        return _stateCtrl.getTopIndex();\r
-    }\r
-\r
-    /**\r
-     * Set the expanded state of an entry\r
-     *\r
-     * @param entry\r
-     *            The entry to expand/collapse\r
-     * @param expanded\r
-     *            True for expanded, false for collapsed\r
-     */\r
-    public void setExpandedState(ITimeGraphEntry entry, boolean expanded) {\r
-        _stateCtrl.setExpandedState(entry, expanded);\r
-        adjustVerticalScrollBar();\r
-    }\r
-\r
-    /**\r
-     * Get the number of sub-elements when expanded\r
-     *\r
-     * @return The element count\r
-     */\r
-    public int getExpandedElementCount() {\r
-        return _stateCtrl.getExpandedElementCount();\r
-    }\r
-\r
-    /**\r
-     * Get the sub-elements\r
-     *\r
-     * @return The array of entries that are below this one\r
-     */\r
-    public ITimeGraphEntry[] getExpandedElements() {\r
-        return _stateCtrl.getExpandedElements();\r
-    }\r
-\r
-    /**\r
-     * Add a tree listener\r
-     *\r
-     * @param listener\r
-     *            The listener to add\r
-     */\r
-    public void addTreeListener(ITimeGraphTreeListener listener) {\r
-        _stateCtrl.addTreeListener(listener);\r
-    }\r
-\r
-    /**\r
-     * Remove a tree listener\r
-     *\r
-     * @param listener\r
-     *            The listener to remove\r
-     */\r
-    public void removeTreeListener(ITimeGraphTreeListener listener) {\r
-        _stateCtrl.removeTreeListener(listener);\r
-    }\r
-\r
-    /**\r
-     * Get the reset scale action.\r
-     *\r
-     * @return The Action object\r
-     */\r
-    public Action getResetScaleAction() {\r
-        if (resetScale == null) {\r
-            // resetScale\r
-            resetScale = new Action() {\r
-                @Override\r
-                public void run() {\r
-                    resetStartFinishTime();\r
-                }\r
-            };\r
-            resetScale.setText(Messages.TmfTimeGraphViewer_ResetScaleActionNameText);\r
-            resetScale.setToolTipText(Messages.TmfTimeGraphViewer_ResetScaleActionToolTipText);\r
-            resetScale.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_HOME_MENU));\r
-        }\r
-        return resetScale;\r
-    }\r
-\r
-    /**\r
-     * Get the show legend action.\r
-     *\r
-     * @return The Action object\r
-     */\r
-    public Action getShowLegendAction() {\r
-        if (showLegendAction == null) {\r
-            // showLegend\r
-            showLegendAction = new Action() {\r
-                @Override\r
-                public void run() {\r
-                    showLegend();\r
-                }\r
-            };\r
-            showLegendAction.setText(Messages.TmfTimeGraphViewer_LegendActionNameText);\r
-            showLegendAction.setToolTipText(Messages.TmfTimeGraphViewer_LegendActionToolTipText);\r
-            showLegendAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_SHOW_LEGEND));\r
-        }\r
-\r
-        return showLegendAction;\r
-    }\r
-\r
-    /**\r
-     * Get the the next event action.\r
-     *\r
-     * @return The action object\r
-     */\r
-    public Action getNextEventAction() {\r
-        if (nextEventAction == null) {\r
-            nextEventAction = new Action() {\r
-                @Override\r
-                public void run() {\r
-                    selectNextEvent();\r
-                }\r
-            };\r
-\r
-            nextEventAction.setText(Messages.TmfTimeGraphViewer_NextEventActionNameText);\r
-            nextEventAction.setToolTipText(Messages.TmfTimeGraphViewer_NextEventActionToolTipText);\r
-            nextEventAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_NEXT_EVENT));\r
-        }\r
-\r
-        return nextEventAction;\r
-    }\r
-\r
-    /**\r
-     * Get the previous event action.\r
-     *\r
-     * @return The Action object\r
-     */\r
-    public Action getPreviousEventAction() {\r
-        if (prevEventAction == null) {\r
-            prevEventAction = new Action() {\r
-                @Override\r
-                public void run() {\r
-                    selectPrevEvent();\r
-                }\r
-            };\r
-\r
-            prevEventAction.setText(Messages.TmfTimeGraphViewer_PreviousEventActionNameText);\r
-            prevEventAction.setToolTipText(Messages.TmfTimeGraphViewer_PreviousEventActionToolTipText);\r
-            prevEventAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_PREV_EVENT));\r
-        }\r
-\r
-        return prevEventAction;\r
-    }\r
-\r
-    /**\r
-     * Get the next item action.\r
-     *\r
-     * @return The Action object\r
-     */\r
-    public Action getNextItemAction() {\r
-        if (nextItemAction == null) {\r
-\r
-            nextItemAction = new Action() {\r
-                @Override\r
-                public void run() {\r
-                    selectNextItem();\r
-                }\r
-            };\r
-            nextItemAction.setText(Messages.TmfTimeGraphViewer_NextItemActionNameText);\r
-            nextItemAction.setToolTipText(Messages.TmfTimeGraphViewer_NextItemActionToolTipText);\r
-            nextItemAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_NEXT_ITEM));\r
-        }\r
-        return nextItemAction;\r
-    }\r
-\r
-    /**\r
-     * Get the previous item action.\r
-     *\r
-     * @return The Action object\r
-     */\r
-    public Action getPreviousItemAction() {\r
-        if (previousItemAction == null) {\r
-\r
-            previousItemAction = new Action() {\r
-                @Override\r
-                public void run() {\r
-                    selectPrevItem();\r
-                }\r
-            };\r
-            previousItemAction.setText(Messages.TmfTimeGraphViewer_PreviousItemActionNameText);\r
-            previousItemAction.setToolTipText(Messages.TmfTimeGraphViewer_PreviousItemActionToolTipText);\r
-            previousItemAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_PREV_ITEM));\r
-        }\r
-        return previousItemAction;\r
-    }\r
-\r
-    /**\r
-     * Get the zoom in action\r
-     *\r
-     * @return The Action object\r
-     */\r
-    public Action getZoomInAction() {\r
-        if (zoomInAction == null) {\r
-            zoomInAction = new Action() {\r
-                @Override\r
-                public void run() {\r
-                    zoomIn();\r
-                }\r
-            };\r
-            zoomInAction.setText(Messages.TmfTimeGraphViewer_ZoomInActionNameText);\r
-            zoomInAction.setToolTipText(Messages.TmfTimeGraphViewer_ZoomInActionToolTipText);\r
-            zoomInAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_ZOOM_IN_MENU));\r
-        }\r
-        return zoomInAction;\r
-    }\r
-\r
-    /**\r
-     * Get the zoom out action\r
-     *\r
-     * @return The Action object\r
-     */\r
-    public Action getZoomOutAction() {\r
-        if (zoomOutAction == null) {\r
-            zoomOutAction = new Action() {\r
-                @Override\r
-                public void run() {\r
-                    zoomOut();\r
-                }\r
-            };\r
-            zoomOutAction.setText(Messages.TmfTimeGraphViewer_ZoomOutActionNameText);\r
-            zoomOutAction.setToolTipText(Messages.TmfTimeGraphViewer_ZoomOutActionToolTipText);\r
-            zoomOutAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_ZOOM_OUT_MENU));\r
-        }\r
-        return zoomOutAction;\r
-    }\r
-\r
-\r
-    private void adjustVerticalScrollBar() {\r
-        int topIndex = _stateCtrl.getTopIndex();\r
-        int countPerPage = _stateCtrl.countPerPage();\r
-        int expandedElementCount = _stateCtrl.getExpandedElementCount();\r
-        if (topIndex + countPerPage > expandedElementCount) {\r
-            _stateCtrl.setTopIndex(Math.max(0, expandedElementCount - countPerPage));\r
-        }\r
-\r
-        int selection = _stateCtrl.getTopIndex();\r
-        int min = 0;\r
-        int max = Math.max(1, expandedElementCount - 1);\r
-        int thumb = Math.min(max, Math.max(1, countPerPage - 1));\r
-        int increment = 1;\r
-        int pageIncrement = Math.max(1, countPerPage);\r
-        _verticalScrollBar.setValues(selection, min, max, thumb, increment, pageIncrement);\r
-    }\r
-\r
-\r
-\r
-}\r
+/*****************************************************************************
+ * Copyright (c) 2007, 2008 Intel Corporation, 2009, 2010, 2011, 2012 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:
+ *   Intel Corporation - Initial API and implementation
+ *   Ruslan A. Scherbakov, Intel - Initial API and implementation
+ *   Alexander N. Alexeev, Intel - Add monitors statistics support
+ *   Alvaro Sanchez-Leon - Adapted for TMF
+ *   Patrick Tasse - Refactoring
+ *
+ *****************************************************************************/
+
+package org.eclipse.linuxtools.tmf.ui.widgets.timegraph;
+
+import java.util.ArrayList;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.viewers.ISelectionProvider;
+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.TimeGraphLegend;
+import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.model.ITimeEvent;
+import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
+import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.widgets.ITimeDataProvider;
+import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.widgets.TimeGraphColorScheme;
+import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.widgets.TimeGraphControl;
+import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.widgets.TimeGraphScale;
+import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.widgets.TimeGraphTooltipHandler;
+import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.widgets.Utils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseWheelListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.ScrollBar;
+import org.eclipse.swt.widgets.Slider;
+
+/**
+ * Generic time graph viewer implementation
+ *
+ * @version 1.0
+ * @author Patrick Tasse, and others
+ */
+public class TimeGraphViewer implements ITimeDataProvider, SelectionListener {
+
+    /** vars */
+    private long _minTimeInterval;
+    private long _selectedTime;
+    private ITimeGraphEntry _selectedEntry;
+    private long _beginTime;
+    private long _endTime;
+    private long _time0;
+    private long _time1;
+    private long _time0_;
+    private long _time1_;
+    private long _time0_extSynch = 0;
+    private long _time1_extSynch = 0;
+    private boolean _timeRangeFixed;
+    private int _nameWidthPref = 200;
+    private int _minNameWidth = 6;
+    private int _nameWidth;
+    private Composite _dataViewer;
+
+    private TimeGraphControl _stateCtrl;
+    private TimeGraphScale _timeScaleCtrl;
+    private Slider _verticalScrollBar;
+    private TimeGraphTooltipHandler _threadTip;
+    private TimeGraphColorScheme _colors;
+    private ITimeGraphPresentationProvider fTimeGraphProvider;
+
+    ArrayList<ITimeGraphSelectionListener> fSelectionListeners = new ArrayList<ITimeGraphSelectionListener>();
+    ArrayList<ITimeGraphTimeListener> fTimeListeners = new ArrayList<ITimeGraphTimeListener>();
+    ArrayList<ITimeGraphRangeListener> fRangeListeners = new ArrayList<ITimeGraphRangeListener>();
+
+    // Calender Time format, using Epoch reference or Relative time
+    // format(default
+    private boolean calendarTimeFormat = false;
+    private int borderWidth = 0;
+    private int timeScaleHeight = 22;
+
+    private Action resetScale;
+    private Action showLegendAction;
+    private Action nextEventAction;
+    private Action prevEventAction;
+    private Action nextItemAction;
+    private Action previousItemAction;
+    private Action zoomInAction;
+    private Action zoomOutAction;
+
+    /**
+     * Standard constructor
+     *
+     * @param parent
+     *            The parent UI composite object
+     * @param style
+     *            The style to use
+     */
+    public TimeGraphViewer(Composite parent, int style) {
+        createDataViewer(parent, style);
+    }
+
+    /**
+     * Sets the timegraph provider used by this timegraph viewer.
+     *
+     * @param timeGraphProvider the timegraph provider
+     */
+    public void setTimeGraphProvider(ITimeGraphPresentationProvider timeGraphProvider) {
+        fTimeGraphProvider = timeGraphProvider;
+        _stateCtrl.setTimeGraphProvider(timeGraphProvider);
+        _threadTip = new TimeGraphTooltipHandler(_dataViewer.getShell(), fTimeGraphProvider, this);
+        _threadTip.activateHoverHelp(_stateCtrl);
+    }
+
+    /**
+     * Sets or clears the input for this time graph viewer.
+     * The input array should only contain top-level elements.
+     *
+     * @param input the input of this time graph viewer, or <code>null</code> if none
+     */
+    public void setInput(ITimeGraphEntry[] input) {
+        if (null != _stateCtrl) {
+            if (null == input) {
+                input = new ITimeGraphEntry[0];
+            }
+            setTimeRange(input);
+            _verticalScrollBar.setEnabled(true);
+            setTopIndex(0);
+            _selectedTime = 0;
+            _selectedEntry = null;
+            refreshAllData(input);
+        }
+    }
+
+    /**
+     * Refresh the view
+     */
+    public void refresh() {
+        setInput(_stateCtrl.getTraces());
+    }
+
+    /**
+     * Callback for when the control is moved
+     *
+     * @param e
+     *            The caller event
+     */
+    public void controlMoved(ControlEvent e) {
+    }
+
+    /**
+     * Callback for when the control is resized
+     *
+     * @param e
+     *            The caller event
+     */
+    public void controlResized(ControlEvent e) {
+        resizeControls();
+    }
+
+    /**
+     * Handler for when the model is updated. Called from the display order in
+     * the API
+     *
+     * @param traces
+     *            The traces in the model
+     * @param start
+     *            The start time
+     * @param end
+     *            The end time
+     * @param updateTimeBounds
+     *            Should we updated the time bounds too
+     */
+    public void modelUpdate(ITimeGraphEntry[] traces, long start,
+            long end, boolean updateTimeBounds) {
+        if (null != _stateCtrl) {
+            //loadOptions();
+            updateInternalData(traces, start, end);
+            if (updateTimeBounds) {
+                _timeRangeFixed = true;
+                // set window to match limits
+                setStartFinishTime(_time0_, _time1_);
+            } else {
+                _stateCtrl.redraw();
+                _timeScaleCtrl.redraw();
+            }
+        }
+    }
+
+    protected String getViewTypeStr() {
+        return "viewoption.threads"; //$NON-NLS-1$
+    }
+
+    int getMarginWidth(int idx) {
+        return 0;
+    }
+
+    int getMarginHeight(int idx) {
+        return 0;
+    }
+
+    void loadOptions() {
+        _minTimeInterval = 1;
+        _selectedTime = -1;
+        _nameWidth = Utils.loadIntOption(getPreferenceString("namewidth"), //$NON-NLS-1$
+                _nameWidthPref, _minNameWidth, 1000);
+    }
+
+    void saveOptions() {
+        Utils.saveIntOption(getPreferenceString("namewidth"), _nameWidth); //$NON-NLS-1$
+    }
+
+    protected Control createDataViewer(Composite parent, int style) {
+        loadOptions();
+        _colors = new TimeGraphColorScheme();
+        _dataViewer = new Composite(parent, style) {
+            @Override
+            public void redraw() {
+                _timeScaleCtrl.redraw();
+                _stateCtrl.redraw();
+                super.redraw();
+            }
+        };
+        GridLayout gl = new GridLayout(2, false);
+        gl.marginHeight = borderWidth;
+        gl.marginWidth = 0;
+        gl.verticalSpacing = 0;
+        gl.horizontalSpacing = 0;
+        _dataViewer.setLayout(gl);
+
+        _timeScaleCtrl = new TimeGraphScale(_dataViewer, _colors);
+        _timeScaleCtrl.setTimeProvider(this);
+        _timeScaleCtrl.setLayoutData(new GridData(SWT.FILL, SWT.DEFAULT, true, false));
+        _timeScaleCtrl.setHeight(timeScaleHeight);
+
+        _verticalScrollBar = new Slider(_dataViewer, SWT.VERTICAL | SWT.NO_FOCUS);
+        _verticalScrollBar.setLayoutData(new GridData(SWT.DEFAULT, SWT.FILL, false, true, 1, 2));
+        _verticalScrollBar.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                setTopIndex(_verticalScrollBar.getSelection());
+            }
+        });
+        _verticalScrollBar.setEnabled(false);
+
+        _stateCtrl = createTimeGraphControl();
+
+        _stateCtrl.setTimeProvider(this);
+        _stateCtrl.addSelectionListener(this);
+        _stateCtrl.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 2));
+        _stateCtrl.addMouseWheelListener(new MouseWheelListener() {
+            @Override
+            public void mouseScrolled(MouseEvent e) {
+                adjustVerticalScrollBar();
+            }
+        });
+        _stateCtrl.addKeyListener(new KeyAdapter() {
+            @Override
+            public void keyPressed(KeyEvent e) {
+                adjustVerticalScrollBar();
+            }
+        });
+
+        Composite filler = new Composite(_dataViewer, SWT.NONE);
+        GridData gd = new GridData(SWT.DEFAULT, SWT.DEFAULT, false, false);
+        gd.heightHint = _stateCtrl.getHorizontalBar().getSize().y;
+        filler.setLayoutData(gd);
+        filler.setLayout(new FillLayout());
+
+        _stateCtrl.addControlListener(new ControlAdapter() {
+            @Override
+            public void controlResized(ControlEvent event) {
+                resizeControls();
+            }
+        });
+        resizeControls();
+        _dataViewer.update();
+        adjustVerticalScrollBar();
+        return _dataViewer;
+    }
+
+    /**
+     * Dispose the view.
+     */
+    public void dispose() {
+        saveOptions();
+        _stateCtrl.dispose();
+        _dataViewer.dispose();
+        _colors.dispose();
+    }
+
+    protected TimeGraphControl createTimeGraphControl() {
+        return new TimeGraphControl(_dataViewer, _colors);
+    }
+
+    /**
+     * Resize the controls
+     */
+    public void resizeControls() {
+        Rectangle r = _dataViewer.getClientArea();
+        if (r.isEmpty()) {
+            return;
+        }
+
+        int width = r.width;
+        if (_nameWidth > width - _minNameWidth) {
+            _nameWidth = width - _minNameWidth;
+        }
+        if (_nameWidth < _minNameWidth) {
+            _nameWidth = _minNameWidth;
+        }
+        adjustVerticalScrollBar();
+    }
+
+    /**
+     * Try to set most convenient time range for display.
+     *
+     * @param traces
+     *            The traces in the model
+     */
+    public void setTimeRange(ITimeGraphEntry traces[]) {
+        _endTime = 0;
+        _beginTime = -1;
+        for (int i = 0; i < traces.length; i++) {
+            ITimeGraphEntry entry = traces[i];
+            if (entry.getEndTime() >= entry.getStartTime() && entry.getEndTime() > 0) {
+                if (_beginTime < 0 || entry.getStartTime() < _beginTime) {
+                    _beginTime = entry.getStartTime();
+                }
+                if (entry.getEndTime() > _endTime) {
+                    _endTime = entry.getEndTime();
+                }
+            }
+        }
+
+        if (_beginTime < 0) {
+            _beginTime = 0;
+        }
+    }
+
+    /**
+     * Recalculate the time bounds
+     */
+    public void setTimeBounds() {
+        //_time0_ = _beginTime - (long) ((_endTime - _beginTime) * 0.02);
+        _time0_ = _beginTime;
+        if (_time0_ < 0) {
+            _time0_ = 0;
+        }
+        // _time1_ = _time0_ + (_endTime - _time0_) * 1.05;
+        _time1_ = _endTime;
+        // _time0_ = Math.floor(_time0_);
+        // _time1_ = Math.ceil(_time1_);
+        if (!_timeRangeFixed) {
+            _time0 = _time0_;
+            _time1 = _time1_;
+        }
+        if (_time1 - _time0 < _minTimeInterval) {
+            _time1 = Math.min(_time1_, _time0 + _minTimeInterval);
+        }
+    }
+
+    /**
+     * @param traces
+     * @param start
+     * @param end
+     */
+    void updateInternalData(ITimeGraphEntry[] traces, long start, long end) {
+        if (null == traces) {
+            traces = new ITimeGraphEntry[0];
+        }
+        if ((start == 0 && end == 0) || start < 0 || end < 0) {
+            // Start and end time are unspecified and need to be determined from
+            // individual processes
+            setTimeRange(traces);
+        } else {
+            _beginTime = start;
+            _endTime = end;
+        }
+
+        refreshAllData(traces);
+    }
+
+    /**
+     * @param traces
+     */
+    private void refreshAllData(ITimeGraphEntry[] traces) {
+        setTimeBounds();
+        if (_selectedTime < _beginTime) {
+            _selectedTime = _beginTime;
+        } else if (_selectedTime > _endTime) {
+            _selectedTime = _endTime;
+        }
+        _stateCtrl.refreshData(traces);
+        _timeScaleCtrl.redraw();
+        adjustVerticalScrollBar();
+    }
+
+    /**
+     * Callback for when this view is focused
+     */
+    public void setFocus() {
+        if (null != _stateCtrl) {
+            _stateCtrl.setFocus();
+        }
+    }
+
+    /**
+     * Get the current focus status of this view.
+     *
+     * @return If the view is currently focused, or not
+     */
+    public boolean isInFocus() {
+        return _stateCtrl.isInFocus();
+    }
+
+    /**
+     * Get the view's current selection
+     *
+     * @return The entry that is selected
+     */
+    public ITimeGraphEntry getSelection() {
+        return _stateCtrl.getSelectedTrace();
+    }
+
+    /**
+     * Get the index of the current selection
+     *
+     * @return The index
+     */
+    public int getSelectionIndex() {
+        return _stateCtrl.getSelectedIndex();
+    }
+
+    @Override
+    public long getTime0() {
+        return _time0;
+    }
+
+    @Override
+    public long getTime1() {
+        return _time1;
+    }
+
+    @Override
+    public long getMinTimeInterval() {
+        return _minTimeInterval;
+    }
+
+    @Override
+    public int getNameSpace() {
+        return _nameWidth;
+    }
+
+    @Override
+    public void setNameSpace(int width) {
+        _nameWidth = width;
+        width = _stateCtrl.getClientArea().width;
+        if (_nameWidth > width - 6) {
+            _nameWidth = width - 6;
+        }
+        if (_nameWidth < 6) {
+            _nameWidth = 6;
+        }
+        _stateCtrl.adjustScrolls();
+        _stateCtrl.redraw();
+        _timeScaleCtrl.redraw();
+    }
+
+    @Override
+    public int getTimeSpace() {
+        int w = _stateCtrl.getClientArea().width;
+        return w - _nameWidth;
+    }
+
+    @Override
+    public long getSelectedTime() {
+        return _selectedTime;
+    }
+
+    @Override
+    public long getBeginTime() {
+        return _beginTime;
+    }
+
+    @Override
+    public long getEndTime() {
+        return _endTime;
+    }
+
+    @Override
+    public long getMaxTime() {
+        return _time1_;
+    }
+
+    @Override
+    public long getMinTime() {
+        return _time0_;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.eclipse.linuxtools.tmf.ui.viewers.timeAnalysis.widgets.ITimeDataProvider
+     * #setStartFinishTimeNotify(long, long)
+     */
+    @Override
+    public void setStartFinishTimeNotify(long time0, long time1) {
+        setStartFinishTime(time0, time1);
+        notifyRangeListeners(time0, time1);
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.eclipse.linuxtools.tmf.ui.viewers.timeAnalysis.widgets.ITimeDataProvider#notifyStartFinishTime()
+     */
+    @Override
+    public void notifyStartFinishTime() {
+        notifyRangeListeners(_time0, _time1);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.eclipse.linuxtools.tmf.ui.viewers.timeAnalysis.widgets.ITimeDataProvider
+     * #setStartFinishTime(long, long)
+     */
+    @Override
+    public void setStartFinishTime(long time0, long time1) {
+        _time0 = time0;
+        if (_time0 < _time0_) {
+            _time0 = _time0_;
+        }
+        if (_time0 > _time1_) {
+            _time0 = _time1_;
+        }
+        _time1 = time1;
+        if (_time1 < _time0_) {
+            _time1 = _time0_;
+        }
+        if (_time1 > _time1_) {
+            _time1 = _time1_;
+        }
+        if (_time1 - _time0 < _minTimeInterval) {
+            _time1 = Math.min(_time1_, _time0 + _minTimeInterval);
+        }
+        _timeRangeFixed = true;
+        _stateCtrl.adjustScrolls();
+        _stateCtrl.redraw();
+        _timeScaleCtrl.redraw();
+    }
+
+    /**
+     * Set the time bounds to the provided values
+     *
+     * @param beginTime
+     *            The start time of the window
+     * @param endTime
+     *            The end time
+     */
+    public void setTimeBounds(long beginTime, long endTime) {
+        _beginTime = beginTime;
+        _endTime = endTime;
+        _time0_ = beginTime;
+        _time1_ = endTime;
+        _stateCtrl.adjustScrolls();
+    }
+
+    @Override
+    public void resetStartFinishTime() {
+        setStartFinishTimeNotify(_time0_, _time1_);
+        _timeRangeFixed = false;
+    }
+
+    @Override
+    public void setSelectedTimeNotify(long time, boolean ensureVisible) {
+        setSelectedTimeInt(time, ensureVisible, true);
+    }
+
+    @Override
+    public void setSelectedTime(long time, boolean ensureVisible) {
+        setSelectedTimeInt(time, ensureVisible, false);
+    }
+
+    private void setSelectedTimeInt(long time, boolean ensureVisible, boolean doNotify) {
+        long time0 = _time0;
+        long time1 = _time1;
+        if (ensureVisible) {
+            long timeSpace = (long) ((_time1 - _time0) * .02);
+            long timeMid = (long) ((_time1 - _time0) * .5);
+            if (time < _time0 + timeSpace) {
+                long dt = _time0 - time + timeMid;
+                _time0 -= dt;
+                _time1 -= dt;
+            } else if (time > _time1 - timeSpace) {
+                long dt = time - _time1 + timeMid;
+                _time0 += dt;
+                _time1 += dt;
+            }
+            if (_time0 < _time0_) {
+                _time1 = Math.min(_time1_, _time1 + (_time0_ - _time0));
+                _time0 = _time0_;
+            } else if (_time1 > _time1_) {
+                _time0 = Math.max(_time0_, _time0 - (_time1 - _time1_));
+                _time1 = _time1_;
+            }
+        }
+        if (_time1 - _time0 < _minTimeInterval) {
+            _time1 = Math.min(_time1_, _time0 + _minTimeInterval);
+        }
+        _stateCtrl.adjustScrolls();
+        _stateCtrl.redraw();
+        _timeScaleCtrl.redraw();
+
+
+        boolean notifySelectedTime = (time != _selectedTime);
+        _selectedTime = time;
+
+        if (doNotify && ((time0 != _time0) || (time1 != _time1))) {
+            notifyRangeListeners(_time0, _time1);
+        }
+
+        if (doNotify && notifySelectedTime) {
+            notifyTimeListeners(_selectedTime);
+        }
+    }
+
+    @Override
+    public void widgetDefaultSelected(SelectionEvent e) {
+        if (_selectedEntry != getSelection()) {
+            _selectedEntry = getSelection();
+            notifySelectionListeners(_selectedEntry);
+        }
+    }
+
+    @Override
+    public void widgetSelected(SelectionEvent e) {
+        if (_selectedEntry != getSelection()) {
+            _selectedEntry = getSelection();
+            notifySelectionListeners(_selectedEntry);
+        }
+    }
+
+    /**
+     * Callback for when the next event is selected
+     */
+    public void selectNextEvent() {
+        _stateCtrl.selectNextEvent();
+        adjustVerticalScrollBar();
+    }
+
+    /**
+     * Callback for when the previous event is selected
+     */
+    public void selectPrevEvent() {
+        _stateCtrl.selectPrevEvent();
+        adjustVerticalScrollBar();
+    }
+
+    /**
+     * Callback for when the next item is selected
+     */
+    public void selectNextItem() {
+        _stateCtrl.selectNextTrace();
+        adjustVerticalScrollBar();
+    }
+
+    /**
+     * Callback for when the previous item is selected
+     */
+    public void selectPrevItem() {
+        _stateCtrl.selectPrevTrace();
+        adjustVerticalScrollBar();
+    }
+
+    /**
+     * Callback for the show legend action
+     */
+    public void showLegend() {
+        if (_dataViewer == null || _dataViewer.isDisposed()) {
+            return;
+        }
+
+        TimeGraphLegend.open(_dataViewer.getShell(), fTimeGraphProvider);
+    }
+
+    /**
+     * Callback for the Zoom In action
+     */
+    public void zoomIn() {
+        _stateCtrl.zoomIn();
+    }
+
+    /**
+     * Callback for the Zoom Out action
+     */
+    public void zoomOut() {
+        _stateCtrl.zoomOut();
+    }
+
+    private String getPreferenceString(String string) {
+        return getViewTypeStr() + "." + string; //$NON-NLS-1$
+    }
+
+    /**
+     * Add a selection listener
+     *
+     * @param listener
+     *            The listener to add
+     */
+    public void addSelectionListener(ITimeGraphSelectionListener listener) {
+        fSelectionListeners.add(listener);
+    }
+
+    /**
+     * Remove a selection listener
+     *
+     * @param listener
+     *            The listener to remove
+     */
+    public void removeSelectionListener(ITimeGraphSelectionListener listener) {
+        fSelectionListeners.remove(listener);
+    }
+
+    private void notifySelectionListeners(ITimeGraphEntry selection) {
+        TimeGraphSelectionEvent event = new TimeGraphSelectionEvent(this, selection);
+
+        for (ITimeGraphSelectionListener listener : fSelectionListeners) {
+            listener.selectionChanged(event);
+        }
+    }
+
+    /**
+     * Add a time listener
+     *
+     * @param listener
+     *            The listener to add
+     */
+    public void addTimeListener(ITimeGraphTimeListener listener) {
+        fTimeListeners.add(listener);
+    }
+
+    /**
+     * Remove a time listener
+     *
+     * @param listener
+     *            The listener to remove
+     */
+    public void removeTimeListener(ITimeGraphTimeListener listener) {
+        fTimeListeners.remove(listener);
+    }
+
+    private void notifyTimeListeners(long time) {
+        TimeGraphTimeEvent event = new TimeGraphTimeEvent(this, time);
+
+        for (ITimeGraphTimeListener listener : fTimeListeners) {
+            listener.timeSelected(event);
+        }
+    }
+
+    /**
+     * Add a range listener
+     *
+     * @param listener
+     *            The listener to add
+     */
+    public void addRangeListener(ITimeGraphRangeListener listener) {
+        fRangeListeners.add(listener);
+    }
+
+    /**
+     * Remove a range listener
+     *
+     * @param listener
+     *            The listener to remove
+     */
+    public void removeRangeListener(ITimeGraphRangeListener listener) {
+        fRangeListeners.remove(listener);
+    }
+
+    private void notifyRangeListeners(long startTime, long endTime) {
+        // Check if the time has actually changed from last notification
+        if (startTime != _time0_extSynch || endTime != _time1_extSynch) {
+            // Notify Time Scale Selection Listeners
+            TimeGraphRangeUpdateEvent event = new TimeGraphRangeUpdateEvent(this, startTime, endTime);
+
+            for (ITimeGraphRangeListener listener : fRangeListeners) {
+                listener.timeRangeUpdated(event);
+            }
+
+            // update external synch timers
+            updateExtSynchTimers();
+        }
+    }
+
+    /**
+     * Callback to set a selected event in the view
+     *
+     * @param event
+     *            The event that was selected
+     * @param source
+     *            The source of this selection event
+     */
+    public void setSelectedEvent(ITimeEvent event, Object source) {
+        if (event == null || source == this) {
+            return;
+        }
+        _selectedEntry = event.getEntry();
+        _stateCtrl.selectItem(_selectedEntry, false);
+
+        setSelectedTimeInt(event.getTime(), true, true);
+        adjustVerticalScrollBar();
+    }
+
+    /**
+     * Set the seeked time of a trace
+     *
+     * @param trace
+     *            The trace that was seeked
+     * @param time
+     *            The target time
+     * @param source
+     *            The source of this seek event
+     */
+    public void setSelectedTraceTime(ITimeGraphEntry trace, long time, Object source) {
+        if (trace == null || source == this) {
+            return;
+        }
+        _selectedEntry = trace;
+        _stateCtrl.selectItem(trace, false);
+
+        setSelectedTimeInt(time, true, true);
+    }
+
+    /**
+     * Callback for a trace selection
+     *
+     * @param trace
+     *            The trace that was selected
+     */
+    public void setSelection(ITimeGraphEntry trace) {
+        _selectedEntry = trace;
+        _stateCtrl.selectItem(trace, false);
+        adjustVerticalScrollBar();
+    }
+
+    /**
+     * Callback for a time window selection
+     *
+     * @param time0
+     *            Start time of the range
+     * @param time1
+     *            End time of the range
+     * @param source
+     *            Source of the event
+     */
+    public void setSelectVisTimeWindow(long time0, long time1, Object source) {
+        if (source == this) {
+            return;
+        }
+
+        setStartFinishTime(time0, time1);
+
+        // update notification time values since we are now in synch with the
+        // external application
+        updateExtSynchTimers();
+    }
+
+    /**
+     * update the cache timers used to identify the need to send a time window
+     * update to external registered listeners
+     */
+    private void updateExtSynchTimers() {
+        // last time notification cache
+        _time0_extSynch = _time0;
+        _time1_extSynch = _time1;
+    }
+
+    /**
+     * Set the calendar format
+     *
+     * @param toAbsoluteCaltime
+     *            True for absolute time, false for relative
+     */
+    public void setTimeCalendarFormat(boolean toAbsoluteCaltime) {
+        calendarTimeFormat = toAbsoluteCaltime;
+    }
+
+    @Override
+    public boolean isCalendarFormat() {
+        return calendarTimeFormat;
+    }
+
+    /**
+     * Retrieve the border width
+     *
+     * @return The width
+     */
+    public int getBorderWidth() {
+        return borderWidth;
+    }
+
+    /**
+     * Set the border width
+     *
+     * @param borderWidth
+     *            The width
+     */
+    public void setBorderWidth(int borderWidth) {
+        if (borderWidth > -1) {
+            this.borderWidth = borderWidth;
+            GridLayout gl = (GridLayout)_dataViewer.getLayout();
+            gl.marginHeight = borderWidth;
+        }
+    }
+
+    /**
+     * Retrieve the height of the header
+     *
+     * @return The height
+     */
+    public int getHeaderHeight() {
+        return timeScaleHeight;
+    }
+
+    /**
+     * Set the height of the header
+     *
+     * @param headerHeight
+     *            The height to set
+     */
+    public void setHeaderHeight(int headerHeight) {
+        if (headerHeight > -1) {
+            this.timeScaleHeight = headerHeight;
+            _timeScaleCtrl.setHeight(headerHeight);
+        }
+    }
+
+    /**
+     * Retrieve the height of an item row
+     *
+     * @return The height
+     */
+    public int getItemHeight() {
+        if (_stateCtrl != null) {
+            return _stateCtrl.getItemHeight();
+        }
+        return 0;
+    }
+
+    /**
+     * Set the height of an item row
+     *
+     * @param rowHeight
+     *            The height to set
+     */
+    public void setItemHeight(int rowHeight) {
+        if (_stateCtrl != null) {
+            _stateCtrl.setItemHeight(rowHeight);
+        }
+    }
+
+    /**
+     * Set the minimum item width
+     *
+     * @param width
+     *            The min width
+     */
+    public void setMinimumItemWidth(int width) {
+        if (_stateCtrl != null) {
+            _stateCtrl.setMinimumItemWidth(width);
+        }
+    }
+
+    /**
+     * Set the width for the name column
+     *
+     * @param width The width
+     */
+    public void setNameWidthPref(int width) {
+        _nameWidthPref = width;
+        if (width == 0) {
+            _minNameWidth = 0;
+            _nameWidth = 0;
+        }
+    }
+
+    /**
+     * Retrieve the configure width for the name column
+     *
+     * @param width
+     *            Unused?
+     * @return The width
+     */
+    public int getNameWidthPref(int width) {
+        return _nameWidthPref;
+    }
+
+    /**
+     * Returns the primary control associated with this viewer.
+     *
+     * @return the SWT control which displays this viewer's content
+     */
+    public Control getControl() {
+        return _dataViewer;
+    }
+
+    /**
+     * Returns the time graph control associated with this viewer.
+     *
+     * @return the time graph control
+     */
+    TimeGraphControl getTimeGraphControl() {
+        return _stateCtrl;
+    }
+
+    /**
+     * Returns the time graph scale associated with this viewer.
+     *
+     * @return the time graph scale
+     */
+    TimeGraphScale getTimeGraphScale() {
+        return _timeScaleCtrl;
+    }
+
+    /**
+     * Get the selection provider
+     *
+     * @return the selection provider
+     */
+    public ISelectionProvider getSelectionProvider() {
+        return _stateCtrl;
+    }
+
+    /**
+     * Wait for the cursor
+     *
+     * @param waitInd
+     *            Wait indefinitely?
+     */
+    public void waitCursor(boolean waitInd) {
+        _stateCtrl.waitCursor(waitInd);
+    }
+
+    /**
+     * Get the horizontal scroll bar object
+     *
+     * @return The scroll bar
+     */
+    public ScrollBar getHorizontalBar() {
+        return _stateCtrl.getHorizontalBar();
+    }
+
+    /**
+     * Get the vertical scroll bar object
+     *
+     * @return The scroll bar
+     */
+    public Slider getVerticalBar() {
+        return _verticalScrollBar;
+    }
+
+    /**
+     * Set the given index as the top one
+     *
+     * @param index
+     *            The index that will go to the top
+     */
+    public void setTopIndex(int index) {
+        _stateCtrl.setTopIndex(index);
+        adjustVerticalScrollBar();
+    }
+
+    /**
+     * Retrieve the current top index
+     *
+     * @return The top index
+     */
+    public int getTopIndex() {
+        return _stateCtrl.getTopIndex();
+    }
+
+    /**
+     * Set the expanded state of an entry
+     *
+     * @param entry
+     *            The entry to expand/collapse
+     * @param expanded
+     *            True for expanded, false for collapsed
+     */
+    public void setExpandedState(ITimeGraphEntry entry, boolean expanded) {
+        _stateCtrl.setExpandedState(entry, expanded);
+        adjustVerticalScrollBar();
+    }
+
+    /**
+     * Collapses all nodes of the viewer's tree, starting with the root.
+     *
+     * @since 2.0
+     */
+    public void collapseAll() {
+        _stateCtrl.collapseAll();
+        adjustVerticalScrollBar();
+    }
+
+    /**
+     * Expands all nodes of the viewer's tree, starting with the root.
+     *
+     * @since 2.0
+     */
+    public void expandAll() {
+        _stateCtrl.expandAll();
+        adjustVerticalScrollBar();
+    }
+
+    /**
+     * Get the number of sub-elements when expanded
+     *
+     * @return The element count
+     */
+    public int getExpandedElementCount() {
+        return _stateCtrl.getExpandedElementCount();
+    }
+
+    /**
+     * Get the sub-elements
+     *
+     * @return The array of entries that are below this one
+     */
+    public ITimeGraphEntry[] getExpandedElements() {
+        return _stateCtrl.getExpandedElements();
+    }
+
+    /**
+     * Add a tree listener
+     *
+     * @param listener
+     *            The listener to add
+     */
+    public void addTreeListener(ITimeGraphTreeListener listener) {
+        _stateCtrl.addTreeListener(listener);
+    }
+
+    /**
+     * Remove a tree listener
+     *
+     * @param listener
+     *            The listener to remove
+     */
+    public void removeTreeListener(ITimeGraphTreeListener listener) {
+        _stateCtrl.removeTreeListener(listener);
+    }
+
+    /**
+     * Get the reset scale action.
+     *
+     * @return The Action object
+     */
+    public Action getResetScaleAction() {
+        if (resetScale == null) {
+            // resetScale
+            resetScale = new Action() {
+                @Override
+                public void run() {
+                    resetStartFinishTime();
+                }
+            };
+            resetScale.setText(Messages.TmfTimeGraphViewer_ResetScaleActionNameText);
+            resetScale.setToolTipText(Messages.TmfTimeGraphViewer_ResetScaleActionToolTipText);
+            resetScale.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_HOME_MENU));
+        }
+        return resetScale;
+    }
+
+    /**
+     * Get the show legend action.
+     *
+     * @return The Action object
+     */
+    public Action getShowLegendAction() {
+        if (showLegendAction == null) {
+            // showLegend
+            showLegendAction = new Action() {
+                @Override
+                public void run() {
+                    showLegend();
+                }
+            };
+            showLegendAction.setText(Messages.TmfTimeGraphViewer_LegendActionNameText);
+            showLegendAction.setToolTipText(Messages.TmfTimeGraphViewer_LegendActionToolTipText);
+            showLegendAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_SHOW_LEGEND));
+        }
+
+        return showLegendAction;
+    }
+
+    /**
+     * Get the the next event action.
+     *
+     * @return The action object
+     */
+    public Action getNextEventAction() {
+        if (nextEventAction == null) {
+            nextEventAction = new Action() {
+                @Override
+                public void run() {
+                    selectNextEvent();
+                }
+            };
+
+            nextEventAction.setText(Messages.TmfTimeGraphViewer_NextEventActionNameText);
+            nextEventAction.setToolTipText(Messages.TmfTimeGraphViewer_NextEventActionToolTipText);
+            nextEventAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_NEXT_EVENT));
+        }
+
+        return nextEventAction;
+    }
+
+    /**
+     * Get the previous event action.
+     *
+     * @return The Action object
+     */
+    public Action getPreviousEventAction() {
+        if (prevEventAction == null) {
+            prevEventAction = new Action() {
+                @Override
+                public void run() {
+                    selectPrevEvent();
+                }
+            };
+
+            prevEventAction.setText(Messages.TmfTimeGraphViewer_PreviousEventActionNameText);
+            prevEventAction.setToolTipText(Messages.TmfTimeGraphViewer_PreviousEventActionToolTipText);
+            prevEventAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_PREV_EVENT));
+        }
+
+        return prevEventAction;
+    }
+
+    /**
+     * Get the next item action.
+     *
+     * @return The Action object
+     */
+    public Action getNextItemAction() {
+        if (nextItemAction == null) {
+
+            nextItemAction = new Action() {
+                @Override
+                public void run() {
+                    selectNextItem();
+                }
+            };
+            nextItemAction.setText(Messages.TmfTimeGraphViewer_NextItemActionNameText);
+            nextItemAction.setToolTipText(Messages.TmfTimeGraphViewer_NextItemActionToolTipText);
+            nextItemAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_NEXT_ITEM));
+        }
+        return nextItemAction;
+    }
+
+    /**
+     * Get the previous item action.
+     *
+     * @return The Action object
+     */
+    public Action getPreviousItemAction() {
+        if (previousItemAction == null) {
+
+            previousItemAction = new Action() {
+                @Override
+                public void run() {
+                    selectPrevItem();
+                }
+            };
+            previousItemAction.setText(Messages.TmfTimeGraphViewer_PreviousItemActionNameText);
+            previousItemAction.setToolTipText(Messages.TmfTimeGraphViewer_PreviousItemActionToolTipText);
+            previousItemAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_PREV_ITEM));
+        }
+        return previousItemAction;
+    }
+
+    /**
+     * Get the zoom in action
+     *
+     * @return The Action object
+     */
+    public Action getZoomInAction() {
+        if (zoomInAction == null) {
+            zoomInAction = new Action() {
+                @Override
+                public void run() {
+                    zoomIn();
+                }
+            };
+            zoomInAction.setText(Messages.TmfTimeGraphViewer_ZoomInActionNameText);
+            zoomInAction.setToolTipText(Messages.TmfTimeGraphViewer_ZoomInActionToolTipText);
+            zoomInAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_ZOOM_IN_MENU));
+        }
+        return zoomInAction;
+    }
+
+    /**
+     * Get the zoom out action
+     *
+     * @return The Action object
+     */
+    public Action getZoomOutAction() {
+        if (zoomOutAction == null) {
+            zoomOutAction = new Action() {
+                @Override
+                public void run() {
+                    zoomOut();
+                }
+            };
+            zoomOutAction.setText(Messages.TmfTimeGraphViewer_ZoomOutActionNameText);
+            zoomOutAction.setToolTipText(Messages.TmfTimeGraphViewer_ZoomOutActionToolTipText);
+            zoomOutAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_ZOOM_OUT_MENU));
+        }
+        return zoomOutAction;
+    }
+
+
+    private void adjustVerticalScrollBar() {
+        int topIndex = _stateCtrl.getTopIndex();
+        int countPerPage = _stateCtrl.countPerPage();
+        int expandedElementCount = _stateCtrl.getExpandedElementCount();
+        if (topIndex + countPerPage > expandedElementCount) {
+            _stateCtrl.setTopIndex(Math.max(0, expandedElementCount - countPerPage));
+        }
+
+        int selection = _stateCtrl.getTopIndex();
+        int min = 0;
+        int max = Math.max(1, expandedElementCount - 1);
+        int thumb = Math.min(max, Math.max(1, countPerPage - 1));
+        int increment = 1;
+        int pageIncrement = Math.max(1, countPerPage);
+        _verticalScrollBar.setValues(selection, min, max, thumb, increment, pageIncrement);
+    }
+
+
+
+}
index 8bce4c89b23c0b5a10f148ddcb15140120d080ad..202d5bb1bceea035fe3a95922141fc141562e84f 100644 (file)
-/*****************************************************************************\r
- * Copyright (c) 2007, 2008 Intel Corporation, 2009, 2010, 2011, 2012 Ericsson.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- *   Intel Corporation - Initial API and implementation\r
- *   Ruslan A. Scherbakov, Intel - Initial API and implementation\r
- *   Alvaro Sanchez-Leon - Updated for TMF\r
- *   Patrick Tasse - Refactoring\r
- *\r
- *****************************************************************************/\r
-\r
-package org.eclipse.linuxtools.tmf.ui.widgets.timegraph.widgets;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Iterator;\r
-import java.util.List;\r
-import java.util.Vector;\r
-\r
-import org.eclipse.jface.resource.JFaceResources;\r
-import org.eclipse.jface.resource.LocalResourceManager;\r
-import org.eclipse.jface.viewers.ISelection;\r
-import org.eclipse.jface.viewers.ISelectionChangedListener;\r
-import org.eclipse.jface.viewers.ISelectionProvider;\r
-import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.ITimeGraphPresentationProvider;\r
-import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.ITimeGraphTreeListener;\r
-import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.StateItem;\r
-import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.TimeGraphTreeExpansionEvent;\r
-import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.model.ITimeEvent;\r
-import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;\r
-import org.eclipse.swt.SWT;\r
-import org.eclipse.swt.events.ControlEvent;\r
-import org.eclipse.swt.events.ControlListener;\r
-import org.eclipse.swt.events.FocusEvent;\r
-import org.eclipse.swt.events.FocusListener;\r
-import org.eclipse.swt.events.KeyEvent;\r
-import org.eclipse.swt.events.KeyListener;\r
-import org.eclipse.swt.events.MouseEvent;\r
-import org.eclipse.swt.events.MouseListener;\r
-import org.eclipse.swt.events.MouseMoveListener;\r
-import org.eclipse.swt.events.MouseTrackListener;\r
-import org.eclipse.swt.events.MouseWheelListener;\r
-import org.eclipse.swt.events.PaintEvent;\r
-import org.eclipse.swt.events.SelectionEvent;\r
-import org.eclipse.swt.events.SelectionListener;\r
-import org.eclipse.swt.events.TraverseEvent;\r
-import org.eclipse.swt.events.TraverseListener;\r
-import org.eclipse.swt.graphics.Color;\r
-import org.eclipse.swt.graphics.Cursor;\r
-import org.eclipse.swt.graphics.GC;\r
-import org.eclipse.swt.graphics.Image;\r
-import org.eclipse.swt.graphics.Point;\r
-import org.eclipse.swt.graphics.Rectangle;\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.ScrollBar;\r
-\r
-/**\r
- * Time graph control implementation\r
- *\r
- * @version 1.0\r
- * @author Alvaro Sanchez-Leon\r
- * @author Patrick Tasse\r
- */\r
-public class TimeGraphControl extends TimeGraphBaseControl implements FocusListener, KeyListener, MouseMoveListener, MouseListener, MouseWheelListener, ControlListener, SelectionListener, MouseTrackListener, TraverseListener, ISelectionProvider {\r
-\r
-    private static final int DRAG_NONE = 0;\r
-    private static final int DRAG_TRACE_ITEM = 1;\r
-    private static final int DRAG_SPLIT_LINE = 2;\r
-    public static final boolean DEFAULT_DRAW_THREAD_JOIN = true;\r
-    public static final boolean DEFAULT_DRAW_THREAD_WAIT = true;\r
-    public static final boolean DEFAULT_DRAW_THREAD_RELEASE = true;\r
-    public static final int H_SCROLLBAR_MAX = Integer.MAX_VALUE - 1;\r
-    private static final int CUSTOM_ITEM_HEIGHT = -1; // get item height from provider\r
-\r
-    private static final double zoomCoeff = 1.5;\r
-\r
-    private ITimeDataProvider _timeProvider;\r
-    private boolean _isInFocus = false;\r
-    private boolean _isDragCursor3 = false;\r
-    private boolean _isWaitCursor = true;\r
-    private boolean _mouseOverSplitLine = false;\r
-    private int _itemHeight = CUSTOM_ITEM_HEIGHT;\r
-    private int _minimumItemWidth = 0;\r
-    private int _topIndex = 0;\r
-    private int _dragState = DRAG_NONE;\r
-    private int _dragX0 = 0;\r
-    private int _dragX = 0;\r
-    private int _idealNameSpace = 0;\r
-    // private double _timeStep = 10000000;\r
-    private long _time0bak;\r
-    private long _time1bak;\r
-    private ITimeGraphPresentationProvider fTimeGraphProvider = null;\r
-    private ItemData _data = null;\r
-    private List<SelectionListener> _selectionListeners;\r
-    private final List<ISelectionChangedListener> _selectionChangedListeners = new ArrayList<ISelectionChangedListener>();\r
-    private final List<ITimeGraphTreeListener> _treeListeners = new ArrayList<ITimeGraphTreeListener>();\r
-    private final Cursor _dragCursor3;\r
-    private final Cursor _WaitCursor;\r
-\r
-    // Vertical formatting formatting for the state control view\r
-    private final boolean _visibleVerticalScroll = true;\r
-    private int _borderWidth = 0;\r
-    private int _headerHeight = 0;\r
-\r
-    private Listener mouseScrollFilterListener;\r
-\r
-    protected LocalResourceManager fResourceManager = new LocalResourceManager(JFaceResources.getResources());\r
-    protected Color[] fEventColorMap = null;\r
-\r
-    private MouseScrollNotifier fMouseScrollNotifier;\r
-    private final Object fMouseScrollNotifierLock = new Object();\r
-    private class MouseScrollNotifier extends Thread {\r
-        private final static long DELAY = 400L;\r
-        private final static long POLLING_INTERVAL = 10L;\r
-        private long fLastScrollTime = Long.MAX_VALUE;\r
-\r
-        @Override\r
-        public void run() {\r
-            while ((System.currentTimeMillis() - fLastScrollTime) < DELAY) {\r
-                try {\r
-                    Thread.sleep(POLLING_INTERVAL);\r
-                } catch (Exception e) {\r
-                    return;\r
-                }\r
-            }\r
-            if (!isInterrupted()) {\r
-                Display.getDefault().asyncExec(new Runnable() {\r
-                    @Override\r
-                    public void run() {\r
-                        if (isDisposed()) {\r
-                            return;\r
-                        }\r
-                        _timeProvider.notifyStartFinishTime();\r
-                    }\r
-                });\r
-            }\r
-            synchronized (fMouseScrollNotifierLock) {\r
-                fMouseScrollNotifier = null;\r
-            }\r
-        }\r
-\r
-        public void mouseScrolled() {\r
-            fLastScrollTime = System.currentTimeMillis();\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Standard constructor\r
-     *\r
-     * @param parent\r
-     *            The parent composite object\r
-     * @param colors\r
-     *            The color scheme to use\r
-     */\r
-    public TimeGraphControl(Composite parent, TimeGraphColorScheme colors) {\r
-\r
-        super(parent, colors, SWT.NO_BACKGROUND | SWT.H_SCROLL | SWT.DOUBLE_BUFFERED);\r
-\r
-        _data = new ItemData();\r
-\r
-        addFocusListener(this);\r
-        addMouseListener(this);\r
-        addMouseMoveListener(this);\r
-        addMouseTrackListener(this);\r
-        addMouseWheelListener(this);\r
-        addTraverseListener(this);\r
-        addKeyListener(this);\r
-        addControlListener(this);\r
-        ScrollBar scrollHor = getHorizontalBar();\r
-\r
-        if (scrollHor != null) {\r
-            scrollHor.addSelectionListener(this);\r
-        }\r
-\r
-        _dragCursor3 = new Cursor(super.getDisplay(), SWT.CURSOR_SIZEWE);\r
-        _WaitCursor = new Cursor(super.getDisplay(), SWT.CURSOR_WAIT);\r
-    }\r
-\r
-    @Override\r
-    public void dispose() {\r
-        super.dispose();\r
-        _dragCursor3.dispose();\r
-        _WaitCursor.dispose();\r
-        fResourceManager.dispose();\r
-    }\r
-\r
-    /**\r
-     * Sets the timegraph provider used by this timegraph viewer.\r
-     *\r
-     * @param timeGraphProvider the timegraph provider\r
-     */\r
-    public void setTimeGraphProvider(ITimeGraphPresentationProvider timeGraphProvider) {\r
-        fTimeGraphProvider = timeGraphProvider;\r
-        _data.provider = timeGraphProvider;\r
-\r
-        if (fEventColorMap != null) {\r
-            for (Color color : fEventColorMap) {\r
-                fResourceManager.destroyColor(color.getRGB());\r
-            }\r
-        }\r
-        StateItem[] stateItems = fTimeGraphProvider.getStateTable();\r
-        if (stateItems != null) {\r
-            fEventColorMap = new Color[stateItems.length];\r
-            for (int i = 0; i < stateItems.length; i++) {\r
-                fEventColorMap[i] = fResourceManager.createColor(stateItems[i].getStateColor());\r
-            }\r
-        } else {\r
-            fEventColorMap = new Color[] { };\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Assign the given time provider\r
-     *\r
-     * @param timeProvider\r
-     *            The time provider\r
-     */\r
-    public void setTimeProvider(ITimeDataProvider timeProvider) {\r
-        _timeProvider = timeProvider;\r
-        adjustScrolls();\r
-        redraw();\r
-    }\r
-\r
-    /**\r
-     * Add a selection listener\r
-     *\r
-     * @param listener\r
-     *            The listener to add\r
-     */\r
-    public void addSelectionListener(SelectionListener listener) {\r
-        if (listener == null) {\r
-            SWT.error(SWT.ERROR_NULL_ARGUMENT);\r
-        }\r
-        if (null == _selectionListeners) {\r
-            _selectionListeners = new ArrayList<SelectionListener>();\r
-        }\r
-        _selectionListeners.add(listener);\r
-    }\r
-\r
-    /**\r
-     * Remove a selection listener\r
-     *\r
-     * @param listener\r
-     *            The listener to remove\r
-     */\r
-    public void removeSelectionListener(SelectionListener listener) {\r
-        if (null != _selectionListeners) {\r
-            _selectionListeners.remove(listener);\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Selection changed callback\r
-     */\r
-    public void fireSelectionChanged() {\r
-        if (null != _selectionListeners) {\r
-            Iterator<SelectionListener> it = _selectionListeners.iterator();\r
-            while (it.hasNext()) {\r
-                SelectionListener listener = it.next();\r
-                listener.widgetSelected(null);\r
-            }\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Default selection callback\r
-     */\r
-    public void fireDefaultSelection() {\r
-        if (null != _selectionListeners) {\r
-            Iterator<SelectionListener> it = _selectionListeners.iterator();\r
-            while (it.hasNext()) {\r
-                SelectionListener listener = it.next();\r
-                listener.widgetDefaultSelected(null);\r
-            }\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Get the traces in the model\r
-     *\r
-     * @return The array of traces\r
-     */\r
-    public ITimeGraphEntry[] getTraces() {\r
-        return _data.getTraces();\r
-    }\r
-\r
-    /**\r
-     * Get the on/off trace filters\r
-     *\r
-     * @return The array of filters\r
-     */\r
-    public boolean[] getTraceFilter() {\r
-        return _data.getTraceFilter();\r
-    }\r
-\r
-    /**\r
-     * Refresh the data for the thing\r
-     */\r
-    public void refreshData() {\r
-        _data.refreshData();\r
-        adjustScrolls();\r
-        redraw();\r
-    }\r
-\r
-    /**\r
-     * Refresh data for the given traces\r
-     *\r
-     * @param traces\r
-     *            The traces to refresh\r
-     */\r
-    public void refreshData(ITimeGraphEntry[] traces) {\r
-        _data.refreshData(traces);\r
-        adjustScrolls();\r
-        redraw();\r
-    }\r
-\r
-    /**\r
-     * Adjust the scoll bars\r
-     */\r
-    public void adjustScrolls() {\r
-        if (null == _timeProvider) {\r
-            getHorizontalBar().setValues(0, 1, 1, 1, 1, 1);\r
-            return;\r
-        }\r
-\r
-        // HORIZONTAL BAR\r
-        // Visible window\r
-        long time0 = _timeProvider.getTime0();\r
-        long time1 = _timeProvider.getTime1();\r
-        // Time boundaries\r
-        long timeMin = _timeProvider.getMinTime();\r
-        long timeMax = _timeProvider.getMaxTime();\r
-\r
-        long delta = timeMax - timeMin;\r
-\r
-        int timePos = 0;\r
-        int thumb = H_SCROLLBAR_MAX;\r
-\r
-        if (delta != 0) {\r
-            // Thumb size (page size)\r
-            thumb = Math.max(1, (int) (H_SCROLLBAR_MAX * ((double) (time1 - time0) / delta)));\r
-            // At the beginning of visible window\r
-            timePos = (int) (H_SCROLLBAR_MAX * ((double) (time0 - timeMin) / delta));\r
-        }\r
-\r
-        // position, minimum, maximum, thumb size, increment (half page)t, page\r
-        // increment size (full page)\r
-        getHorizontalBar().setValues(timePos, 0, H_SCROLLBAR_MAX, thumb, Math.max(1, thumb / 2), Math.max(2, thumb));\r
-    }\r
-\r
-    boolean ensureVisibleItem(int idx, boolean redraw) {\r
-        boolean changed = false;\r
-        if (idx < 0) {\r
-            for (idx = 0; idx < _data._expandedItems.length; idx++) {\r
-                if (_data._expandedItems[idx]._selected) {\r
-                    break;\r
-                }\r
-            }\r
-        }\r
-        if (idx >= _data._expandedItems.length) {\r
-            return changed;\r
-        }\r
-        if (idx < _topIndex) {\r
-            setTopIndex(idx);\r
-            //FIXME:getVerticalBar().setSelection(_topItem);\r
-            if (redraw) {\r
-                redraw();\r
-            }\r
-            changed = true;\r
-        } else {\r
-            int page = countPerPage();\r
-            if (idx >= _topIndex + page) {\r
-                setTopIndex(idx - page + 1);\r
-                //FIXME:getVerticalBar().setSelection(_topItem);\r
-                if (redraw) {\r
-                    redraw();\r
-                }\r
-                changed = true;\r
-            }\r
-        }\r
-        return changed;\r
-    }\r
-\r
-    /**\r
-     * Assign the given index as the top one\r
-     *\r
-     * @param idx\r
-     *            The index\r
-     */\r
-    public void setTopIndex(int idx) {\r
-        idx = Math.min(idx, _data._expandedItems.length - countPerPage());\r
-        idx = Math.max(0,  idx);\r
-        _topIndex = idx;\r
-        redraw();\r
-    }\r
-\r
-    /**\r
-     * Set the expanded state of a given entry\r
-     *\r
-     * @param entry\r
-     *            The entry\r
-     * @param expanded\r
-     *            True if expanded, false if collapsed\r
-     */\r
-    public void setExpandedState(ITimeGraphEntry entry, boolean expanded) {\r
-        Item item = _data.findItem(entry);\r
-        if (item != null && item._expanded != expanded) {\r
-            item._expanded = expanded;\r
-            _data.updateExpandedItems();\r
-            redraw();\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Add a tree listener\r
-     *\r
-     * @param listener\r
-     *            The listener to add\r
-     */\r
-    public void addTreeListener(ITimeGraphTreeListener listener) {\r
-        if (!_treeListeners.contains(listener)) {\r
-            _treeListeners.add(listener);\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Remove a tree listener\r
-     *\r
-     * @param listener\r
-     *            The listener to remove\r
-     */\r
-    public void removeTreeListener(ITimeGraphTreeListener listener) {\r
-        if (_treeListeners.contains(listener)) {\r
-            _treeListeners.remove(listener);\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Tree event callback\r
-     *\r
-     * @param entry\r
-     *            The affected entry\r
-     * @param expanded\r
-     *            The expanded state (true for expanded, false for collapsed)\r
-     */\r
-    public void fireTreeEvent(ITimeGraphEntry entry, boolean expanded) {\r
-        TimeGraphTreeExpansionEvent event = new TimeGraphTreeExpansionEvent(this, entry);\r
-        for (ITimeGraphTreeListener listener : _treeListeners) {\r
-            if (expanded) {\r
-                listener.treeExpanded(event);\r
-            } else {\r
-                listener.treeCollapsed(event);\r
-            }\r
-        }\r
-    }\r
-\r
-    @Override\r
-    public ISelection getSelection() {\r
-        TimeGraphSelection sel = new TimeGraphSelection();\r
-        ITimeGraphEntry trace = getSelectedTrace();\r
-        if (null != trace && null != _timeProvider) {\r
-            long selectedTime = _timeProvider.getSelectedTime();\r
-            ITimeEvent event = Utils.findEvent(trace, selectedTime, 0);\r
-            if (event != null) {\r
-                sel.add(event);\r
-            } else {\r
-                sel.add(trace);\r
-            }\r
-        }\r
-        return sel;\r
-    }\r
-\r
-    /**\r
-     * Get the selection object\r
-     *\r
-     * @return The selection\r
-     */\r
-    public ISelection getSelectionTrace() {\r
-        TimeGraphSelection sel = new TimeGraphSelection();\r
-        ITimeGraphEntry trace = getSelectedTrace();\r
-        if (null != trace) {\r
-            sel.add(trace);\r
-        }\r
-        return sel;\r
-    }\r
-\r
-    /**\r
-     * Enable/disable one of the traces in the model\r
-     *\r
-     * @param n\r
-     *            1 to enable it, -1 to disable. The method returns immediately\r
-     *            if another value is used.\r
-     */\r
-    public void selectTrace(int n) {\r
-        if ((n != 1) && (n != -1)) {\r
-            return;\r
-        }\r
-\r
-        boolean changed = false;\r
-        int lastSelection = -1;\r
-        for (int i = 0; i < _data._expandedItems.length; i++) {\r
-            Item item = _data._expandedItems[i];\r
-            if (item._selected) {\r
-                lastSelection = i;\r
-                if ((1 == n) && (i < _data._expandedItems.length - 1)) {\r
-                    item._selected = false;\r
-                    item = _data._expandedItems[i + 1];\r
-                    item._selected = true;\r
-                    changed = true;\r
-                } else if ((-1 == n) && (i > 0)) {\r
-                    item._selected = false;\r
-                    item = _data._expandedItems[i - 1];\r
-                    item._selected = true;\r
-                    changed = true;\r
-                }\r
-                break;\r
-            }\r
-        }\r
-\r
-        if (lastSelection < 0 && _data._expandedItems.length > 0) {\r
-            Item item = _data._expandedItems[0];\r
-            item._selected = true;\r
-            changed = true;\r
-        }\r
-\r
-        if (changed) {\r
-            ensureVisibleItem(-1, false);\r
-            redraw();\r
-            fireSelectionChanged();\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Select an event\r
-     *\r
-     * @param n\r
-     *            1 for next event, -1 for previous event\r
-     */\r
-    public void selectEvent(int n) {\r
-        if (null == _timeProvider) {\r
-            return;\r
-        }\r
-        ITimeGraphEntry trace = getSelectedTrace();\r
-        if (trace == null) {\r
-            return;\r
-        }\r
-        long selectedTime = _timeProvider.getSelectedTime();\r
-        long endTime = _timeProvider.getEndTime();\r
-        ITimeEvent nextEvent;\r
-        if (-1 == n && selectedTime > endTime) {\r
-            nextEvent = Utils.findEvent(trace, selectedTime, 0);\r
-        } else {\r
-            nextEvent = Utils.findEvent(trace, selectedTime, n);\r
-        }\r
-        if (null == nextEvent && -1 == n) {\r
-            nextEvent = Utils.getFirstEvent(trace);\r
-        }\r
-        if (null != nextEvent) {\r
-            long nextTime = nextEvent.getTime();\r
-            // If last event detected e.g. going back or not moving to a next\r
-            // event\r
-            if (nextTime <= selectedTime && n == 1) {\r
-                // Select to the end of this last event\r
-                nextTime = nextEvent.getTime() + nextEvent.getDuration();\r
-                // but not beyond the end of the trace\r
-                if (nextTime > endTime) {\r
-                    nextTime = endTime;\r
-                }\r
-            } else if (n == -1) {\r
-                // for previous event go to its end time unless we were already there\r
-                if (nextEvent.getTime() + nextEvent.getDuration() < selectedTime) {\r
-                    nextTime = nextEvent.getTime() + nextEvent.getDuration();\r
-                }\r
-            }\r
-            _timeProvider.setSelectedTimeNotify(nextTime, true);\r
-            fireSelectionChanged();\r
-        } else if (1 == n) {\r
-            _timeProvider.setSelectedTimeNotify(endTime, true);\r
-            fireSelectionChanged();\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Select the next event\r
-     */\r
-    public void selectNextEvent() {\r
-        selectEvent(1);\r
-        // Notify if visible time window has been adjusted\r
-        _timeProvider.setStartFinishTimeNotify(_timeProvider.getTime0(), _timeProvider.getTime1());\r
-    }\r
-\r
-    /**\r
-     * Select the previous event\r
-     */\r
-    public void selectPrevEvent() {\r
-        selectEvent(-1);\r
-        // Notify if visible time window has been adjusted\r
-        _timeProvider.setStartFinishTimeNotify(_timeProvider.getTime0(), _timeProvider.getTime1());\r
-    }\r
-\r
-    /**\r
-     * Select the next trace\r
-     */\r
-    public void selectNextTrace() {\r
-        selectTrace(1);\r
-    }\r
-\r
-    /**\r
-     * Select the previous trace\r
-     */\r
-    public void selectPrevTrace() {\r
-        selectTrace(-1);\r
-    }\r
-\r
-    /**\r
-     * Zoom based on mouse cursor location with mouse scrolling\r
-     *\r
-     * @param zoomIn true to zoom in, false to zoom out\r
-     */\r
-    public void zoom(boolean zoomIn) {\r
-        int globalX = getDisplay().getCursorLocation().x;\r
-        Point p = toControl(globalX, 0);\r
-        int nameSpace = _timeProvider.getNameSpace();\r
-        int timeSpace = _timeProvider.getTimeSpace();\r
-        int xPos = Math.max(nameSpace, Math.min(nameSpace + timeSpace, p.x));\r
-        long time0 = _timeProvider.getTime0();\r
-        long time1 = _timeProvider.getTime1();\r
-        long interval = time1 - time0;\r
-        if (interval == 0) {\r
-            interval = 1;\r
-        } // to allow getting out of single point interval\r
-        long newInterval;\r
-        if (zoomIn) {\r
-            newInterval = Math.max(Math.round(interval * 0.8), _timeProvider.getMinTimeInterval());\r
-        } else {\r
-            newInterval = (long) Math.ceil(interval * 1.25);\r
-        }\r
-        long center = time0 + Math.round(((double) (xPos - nameSpace) / timeSpace * interval));\r
-        long newTime0 = center - Math.round((double) newInterval * (center - time0) / interval);\r
-        long newTime1 = newTime0 + newInterval;\r
-        _timeProvider.setStartFinishTime(newTime0, newTime1);\r
-        synchronized (fMouseScrollNotifierLock) {\r
-            if (fMouseScrollNotifier == null) {\r
-                fMouseScrollNotifier = new MouseScrollNotifier();\r
-                fMouseScrollNotifier.start();\r
-            }\r
-            fMouseScrollNotifier.mouseScrolled();\r
-        }\r
-    }\r
-\r
-    /**\r
-     * zoom in using single click\r
-     */\r
-    public void zoomIn() {\r
-        long _time0 = _timeProvider.getTime0();\r
-        long _time1 = _timeProvider.getTime1();\r
-        long _range = _time1 - _time0;\r
-        long selTime = _timeProvider.getSelectedTime();\r
-        if (selTime <= _time0 || selTime >= _time1) {\r
-            selTime = (_time0 + _time1) / 2;\r
-        }\r
-        long time0 = selTime - (long) ((selTime - _time0) / zoomCoeff);\r
-        long time1 = selTime + (long) ((_time1 - selTime) / zoomCoeff);\r
-\r
-        long inaccuracy = (_timeProvider.getMaxTime() - _timeProvider.getMinTime()) - (time1 - time0);\r
-\r
-        // Trace.debug("selTime:" + selTime + " time0:" + time0 + " time1:"\r
-        // + time1 + " inaccuracy:" + inaccuracy);\r
-\r
-        if (inaccuracy > 0 && inaccuracy < 100) {\r
-            _timeProvider.setStartFinishTimeNotify(_timeProvider.getMinTime(), _timeProvider.getMaxTime());\r
-            return;\r
-        }\r
-\r
-        long m = _timeProvider.getMinTimeInterval();\r
-        if ((time1 - time0) < m) {\r
-            time0 = selTime - (selTime - _time0) * m / _range;\r
-            time1 = time0 + m;\r
-        }\r
-\r
-        _timeProvider.setStartFinishTimeNotify(time0, time1);\r
-    }\r
-\r
-    /**\r
-     * zoom out using single click\r
-     */\r
-    public void zoomOut() {\r
-        long _time0 = _timeProvider.getTime0();\r
-        long _time1 = _timeProvider.getTime1();\r
-        long selTime = _timeProvider.getSelectedTime();\r
-        if (selTime <= _time0 || selTime >= _time1) {\r
-            selTime = (_time0 + _time1) / 2;\r
-        }\r
-        long time0 = (long) (selTime - (selTime - _time0) * zoomCoeff);\r
-        long time1 = (long) (selTime + (_time1 - selTime) * zoomCoeff);\r
-\r
-        long inaccuracy = (_timeProvider.getMaxTime() - _timeProvider.getMinTime()) - (time1 - time0);\r
-        if (inaccuracy > 0 && inaccuracy < 100) {\r
-            _timeProvider.setStartFinishTimeNotify(_timeProvider.getMinTime(), _timeProvider.getMaxTime());\r
-            return;\r
-        }\r
-\r
-        _timeProvider.setStartFinishTimeNotify(time0, time1);\r
-    }\r
-\r
-    /**\r
-     * Return the currently selected trace\r
-     *\r
-     * @return The entry matching the trace\r
-     */\r
-    public ITimeGraphEntry getSelectedTrace() {\r
-        ITimeGraphEntry trace = null;\r
-        int idx = getSelectedIndex();\r
-        if (idx >= 0) {\r
-            trace = _data._expandedItems[idx]._trace;\r
-        }\r
-        return trace;\r
-    }\r
-\r
-    /**\r
-     * Retrieve the index of the currently selected item\r
-     *\r
-     * @return The index\r
-     */\r
-    public int getSelectedIndex() {\r
-        int idx = -1;\r
-        for (int i = 0; i < _data._expandedItems.length; i++) {\r
-            Item item = _data._expandedItems[i];\r
-            if (item._selected) {\r
-                idx = i;\r
-                break;\r
-            }\r
-        }\r
-        return idx;\r
-    }\r
-\r
-    boolean toggle(int idx) {\r
-        boolean toggled = false;\r
-        if (idx >= 0 && idx < _data._expandedItems.length) {\r
-            Item item = _data._expandedItems[idx];\r
-            if (item._hasChildren) {\r
-                item._expanded = !item._expanded;\r
-                _data.updateExpandedItems();\r
-                adjustScrolls();\r
-                redraw();\r
-                toggled = true;\r
-                fireTreeEvent(item._trace, item._expanded);\r
-            }\r
-        }\r
-        return toggled;\r
-    }\r
-\r
-    int getItemIndexAtY(int y) {\r
-        if (y < 0) {\r
-            return -1;\r
-        }\r
-        if (_itemHeight == CUSTOM_ITEM_HEIGHT) {\r
-            int ySum = 0;\r
-            for (int idx = _topIndex; idx < _data._expandedItems.length; idx++) {\r
-                ySum += _data._expandedItems[idx].itemHeight;\r
-                if (y < ySum) {\r
-                    return idx;\r
-                }\r
-            }\r
-            return -1;\r
-        }\r
-        int idx = y / _itemHeight;\r
-        idx += _topIndex;\r
-        if (idx < _data._expandedItems.length) {\r
-            return idx;\r
-        }\r
-        return -1;\r
-    }\r
-\r
-    boolean isOverSplitLine(int x) {\r
-        if (x < 0 || null == _timeProvider) {\r
-            return false;\r
-        }\r
-        int w = 4;\r
-        int nameWidth = _timeProvider.getNameSpace();\r
-        if (x > nameWidth - w && x < nameWidth + w) {\r
-            return true;\r
-        }\r
-        return false;\r
-    }\r
-\r
-    ITimeGraphEntry getEntry(Point pt) {\r
-        int idx = getItemIndexAtY(pt.y);\r
-        return idx >= 0 ? _data._expandedItems[idx]._trace : null;\r
-    }\r
-\r
-    long getTimeAtX(int x) {\r
-        if (null == _timeProvider) {\r
-            return -1;\r
-        }\r
-        long hitTime = -1;\r
-        Point size = getCtrlSize();\r
-        long time0 = _timeProvider.getTime0();\r
-        long time1 = _timeProvider.getTime1();\r
-        int nameWidth = _timeProvider.getNameSpace();\r
-        x -= nameWidth;\r
-        int timeWidth = size.x - nameWidth - RIGHT_MARGIN;\r
-        if (x >= 0 && size.x >= nameWidth) {\r
-            if (time1 - time0 > timeWidth) {\r
-                // nanosecond smaller than one pixel: use the first integer nanosecond of this pixel's time range\r
-                hitTime = time0 + (long) Math.ceil((time1 - time0) * ((double) x / timeWidth));\r
-            } else {\r
-                // nanosecond greater than one pixel: use the nanosecond that covers this pixel start position\r
-                hitTime = time0 + (long) Math.floor((time1 - time0) * ((double) x / timeWidth));\r
-            }\r
-        }\r
-        return hitTime;\r
-    }\r
-\r
-    void selectItem(int idx, boolean addSelection) {\r
-        boolean changed = false;\r
-        if (addSelection) {\r
-            if (idx >= 0 && idx < _data._expandedItems.length) {\r
-                Item item = _data._expandedItems[idx];\r
-                changed = (item._selected == false);\r
-                item._selected = true;\r
-            }\r
-        } else {\r
-            for (int i = 0; i < _data._expandedItems.length; i++) {\r
-                Item item = _data._expandedItems[i];\r
-                if ((i == idx && !item._selected) || (idx == -1 && item._selected)) {\r
-                    changed = true;\r
-                }\r
-                item._selected = i == idx;\r
-            }\r
-        }\r
-        changed |= ensureVisibleItem(idx, true);\r
-        if (changed) {\r
-            redraw();\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Callback for item selection\r
-     *\r
-     * @param trace\r
-     *            The entry matching the trace\r
-     * @param addSelection\r
-     *            If the selection is added or removed\r
-     */\r
-    public void selectItem(ITimeGraphEntry trace, boolean addSelection) {\r
-        int idx = _data.findItemIndex(trace);\r
-        selectItem(idx, addSelection);\r
-    }\r
-\r
-    /**\r
-     * Retrieve the number of entries shown per page.\r
-     *\r
-     * @return The count\r
-     */\r
-    public int countPerPage() {\r
-        int height = getCtrlSize().y;\r
-        int count = 0;\r
-        if (_itemHeight == CUSTOM_ITEM_HEIGHT) {\r
-            int ySum = 0;\r
-            for (int idx = _topIndex; idx < _data._expandedItems.length; idx++) {\r
-                ySum += _data._expandedItems[idx].itemHeight;\r
-                if (ySum >= height) {\r
-                    return count;\r
-                }\r
-                count++;\r
-            }\r
-            for (int idx = _topIndex - 1; idx >= 0; idx--) {\r
-                ySum += _data._expandedItems[idx].itemHeight;\r
-                if (ySum >= height) {\r
-                    return count;\r
-                }\r
-                count++;\r
-            }\r
-            return count;\r
-        }\r
-        if (height > 0) {\r
-            count = height / _itemHeight;\r
-        }\r
-        return count;\r
-    }\r
-\r
-    /**\r
-     * Get the index of the top element\r
-     *\r
-     * @return The index\r
-     */\r
-    public int getTopIndex() {\r
-        return _topIndex;\r
-    }\r
-\r
-    /**\r
-     * Get the number of expanded items\r
-     *\r
-     * @return The count of expanded items\r
-     */\r
-    public int getExpandedElementCount() {\r
-        return _data._expandedItems.length;\r
-    }\r
-\r
-    /**\r
-     * Get an array of all expanded elements\r
-     *\r
-     * @return The expanded elements\r
-     */\r
-    public ITimeGraphEntry[] getExpandedElements() {\r
-        ArrayList<ITimeGraphEntry> elements = new ArrayList<ITimeGraphEntry>();\r
-        for (Item item : _data._expandedItems) {\r
-            elements.add(item._trace);\r
-        }\r
-        return elements.toArray(new ITimeGraphEntry[0]);\r
-    }\r
-\r
-    Point getCtrlSize() {\r
-        Point size = getSize();\r
-        if (getHorizontalBar().isVisible()) {\r
-            size.y -= getHorizontalBar().getSize().y;\r
-        }\r
-        return size;\r
-    }\r
-\r
-    Rectangle getNameRect(Rectangle bound, int idx, int nameWidth) {\r
-        int x = bound.x;\r
-        int y = bound.y + (idx - _topIndex) * _itemHeight;\r
-        int width = nameWidth;\r
-        int height = _itemHeight;\r
-        if (_itemHeight == CUSTOM_ITEM_HEIGHT) {\r
-            int ySum = 0;\r
-            for (int i = _topIndex; i < idx; i++) {\r
-                ySum += _data._expandedItems[i].itemHeight;\r
-            }\r
-            y = bound.y + ySum;\r
-            height = _data._expandedItems[idx].itemHeight;\r
-        }\r
-        return new Rectangle(x, y, width, height);\r
-    }\r
-\r
-    Rectangle getStatesRect(Rectangle bound, int idx, int nameWidth) {\r
-        int x = bound.x + nameWidth;\r
-        int y = bound.y + (idx - _topIndex) * _itemHeight;\r
-        int width = bound.width - x;\r
-        int height = _itemHeight;\r
-        if (_itemHeight == CUSTOM_ITEM_HEIGHT) {\r
-            int ySum = 0;\r
-            for (int i = _topIndex; i < idx; i++) {\r
-                ySum += _data._expandedItems[i].itemHeight;\r
-            }\r
-            y = bound.y + ySum;\r
-            height = _data._expandedItems[idx].itemHeight;\r
-        }\r
-        return new Rectangle(x, y, width, height);\r
-    }\r
-\r
-    @Override\r
-    void paint(Rectangle bounds, PaintEvent e) {\r
-        GC gc = e.gc;\r
-        gc.setBackground(_colors.getColor(TimeGraphColorScheme.BACKGROUND));\r
-        drawBackground(gc, bounds.x, bounds.y, bounds.width, bounds.height);\r
-\r
-        if (bounds.width < 2 || bounds.height < 2 || null == _timeProvider) {\r
-            return;\r
-        }\r
-\r
-        _idealNameSpace = 0;\r
-        int nameSpace = _timeProvider.getNameSpace();\r
-\r
-        // draw empty name space background\r
-        gc.setBackground(_colors.getBkColor(false, false, true));\r
-        drawBackground(gc, bounds.x, bounds.y, nameSpace, bounds.height);\r
-\r
-        drawItems(bounds, _timeProvider, _data._expandedItems, _topIndex, nameSpace, gc);\r
-\r
-        // draw selected time\r
-        long time0 = _timeProvider.getTime0();\r
-        long time1 = _timeProvider.getTime1();\r
-        long selectedTime = _timeProvider.getSelectedTime();\r
-        double pixelsPerNanoSec = (bounds.width - nameSpace <= RIGHT_MARGIN) ? 0 : (double) (bounds.width - nameSpace - RIGHT_MARGIN) / (time1 - time0);\r
-        int x = bounds.x + nameSpace + (int) ((selectedTime - time0) * pixelsPerNanoSec);\r
-        if (x >= nameSpace && x < bounds.x + bounds.width) {\r
-            gc.setForeground(_colors.getColor(TimeGraphColorScheme.SELECTED_TIME));\r
-            gc.drawLine(x, bounds.y, x, bounds.y + bounds.height);\r
-        }\r
-\r
-        // draw drag line, no line if name space is 0.\r
-        if (DRAG_SPLIT_LINE == _dragState) {\r
-            gc.setForeground(_colors.getColor(TimeGraphColorScheme.BLACK));\r
-            gc.drawLine(bounds.x + nameSpace, bounds.y, bounds.x + nameSpace, bounds.y + bounds.height - 1);\r
-        } else if (DRAG_NONE == _dragState && _mouseOverSplitLine && _timeProvider.getNameSpace() > 0) {\r
-            gc.setForeground(_colors.getColor(TimeGraphColorScheme.RED));\r
-            gc.drawLine(bounds.x + nameSpace, bounds.y, bounds.x + nameSpace, bounds.y + bounds.height - 1);\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Draw many items at once\r
-     *\r
-     * @param bounds\r
-     *            The rectangle of the area\r
-     * @param timeProvider\r
-     *            The time provider\r
-     * @param items\r
-     *            The array items to draw\r
-     * @param topIndex\r
-     *            The index of the first element to draw\r
-     * @param nameSpace\r
-     *            The width reserved for the names\r
-     * @param gc\r
-     *            Reference to the SWT GC object\r
-     */\r
-    public void drawItems(Rectangle bounds, ITimeDataProvider timeProvider,\r
-            Item[] items, int topIndex, int nameSpace, GC gc) {\r
-        for (int i = topIndex; i < items.length; i++) {\r
-            Item item = items[i];\r
-            drawItem(item, bounds, timeProvider, i, nameSpace, gc);\r
-        }\r
-        fTimeGraphProvider.postDrawControl(bounds, gc);\r
-    }\r
-\r
-    /**\r
-     * Draws the item\r
-     *\r
-     * @param item the item to draw\r
-     * @param bounds the container rectangle\r
-     * @param i the item index\r
-     * @param nameSpace the name space\r
-     * @param gc\r
-     */\r
-    protected void drawItem(Item item, Rectangle bounds, ITimeDataProvider timeProvider, int i, int nameSpace, GC gc) {\r
-        ITimeGraphEntry entry = item._trace;\r
-        long time0 = timeProvider.getTime0();\r
-        long time1 = timeProvider.getTime1();\r
-        long selectedTime = timeProvider.getSelectedTime();\r
-\r
-        Rectangle nameRect = getNameRect(bounds, i, nameSpace);\r
-        if (nameRect.y >= bounds.y + bounds.height) {\r
-            return;\r
-        }\r
-\r
-        if (! item._trace.hasTimeEvents()) {\r
-            Rectangle statesRect = getStatesRect(bounds, i, nameSpace);\r
-            nameRect.width += statesRect.width;\r
-            drawName(item, nameRect, gc);\r
-        } else {\r
-            drawName(item, nameRect, gc);\r
-        }\r
-        Rectangle rect = getStatesRect(bounds, i, nameSpace);\r
-        if (rect.isEmpty()) {\r
-            fTimeGraphProvider.postDrawEntry(entry, rect, gc);\r
-            return;\r
-        }\r
-        if (time1 <= time0) {\r
-            gc.setBackground(_colors.getBkColor(false, false, false));\r
-            gc.fillRectangle(rect);\r
-            fTimeGraphProvider.postDrawEntry(entry, rect, gc);\r
-            return;\r
-        }\r
-\r
-        // Initialize _rect1 to same values as enclosing rectangle rect\r
-        Rectangle stateRect = Utils.clone(rect);\r
-        boolean selected = item._selected;\r
-        // K pixels per second\r
-        double pixelsPerNanoSec = (rect.width <= RIGHT_MARGIN) ? 0 : (double) (rect.width - RIGHT_MARGIN) / (time1 - time0);\r
-\r
-        if (item._trace.hasTimeEvents()) {\r
-            fillSpace(rect, gc, selected);\r
-            // Drawing rectangle is smaller than reserved space\r
-            stateRect.y += 3;\r
-            stateRect.height -= 6;\r
-\r
-            long maxDuration = (timeProvider.getTimeSpace() == 0) ? Long.MAX_VALUE : 1 * (time1 - time0) / timeProvider.getTimeSpace();\r
-            Iterator<ITimeEvent> iterator = entry.getTimeEventsIterator(time0, time1, maxDuration);\r
-\r
-            int lastX = -1;\r
-            while (iterator.hasNext()) {\r
-                ITimeEvent event = iterator.next();\r
-                int x = rect.x + (int) ((event.getTime() - time0) * pixelsPerNanoSec);\r
-                int xEnd = rect.x + (int) ((event.getTime() + event.getDuration() - time0) * pixelsPerNanoSec);\r
-                if (x >= rect.x + rect.width || xEnd < rect.x) {\r
-                    // event is out of bounds\r
-                    continue;\r
-                }\r
-                xEnd = Math.min(rect.x + rect.width, xEnd);\r
-                stateRect.x = Math.max(rect.x, x);\r
-                stateRect.width = Math.max(0, xEnd - stateRect.x + 1);\r
-                if (stateRect.x == lastX) {\r
-                    stateRect.width -= 1;\r
-                    if (stateRect.width > 0) {\r
-                        gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));\r
-                        gc.drawPoint(stateRect.x, stateRect.y - 2);\r
-                        stateRect.x += 1;\r
-                    }\r
-                } else {\r
-                    lastX = x;\r
-                }\r
-                boolean timeSelected = selectedTime >= event.getTime() && selectedTime < event.getTime() + event.getDuration();\r
-                drawState(_colors, event, stateRect, gc, selected, timeSelected);\r
-            }\r
-        }\r
-        fTimeGraphProvider.postDrawEntry(entry, rect, gc);\r
-    }\r
-\r
-    protected void drawName(Item item, Rectangle bounds, GC gc) {\r
-        boolean hasTimeEvents = item._trace.hasTimeEvents();\r
-        if (! hasTimeEvents) {\r
-            gc.setBackground(_colors.getBkColorGroup(item._selected, _isInFocus));\r
-            gc.fillRectangle(bounds);\r
-            if (item._selected && _isInFocus) {\r
-                gc.setForeground(_colors.getBkColor(item._selected, _isInFocus, false));\r
-                gc.drawRectangle(bounds.x, bounds.y, bounds.width - 1, bounds.height - 1);\r
-            }\r
-        } else {\r
-            gc.setBackground(_colors.getBkColor(item._selected, _isInFocus, true));\r
-            gc.setForeground(_colors.getFgColor(item._selected, _isInFocus));\r
-            gc.fillRectangle(bounds);\r
-        }\r
-\r
-        // No name to be drawn\r
-        if (_timeProvider.getNameSpace() == 0) {\r
-            return;\r
-        }\r
-\r
-        int leftMargin = MARGIN + item.level * EXPAND_SIZE;\r
-        if (item._hasChildren) {\r
-            gc.setForeground(_colors.getFgColorGroup(false, false));\r
-            gc.setBackground(_colors.getBkColor(false, false, false));\r
-            Rectangle rect = Utils.clone(bounds);\r
-            rect.x += leftMargin;\r
-            rect.y += (bounds.height - EXPAND_SIZE) / 2;\r
-            rect.width = EXPAND_SIZE;\r
-            rect.height = EXPAND_SIZE;\r
-            gc.fillRectangle(rect);\r
-            gc.drawRectangle(rect.x, rect.y, rect.width - 1, rect.height - 1);\r
-            int midy = rect.y + rect.height / 2;\r
-            gc.drawLine(rect.x + 2, midy, rect.x + rect.width - 3, midy);\r
-            if (!item._expanded) {\r
-                int midx = rect.x + rect.width / 2;\r
-                gc.drawLine(midx, rect.y + 2, midx, rect.y + rect.height - 3);\r
-            }\r
-        }\r
-        leftMargin += EXPAND_SIZE + MARGIN;\r
-\r
-        Image img = fTimeGraphProvider.getItemImage(item._trace);\r
-        if (img != null) {\r
-            // draw icon\r
-            int imgHeight = img.getImageData().height;\r
-            int imgWidth = img.getImageData().width;\r
-            int x = leftMargin;\r
-            int y = bounds.y + (bounds.height - imgHeight) / 2;\r
-            gc.drawImage(img, x, y);\r
-            leftMargin += imgWidth + MARGIN;\r
-        }\r
-        String name = item._name;\r
-        Point size = gc.stringExtent(name);\r
-        if (_idealNameSpace < leftMargin + size.x + MARGIN) {\r
-            _idealNameSpace = leftMargin + size.x + MARGIN;\r
-        }\r
-        if (hasTimeEvents) {\r
-            // cut long string with "..."\r
-            int width = bounds.width - leftMargin;\r
-            int cuts = 0;\r
-            while (size.x > width && name.length() > 1) {\r
-                cuts++;\r
-                name = name.substring(0, name.length() - 1);\r
-                size = gc.stringExtent(name + "..."); //$NON-NLS-1$\r
-            }\r
-            if (cuts > 0) {\r
-                name += "..."; //$NON-NLS-1$\r
-            }\r
-        }\r
-        Rectangle rect = Utils.clone(bounds);\r
-        rect.x += leftMargin;\r
-        rect.width -= leftMargin;\r
-        // draw text\r
-        if (rect.width > 0) {\r
-            rect.y += (bounds.height - gc.stringExtent(name).y) / 2;\r
-            gc.setForeground(_colors.getFgColor(item._selected, _isInFocus));\r
-            int textWidth = Utils.drawText(gc, name, rect, true);\r
-            leftMargin += textWidth + MARGIN;\r
-            rect.y -= 2;\r
-\r
-            if (hasTimeEvents) {\r
-                // draw middle line\r
-                int x = bounds.x + leftMargin;\r
-                int width = bounds.width - x;\r
-                int midy = bounds.y + bounds.height / 2;\r
-                gc.setForeground(_colors.getColor(TimeGraphColorScheme.MID_LINE));\r
-                gc.drawLine(x, midy, x + width, midy);\r
-            }\r
-        }\r
-    }\r
-\r
-    protected void drawState(TimeGraphColorScheme colors, ITimeEvent event,\r
-            Rectangle rect, GC gc, boolean selected, boolean timeSelected) {\r
-\r
-        int colorIdx = fTimeGraphProvider.getStateTableIndex(event);\r
-        if (colorIdx < 0) {\r
-            return;\r
-        }\r
-        boolean visible = rect.width == 0 ? false : true;\r
-\r
-        if (visible) {\r
-            Color stateColor = null;\r
-            if (colorIdx < fEventColorMap.length) {\r
-                stateColor = fEventColorMap[colorIdx];\r
-            } else {\r
-                stateColor = Display.getDefault().getSystemColor(SWT.COLOR_BLACK);\r
-            }\r
-\r
-            timeSelected = timeSelected && selected;\r
-            if (timeSelected) {\r
-                // modify the color?\r
-            }\r
-            // fill all rect area\r
-            gc.setBackground(stateColor);\r
-            gc.fillRectangle(rect);\r
-            // get the border color?\r
-            gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));\r
-\r
-            // draw bounds\r
-            if (!timeSelected) {\r
-                // Draw the top and bottom borders i.e. no side borders\r
-                // top\r
-                gc.drawLine(rect.x, rect.y, rect.x + rect.width - 1, rect.y);\r
-                // bottom\r
-                gc.drawLine(rect.x, rect.y + rect.height - 1, rect.x + rect.width - 1, rect.y + rect.height - 1);\r
-            }\r
-        } else {\r
-            gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));\r
-            gc.drawPoint(rect.x, rect.y - 2);\r
-            /*\r
-            // selected rectangle area is not visible but can be represented\r
-            // with a broken vertical line of specified width.\r
-            int width = 1;\r
-            rect.width = width;\r
-            gc.setForeground(stateColor);\r
-            int s = gc.getLineStyle();\r
-            int w = gc.getLineWidth();\r
-            gc.setLineStyle(SWT.LINE_DOT);\r
-            gc.setLineWidth(width);\r
-            // Trace.debug("Rectangle not visible, drawing vertical line with: "\r
-            // + rect.x + "," + rect.y + "," + rect.x + "," + rect.y\r
-            // + rect.height);\r
-            gc.drawLine(rect.x, rect.y, rect.x, rect.y + rect.height - 1);\r
-            gc.setLineStyle(s);\r
-            gc.setLineWidth(w);\r
-            if (!timeSelected) {\r
-                gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));\r
-                gc.drawPoint(rect.x, rect.y);\r
-                gc.drawPoint(rect.x, rect.y + rect.height - 1);\r
-            }\r
-            */\r
-        }\r
-        fTimeGraphProvider.postDrawEvent(event, rect, gc);\r
-    }\r
-\r
-    protected void fillSpace(Rectangle rect, GC gc, boolean selected) {\r
-        gc.setBackground(_colors.getBkColor(selected, _isInFocus, false));\r
-        gc.fillRectangle(rect);\r
-        // draw middle line\r
-        gc.setForeground(_colors.getColor(TimeGraphColorScheme.MID_LINE));\r
-        int midy = rect.y + rect.height / 2;\r
-        gc.drawLine(rect.x, midy, rect.x + rect.width, midy);\r
-    }\r
-\r
-    @Override\r
-    public void keyTraversed(TraverseEvent e) {\r
-        if ((e.detail == SWT.TRAVERSE_TAB_NEXT) || (e.detail == SWT.TRAVERSE_TAB_PREVIOUS)) {\r
-            e.doit = true;\r
-        }\r
-    }\r
-\r
-    @Override\r
-    public void keyPressed(KeyEvent e) {\r
-        int idx = -1;\r
-        if (_data._expandedItems.length == 0) {\r
-            return;\r
-        }\r
-        if (SWT.HOME == e.keyCode) {\r
-            idx = 0;\r
-        } else if (SWT.END == e.keyCode) {\r
-            idx = _data._expandedItems.length - 1;\r
-        } else if (SWT.ARROW_DOWN == e.keyCode) {\r
-            idx = getSelectedIndex();\r
-            if (idx < 0) {\r
-                idx = 0;\r
-            } else if (idx < _data._expandedItems.length - 1) {\r
-                idx++;\r
-            }\r
-        } else if (SWT.ARROW_UP == e.keyCode) {\r
-            idx = getSelectedIndex();\r
-            if (idx < 0) {\r
-                idx = 0;\r
-            } else if (idx > 0) {\r
-                idx--;\r
-            }\r
-        } else if (SWT.ARROW_LEFT == e.keyCode) {\r
-            selectPrevEvent();\r
-        } else if (SWT.ARROW_RIGHT == e.keyCode) {\r
-            selectNextEvent();\r
-        } else if (SWT.PAGE_DOWN == e.keyCode) {\r
-            int page = countPerPage();\r
-            idx = getSelectedIndex();\r
-            if (idx < 0) {\r
-                idx = 0;\r
-            }\r
-            idx += page;\r
-            if (idx >= _data._expandedItems.length) {\r
-                idx = _data._expandedItems.length - 1;\r
-            }\r
-        } else if (SWT.PAGE_UP == e.keyCode) {\r
-            int page = countPerPage();\r
-            idx = getSelectedIndex();\r
-            if (idx < 0) {\r
-                idx = 0;\r
-            }\r
-            idx -= page;\r
-            if (idx < 0) {\r
-                idx = 0;\r
-            }\r
-        } else if (SWT.CR == e.keyCode) {\r
-            idx = getSelectedIndex();\r
-            if (idx >= 0) {\r
-                if (_data._expandedItems[idx]._hasChildren) {\r
-                    toggle(idx);\r
-                } else {\r
-                    fireDefaultSelection();\r
-                }\r
-            }\r
-            idx = -1;\r
-        }\r
-        if (idx >= 0) {\r
-            selectItem(idx, false);\r
-            fireSelectionChanged();\r
-        }\r
-    }\r
-\r
-    @Override\r
-    public void keyReleased(KeyEvent e) {\r
-    }\r
-\r
-    @Override\r
-    public void focusGained(FocusEvent e) {\r
-        _isInFocus = true;\r
-        if (mouseScrollFilterListener == null) {\r
-            mouseScrollFilterListener = new Listener() {\r
-                // This filter is used to prevent horizontal scrolling of the view\r
-                // when the mouse wheel is used to zoom\r
-                @Override\r
-                public void handleEvent(Event event) {\r
-                    event.doit = false;\r
-                }\r
-            };\r
-            getDisplay().addFilter(SWT.MouseWheel, mouseScrollFilterListener);\r
-        }\r
-        redraw();\r
-    }\r
-\r
-    @Override\r
-    public void focusLost(FocusEvent e) {\r
-        _isInFocus = false;\r
-        if (mouseScrollFilterListener != null) {\r
-            getDisplay().removeFilter(SWT.MouseWheel, mouseScrollFilterListener);\r
-            mouseScrollFilterListener = null;\r
-        }\r
-        if (DRAG_NONE != _dragState) {\r
-            setCapture(false);\r
-            _dragState = DRAG_NONE;\r
-        }\r
-        redraw();\r
-    }\r
-\r
-    /**\r
-     * @return If the current view is focused\r
-     */\r
-    public boolean isInFocus() {\r
-        return _isInFocus;\r
-    }\r
-\r
-    /**\r
-     * Provide the possibility to control the wait cursor externally e.g. data\r
-     * requests in progress\r
-     *\r
-     * @param waitInd Should we wait indefinitely?\r
-     */\r
-    public void waitCursor(boolean waitInd) {\r
-        // Update cursor as indicated\r
-        if (waitInd) {\r
-            setCursor(_WaitCursor);\r
-            _isWaitCursor = true;\r
-        } else {\r
-            setCursor(null);\r
-            _isWaitCursor = false;\r
-        }\r
-\r
-        // Get ready for next mouse move\r
-        _isDragCursor3 = false;\r
-    }\r
-\r
-    /**\r
-     * <p>\r
-     * If the x, y position is over the vertical split line (name to time\r
-     * ranges), then change the cursor to a drag cursor to indicate the user the\r
-     * possibility of resizing\r
-     * </p>\r
-     *\r
-     * @param x\r
-     * @param y\r
-     */\r
-    void updateCursor(int x, int y) {\r
-        // if Wait cursor not active, check for the need to change to a drag\r
-        // cursor\r
-        if (_isWaitCursor == false) {\r
-            boolean isSplitLine = isOverSplitLine(x);\r
-            // No dragcursor is name space is fixed to zero\r
-            if (isSplitLine && !_isDragCursor3 && _timeProvider.getNameSpace() > 0) {\r
-                setCursor(_dragCursor3);\r
-                _isDragCursor3 = true;\r
-            } else if (!isSplitLine && _isDragCursor3) {\r
-                setCursor(null);\r
-                _isDragCursor3 = false;\r
-            }\r
-        }\r
-    }\r
-\r
-    @Override\r
-    public void mouseMove(MouseEvent e) {\r
-        if (null == _timeProvider) {\r
-            return;\r
-        }\r
-        Point size = getCtrlSize();\r
-        if (DRAG_TRACE_ITEM == _dragState) {\r
-            int nameWidth = _timeProvider.getNameSpace();\r
-            int x = e.x - nameWidth;\r
-            if (x > 0 && size.x > nameWidth && _dragX != x) {\r
-                _dragX = x;\r
-                double pixelsPerNanoSec = (size.x - nameWidth <= RIGHT_MARGIN) ? 0 : (double) (size.x - nameWidth - RIGHT_MARGIN) / (_time1bak - _time0bak);\r
-                long timeDelta = (long) ((pixelsPerNanoSec == 0) ? 0 : ((_dragX - _dragX0) / pixelsPerNanoSec));\r
-                long time1 = _time1bak - timeDelta;\r
-                long maxTime = _timeProvider.getMaxTime();\r
-                if (time1 > maxTime) {\r
-                    time1 = maxTime;\r
-                }\r
-                long time0 = time1 - (_time1bak - _time0bak);\r
-                if (time0 < _timeProvider.getMinTime()) {\r
-                    time0 = _timeProvider.getMinTime();\r
-                    time1 = time0 + (_time1bak - _time0bak);\r
-                }\r
-                _timeProvider.setStartFinishTime(time0, time1);\r
-            }\r
-        } else if (DRAG_SPLIT_LINE == _dragState) {\r
-            _dragX = e.x;\r
-            _timeProvider.setNameSpace(e.x);\r
-        } else if (DRAG_NONE == _dragState) {\r
-            boolean mouseOverSplitLine = isOverSplitLine(e.x);\r
-            if (_mouseOverSplitLine != mouseOverSplitLine) {\r
-                redraw();\r
-            }\r
-            _mouseOverSplitLine = mouseOverSplitLine;\r
-        }\r
-        updateCursor(e.x, e.y);\r
-    }\r
-\r
-    @Override\r
-    public void mouseDoubleClick(MouseEvent e) {\r
-        if (null == _timeProvider) {\r
-            return;\r
-        }\r
-        if (1 == e.button) {\r
-            if (isOverSplitLine(e.x) && _timeProvider.getNameSpace() != 0) {\r
-                _timeProvider.setNameSpace(_idealNameSpace);\r
-                boolean mouseOverSplitLine = isOverSplitLine(e.x);\r
-                if (_mouseOverSplitLine != mouseOverSplitLine) {\r
-                    redraw();\r
-                }\r
-                _mouseOverSplitLine = mouseOverSplitLine;\r
-                return;\r
-            }\r
-            int idx = getItemIndexAtY(e.y);\r
-            if (idx >= 0) {\r
-                selectItem(idx, false);\r
-                fireDefaultSelection();\r
-            }\r
-        }\r
-    }\r
-\r
-    @Override\r
-    public void mouseDown(MouseEvent e) {\r
-        if (null == _timeProvider) {\r
-            return;\r
-        }\r
-        int idx;\r
-        if (1 == e.button) {\r
-            int nameSpace = _timeProvider.getNameSpace();\r
-            if (nameSpace != 0) {\r
-                if (isOverSplitLine(e.x)) {\r
-                    _dragState = DRAG_SPLIT_LINE;\r
-                    _dragX = _dragX0 = e.x;\r
-                    _time0bak = _timeProvider.getTime0();\r
-                    _time1bak = _timeProvider.getTime1();\r
-                    redraw();\r
-                    return;\r
-                }\r
-            }\r
-\r
-            idx = getItemIndexAtY(e.y);\r
-            if (idx >= 0) {\r
-                Item item = _data._expandedItems[idx];\r
-                if (item._hasChildren && e.x < nameSpace && e.x < MARGIN + (item.level + 1) * EXPAND_SIZE) {\r
-                    toggle(idx);\r
-                } else {\r
-                    long hitTime = getTimeAtX(e.x);\r
-                    if (hitTime >= 0) {\r
-                        // _timeProvider.setSelectedTimeInt(hitTime, false);\r
-                        setCapture(true);\r
-                        _dragState = DRAG_TRACE_ITEM;\r
-                        _dragX = _dragX0 = e.x - nameSpace;\r
-                        _time0bak = _timeProvider.getTime0();\r
-                        _time1bak = _timeProvider.getTime1();\r
-                    }\r
-                }\r
-                selectItem(idx, false);\r
-                fireSelectionChanged();\r
-            } else {\r
-                selectItem(idx, false); // clear selection\r
-                redraw();\r
-                fireSelectionChanged();\r
-            }\r
-        }\r
-    }\r
-\r
-    @Override\r
-    public void mouseUp(MouseEvent e) {\r
-        if (DRAG_NONE != _dragState) {\r
-            setCapture(false);\r
-            if (DRAG_TRACE_ITEM == _dragState) {\r
-                // Notify time provider to check the need for listener\r
-                // notification\r
-                _timeProvider.notifyStartFinishTime();\r
-                if (_dragX == _dragX0) { // click without drag\r
-                    long time = getTimeAtX(e.x);\r
-                    _timeProvider.setSelectedTimeNotify(time, false);\r
-                }\r
-            } else if (DRAG_SPLIT_LINE == _dragState) {\r
-                redraw();\r
-            }\r
-            _dragState = DRAG_NONE;\r
-        }\r
-    }\r
-\r
-    @Override\r
-    public void mouseEnter(MouseEvent e) {\r
-    }\r
-\r
-    @Override\r
-    public void mouseExit(MouseEvent e) {\r
-        if (_mouseOverSplitLine) {\r
-            _mouseOverSplitLine = false;\r
-            redraw();\r
-        }\r
-    }\r
-\r
-    @Override\r
-    public void mouseHover(MouseEvent e) {\r
-    }\r
-\r
-    @Override\r
-    public void mouseScrolled(MouseEvent e) {\r
-        if ((mouseScrollFilterListener == null) || _dragState != DRAG_NONE) {\r
-            return;\r
-        }\r
-        boolean zoomScroll = false;\r
-        Point p = getParent().toControl(getDisplay().getCursorLocation());\r
-        Point parentSize = getParent().getSize();\r
-        if (p.x >= 0 && p.x < parentSize.x && p.y >= 0 && p.y < parentSize.y) {\r
-            // over the parent control\r
-            if (e.x > getCtrlSize().x) {\r
-                // over the horizontal scroll bar\r
-                zoomScroll = false;\r
-            } else if (e.y >= 0 && e.y < getCtrlSize().y && e.x < _timeProvider.getNameSpace()) {\r
-                // over the name space\r
-                zoomScroll = false;\r
-            } else {\r
-                zoomScroll = true;\r
-            }\r
-        }\r
-        if (zoomScroll && _timeProvider.getTime0() != _timeProvider.getTime1()) {\r
-            if (e.count > 0) {\r
-                zoom(true);\r
-            } else if (e.count < 0) {\r
-                zoom(false);\r
-            }\r
-        } else {\r
-            setTopIndex(getTopIndex() - e.count);\r
-        }\r
-    }\r
-\r
-    @Override\r
-    public void controlMoved(ControlEvent e) {\r
-    }\r
-\r
-    @Override\r
-    public void controlResized(ControlEvent e) {\r
-        adjustScrolls();\r
-    }\r
-\r
-    @Override\r
-    public void widgetDefaultSelected(SelectionEvent e) {\r
-    }\r
-\r
-    @Override\r
-    public void widgetSelected(SelectionEvent e) {\r
-        if (e.widget == getVerticalBar()) {\r
-            setTopIndex(getVerticalBar().getSelection());\r
-        } else if (e.widget == getHorizontalBar() && null != _timeProvider) {\r
-            int start = getHorizontalBar().getSelection();\r
-            long time0 = _timeProvider.getTime0();\r
-            long time1 = _timeProvider.getTime1();\r
-            long timeMin = _timeProvider.getMinTime();\r
-            long timeMax = _timeProvider.getMaxTime();\r
-            long delta = timeMax - timeMin;\r
-\r
-            long range = time1 - time0;\r
-            // _timeRangeFixed = true;\r
-            time0 = timeMin + Math.round(delta * ((double) start / H_SCROLLBAR_MAX));\r
-            time1 = time0 + range;\r
-\r
-            // TODO: Follow-up with Bug 310310\r
-            // In Linux SWT.DRAG is the only value received\r
-            // https://bugs.eclipse.org/bugs/show_bug.cgi?id=310310\r
-            if (e.detail == SWT.DRAG) {\r
-                _timeProvider.setStartFinishTime(time0, time1);\r
-            } else {\r
-                _timeProvider.setStartFinishTimeNotify(time0, time1);\r
-            }\r
-        }\r
-    }\r
-\r
-    /**\r
-     * @return The current visibility of the vertical scroll bar\r
-     */\r
-    public boolean isVisibleVerticalScroll() {\r
-        return _visibleVerticalScroll;\r
-    }\r
-\r
-    @Override\r
-    public int getBorderWidth() {\r
-        return _borderWidth;\r
-    }\r
-\r
-    /**\r
-     * Set the border width\r
-     *\r
-     * @param borderWidth\r
-     *            The width\r
-     */\r
-    public void setBorderWidth(int borderWidth) {\r
-        this._borderWidth = borderWidth;\r
-    }\r
-\r
-    /**\r
-     * @return The current height of the header row\r
-     */\r
-    public int getHeaderHeight() {\r
-        return _headerHeight;\r
-    }\r
-\r
-    /**\r
-     * Set the height of the header row\r
-     *\r
-     * @param headerHeight\r
-     *            The height\r
-     */\r
-    public void setHeaderHeight(int headerHeight) {\r
-        this._headerHeight = headerHeight;\r
-    }\r
-\r
-    /**\r
-     * @return The height of regular item rows\r
-     */\r
-    public int getItemHeight() {\r
-        return _itemHeight;\r
-    }\r
-\r
-    /**\r
-     * Set the height of regular itew rows\r
-     *\r
-     * @param rowHeight\r
-     *            The height\r
-     */\r
-    public void setItemHeight(int rowHeight) {\r
-        this._itemHeight = rowHeight;\r
-    }\r
-\r
-    /**\r
-     * Set the minimum item width\r
-     *\r
-     * @param width The minimum width\r
-     */\r
-    public void setMinimumItemWidth(int width) {\r
-        this._minimumItemWidth = width;\r
-    }\r
-\r
-    /**\r
-     * @return The minimum item width\r
-     */\r
-    public int getMinimumItemWidth() {\r
-        return _minimumItemWidth;\r
-    }\r
-\r
-    /**\r
-     * @return The entries that are currently filtered out\r
-     */\r
-    public Vector<ITimeGraphEntry> getFilteredOut() {\r
-        return _data.getFilteredOut();\r
-    }\r
-\r
-    // @Override\r
-    @Override\r
-    public void addSelectionChangedListener(ISelectionChangedListener listener) {\r
-        if (listener != null) {\r
-            if (!_selectionChangedListeners.contains(listener)) {\r
-                _selectionChangedListeners.add(listener);\r
-            }\r
-        }\r
-    }\r
-\r
-    // @Override\r
-    @Override\r
-    public void removeSelectionChangedListener(ISelectionChangedListener listener) {\r
-        if (listener != null) {\r
-            _selectionChangedListeners.remove(listener);\r
-        }\r
-    }\r
-\r
-    // @Override\r
-    @Override\r
-    public void setSelection(ISelection selection) {\r
-        if (selection instanceof TimeGraphSelection) {\r
-            TimeGraphSelection sel = (TimeGraphSelection) selection;\r
-            Object ob = sel.getFirstElement();\r
-            if (ob instanceof ITimeGraphEntry) {\r
-                ITimeGraphEntry trace = (ITimeGraphEntry) ob;\r
-                selectItem(trace, false);\r
-            }\r
-        }\r
-\r
-    }\r
-\r
-    private class ItemData {\r
-        public Item[] _expandedItems = new Item[0];\r
-        public Item[] _items = new Item[0];\r
-        private ITimeGraphEntry _traces[] = new ITimeGraphEntry[0];\r
-        private boolean traceFilter[] = new boolean[0];\r
-        private final Vector<ITimeGraphEntry> filteredOut = new Vector<ITimeGraphEntry>();\r
-        public ITimeGraphPresentationProvider provider;\r
-\r
-        public ItemData() {\r
-        }\r
-\r
-        Item findItem(ITimeGraphEntry entry) {\r
-            if (entry == null) {\r
-                return null;\r
-            }\r
-\r
-            for (int i = 0; i < _items.length; i++) {\r
-                Item item = _items[i];\r
-                if (item._trace == entry) {\r
-                    return item;\r
-                }\r
-            }\r
-\r
-            return null;\r
-        }\r
-\r
-        int findItemIndex(ITimeGraphEntry trace) {\r
-            if (trace == null) {\r
-                return -1;\r
-            }\r
-\r
-            for (int i = 0; i < _expandedItems.length; i++) {\r
-                Item item = _expandedItems[i];\r
-                if (item._trace == trace) {\r
-                    return i;\r
-                }\r
-            }\r
-\r
-            return -1;\r
-        }\r
-\r
-        public void refreshData() {\r
-            List<Item> itemList = new ArrayList<Item>();\r
-            filteredOut.clear();\r
-            for (int i = 0; i < _traces.length; i++) {\r
-                ITimeGraphEntry entry = _traces[i];\r
-                refreshData(itemList, null, 0, entry);\r
-            }\r
-            _items = itemList.toArray(new Item[0]);\r
-            updateExpandedItems();\r
-        }\r
-\r
-        private void refreshData(List<Item> itemList, Item parent, int level, ITimeGraphEntry entry) {\r
-            Item item = new Item(entry, entry.getName(), level);\r
-            if (parent != null) {\r
-                parent.children.add(item);\r
-            }\r
-            item.itemHeight = provider.getItemHeight(entry);\r
-            itemList.add(item);\r
-            if (entry.hasChildren()) {\r
-                item._expanded = true;\r
-                item._hasChildren = true;\r
-                for (ITimeGraphEntry child : entry.getChildren()) {\r
-                    refreshData(itemList, item, level + 1, child);\r
-                }\r
-            }\r
-        }\r
-\r
-        public void updateExpandedItems() {\r
-            List<Item> expandedItemList = new ArrayList<Item>();\r
-            for (int i = 0; i < _traces.length; i++) {\r
-                ITimeGraphEntry entry = _traces[i];\r
-                Item item = findItem(entry);\r
-                refreshExpanded(expandedItemList, item);\r
-            }\r
-            _expandedItems = expandedItemList.toArray(new Item[0]);\r
-        }\r
-\r
-        private void refreshExpanded(List<Item> expandedItemList, Item item) {\r
-            expandedItemList.add(item);\r
-            if (item._hasChildren && item._expanded) {\r
-                for (Item child : item.children) {\r
-                    refreshExpanded(expandedItemList, child);\r
-                }\r
-            }\r
-        }\r
-\r
-        public void refreshData(ITimeGraphEntry traces[]) {\r
-            if (traces == null || traces.length == 0) {\r
-                traceFilter = null;\r
-            } else if (traceFilter == null || traces.length != traceFilter.length) {\r
-                traceFilter = new boolean[traces.length];\r
-                java.util.Arrays.fill(traceFilter, true);\r
-            }\r
-\r
-            _traces = traces;\r
-            refreshData();\r
-        }\r
-\r
-        public ITimeGraphEntry[] getTraces() {\r
-            return _traces;\r
-        }\r
-\r
-        public boolean[] getTraceFilter() {\r
-            return traceFilter;\r
-        }\r
-\r
-        public Vector<ITimeGraphEntry> getFilteredOut() {\r
-            return filteredOut;\r
-        }\r
-    }\r
-\r
-    private class Item {\r
-        public boolean _expanded;\r
-        public boolean _selected;\r
-        public boolean _hasChildren;\r
-        public int itemHeight;\r
-        public int level;\r
-        public List<Item> children;\r
-        public String _name;\r
-        public ITimeGraphEntry _trace;\r
-\r
-        public Item(ITimeGraphEntry trace, String name, int level) {\r
-            this._trace = trace;\r
-            this._name = name;\r
-            this.level = level;\r
-            this.children = new ArrayList<Item>();\r
-        }\r
-\r
-        @Override\r
-        public String toString() {\r
-            return _name;\r
-        }\r
-    }\r
-\r
-}\r
-\r
+/*****************************************************************************
+ * Copyright (c) 2007, 2008 Intel Corporation, 2009, 2010, 2011, 2012 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:
+ *   Intel Corporation - Initial API and implementation
+ *   Ruslan A. Scherbakov, Intel - Initial API and implementation
+ *   Alvaro Sanchez-Leon - Updated for TMF
+ *   Patrick Tasse - Refactoring
+ *
+ *****************************************************************************/
+
+package org.eclipse.linuxtools.tmf.ui.widgets.timegraph.widgets;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.resource.LocalResourceManager;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.ITimeGraphPresentationProvider;
+import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.ITimeGraphTreeListener;
+import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.StateItem;
+import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.TimeGraphTreeExpansionEvent;
+import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.model.ITimeEvent;
+import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.events.MouseTrackListener;
+import org.eclipse.swt.events.MouseWheelListener;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.events.TraverseEvent;
+import org.eclipse.swt.events.TraverseListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Cursor;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+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.ScrollBar;
+
+/**
+ * Time graph control implementation
+ *
+ * @version 1.0
+ * @author Alvaro Sanchez-Leon
+ * @author Patrick Tasse
+ */
+public class TimeGraphControl extends TimeGraphBaseControl implements FocusListener, KeyListener, MouseMoveListener, MouseListener, MouseWheelListener, ControlListener, SelectionListener, MouseTrackListener, TraverseListener, ISelectionProvider {
+
+    private static final int DRAG_NONE = 0;
+    private static final int DRAG_TRACE_ITEM = 1;
+    private static final int DRAG_SPLIT_LINE = 2;
+    public static final boolean DEFAULT_DRAW_THREAD_JOIN = true;
+    public static final boolean DEFAULT_DRAW_THREAD_WAIT = true;
+    public static final boolean DEFAULT_DRAW_THREAD_RELEASE = true;
+    public static final int H_SCROLLBAR_MAX = Integer.MAX_VALUE - 1;
+    private static final int CUSTOM_ITEM_HEIGHT = -1; // get item height from provider
+
+    private static final double zoomCoeff = 1.5;
+
+    private ITimeDataProvider _timeProvider;
+    private boolean _isInFocus = false;
+    private boolean _isDragCursor3 = false;
+    private boolean _isWaitCursor = true;
+    private boolean _mouseOverSplitLine = false;
+    private int _itemHeight = CUSTOM_ITEM_HEIGHT;
+    private int _minimumItemWidth = 0;
+    private int _topIndex = 0;
+    private int _dragState = DRAG_NONE;
+    private int _dragX0 = 0;
+    private int _dragX = 0;
+    private int _idealNameSpace = 0;
+    // private double _timeStep = 10000000;
+    private long _time0bak;
+    private long _time1bak;
+    private ITimeGraphPresentationProvider fTimeGraphProvider = null;
+    private ItemData _data = null;
+    private List<SelectionListener> _selectionListeners;
+    private final List<ISelectionChangedListener> _selectionChangedListeners = new ArrayList<ISelectionChangedListener>();
+    private final List<ITimeGraphTreeListener> _treeListeners = new ArrayList<ITimeGraphTreeListener>();
+    private final Cursor _dragCursor3;
+    private final Cursor _WaitCursor;
+
+    // Vertical formatting formatting for the state control view
+    private final boolean _visibleVerticalScroll = true;
+    private int _borderWidth = 0;
+    private int _headerHeight = 0;
+
+    private Listener mouseScrollFilterListener;
+
+    protected LocalResourceManager fResourceManager = new LocalResourceManager(JFaceResources.getResources());
+    protected Color[] fEventColorMap = null;
+
+    private MouseScrollNotifier fMouseScrollNotifier;
+    private final Object fMouseScrollNotifierLock = new Object();
+    private class MouseScrollNotifier extends Thread {
+        private final static long DELAY = 400L;
+        private final static long POLLING_INTERVAL = 10L;
+        private long fLastScrollTime = Long.MAX_VALUE;
+
+        @Override
+        public void run() {
+            while ((System.currentTimeMillis() - fLastScrollTime) < DELAY) {
+                try {
+                    Thread.sleep(POLLING_INTERVAL);
+                } catch (Exception e) {
+                    return;
+                }
+            }
+            if (!isInterrupted()) {
+                Display.getDefault().asyncExec(new Runnable() {
+                    @Override
+                    public void run() {
+                        if (isDisposed()) {
+                            return;
+                        }
+                        _timeProvider.notifyStartFinishTime();
+                    }
+                });
+            }
+            synchronized (fMouseScrollNotifierLock) {
+                fMouseScrollNotifier = null;
+            }
+        }
+
+        public void mouseScrolled() {
+            fLastScrollTime = System.currentTimeMillis();
+        }
+    }
+
+    /**
+     * Standard constructor
+     *
+     * @param parent
+     *            The parent composite object
+     * @param colors
+     *            The color scheme to use
+     */
+    public TimeGraphControl(Composite parent, TimeGraphColorScheme colors) {
+
+        super(parent, colors, SWT.NO_BACKGROUND | SWT.H_SCROLL | SWT.DOUBLE_BUFFERED);
+
+        _data = new ItemData();
+
+        addFocusListener(this);
+        addMouseListener(this);
+        addMouseMoveListener(this);
+        addMouseTrackListener(this);
+        addMouseWheelListener(this);
+        addTraverseListener(this);
+        addKeyListener(this);
+        addControlListener(this);
+        ScrollBar scrollHor = getHorizontalBar();
+
+        if (scrollHor != null) {
+            scrollHor.addSelectionListener(this);
+        }
+
+        _dragCursor3 = new Cursor(super.getDisplay(), SWT.CURSOR_SIZEWE);
+        _WaitCursor = new Cursor(super.getDisplay(), SWT.CURSOR_WAIT);
+    }
+
+    @Override
+    public void dispose() {
+        super.dispose();
+        _dragCursor3.dispose();
+        _WaitCursor.dispose();
+        fResourceManager.dispose();
+    }
+
+    /**
+     * Sets the timegraph provider used by this timegraph viewer.
+     *
+     * @param timeGraphProvider the timegraph provider
+     */
+    public void setTimeGraphProvider(ITimeGraphPresentationProvider timeGraphProvider) {
+        fTimeGraphProvider = timeGraphProvider;
+        _data.provider = timeGraphProvider;
+
+        if (fEventColorMap != null) {
+            for (Color color : fEventColorMap) {
+                fResourceManager.destroyColor(color.getRGB());
+            }
+        }
+        StateItem[] stateItems = fTimeGraphProvider.getStateTable();
+        if (stateItems != null) {
+            fEventColorMap = new Color[stateItems.length];
+            for (int i = 0; i < stateItems.length; i++) {
+                fEventColorMap[i] = fResourceManager.createColor(stateItems[i].getStateColor());
+            }
+        } else {
+            fEventColorMap = new Color[] { };
+        }
+    }
+
+    /**
+     * Assign the given time provider
+     *
+     * @param timeProvider
+     *            The time provider
+     */
+    public void setTimeProvider(ITimeDataProvider timeProvider) {
+        _timeProvider = timeProvider;
+        adjustScrolls();
+        redraw();
+    }
+
+    /**
+     * Add a selection listener
+     *
+     * @param listener
+     *            The listener to add
+     */
+    public void addSelectionListener(SelectionListener listener) {
+        if (listener == null) {
+            SWT.error(SWT.ERROR_NULL_ARGUMENT);
+        }
+        if (null == _selectionListeners) {
+            _selectionListeners = new ArrayList<SelectionListener>();
+        }
+        _selectionListeners.add(listener);
+    }
+
+    /**
+     * Remove a selection listener
+     *
+     * @param listener
+     *            The listener to remove
+     */
+    public void removeSelectionListener(SelectionListener listener) {
+        if (null != _selectionListeners) {
+            _selectionListeners.remove(listener);
+        }
+    }
+
+    /**
+     * Selection changed callback
+     */
+    public void fireSelectionChanged() {
+        if (null != _selectionListeners) {
+            Iterator<SelectionListener> it = _selectionListeners.iterator();
+            while (it.hasNext()) {
+                SelectionListener listener = it.next();
+                listener.widgetSelected(null);
+            }
+        }
+    }
+
+    /**
+     * Default selection callback
+     */
+    public void fireDefaultSelection() {
+        if (null != _selectionListeners) {
+            Iterator<SelectionListener> it = _selectionListeners.iterator();
+            while (it.hasNext()) {
+                SelectionListener listener = it.next();
+                listener.widgetDefaultSelected(null);
+            }
+        }
+    }
+
+    /**
+     * Get the traces in the model
+     *
+     * @return The array of traces
+     */
+    public ITimeGraphEntry[] getTraces() {
+        return _data.getTraces();
+    }
+
+    /**
+     * Get the on/off trace filters
+     *
+     * @return The array of filters
+     */
+    public boolean[] getTraceFilter() {
+        return _data.getTraceFilter();
+    }
+
+    /**
+     * Refresh the data for the thing
+     */
+    public void refreshData() {
+        _data.refreshData();
+        adjustScrolls();
+        redraw();
+    }
+
+    /**
+     * Refresh data for the given traces
+     *
+     * @param traces
+     *            The traces to refresh
+     */
+    public void refreshData(ITimeGraphEntry[] traces) {
+        _data.refreshData(traces);
+        adjustScrolls();
+        redraw();
+    }
+
+    /**
+     * Adjust the scoll bars
+     */
+    public void adjustScrolls() {
+        if (null == _timeProvider) {
+            getHorizontalBar().setValues(0, 1, 1, 1, 1, 1);
+            return;
+        }
+
+        // HORIZONTAL BAR
+        // Visible window
+        long time0 = _timeProvider.getTime0();
+        long time1 = _timeProvider.getTime1();
+        // Time boundaries
+        long timeMin = _timeProvider.getMinTime();
+        long timeMax = _timeProvider.getMaxTime();
+
+        long delta = timeMax - timeMin;
+
+        int timePos = 0;
+        int thumb = H_SCROLLBAR_MAX;
+
+        if (delta != 0) {
+            // Thumb size (page size)
+            thumb = Math.max(1, (int) (H_SCROLLBAR_MAX * ((double) (time1 - time0) / delta)));
+            // At the beginning of visible window
+            timePos = (int) (H_SCROLLBAR_MAX * ((double) (time0 - timeMin) / delta));
+        }
+
+        // position, minimum, maximum, thumb size, increment (half page)t, page
+        // increment size (full page)
+        getHorizontalBar().setValues(timePos, 0, H_SCROLLBAR_MAX, thumb, Math.max(1, thumb / 2), Math.max(2, thumb));
+    }
+
+    boolean ensureVisibleItem(int idx, boolean redraw) {
+        boolean changed = false;
+        if (idx < 0) {
+            for (idx = 0; idx < _data._expandedItems.length; idx++) {
+                if (_data._expandedItems[idx]._selected) {
+                    break;
+                }
+            }
+        }
+        if (idx >= _data._expandedItems.length) {
+            return changed;
+        }
+        if (idx < _topIndex) {
+            setTopIndex(idx);
+            //FIXME:getVerticalBar().setSelection(_topItem);
+            if (redraw) {
+                redraw();
+            }
+            changed = true;
+        } else {
+            int page = countPerPage();
+            if (idx >= _topIndex + page) {
+                setTopIndex(idx - page + 1);
+                //FIXME:getVerticalBar().setSelection(_topItem);
+                if (redraw) {
+                    redraw();
+                }
+                changed = true;
+            }
+        }
+        return changed;
+    }
+
+    /**
+     * Assign the given index as the top one
+     *
+     * @param idx
+     *            The index
+     */
+    public void setTopIndex(int idx) {
+        idx = Math.min(idx, _data._expandedItems.length - countPerPage());
+        idx = Math.max(0,  idx);
+        _topIndex = idx;
+        redraw();
+    }
+
+    /**
+     * Set the expanded state of a given entry
+     *
+     * @param entry
+     *            The entry
+     * @param expanded
+     *            True if expanded, false if collapsed
+     */
+    public void setExpandedState(ITimeGraphEntry entry, boolean expanded) {
+        Item item = _data.findItem(entry);
+        if (item != null && item._expanded != expanded) {
+            item._expanded = expanded;
+            _data.updateExpandedItems();
+            redraw();
+        }
+    }
+
+    /**
+     * Collapses all nodes of the viewer's tree, starting with the root.
+     *
+     * @since 2.0
+     */
+    public void collapseAll() {
+        for (Item item : _data._items) {
+            item._expanded = false;
+        }
+        _data.updateExpandedItems();
+        redraw();
+    }
+
+    /**
+     * Expands all nodes of the viewer's tree, starting with the root.
+     *
+     * @since 2.0
+     */
+    public void expandAll() {
+        for (Item item : _data._items) {
+            item._expanded = true;
+        }
+        _data.updateExpandedItems();
+        redraw();
+    }
+
+    /**
+     * Add a tree listener
+     *
+     * @param listener
+     *            The listener to add
+     */
+    public void addTreeListener(ITimeGraphTreeListener listener) {
+        if (!_treeListeners.contains(listener)) {
+            _treeListeners.add(listener);
+        }
+    }
+
+    /**
+     * Remove a tree listener
+     *
+     * @param listener
+     *            The listener to remove
+     */
+    public void removeTreeListener(ITimeGraphTreeListener listener) {
+        if (_treeListeners.contains(listener)) {
+            _treeListeners.remove(listener);
+        }
+    }
+
+    /**
+     * Tree event callback
+     *
+     * @param entry
+     *            The affected entry
+     * @param expanded
+     *            The expanded state (true for expanded, false for collapsed)
+     */
+    public void fireTreeEvent(ITimeGraphEntry entry, boolean expanded) {
+        TimeGraphTreeExpansionEvent event = new TimeGraphTreeExpansionEvent(this, entry);
+        for (ITimeGraphTreeListener listener : _treeListeners) {
+            if (expanded) {
+                listener.treeExpanded(event);
+            } else {
+                listener.treeCollapsed(event);
+            }
+        }
+    }
+
+    @Override
+    public ISelection getSelection() {
+        TimeGraphSelection sel = new TimeGraphSelection();
+        ITimeGraphEntry trace = getSelectedTrace();
+        if (null != trace && null != _timeProvider) {
+            long selectedTime = _timeProvider.getSelectedTime();
+            ITimeEvent event = Utils.findEvent(trace, selectedTime, 0);
+            if (event != null) {
+                sel.add(event);
+            } else {
+                sel.add(trace);
+            }
+        }
+        return sel;
+    }
+
+    /**
+     * Get the selection object
+     *
+     * @return The selection
+     */
+    public ISelection getSelectionTrace() {
+        TimeGraphSelection sel = new TimeGraphSelection();
+        ITimeGraphEntry trace = getSelectedTrace();
+        if (null != trace) {
+            sel.add(trace);
+        }
+        return sel;
+    }
+
+    /**
+     * Enable/disable one of the traces in the model
+     *
+     * @param n
+     *            1 to enable it, -1 to disable. The method returns immediately
+     *            if another value is used.
+     */
+    public void selectTrace(int n) {
+        if ((n != 1) && (n != -1)) {
+            return;
+        }
+
+        boolean changed = false;
+        int lastSelection = -1;
+        for (int i = 0; i < _data._expandedItems.length; i++) {
+            Item item = _data._expandedItems[i];
+            if (item._selected) {
+                lastSelection = i;
+                if ((1 == n) && (i < _data._expandedItems.length - 1)) {
+                    item._selected = false;
+                    item = _data._expandedItems[i + 1];
+                    item._selected = true;
+                    changed = true;
+                } else if ((-1 == n) && (i > 0)) {
+                    item._selected = false;
+                    item = _data._expandedItems[i - 1];
+                    item._selected = true;
+                    changed = true;
+                }
+                break;
+            }
+        }
+
+        if (lastSelection < 0 && _data._expandedItems.length > 0) {
+            Item item = _data._expandedItems[0];
+            item._selected = true;
+            changed = true;
+        }
+
+        if (changed) {
+            ensureVisibleItem(-1, false);
+            redraw();
+            fireSelectionChanged();
+        }
+    }
+
+    /**
+     * Select an event
+     *
+     * @param n
+     *            1 for next event, -1 for previous event
+     */
+    public void selectEvent(int n) {
+        if (null == _timeProvider) {
+            return;
+        }
+        ITimeGraphEntry trace = getSelectedTrace();
+        if (trace == null) {
+            return;
+        }
+        long selectedTime = _timeProvider.getSelectedTime();
+        long endTime = _timeProvider.getEndTime();
+        ITimeEvent nextEvent;
+        if (-1 == n && selectedTime > endTime) {
+            nextEvent = Utils.findEvent(trace, selectedTime, 0);
+        } else {
+            nextEvent = Utils.findEvent(trace, selectedTime, n);
+        }
+        if (null == nextEvent && -1 == n) {
+            nextEvent = Utils.getFirstEvent(trace);
+        }
+        if (null != nextEvent) {
+            long nextTime = nextEvent.getTime();
+            // If last event detected e.g. going back or not moving to a next
+            // event
+            if (nextTime <= selectedTime && n == 1) {
+                // Select to the end of this last event
+                nextTime = nextEvent.getTime() + nextEvent.getDuration();
+                // but not beyond the end of the trace
+                if (nextTime > endTime) {
+                    nextTime = endTime;
+                }
+            } else if (n == -1) {
+                // for previous event go to its end time unless we were already there
+                if (nextEvent.getTime() + nextEvent.getDuration() < selectedTime) {
+                    nextTime = nextEvent.getTime() + nextEvent.getDuration();
+                }
+            }
+            _timeProvider.setSelectedTimeNotify(nextTime, true);
+            fireSelectionChanged();
+        } else if (1 == n) {
+            _timeProvider.setSelectedTimeNotify(endTime, true);
+            fireSelectionChanged();
+        }
+    }
+
+    /**
+     * Select the next event
+     */
+    public void selectNextEvent() {
+        selectEvent(1);
+        // Notify if visible time window has been adjusted
+        _timeProvider.setStartFinishTimeNotify(_timeProvider.getTime0(), _timeProvider.getTime1());
+    }
+
+    /**
+     * Select the previous event
+     */
+    public void selectPrevEvent() {
+        selectEvent(-1);
+        // Notify if visible time window has been adjusted
+        _timeProvider.setStartFinishTimeNotify(_timeProvider.getTime0(), _timeProvider.getTime1());
+    }
+
+    /**
+     * Select the next trace
+     */
+    public void selectNextTrace() {
+        selectTrace(1);
+    }
+
+    /**
+     * Select the previous trace
+     */
+    public void selectPrevTrace() {
+        selectTrace(-1);
+    }
+
+    /**
+     * Zoom based on mouse cursor location with mouse scrolling
+     *
+     * @param zoomIn true to zoom in, false to zoom out
+     */
+    public void zoom(boolean zoomIn) {
+        int globalX = getDisplay().getCursorLocation().x;
+        Point p = toControl(globalX, 0);
+        int nameSpace = _timeProvider.getNameSpace();
+        int timeSpace = _timeProvider.getTimeSpace();
+        int xPos = Math.max(nameSpace, Math.min(nameSpace + timeSpace, p.x));
+        long time0 = _timeProvider.getTime0();
+        long time1 = _timeProvider.getTime1();
+        long interval = time1 - time0;
+        if (interval == 0) {
+            interval = 1;
+        } // to allow getting out of single point interval
+        long newInterval;
+        if (zoomIn) {
+            newInterval = Math.max(Math.round(interval * 0.8), _timeProvider.getMinTimeInterval());
+        } else {
+            newInterval = (long) Math.ceil(interval * 1.25);
+        }
+        long center = time0 + Math.round(((double) (xPos - nameSpace) / timeSpace * interval));
+        long newTime0 = center - Math.round((double) newInterval * (center - time0) / interval);
+        long newTime1 = newTime0 + newInterval;
+        _timeProvider.setStartFinishTime(newTime0, newTime1);
+        synchronized (fMouseScrollNotifierLock) {
+            if (fMouseScrollNotifier == null) {
+                fMouseScrollNotifier = new MouseScrollNotifier();
+                fMouseScrollNotifier.start();
+            }
+            fMouseScrollNotifier.mouseScrolled();
+        }
+    }
+
+    /**
+     * zoom in using single click
+     */
+    public void zoomIn() {
+        long _time0 = _timeProvider.getTime0();
+        long _time1 = _timeProvider.getTime1();
+        long _range = _time1 - _time0;
+        long selTime = _timeProvider.getSelectedTime();
+        if (selTime <= _time0 || selTime >= _time1) {
+            selTime = (_time0 + _time1) / 2;
+        }
+        long time0 = selTime - (long) ((selTime - _time0) / zoomCoeff);
+        long time1 = selTime + (long) ((_time1 - selTime) / zoomCoeff);
+
+        long inaccuracy = (_timeProvider.getMaxTime() - _timeProvider.getMinTime()) - (time1 - time0);
+
+        // Trace.debug("selTime:" + selTime + " time0:" + time0 + " time1:"
+        // + time1 + " inaccuracy:" + inaccuracy);
+
+        if (inaccuracy > 0 && inaccuracy < 100) {
+            _timeProvider.setStartFinishTimeNotify(_timeProvider.getMinTime(), _timeProvider.getMaxTime());
+            return;
+        }
+
+        long m = _timeProvider.getMinTimeInterval();
+        if ((time1 - time0) < m) {
+            time0 = selTime - (selTime - _time0) * m / _range;
+            time1 = time0 + m;
+        }
+
+        _timeProvider.setStartFinishTimeNotify(time0, time1);
+    }
+
+    /**
+     * zoom out using single click
+     */
+    public void zoomOut() {
+        long _time0 = _timeProvider.getTime0();
+        long _time1 = _timeProvider.getTime1();
+        long selTime = _timeProvider.getSelectedTime();
+        if (selTime <= _time0 || selTime >= _time1) {
+            selTime = (_time0 + _time1) / 2;
+        }
+        long time0 = (long) (selTime - (selTime - _time0) * zoomCoeff);
+        long time1 = (long) (selTime + (_time1 - selTime) * zoomCoeff);
+
+        long inaccuracy = (_timeProvider.getMaxTime() - _timeProvider.getMinTime()) - (time1 - time0);
+        if (inaccuracy > 0 && inaccuracy < 100) {
+            _timeProvider.setStartFinishTimeNotify(_timeProvider.getMinTime(), _timeProvider.getMaxTime());
+            return;
+        }
+
+        _timeProvider.setStartFinishTimeNotify(time0, time1);
+    }
+
+    /**
+     * Return the currently selected trace
+     *
+     * @return The entry matching the trace
+     */
+    public ITimeGraphEntry getSelectedTrace() {
+        ITimeGraphEntry trace = null;
+        int idx = getSelectedIndex();
+        if (idx >= 0) {
+            trace = _data._expandedItems[idx]._trace;
+        }
+        return trace;
+    }
+
+    /**
+     * Retrieve the index of the currently selected item
+     *
+     * @return The index
+     */
+    public int getSelectedIndex() {
+        int idx = -1;
+        for (int i = 0; i < _data._expandedItems.length; i++) {
+            Item item = _data._expandedItems[i];
+            if (item._selected) {
+                idx = i;
+                break;
+            }
+        }
+        return idx;
+    }
+
+    boolean toggle(int idx) {
+        boolean toggled = false;
+        if (idx >= 0 && idx < _data._expandedItems.length) {
+            Item item = _data._expandedItems[idx];
+            if (item._hasChildren) {
+                item._expanded = !item._expanded;
+                _data.updateExpandedItems();
+                adjustScrolls();
+                redraw();
+                toggled = true;
+                fireTreeEvent(item._trace, item._expanded);
+            }
+        }
+        return toggled;
+    }
+
+    int getItemIndexAtY(int y) {
+        if (y < 0) {
+            return -1;
+        }
+        if (_itemHeight == CUSTOM_ITEM_HEIGHT) {
+            int ySum = 0;
+            for (int idx = _topIndex; idx < _data._expandedItems.length; idx++) {
+                ySum += _data._expandedItems[idx].itemHeight;
+                if (y < ySum) {
+                    return idx;
+                }
+            }
+            return -1;
+        }
+        int idx = y / _itemHeight;
+        idx += _topIndex;
+        if (idx < _data._expandedItems.length) {
+            return idx;
+        }
+        return -1;
+    }
+
+    boolean isOverSplitLine(int x) {
+        if (x < 0 || null == _timeProvider) {
+            return false;
+        }
+        int w = 4;
+        int nameWidth = _timeProvider.getNameSpace();
+        if (x > nameWidth - w && x < nameWidth + w) {
+            return true;
+        }
+        return false;
+    }
+
+    ITimeGraphEntry getEntry(Point pt) {
+        int idx = getItemIndexAtY(pt.y);
+        return idx >= 0 ? _data._expandedItems[idx]._trace : null;
+    }
+
+    long getTimeAtX(int x) {
+        if (null == _timeProvider) {
+            return -1;
+        }
+        long hitTime = -1;
+        Point size = getCtrlSize();
+        long time0 = _timeProvider.getTime0();
+        long time1 = _timeProvider.getTime1();
+        int nameWidth = _timeProvider.getNameSpace();
+        x -= nameWidth;
+        int timeWidth = size.x - nameWidth - RIGHT_MARGIN;
+        if (x >= 0 && size.x >= nameWidth) {
+            if (time1 - time0 > timeWidth) {
+                // nanosecond smaller than one pixel: use the first integer nanosecond of this pixel's time range
+                hitTime = time0 + (long) Math.ceil((time1 - time0) * ((double) x / timeWidth));
+            } else {
+                // nanosecond greater than one pixel: use the nanosecond that covers this pixel start position
+                hitTime = time0 + (long) Math.floor((time1 - time0) * ((double) x / timeWidth));
+            }
+        }
+        return hitTime;
+    }
+
+    void selectItem(int idx, boolean addSelection) {
+        boolean changed = false;
+        if (addSelection) {
+            if (idx >= 0 && idx < _data._expandedItems.length) {
+                Item item = _data._expandedItems[idx];
+                changed = (item._selected == false);
+                item._selected = true;
+            }
+        } else {
+            for (int i = 0; i < _data._expandedItems.length; i++) {
+                Item item = _data._expandedItems[i];
+                if ((i == idx && !item._selected) || (idx == -1 && item._selected)) {
+                    changed = true;
+                }
+                item._selected = i == idx;
+            }
+        }
+        changed |= ensureVisibleItem(idx, true);
+        if (changed) {
+            redraw();
+        }
+    }
+
+    /**
+     * Callback for item selection
+     *
+     * @param trace
+     *            The entry matching the trace
+     * @param addSelection
+     *            If the selection is added or removed
+     */
+    public void selectItem(ITimeGraphEntry trace, boolean addSelection) {
+        int idx = _data.findItemIndex(trace);
+        selectItem(idx, addSelection);
+    }
+
+    /**
+     * Retrieve the number of entries shown per page.
+     *
+     * @return The count
+     */
+    public int countPerPage() {
+        int height = getCtrlSize().y;
+        int count = 0;
+        if (_itemHeight == CUSTOM_ITEM_HEIGHT) {
+            int ySum = 0;
+            for (int idx = _topIndex; idx < _data._expandedItems.length; idx++) {
+                ySum += _data._expandedItems[idx].itemHeight;
+                if (ySum >= height) {
+                    return count;
+                }
+                count++;
+            }
+            for (int idx = _topIndex - 1; idx >= 0; idx--) {
+                ySum += _data._expandedItems[idx].itemHeight;
+                if (ySum >= height) {
+                    return count;
+                }
+                count++;
+            }
+            return count;
+        }
+        if (height > 0) {
+            count = height / _itemHeight;
+        }
+        return count;
+    }
+
+    /**
+     * Get the index of the top element
+     *
+     * @return The index
+     */
+    public int getTopIndex() {
+        return _topIndex;
+    }
+
+    /**
+     * Get the number of expanded items
+     *
+     * @return The count of expanded items
+     */
+    public int getExpandedElementCount() {
+        return _data._expandedItems.length;
+    }
+
+    /**
+     * Get an array of all expanded elements
+     *
+     * @return The expanded elements
+     */
+    public ITimeGraphEntry[] getExpandedElements() {
+        ArrayList<ITimeGraphEntry> elements = new ArrayList<ITimeGraphEntry>();
+        for (Item item : _data._expandedItems) {
+            elements.add(item._trace);
+        }
+        return elements.toArray(new ITimeGraphEntry[0]);
+    }
+
+    Point getCtrlSize() {
+        Point size = getSize();
+        if (getHorizontalBar().isVisible()) {
+            size.y -= getHorizontalBar().getSize().y;
+        }
+        return size;
+    }
+
+    Rectangle getNameRect(Rectangle bound, int idx, int nameWidth) {
+        int x = bound.x;
+        int y = bound.y + (idx - _topIndex) * _itemHeight;
+        int width = nameWidth;
+        int height = _itemHeight;
+        if (_itemHeight == CUSTOM_ITEM_HEIGHT) {
+            int ySum = 0;
+            for (int i = _topIndex; i < idx; i++) {
+                ySum += _data._expandedItems[i].itemHeight;
+            }
+            y = bound.y + ySum;
+            height = _data._expandedItems[idx].itemHeight;
+        }
+        return new Rectangle(x, y, width, height);
+    }
+
+    Rectangle getStatesRect(Rectangle bound, int idx, int nameWidth) {
+        int x = bound.x + nameWidth;
+        int y = bound.y + (idx - _topIndex) * _itemHeight;
+        int width = bound.width - x;
+        int height = _itemHeight;
+        if (_itemHeight == CUSTOM_ITEM_HEIGHT) {
+            int ySum = 0;
+            for (int i = _topIndex; i < idx; i++) {
+                ySum += _data._expandedItems[i].itemHeight;
+            }
+            y = bound.y + ySum;
+            height = _data._expandedItems[idx].itemHeight;
+        }
+        return new Rectangle(x, y, width, height);
+    }
+
+    @Override
+    void paint(Rectangle bounds, PaintEvent e) {
+        GC gc = e.gc;
+        gc.setBackground(_colors.getColor(TimeGraphColorScheme.BACKGROUND));
+        drawBackground(gc, bounds.x, bounds.y, bounds.width, bounds.height);
+
+        if (bounds.width < 2 || bounds.height < 2 || null == _timeProvider) {
+            return;
+        }
+
+        _idealNameSpace = 0;
+        int nameSpace = _timeProvider.getNameSpace();
+
+        // draw empty name space background
+        gc.setBackground(_colors.getBkColor(false, false, true));
+        drawBackground(gc, bounds.x, bounds.y, nameSpace, bounds.height);
+
+        drawItems(bounds, _timeProvider, _data._expandedItems, _topIndex, nameSpace, gc);
+
+        // draw selected time
+        long time0 = _timeProvider.getTime0();
+        long time1 = _timeProvider.getTime1();
+        long selectedTime = _timeProvider.getSelectedTime();
+        double pixelsPerNanoSec = (bounds.width - nameSpace <= RIGHT_MARGIN) ? 0 : (double) (bounds.width - nameSpace - RIGHT_MARGIN) / (time1 - time0);
+        int x = bounds.x + nameSpace + (int) ((selectedTime - time0) * pixelsPerNanoSec);
+        if (x >= nameSpace && x < bounds.x + bounds.width) {
+            gc.setForeground(_colors.getColor(TimeGraphColorScheme.SELECTED_TIME));
+            gc.drawLine(x, bounds.y, x, bounds.y + bounds.height);
+        }
+
+        // draw drag line, no line if name space is 0.
+        if (DRAG_SPLIT_LINE == _dragState) {
+            gc.setForeground(_colors.getColor(TimeGraphColorScheme.BLACK));
+            gc.drawLine(bounds.x + nameSpace, bounds.y, bounds.x + nameSpace, bounds.y + bounds.height - 1);
+        } else if (DRAG_NONE == _dragState && _mouseOverSplitLine && _timeProvider.getNameSpace() > 0) {
+            gc.setForeground(_colors.getColor(TimeGraphColorScheme.RED));
+            gc.drawLine(bounds.x + nameSpace, bounds.y, bounds.x + nameSpace, bounds.y + bounds.height - 1);
+        }
+    }
+
+    /**
+     * Draw many items at once
+     *
+     * @param bounds
+     *            The rectangle of the area
+     * @param timeProvider
+     *            The time provider
+     * @param items
+     *            The array items to draw
+     * @param topIndex
+     *            The index of the first element to draw
+     * @param nameSpace
+     *            The width reserved for the names
+     * @param gc
+     *            Reference to the SWT GC object
+     */
+    public void drawItems(Rectangle bounds, ITimeDataProvider timeProvider,
+            Item[] items, int topIndex, int nameSpace, GC gc) {
+        for (int i = topIndex; i < items.length; i++) {
+            Item item = items[i];
+            drawItem(item, bounds, timeProvider, i, nameSpace, gc);
+        }
+        fTimeGraphProvider.postDrawControl(bounds, gc);
+    }
+
+    /**
+     * Draws the item
+     *
+     * @param item the item to draw
+     * @param bounds the container rectangle
+     * @param i the item index
+     * @param nameSpace the name space
+     * @param gc
+     */
+    protected void drawItem(Item item, Rectangle bounds, ITimeDataProvider timeProvider, int i, int nameSpace, GC gc) {
+        ITimeGraphEntry entry = item._trace;
+        long time0 = timeProvider.getTime0();
+        long time1 = timeProvider.getTime1();
+        long selectedTime = timeProvider.getSelectedTime();
+
+        Rectangle nameRect = getNameRect(bounds, i, nameSpace);
+        if (nameRect.y >= bounds.y + bounds.height) {
+            return;
+        }
+
+        if (! item._trace.hasTimeEvents()) {
+            Rectangle statesRect = getStatesRect(bounds, i, nameSpace);
+            nameRect.width += statesRect.width;
+            drawName(item, nameRect, gc);
+        } else {
+            drawName(item, nameRect, gc);
+        }
+        Rectangle rect = getStatesRect(bounds, i, nameSpace);
+        if (rect.isEmpty()) {
+            fTimeGraphProvider.postDrawEntry(entry, rect, gc);
+            return;
+        }
+        if (time1 <= time0) {
+            gc.setBackground(_colors.getBkColor(false, false, false));
+            gc.fillRectangle(rect);
+            fTimeGraphProvider.postDrawEntry(entry, rect, gc);
+            return;
+        }
+
+        // Initialize _rect1 to same values as enclosing rectangle rect
+        Rectangle stateRect = Utils.clone(rect);
+        boolean selected = item._selected;
+        // K pixels per second
+        double pixelsPerNanoSec = (rect.width <= RIGHT_MARGIN) ? 0 : (double) (rect.width - RIGHT_MARGIN) / (time1 - time0);
+
+        if (item._trace.hasTimeEvents()) {
+            fillSpace(rect, gc, selected);
+            // Drawing rectangle is smaller than reserved space
+            stateRect.y += 3;
+            stateRect.height -= 6;
+
+            long maxDuration = (timeProvider.getTimeSpace() == 0) ? Long.MAX_VALUE : 1 * (time1 - time0) / timeProvider.getTimeSpace();
+            Iterator<ITimeEvent> iterator = entry.getTimeEventsIterator(time0, time1, maxDuration);
+
+            int lastX = -1;
+            while (iterator.hasNext()) {
+                ITimeEvent event = iterator.next();
+                int x = rect.x + (int) ((event.getTime() - time0) * pixelsPerNanoSec);
+                int xEnd = rect.x + (int) ((event.getTime() + event.getDuration() - time0) * pixelsPerNanoSec);
+                if (x >= rect.x + rect.width || xEnd < rect.x) {
+                    // event is out of bounds
+                    continue;
+                }
+                xEnd = Math.min(rect.x + rect.width, xEnd);
+                stateRect.x = Math.max(rect.x, x);
+                stateRect.width = Math.max(0, xEnd - stateRect.x + 1);
+                if (stateRect.x == lastX) {
+                    stateRect.width -= 1;
+                    if (stateRect.width > 0) {
+                        gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
+                        gc.drawPoint(stateRect.x, stateRect.y - 2);
+                        stateRect.x += 1;
+                    }
+                } else {
+                    lastX = x;
+                }
+                boolean timeSelected = selectedTime >= event.getTime() && selectedTime < event.getTime() + event.getDuration();
+                drawState(_colors, event, stateRect, gc, selected, timeSelected);
+            }
+        }
+        fTimeGraphProvider.postDrawEntry(entry, rect, gc);
+    }
+
+    protected void drawName(Item item, Rectangle bounds, GC gc) {
+        boolean hasTimeEvents = item._trace.hasTimeEvents();
+        if (! hasTimeEvents) {
+            gc.setBackground(_colors.getBkColorGroup(item._selected, _isInFocus));
+            gc.fillRectangle(bounds);
+            if (item._selected && _isInFocus) {
+                gc.setForeground(_colors.getBkColor(item._selected, _isInFocus, false));
+                gc.drawRectangle(bounds.x, bounds.y, bounds.width - 1, bounds.height - 1);
+            }
+        } else {
+            gc.setBackground(_colors.getBkColor(item._selected, _isInFocus, true));
+            gc.setForeground(_colors.getFgColor(item._selected, _isInFocus));
+            gc.fillRectangle(bounds);
+        }
+
+        // No name to be drawn
+        if (_timeProvider.getNameSpace() == 0) {
+            return;
+        }
+
+        int leftMargin = MARGIN + item.level * EXPAND_SIZE;
+        if (item._hasChildren) {
+            gc.setForeground(_colors.getFgColorGroup(false, false));
+            gc.setBackground(_colors.getBkColor(false, false, false));
+            Rectangle rect = Utils.clone(bounds);
+            rect.x += leftMargin;
+            rect.y += (bounds.height - EXPAND_SIZE) / 2;
+            rect.width = EXPAND_SIZE;
+            rect.height = EXPAND_SIZE;
+            gc.fillRectangle(rect);
+            gc.drawRectangle(rect.x, rect.y, rect.width - 1, rect.height - 1);
+            int midy = rect.y + rect.height / 2;
+            gc.drawLine(rect.x + 2, midy, rect.x + rect.width - 3, midy);
+            if (!item._expanded) {
+                int midx = rect.x + rect.width / 2;
+                gc.drawLine(midx, rect.y + 2, midx, rect.y + rect.height - 3);
+            }
+        }
+        leftMargin += EXPAND_SIZE + MARGIN;
+
+        Image img = fTimeGraphProvider.getItemImage(item._trace);
+        if (img != null) {
+            // draw icon
+            int imgHeight = img.getImageData().height;
+            int imgWidth = img.getImageData().width;
+            int x = leftMargin;
+            int y = bounds.y + (bounds.height - imgHeight) / 2;
+            gc.drawImage(img, x, y);
+            leftMargin += imgWidth + MARGIN;
+        }
+        String name = item._name;
+        Point size = gc.stringExtent(name);
+        if (_idealNameSpace < leftMargin + size.x + MARGIN) {
+            _idealNameSpace = leftMargin + size.x + MARGIN;
+        }
+        if (hasTimeEvents) {
+            // cut long string with "..."
+            int width = bounds.width - leftMargin;
+            int cuts = 0;
+            while (size.x > width && name.length() > 1) {
+                cuts++;
+                name = name.substring(0, name.length() - 1);
+                size = gc.stringExtent(name + "..."); //$NON-NLS-1$
+            }
+            if (cuts > 0) {
+                name += "..."; //$NON-NLS-1$
+            }
+        }
+        Rectangle rect = Utils.clone(bounds);
+        rect.x += leftMargin;
+        rect.width -= leftMargin;
+        // draw text
+        if (rect.width > 0) {
+            rect.y += (bounds.height - gc.stringExtent(name).y) / 2;
+            gc.setForeground(_colors.getFgColor(item._selected, _isInFocus));
+            int textWidth = Utils.drawText(gc, name, rect, true);
+            leftMargin += textWidth + MARGIN;
+            rect.y -= 2;
+
+            if (hasTimeEvents) {
+                // draw middle line
+                int x = bounds.x + leftMargin;
+                int width = bounds.width - x;
+                int midy = bounds.y + bounds.height / 2;
+                gc.setForeground(_colors.getColor(TimeGraphColorScheme.MID_LINE));
+                gc.drawLine(x, midy, x + width, midy);
+            }
+        }
+    }
+
+    protected void drawState(TimeGraphColorScheme colors, ITimeEvent event,
+            Rectangle rect, GC gc, boolean selected, boolean timeSelected) {
+
+        int colorIdx = fTimeGraphProvider.getStateTableIndex(event);
+        if (colorIdx < 0) {
+            return;
+        }
+        boolean visible = rect.width == 0 ? false : true;
+
+        if (visible) {
+            Color stateColor = null;
+            if (colorIdx < fEventColorMap.length) {
+                stateColor = fEventColorMap[colorIdx];
+            } else {
+                stateColor = Display.getDefault().getSystemColor(SWT.COLOR_BLACK);
+            }
+
+            timeSelected = timeSelected && selected;
+            if (timeSelected) {
+                // modify the color?
+            }
+            // fill all rect area
+            gc.setBackground(stateColor);
+            gc.fillRectangle(rect);
+            // get the border color?
+            gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
+
+            // draw bounds
+            if (!timeSelected) {
+                // Draw the top and bottom borders i.e. no side borders
+                // top
+                gc.drawLine(rect.x, rect.y, rect.x + rect.width - 1, rect.y);
+                // bottom
+                gc.drawLine(rect.x, rect.y + rect.height - 1, rect.x + rect.width - 1, rect.y + rect.height - 1);
+            }
+        } else {
+            gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
+            gc.drawPoint(rect.x, rect.y - 2);
+            /*
+            // selected rectangle area is not visible but can be represented
+            // with a broken vertical line of specified width.
+            int width = 1;
+            rect.width = width;
+            gc.setForeground(stateColor);
+            int s = gc.getLineStyle();
+            int w = gc.getLineWidth();
+            gc.setLineStyle(SWT.LINE_DOT);
+            gc.setLineWidth(width);
+            // Trace.debug("Rectangle not visible, drawing vertical line with: "
+            // + rect.x + "," + rect.y + "," + rect.x + "," + rect.y
+            // + rect.height);
+            gc.drawLine(rect.x, rect.y, rect.x, rect.y + rect.height - 1);
+            gc.setLineStyle(s);
+            gc.setLineWidth(w);
+            if (!timeSelected) {
+                gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
+                gc.drawPoint(rect.x, rect.y);
+                gc.drawPoint(rect.x, rect.y + rect.height - 1);
+            }
+            */
+        }
+        fTimeGraphProvider.postDrawEvent(event, rect, gc);
+    }
+
+    protected void fillSpace(Rectangle rect, GC gc, boolean selected) {
+        gc.setBackground(_colors.getBkColor(selected, _isInFocus, false));
+        gc.fillRectangle(rect);
+        // draw middle line
+        gc.setForeground(_colors.getColor(TimeGraphColorScheme.MID_LINE));
+        int midy = rect.y + rect.height / 2;
+        gc.drawLine(rect.x, midy, rect.x + rect.width, midy);
+    }
+
+    @Override
+    public void keyTraversed(TraverseEvent e) {
+        if ((e.detail == SWT.TRAVERSE_TAB_NEXT) || (e.detail == SWT.TRAVERSE_TAB_PREVIOUS)) {
+            e.doit = true;
+        }
+    }
+
+    @Override
+    public void keyPressed(KeyEvent e) {
+        int idx = -1;
+        if (_data._expandedItems.length == 0) {
+            return;
+        }
+        if (SWT.HOME == e.keyCode) {
+            idx = 0;
+        } else if (SWT.END == e.keyCode) {
+            idx = _data._expandedItems.length - 1;
+        } else if (SWT.ARROW_DOWN == e.keyCode) {
+            idx = getSelectedIndex();
+            if (idx < 0) {
+                idx = 0;
+            } else if (idx < _data._expandedItems.length - 1) {
+                idx++;
+            }
+        } else if (SWT.ARROW_UP == e.keyCode) {
+            idx = getSelectedIndex();
+            if (idx < 0) {
+                idx = 0;
+            } else if (idx > 0) {
+                idx--;
+            }
+        } else if (SWT.ARROW_LEFT == e.keyCode) {
+            selectPrevEvent();
+        } else if (SWT.ARROW_RIGHT == e.keyCode) {
+            selectNextEvent();
+        } else if (SWT.PAGE_DOWN == e.keyCode) {
+            int page = countPerPage();
+            idx = getSelectedIndex();
+            if (idx < 0) {
+                idx = 0;
+            }
+            idx += page;
+            if (idx >= _data._expandedItems.length) {
+                idx = _data._expandedItems.length - 1;
+            }
+        } else if (SWT.PAGE_UP == e.keyCode) {
+            int page = countPerPage();
+            idx = getSelectedIndex();
+            if (idx < 0) {
+                idx = 0;
+            }
+            idx -= page;
+            if (idx < 0) {
+                idx = 0;
+            }
+        } else if (SWT.CR == e.keyCode) {
+            idx = getSelectedIndex();
+            if (idx >= 0) {
+                if (_data._expandedItems[idx]._hasChildren) {
+                    toggle(idx);
+                } else {
+                    fireDefaultSelection();
+                }
+            }
+            idx = -1;
+        }
+        if (idx >= 0) {
+            selectItem(idx, false);
+            fireSelectionChanged();
+        }
+    }
+
+    @Override
+    public void keyReleased(KeyEvent e) {
+    }
+
+    @Override
+    public void focusGained(FocusEvent e) {
+        _isInFocus = true;
+        if (mouseScrollFilterListener == null) {
+            mouseScrollFilterListener = new Listener() {
+                // This filter is used to prevent horizontal scrolling of the view
+                // when the mouse wheel is used to zoom
+                @Override
+                public void handleEvent(Event event) {
+                    event.doit = false;
+                }
+            };
+            getDisplay().addFilter(SWT.MouseWheel, mouseScrollFilterListener);
+        }
+        redraw();
+    }
+
+    @Override
+    public void focusLost(FocusEvent e) {
+        _isInFocus = false;
+        if (mouseScrollFilterListener != null) {
+            getDisplay().removeFilter(SWT.MouseWheel, mouseScrollFilterListener);
+            mouseScrollFilterListener = null;
+        }
+        if (DRAG_NONE != _dragState) {
+            setCapture(false);
+            _dragState = DRAG_NONE;
+        }
+        redraw();
+    }
+
+    /**
+     * @return If the current view is focused
+     */
+    public boolean isInFocus() {
+        return _isInFocus;
+    }
+
+    /**
+     * Provide the possibility to control the wait cursor externally e.g. data
+     * requests in progress
+     *
+     * @param waitInd Should we wait indefinitely?
+     */
+    public void waitCursor(boolean waitInd) {
+        // Update cursor as indicated
+        if (waitInd) {
+            setCursor(_WaitCursor);
+            _isWaitCursor = true;
+        } else {
+            setCursor(null);
+            _isWaitCursor = false;
+        }
+
+        // Get ready for next mouse move
+        _isDragCursor3 = false;
+    }
+
+    /**
+     * <p>
+     * If the x, y position is over the vertical split line (name to time
+     * ranges), then change the cursor to a drag cursor to indicate the user the
+     * possibility of resizing
+     * </p>
+     *
+     * @param x
+     * @param y
+     */
+    void updateCursor(int x, int y) {
+        // if Wait cursor not active, check for the need to change to a drag
+        // cursor
+        if (_isWaitCursor == false) {
+            boolean isSplitLine = isOverSplitLine(x);
+            // No dragcursor is name space is fixed to zero
+            if (isSplitLine && !_isDragCursor3 && _timeProvider.getNameSpace() > 0) {
+                setCursor(_dragCursor3);
+                _isDragCursor3 = true;
+            } else if (!isSplitLine && _isDragCursor3) {
+                setCursor(null);
+                _isDragCursor3 = false;
+            }
+        }
+    }
+
+    @Override
+    public void mouseMove(MouseEvent e) {
+        if (null == _timeProvider) {
+            return;
+        }
+        Point size = getCtrlSize();
+        if (DRAG_TRACE_ITEM == _dragState) {
+            int nameWidth = _timeProvider.getNameSpace();
+            int x = e.x - nameWidth;
+            if (x > 0 && size.x > nameWidth && _dragX != x) {
+                _dragX = x;
+                double pixelsPerNanoSec = (size.x - nameWidth <= RIGHT_MARGIN) ? 0 : (double) (size.x - nameWidth - RIGHT_MARGIN) / (_time1bak - _time0bak);
+                long timeDelta = (long) ((pixelsPerNanoSec == 0) ? 0 : ((_dragX - _dragX0) / pixelsPerNanoSec));
+                long time1 = _time1bak - timeDelta;
+                long maxTime = _timeProvider.getMaxTime();
+                if (time1 > maxTime) {
+                    time1 = maxTime;
+                }
+                long time0 = time1 - (_time1bak - _time0bak);
+                if (time0 < _timeProvider.getMinTime()) {
+                    time0 = _timeProvider.getMinTime();
+                    time1 = time0 + (_time1bak - _time0bak);
+                }
+                _timeProvider.setStartFinishTime(time0, time1);
+            }
+        } else if (DRAG_SPLIT_LINE == _dragState) {
+            _dragX = e.x;
+            _timeProvider.setNameSpace(e.x);
+        } else if (DRAG_NONE == _dragState) {
+            boolean mouseOverSplitLine = isOverSplitLine(e.x);
+            if (_mouseOverSplitLine != mouseOverSplitLine) {
+                redraw();
+            }
+            _mouseOverSplitLine = mouseOverSplitLine;
+        }
+        updateCursor(e.x, e.y);
+    }
+
+    @Override
+    public void mouseDoubleClick(MouseEvent e) {
+        if (null == _timeProvider) {
+            return;
+        }
+        if (1 == e.button) {
+            if (isOverSplitLine(e.x) && _timeProvider.getNameSpace() != 0) {
+                _timeProvider.setNameSpace(_idealNameSpace);
+                boolean mouseOverSplitLine = isOverSplitLine(e.x);
+                if (_mouseOverSplitLine != mouseOverSplitLine) {
+                    redraw();
+                }
+                _mouseOverSplitLine = mouseOverSplitLine;
+                return;
+            }
+            int idx = getItemIndexAtY(e.y);
+            if (idx >= 0) {
+                selectItem(idx, false);
+                fireDefaultSelection();
+            }
+        }
+    }
+
+    @Override
+    public void mouseDown(MouseEvent e) {
+        if (null == _timeProvider) {
+            return;
+        }
+        int idx;
+        if (1 == e.button) {
+            int nameSpace = _timeProvider.getNameSpace();
+            if (nameSpace != 0) {
+                if (isOverSplitLine(e.x)) {
+                    _dragState = DRAG_SPLIT_LINE;
+                    _dragX = _dragX0 = e.x;
+                    _time0bak = _timeProvider.getTime0();
+                    _time1bak = _timeProvider.getTime1();
+                    redraw();
+                    return;
+                }
+            }
+
+            idx = getItemIndexAtY(e.y);
+            if (idx >= 0) {
+                Item item = _data._expandedItems[idx];
+                if (item._hasChildren && e.x < nameSpace && e.x < MARGIN + (item.level + 1) * EXPAND_SIZE) {
+                    toggle(idx);
+                } else {
+                    long hitTime = getTimeAtX(e.x);
+                    if (hitTime >= 0) {
+                        // _timeProvider.setSelectedTimeInt(hitTime, false);
+                        setCapture(true);
+                        _dragState = DRAG_TRACE_ITEM;
+                        _dragX = _dragX0 = e.x - nameSpace;
+                        _time0bak = _timeProvider.getTime0();
+                        _time1bak = _timeProvider.getTime1();
+                    }
+                }
+                selectItem(idx, false);
+                fireSelectionChanged();
+            } else {
+                selectItem(idx, false); // clear selection
+                redraw();
+                fireSelectionChanged();
+            }
+        }
+    }
+
+    @Override
+    public void mouseUp(MouseEvent e) {
+        if (DRAG_NONE != _dragState) {
+            setCapture(false);
+            if (DRAG_TRACE_ITEM == _dragState) {
+                // Notify time provider to check the need for listener
+                // notification
+                _timeProvider.notifyStartFinishTime();
+                if (_dragX == _dragX0) { // click without drag
+                    long time = getTimeAtX(e.x);
+                    _timeProvider.setSelectedTimeNotify(time, false);
+                }
+            } else if (DRAG_SPLIT_LINE == _dragState) {
+                redraw();
+            }
+            _dragState = DRAG_NONE;
+        }
+    }
+
+    @Override
+    public void mouseEnter(MouseEvent e) {
+    }
+
+    @Override
+    public void mouseExit(MouseEvent e) {
+        if (_mouseOverSplitLine) {
+            _mouseOverSplitLine = false;
+            redraw();
+        }
+    }
+
+    @Override
+    public void mouseHover(MouseEvent e) {
+    }
+
+    @Override
+    public void mouseScrolled(MouseEvent e) {
+        if ((mouseScrollFilterListener == null) || _dragState != DRAG_NONE) {
+            return;
+        }
+        boolean zoomScroll = false;
+        Point p = getParent().toControl(getDisplay().getCursorLocation());
+        Point parentSize = getParent().getSize();
+        if (p.x >= 0 && p.x < parentSize.x && p.y >= 0 && p.y < parentSize.y) {
+            // over the parent control
+            if (e.x > getCtrlSize().x) {
+                // over the horizontal scroll bar
+                zoomScroll = false;
+            } else if (e.y >= 0 && e.y < getCtrlSize().y && e.x < _timeProvider.getNameSpace()) {
+                // over the name space
+                zoomScroll = false;
+            } else {
+                zoomScroll = true;
+            }
+        }
+        if (zoomScroll && _timeProvider.getTime0() != _timeProvider.getTime1()) {
+            if (e.count > 0) {
+                zoom(true);
+            } else if (e.count < 0) {
+                zoom(false);
+            }
+        } else {
+            setTopIndex(getTopIndex() - e.count);
+        }
+    }
+
+    @Override
+    public void controlMoved(ControlEvent e) {
+    }
+
+    @Override
+    public void controlResized(ControlEvent e) {
+        adjustScrolls();
+    }
+
+    @Override
+    public void widgetDefaultSelected(SelectionEvent e) {
+    }
+
+    @Override
+    public void widgetSelected(SelectionEvent e) {
+        if (e.widget == getVerticalBar()) {
+            setTopIndex(getVerticalBar().getSelection());
+        } else if (e.widget == getHorizontalBar() && null != _timeProvider) {
+            int start = getHorizontalBar().getSelection();
+            long time0 = _timeProvider.getTime0();
+            long time1 = _timeProvider.getTime1();
+            long timeMin = _timeProvider.getMinTime();
+            long timeMax = _timeProvider.getMaxTime();
+            long delta = timeMax - timeMin;
+
+            long range = time1 - time0;
+            // _timeRangeFixed = true;
+            time0 = timeMin + Math.round(delta * ((double) start / H_SCROLLBAR_MAX));
+            time1 = time0 + range;
+
+            // TODO: Follow-up with Bug 310310
+            // In Linux SWT.DRAG is the only value received
+            // https://bugs.eclipse.org/bugs/show_bug.cgi?id=310310
+            if (e.detail == SWT.DRAG) {
+                _timeProvider.setStartFinishTime(time0, time1);
+            } else {
+                _timeProvider.setStartFinishTimeNotify(time0, time1);
+            }
+        }
+    }
+
+    /**
+     * @return The current visibility of the vertical scroll bar
+     */
+    public boolean isVisibleVerticalScroll() {
+        return _visibleVerticalScroll;
+    }
+
+    @Override
+    public int getBorderWidth() {
+        return _borderWidth;
+    }
+
+    /**
+     * Set the border width
+     *
+     * @param borderWidth
+     *            The width
+     */
+    public void setBorderWidth(int borderWidth) {
+        this._borderWidth = borderWidth;
+    }
+
+    /**
+     * @return The current height of the header row
+     */
+    public int getHeaderHeight() {
+        return _headerHeight;
+    }
+
+    /**
+     * Set the height of the header row
+     *
+     * @param headerHeight
+     *            The height
+     */
+    public void setHeaderHeight(int headerHeight) {
+        this._headerHeight = headerHeight;
+    }
+
+    /**
+     * @return The height of regular item rows
+     */
+    public int getItemHeight() {
+        return _itemHeight;
+    }
+
+    /**
+     * Set the height of regular itew rows
+     *
+     * @param rowHeight
+     *            The height
+     */
+    public void setItemHeight(int rowHeight) {
+        this._itemHeight = rowHeight;
+    }
+
+    /**
+     * Set the minimum item width
+     *
+     * @param width The minimum width
+     */
+    public void setMinimumItemWidth(int width) {
+        this._minimumItemWidth = width;
+    }
+
+    /**
+     * @return The minimum item width
+     */
+    public int getMinimumItemWidth() {
+        return _minimumItemWidth;
+    }
+
+    /**
+     * @return The entries that are currently filtered out
+     */
+    public Vector<ITimeGraphEntry> getFilteredOut() {
+        return _data.getFilteredOut();
+    }
+
+    // @Override
+    @Override
+    public void addSelectionChangedListener(ISelectionChangedListener listener) {
+        if (listener != null) {
+            if (!_selectionChangedListeners.contains(listener)) {
+                _selectionChangedListeners.add(listener);
+            }
+        }
+    }
+
+    // @Override
+    @Override
+    public void removeSelectionChangedListener(ISelectionChangedListener listener) {
+        if (listener != null) {
+            _selectionChangedListeners.remove(listener);
+        }
+    }
+
+    // @Override
+    @Override
+    public void setSelection(ISelection selection) {
+        if (selection instanceof TimeGraphSelection) {
+            TimeGraphSelection sel = (TimeGraphSelection) selection;
+            Object ob = sel.getFirstElement();
+            if (ob instanceof ITimeGraphEntry) {
+                ITimeGraphEntry trace = (ITimeGraphEntry) ob;
+                selectItem(trace, false);
+            }
+        }
+
+    }
+
+    private class ItemData {
+        public Item[] _expandedItems = new Item[0];
+        public Item[] _items = new Item[0];
+        private ITimeGraphEntry _traces[] = new ITimeGraphEntry[0];
+        private boolean traceFilter[] = new boolean[0];
+        private final Vector<ITimeGraphEntry> filteredOut = new Vector<ITimeGraphEntry>();
+        public ITimeGraphPresentationProvider provider;
+
+        public ItemData() {
+        }
+
+        Item findItem(ITimeGraphEntry entry) {
+            if (entry == null) {
+                return null;
+            }
+
+            for (int i = 0; i < _items.length; i++) {
+                Item item = _items[i];
+                if (item._trace == entry) {
+                    return item;
+                }
+            }
+
+            return null;
+        }
+
+        int findItemIndex(ITimeGraphEntry trace) {
+            if (trace == null) {
+                return -1;
+            }
+
+            for (int i = 0; i < _expandedItems.length; i++) {
+                Item item = _expandedItems[i];
+                if (item._trace == trace) {
+                    return i;
+                }
+            }
+
+            return -1;
+        }
+
+        public void refreshData() {
+            List<Item> itemList = new ArrayList<Item>();
+            filteredOut.clear();
+            for (int i = 0; i < _traces.length; i++) {
+                ITimeGraphEntry entry = _traces[i];
+                refreshData(itemList, null, 0, entry);
+            }
+            _items = itemList.toArray(new Item[0]);
+            updateExpandedItems();
+        }
+
+        private void refreshData(List<Item> itemList, Item parent, int level, ITimeGraphEntry entry) {
+            Item item = new Item(entry, entry.getName(), level);
+            if (parent != null) {
+                parent.children.add(item);
+            }
+            item.itemHeight = provider.getItemHeight(entry);
+            itemList.add(item);
+            if (entry.hasChildren()) {
+                item._expanded = true;
+                item._hasChildren = true;
+                for (ITimeGraphEntry child : entry.getChildren()) {
+                    refreshData(itemList, item, level + 1, child);
+                }
+            }
+        }
+
+        public void updateExpandedItems() {
+            List<Item> expandedItemList = new ArrayList<Item>();
+            for (int i = 0; i < _traces.length; i++) {
+                ITimeGraphEntry entry = _traces[i];
+                Item item = findItem(entry);
+                refreshExpanded(expandedItemList, item);
+            }
+            _expandedItems = expandedItemList.toArray(new Item[0]);
+        }
+
+        private void refreshExpanded(List<Item> expandedItemList, Item item) {
+            expandedItemList.add(item);
+            if (item._hasChildren && item._expanded) {
+                for (Item child : item.children) {
+                    refreshExpanded(expandedItemList, child);
+                }
+            }
+        }
+
+        public void refreshData(ITimeGraphEntry traces[]) {
+            if (traces == null || traces.length == 0) {
+                traceFilter = null;
+            } else if (traceFilter == null || traces.length != traceFilter.length) {
+                traceFilter = new boolean[traces.length];
+                java.util.Arrays.fill(traceFilter, true);
+            }
+
+            _traces = traces;
+            refreshData();
+        }
+
+        public ITimeGraphEntry[] getTraces() {
+            return _traces;
+        }
+
+        public boolean[] getTraceFilter() {
+            return traceFilter;
+        }
+
+        public Vector<ITimeGraphEntry> getFilteredOut() {
+            return filteredOut;
+        }
+    }
+
+    private class Item {
+        public boolean _expanded;
+        public boolean _selected;
+        public boolean _hasChildren;
+        public int itemHeight;
+        public int level;
+        public List<Item> children;
+        public String _name;
+        public ITimeGraphEntry _trace;
+
+        public Item(ITimeGraphEntry trace, String name, int level) {
+            this._trace = trace;
+            this._name = name;
+            this.level = level;
+            this.children = new ArrayList<Item>();
+        }
+
+        @Override
+        public String toString() {
+            return _name;
+        }
+    }
+
+}
+
This page took 0.109066 seconds and 5 git commands to generate.