ctf: make DeclarationScope.java more inline with coding style
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.ui / src / org / eclipse / linuxtools / tmf / ui / widgets / timegraph / widgets / TimeGraphControl.java
index 1bf585db422f5712afc5d0f790cf73b15fe4c472..cfb6fd93fa7f59a3c0afb94a92227ceaf836b078 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
-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 List<ISelectionChangedListener> _selectionChangedListeners = new ArrayList<ISelectionChangedListener>();\r
-    private List<ITimeGraphTreeListener> _treeListeners = new ArrayList<ITimeGraphTreeListener>();\r
-    private Cursor _dragCursor3;\r
-    private Cursor _WaitCursor;\r
-\r
-    // Vertical formatting formatting for the state control view\r
-    private 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
-    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
-//        RGB[] eventColorTable = fTimeGraphProvider.getEventColorTable();\r
-//        if (eventColorTable != null) {\r
-//            fEventColorMap = new Color[eventColorTable.length];\r
-//            for (int i = 0; i < eventColorTable.length; i++) {\r
-//                fEventColorMap[i] = fResourceManager.createColor(eventColorTable[i]);\r
-//            }\r
-//        } else {\r
-//            fEventColorMap = new Color[] { };\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
-\r
-    public void setTimeProvider(ITimeDataProvider timeProvider) {\r
-        _timeProvider = timeProvider;\r
-        adjustScrolls();\r
-        redraw();\r
-    }\r
-\r
-    public void addSelectionListener(SelectionListener listener) {\r
-        if (listener == null)\r
-            SWT.error(SWT.ERROR_NULL_ARGUMENT);\r
-        if (null == _selectionListeners)\r
-            _selectionListeners = new ArrayList<SelectionListener>();\r
-        _selectionListeners.add(listener);\r
-    }\r
-\r
-    public void removeSelectionListener(SelectionListener listener) {\r
-        if (null != _selectionListeners)\r
-            _selectionListeners.remove(listener);\r
-    }\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
-    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
-    public ITimeGraphEntry[] getTraces() {\r
-        return _data.getTraces();\r
-    }\r
-\r
-    public boolean[] getTraceFilter() {\r
-        return _data.getTraceFilter();\r
-    }\r
-\r
-    public void refreshData() {\r
-        _data.refreshData();\r
-        adjustScrolls();\r
-        redraw();\r
-    }\r
-\r
-    public void refreshData(ITimeGraphEntry traces[]) {\r
-        _data.refreshData(traces);\r
-        adjustScrolls();\r
-        redraw();\r
-    }\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 (((TimeGraphItem) _data._expandedItems[idx])._selected)\r
-                    break;\r
-            }\r
-        }\r
-        if (idx >= _data._expandedItems.length)\r
-            return changed;\r
-        if (idx < _topIndex) {\r
-            setTopIndex(idx);\r
-            //FIXME:getVerticalBar().setSelection(_topItem);\r
-            if (redraw)\r
-                redraw();\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
-                changed = true;\r
-            }\r
-        }\r
-        return changed;\r
-    }\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
-    public void setExpandedState(ITimeGraphEntry entry, boolean expanded) {\r
-        TimeGraphItem item = _data.findItem(entry);\r
-        if (item != null && item._expanded != expanded) {\r
-            item._expanded = expanded;\r
-            _data.updateExpandedItems();\r
-            redraw();\r
-        }\r
-    }\r
-\r
-    public void addTreeListener (ITimeGraphTreeListener listener) {\r
-        if (!_treeListeners.contains(listener)) {\r
-            _treeListeners.add(listener);\r
-        }\r
-    }\r
-\r
-    public void removeTreeListener (ITimeGraphTreeListener listener) {\r
-        if (_treeListeners.contains(listener)) {\r
-            _treeListeners.remove(listener);\r
-        }\r
-    }\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
-        return sel;\r
-    }\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
-    // TODO select implementation for selectTrace\r
-//    public void selectTrace(int n) {\r
-//        if (n != 1 && n != -1)\r
-//            return;\r
-//        boolean changed = false;\r
-//        int lastSelection = -1;\r
-//        for (int i = 0; i < _data._expandedItems.length; i++) {\r
-//            TimeGraphItem item = (TimeGraphItem) _data._expandedItems[i];\r
-//            if (item._selected) {\r
-//                lastSelection = i;\r
-//                if (1 == n && i < _data._expandedItems.length - 1) {\r
-//                    item._selected = false;\r
-//                    if (item._hasChildren) {\r
-//                        _data.expandItem(i);\r
-//                        fireTreeEvent(item._trace, item._expanded);\r
-//                    }\r
-//                    item = (TimeGraphItem) _data._expandedItems[i + 1];\r
-//                    if (item._hasChildren) {\r
-//                        _data.expandItem(i + 1);\r
-//                        fireTreeEvent(item._trace, item._expanded);\r
-//                        item = (TimeGraphItem) _data._expandedItems[i + 2];\r
-//                    }\r
-//                    item._selected = true;\r
-//                    changed = true;\r
-//                } else if (-1 == n && i > 0) {\r
-//                    i--;\r
-//                    TimeGraphItem prevItem = (TimeGraphItem) _data._expandedItems[i];\r
-//                    if (prevItem._hasChildren) {\r
-//                        if (prevItem._expanded) {\r
-//                            if (i > 0) {\r
-//                                i--;\r
-//                                prevItem = (TimeGraphItem) _data._expandedItems[i];\r
-//                            }\r
-//                        }\r
-//                        if (!prevItem._expanded) {\r
-//                            _data.expandItem(i);\r
-//                            fireTreeEvent(prevItem._trace, prevItem._expanded);\r
-//                            prevItem = (TimeGraphItem) _data._expandedItems[i + prevItem.children.size()];\r
-//                            item._selected = false;\r
-//                            prevItem._selected = true;\r
-//                            changed = true;\r
-//                        }\r
-//                    } else {\r
-//                        item._selected = false;\r
-//                        prevItem._selected = true;\r
-//                        changed = true;\r
-//                    }\r
-//                }\r
-//                break;\r
-//            }\r
-//        }\r
-//        if (lastSelection < 0 && _data._expandedItems.length > 0) {\r
-//            TimeGraphItem item = (TimeGraphItem) _data._expandedItems[0];\r
-//            if (item._hasChildren) {\r
-//                _data.expandItem(0);\r
-//                fireTreeEvent(item._trace, item._expanded);\r
-//                item = (TimeGraphItem) _data._expandedItems[1];\r
-//                item._selected = true;\r
-//                changed = true;\r
-//            } else {\r
-//                item._selected = true;\r
-//                changed = true;\r
-//            }\r
-//        }\r
-//        if (changed) {\r
-//            ensureVisibleItem(-1, false);\r
-//            redraw();\r
-//            fireSelectionChanged();\r
-//        }\r
-//    }\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
-            TimeGraphItem item = (TimeGraphItem) _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 = (TimeGraphItem) _data._expandedItems[i + 1];\r
-                    item._selected = true;\r
-                    changed = true;\r
-                } else if ((-1 == n) && (i > 0)) {\r
-                    item._selected = false;\r
-                    item = (TimeGraphItem) _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
-            TimeGraphItem item = (TimeGraphItem) _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
-    public void selectEvent(int n) {\r
-        if (null == _timeProvider)\r
-            return;\r
-        ITimeGraphEntry trace = getSelectedTrace();\r
-        if (trace == null)\r
-            return;\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
-        if (null == nextEvent && -1 == n)\r
-            nextEvent = Utils.getFirstEvent(trace);\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
-            }\r
-            _timeProvider.setSelectedTimeNotify(nextTime, true);\r
-            fireSelectionChanged();\r
-        } else if (1 == n) {\r
-            _timeProvider.setSelectedTimeNotify(endTime, true);\r
-            fireSelectionChanged();\r
-        }\r
-    }\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
-    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
-    public void selectNextTrace() {\r
-        selectTrace(1);\r
-    }\r
-\r
-    public void selectPrevTrace() {\r
-        selectTrace(-1);\r
-    }\r
-\r
-    /**\r
-     * Zooming based on mouse cursor location with mouse scrolling\r
-     * \r
-     * @param zoomIn\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((double) interval * 0.8), _timeProvider.getMinTimeInterval());\r
-        } else {\r
-            newInterval = (long) Math.ceil((double) 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
-    }\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 - (long) ((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
-    public ITimeGraphEntry getSelectedTrace() {\r
-        ITimeGraphEntry trace = null;\r
-        int idx = getSelectedIndex();\r
-        if (idx >= 0)\r
-            trace = _data._expandedItems[idx]._trace;\r
-        return trace;\r
-    }\r
-\r
-    public int getSelectedIndex() {\r
-        int idx = -1;\r
-        for (int i = 0; i < _data._expandedItems.length; i++) {\r
-            TimeGraphItem item = (TimeGraphItem) _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
-            TimeGraphItem item = (TimeGraphItem) _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
-        int w = 4;\r
-        int nameWidth = _timeProvider.getNameSpace();\r
-        if (x > nameWidth - w && x < nameWidth + w) {\r
-            return true;\r
-        } else {\r
-            return false;\r
-        }\r
-    }\r
-\r
-    TimeGraphItem getItem(Point pt) {\r
-        int idx = getItemIndexAtY(pt.y);\r
-        return idx >= 0 ? (TimeGraphItem) _data._expandedItems[idx] : null;\r
-    }\r
-\r
-    long getTimeAtX(int x) {\r
-        if (null == _timeProvider)\r
-            return -1;\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
-        if (x >= 0 && size.x >= nameWidth) {\r
-            if (time1 - time0 > size.x - nameWidth - RIGHT_MARGIN) {\r
-                // get the last possible time represented by the pixel position\r
-                // by taking the time of the next pixel position minus 1\r
-                // nanosecond\r
-                hitTime = time0 + (long) ((time1 - time0) * ((double) (x + 1) / (size.x - nameWidth - RIGHT_MARGIN))) - 1;\r
-            } else {\r
-                hitTime = time0 + (long) ((time1 - time0) * ((double) (x) / (size.x - nameWidth - RIGHT_MARGIN)));\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
-                TimeGraphItem item = (TimeGraphItem) _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
-                TimeGraphItem item = (TimeGraphItem) _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
-    public void selectItem(ITimeGraphEntry trace, boolean addSelection) {\r
-        int idx = _data.findItemIndex(trace);\r
-        selectItem(idx, addSelection);\r
-    }\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
-    public int getTopIndex() {\r
-        return _topIndex;\r
-    }\r
-\r
-    public int getExpandedElementCount() {\r
-        return _data._expandedItems.length;\r
-    }\r
-\r
-    public ITimeGraphEntry[] getExpandedElements() {\r
-        ArrayList<ITimeGraphEntry> elements = new ArrayList<ITimeGraphEntry>();\r
-        for (TimeGraphItem 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
-        _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) ((double) (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
-    public void drawItems(Rectangle bounds, ITimeDataProvider timeProvider, TimeGraphItem[] items, int topIndex, int nameSpace, GC gc) {\r
-        for (int i = topIndex; i < items.length; i++) {\r
-            TimeGraphItem item = (TimeGraphItem) 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(TimeGraphItem 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(TimeGraphItem 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
-    @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
-        } 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
-        } 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
-            idx += page;\r
-            if (idx >= _data._expandedItems.length)\r
-                idx = _data._expandedItems.length - 1;\r
-        } else if (SWT.PAGE_UP == e.keyCode) {\r
-            int page = countPerPage();\r
-            idx = getSelectedIndex();\r
-            if (idx < 0)\r
-                idx = 0;\r
-            idx -= page;\r
-            if (idx < 0)\r
-                idx = 0;\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
-        redraw();\r
-    }\r
-\r
-    @Override\r
-    public void focusLost(FocusEvent e) {\r
-        _isInFocus = false;\r
-        if (DRAG_NONE != _dragState) {\r
-            setCapture(false);\r
-            _dragState = DRAG_NONE;\r
-        }\r
-        redraw();\r
-    }\r
-\r
-    public boolean isInFocus() {\r
-        return _isInFocus;\r
-    }\r
-\r
-    /**\r
-     * Provide the possibilty to control the wait cursor externally e.g. data\r
-     * requests in progress\r
-     * \r
-     * @param waitInd\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
-        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
-                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
-            // Make sure any time changes are notified to the application e.g.\r
-            // getting back from the horizontal scroll bar or zoomed using the\r
-            // mouse wheel\r
-            _timeProvider.notifyStartFinishTime();\r
-        }\r
-        updateCursor(e.x, e.y);\r
-    }\r
-\r
-    @Override\r
-    public void mouseDoubleClick(MouseEvent e) {\r
-        if (null == _timeProvider)\r
-            return;\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
-        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
-                TimeGraphItem 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
-        if (mouseScrollFilterListener == null) {\r
-            mouseScrollFilterListener = new Listener() {\r
-                // This filter is used to prevent scrolling of the view when the\r
-                // 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
-    }\r
-\r
-    @Override\r
-    public void mouseExit(MouseEvent e) {\r
-        if (mouseScrollFilterListener != null) {\r
-            getDisplay().removeFilter(SWT.MouseWheel, mouseScrollFilterListener);\r
-            mouseScrollFilterListener = null;\r
-        }\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
-        if (e.x < _timeProvider.getNameSpace() || e.x > getSize().x) {\r
-            setTopIndex(getTopIndex() - e.count);\r
-        } else if (_timeProvider.getTime0() != _timeProvider.getTime1()) {\r
-            if (e.count > 0) {\r
-                zoom(true);\r
-            } else if (e.count < 0) {\r
-                zoom(false);\r
-            }\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
-    public boolean isVisibleVerticalScroll() {\r
-        return _visibleVerticalScroll;\r
-    }\r
-\r
-    @Override\r
-    public int getBorderWidth() {\r
-        return _borderWidth;\r
-    }\r
-\r
-    public void setBorderWidth(int borderWidth) {\r
-        this._borderWidth = borderWidth;\r
-    }\r
-\r
-    public int getHeaderHeight() {\r
-        return _headerHeight;\r
-    }\r
-\r
-    public void setHeaderHeight(int headerHeight) {\r
-        this._headerHeight = headerHeight;\r
-    }\r
-\r
-    public int getItemHeight() {\r
-        return _itemHeight;\r
-    }\r
-\r
-    public void setItemHeight(int rowHeight) {\r
-        this._itemHeight = rowHeight;\r
-    }\r
-\r
-    public void setMinimumItemWidth(int width) {\r
-        this._minimumItemWidth = width;\r
-    }\r
-\r
-    public int getMinimumItemWidth() {\r
-        return _minimumItemWidth;\r
-    }\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
-}\r
-\r
-class ItemData {\r
-    public TimeGraphItem[] _expandedItems = new TimeGraphItem[0];\r
-    public TimeGraphItem[] _items = new TimeGraphItem[0];\r
-    private ITimeGraphEntry _traces[] = new ITimeGraphEntry[0];\r
-    private boolean traceFilter[] = new boolean[0];\r
-    private Vector<ITimeGraphEntry> filteredOut = new Vector<ITimeGraphEntry>();\r
-    public ITimeGraphPresentationProvider provider;\r
-\r
-    public ItemData() {\r
-    }\r
-\r
-    TimeGraphItem findItem(ITimeGraphEntry entry) {\r
-        if (entry == null)\r
-            return null;\r
-\r
-        for (int i = 0; i < _items.length; i++) {\r
-            TimeGraphItem 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
-        for (int i = 0; i < _expandedItems.length; i++) {\r
-            TimeGraphItem 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<TimeGraphItem> itemList = new ArrayList<TimeGraphItem>();\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 TimeGraphItem[0]);\r
-        updateExpandedItems();\r
-    }\r
-\r
-    private void refreshData(List<TimeGraphItem> itemList, TimeGraphItem parent, int level, ITimeGraphEntry entry) {\r
-        TimeGraphItem item = new TimeGraphItem(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<TimeGraphItem> expandedItemList = new ArrayList<TimeGraphItem>();\r
-        for (int i = 0; i < _traces.length; i++) {\r
-            ITimeGraphEntry entry = _traces[i];\r
-            TimeGraphItem item = findItem(entry);\r
-            refreshExpanded(expandedItemList, item);\r
-        }\r
-        _expandedItems = expandedItemList.toArray(new TimeGraphItem[0]);\r
-    }\r
-\r
-    private void refreshExpanded(List<TimeGraphItem> expandedItemList, TimeGraphItem item) {\r
-        expandedItemList.add(item);\r
-        if (item._hasChildren && item._expanded) {\r
-            for (TimeGraphItem child : item.children) {\r
-                refreshExpanded(expandedItemList, child);\r
-            }\r
-        }\r
-    }\r
-\r
-    public void expandItem(int idx) {\r
-        if (idx < 0 || idx >= _expandedItems.length)\r
-            return;\r
-        TimeGraphItem item = (TimeGraphItem) _expandedItems[idx];\r
-        if (item._hasChildren && !item._expanded) {\r
-            item._expanded = true;\r
-            updateExpandedItems();\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
+/*****************************************************************************
+ * Copyright (c) 2007, 2014 Intel Corporation and others
+ *
+ * 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, Ericsson - Updated for TMF
+ *   Patrick Tasse, Ericsson - Refactoring
+ *   Geneviève Bastien, Ã‰cole Polytechnique de Montréal - Move code to
+ *                            provide base classes for time graph view
+ *                            Add display of links between items
+ *   Xavier Raynaud, Kalray - Code optimization
+ *****************************************************************************/
+
+package org.eclipse.linuxtools.tmf.ui.widgets.timegraph.widgets;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.jface.action.IStatusLineManager;
+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.jface.viewers.ViewerFilter;
+import org.eclipse.linuxtools.tmf.core.timestamp.ITmfTimestamp;
+import org.eclipse.linuxtools.tmf.core.timestamp.TmfNanoTimestamp;
+import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimestampDelta;
+import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.ITimeGraphColorListener;
+import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.ITimeGraphPresentationProvider;
+import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.ITimeGraphPresentationProvider2;
+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.ILinkEvent;
+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.MenuDetectEvent;
+import org.eclipse.swt.events.MenuDetectListener;
+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.events.TypedEvent;
+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,
+        MenuDetectListener, ITmfTimeGraphDrawingHelper, ITimeGraphColorListener {
+
+    /** Max scrollbar size */
+    public static final int H_SCROLLBAR_MAX = Integer.MAX_VALUE - 1;
+
+    private static final int DRAG_NONE = 0;
+    private static final int DRAG_TRACE_ITEM = 1;
+    private static final int DRAG_SPLIT_LINE = 2;
+    private static final int DRAG_ZOOM = 3;
+    private static final int DRAG_SELECTION = 4;
+
+    private static final int CUSTOM_ITEM_HEIGHT = -1; // get item height from provider
+
+    private static final double ZOOM_FACTOR = 1.5;
+    private static final double ZOOM_IN_FACTOR = 0.8;
+    private static final double ZOOM_OUT_FACTOR = 1.25;
+
+    private static final int SNAP_WIDTH = 2;
+
+    private static final int NO_STATUS = -1;
+
+    /** Resource manager */
+    private LocalResourceManager fResourceManager = new LocalResourceManager(JFaceResources.getResources());
+
+    /** Color map for event types */
+    private Color[] fEventColorMap = null;
+
+    private ITimeDataProvider fTimeProvider;
+    private IStatusLineManager fStatusLineManager = null;
+    private TimeGraphScale fTimeGraphScale = null;
+
+    private boolean fIsInFocus = false;
+    private boolean fMouseOverSplitLine = false;
+    private int fGlobalItemHeight = CUSTOM_ITEM_HEIGHT;
+    private int fMinimumItemWidth = 0;
+    private int fTopIndex = 0;
+    private int fDragState = DRAG_NONE;
+    private int fDragButton;
+    private int fDragX0 = 0;
+    private int fDragX = 0;
+    private long fDragTime0 = 0; // used to preserve accuracy of modified selection
+    private int fIdealNameSpace = 0;
+    private long fTime0bak;
+    private long fTime1bak;
+    private ITimeGraphPresentationProvider fTimeGraphProvider = null;
+    private ItemData fItemData = null;
+    private List<SelectionListener> fSelectionListeners;
+    private final List<ISelectionChangedListener> fSelectionChangedListeners = new ArrayList<>();
+    private final List<ITimeGraphTreeListener> fTreeListeners = new ArrayList<>();
+    private final List<MenuDetectListener> fTimeGraphEntryMenuListeners = new ArrayList<>();
+    private final List<MenuDetectListener> fTimeEventMenuListeners = new ArrayList<>();
+    private final Cursor fDragCursor = Display.getDefault().getSystemCursor(SWT.CURSOR_HAND);
+    private final Cursor fResizeCursor = Display.getDefault().getSystemCursor(SWT.CURSOR_IBEAM);
+    private final Cursor fWaitCursor = Display.getDefault().getSystemCursor(SWT.CURSOR_WAIT);
+    private final Cursor fZoomCursor = Display.getDefault().getSystemCursor(SWT.CURSOR_SIZEWE);
+    private final List<ViewerFilter> fFilters = new ArrayList<>();
+    private MenuDetectEvent fPendingMenuDetectEvent = null;
+    private boolean fHideArrows = false;
+
+    private int fBorderWidth = 0;
+    private int fHeaderHeight = 0;
+
+    private Listener fMouseScrollFilterListener;
+
+    private MouseScrollNotifier fMouseScrollNotifier;
+    private final Object fMouseScrollNotifierLock = new Object();
+
+    private class MouseScrollNotifier extends Thread {
+        private static final long DELAY = 400L;
+        private static final 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;
+                        }
+                        fTimeProvider.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);
+
+        fItemData = new ItemData();
+
+        addFocusListener(this);
+        addMouseListener(this);
+        addMouseMoveListener(this);
+        addMouseTrackListener(this);
+        addMouseWheelListener(this);
+        addTraverseListener(this);
+        addKeyListener(this);
+        addControlListener(this);
+        addMenuDetectListener(this);
+        ScrollBar scrollHor = getHorizontalBar();
+
+        if (scrollHor != null) {
+            scrollHor.addSelectionListener(this);
+        }
+    }
+
+    @Override
+    public void dispose() {
+        super.dispose();
+        fResourceManager.dispose();
+    }
+
+    /**
+     * Sets the timegraph provider used by this timegraph viewer.
+     *
+     * @param timeGraphProvider the timegraph provider
+     */
+    public void setTimeGraphProvider(ITimeGraphPresentationProvider timeGraphProvider) {
+        fTimeGraphProvider = timeGraphProvider;
+
+        if (timeGraphProvider instanceof ITimeGraphPresentationProvider2) {
+            ((ITimeGraphPresentationProvider2) timeGraphProvider).setDrawingHelper(this);
+            ((ITimeGraphPresentationProvider2) timeGraphProvider).addColorListener(this);
+        }
+
+        StateItem[] stateItems = fTimeGraphProvider.getStateTable();
+        colorSettingsChanged(stateItems);
+    }
+
+    /**
+     * Gets the timegraph provider used by this timegraph viewer.
+     *
+     * @return the timegraph provider, or <code>null</code> if not set.
+     * @since 3.0
+     */
+    public ITimeGraphPresentationProvider getTimeGraphProvider() {
+        return fTimeGraphProvider;
+    }
+
+    /**
+     * Gets the color map used by this timegraph viewer.
+     *
+     * @return a color map, or <code>null</code> if not set.
+     * @since 3.0
+     */
+    public Color[] getEventColorMap() {
+        return fEventColorMap;
+    }
+
+    /**
+     * Assign the given time provider
+     *
+     * @param timeProvider
+     *            The time provider
+     */
+    public void setTimeProvider(ITimeDataProvider timeProvider) {
+        fTimeProvider = timeProvider;
+        adjustScrolls();
+        redraw();
+    }
+
+    /**
+     * Assign the status line manager
+     *
+     * @param statusLineManager
+     *            The status line manager, or null to disable status line messages
+     * @since 2.1
+     */
+    public void setStatusLineManager(IStatusLineManager statusLineManager) {
+        if (fStatusLineManager != null && statusLineManager == null) {
+            fStatusLineManager.setMessage(""); //$NON-NLS-1$
+        }
+        fStatusLineManager = statusLineManager;
+    }
+
+    /**
+     * Assign the time graph scale
+     *
+     * @param timeGraphScale
+     *            The time graph scale
+     * @since 2.1
+     */
+    public void setTimeGraphScale(TimeGraphScale timeGraphScale) {
+        fTimeGraphScale = timeGraphScale;
+    }
+
+    /**
+     * 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 == fSelectionListeners) {
+            fSelectionListeners = new ArrayList<>();
+        }
+        fSelectionListeners.add(listener);
+    }
+
+    /**
+     * Remove a selection listener
+     *
+     * @param listener
+     *            The listener to remove
+     */
+    public void removeSelectionListener(SelectionListener listener) {
+        if (null != fSelectionListeners) {
+            fSelectionListeners.remove(listener);
+        }
+    }
+
+    /**
+     * Selection changed callback
+     */
+    public void fireSelectionChanged() {
+        if (null != fSelectionListeners) {
+            Iterator<SelectionListener> it = fSelectionListeners.iterator();
+            while (it.hasNext()) {
+                SelectionListener listener = it.next();
+                listener.widgetSelected(null);
+            }
+        }
+    }
+
+    /**
+     * Default selection callback
+     */
+    public void fireDefaultSelection() {
+        if (null != fSelectionListeners) {
+            Iterator<SelectionListener> it = fSelectionListeners.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 fItemData.getTraces();
+    }
+
+    /**
+     * Get the on/off trace filters
+     *
+     * @return The array of filters
+     */
+    public boolean[] getTraceFilter() {
+        return fItemData.getTraceFilter();
+    }
+
+    /**
+     * Refresh the data for the thing
+     */
+    public void refreshData() {
+        fItemData.refreshData();
+        adjustScrolls();
+        redraw();
+    }
+
+    /**
+     * Refresh data for the given traces
+     *
+     * @param traces
+     *            The traces to refresh
+     */
+    public void refreshData(ITimeGraphEntry[] traces) {
+        fItemData.refreshData(traces);
+        adjustScrolls();
+        redraw();
+    }
+
+    /**
+     * Refresh the links (arrows) of this widget
+     *
+     * @param events The link events to refresh
+     * @since 2.1
+     */
+    public void refreshArrows(List<ILinkEvent> events) {
+        fItemData.refreshArrows(events);
+    }
+
+    /**
+     * Adjust the scoll bars
+     */
+    public void adjustScrolls() {
+        if (null == fTimeProvider) {
+            getHorizontalBar().setValues(0, 1, 1, 1, 1, 1);
+            return;
+        }
+
+        // HORIZONTAL BAR
+        // Visible window
+        long time0 = fTimeProvider.getTime0();
+        long time1 = fTimeProvider.getTime1();
+        // Time boundaries
+        long timeMin = fTimeProvider.getMinTime();
+        long timeMax = fTimeProvider.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;
+        int index = idx;
+        if (index < 0) {
+            for (index = 0; index < fItemData.fExpandedItems.length; index++) {
+                if (fItemData.fExpandedItems[index].fSelected) {
+                    break;
+                }
+            }
+        }
+        if (index >= fItemData.fExpandedItems.length) {
+            return changed;
+        }
+        if (index < fTopIndex) {
+            setTopIndex(index);
+            if (redraw) {
+                redraw();
+            }
+            changed = true;
+        } else {
+            int page = countPerPage();
+            if (index >= fTopIndex + page) {
+                setTopIndex(index - page + 1);
+                if (redraw) {
+                    redraw();
+                }
+                changed = true;
+            }
+        }
+        return changed;
+    }
+
+    /**
+     * Assign the given index as the top one
+     *
+     * @param idx
+     *            The index
+     */
+    public void setTopIndex(int idx) {
+        int index = Math.min(idx, fItemData.fExpandedItems.length - countPerPage());
+        index = Math.max(0,  index);
+        fTopIndex = index;
+        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 = fItemData.findItem(entry);
+        if (item != null && item.fExpanded != expanded) {
+            item.fExpanded = expanded;
+            fItemData.updateExpandedItems();
+            redraw();
+        }
+    }
+
+    /**
+     * Collapses all nodes of the viewer's tree, starting with the root.
+     *
+     * @since 2.0
+     */
+    public void collapseAll() {
+        for (Item item : fItemData.fItems) {
+            item.fExpanded = false;
+        }
+        fItemData.updateExpandedItems();
+        redraw();
+    }
+
+    /**
+     * Expands all nodes of the viewer's tree, starting with the root.
+     *
+     * @since 2.0
+     */
+    public void expandAll() {
+        for (Item item : fItemData.fItems) {
+            item.fExpanded = true;
+        }
+        fItemData.updateExpandedItems();
+        redraw();
+    }
+
+    /**
+     * Add a tree listener
+     *
+     * @param listener
+     *            The listener to add
+     */
+    public void addTreeListener(ITimeGraphTreeListener listener) {
+        if (!fTreeListeners.contains(listener)) {
+            fTreeListeners.add(listener);
+        }
+    }
+
+    /**
+     * Remove a tree listener
+     *
+     * @param listener
+     *            The listener to remove
+     */
+    public void removeTreeListener(ITimeGraphTreeListener listener) {
+        if (fTreeListeners.contains(listener)) {
+            fTreeListeners.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 : fTreeListeners) {
+            if (expanded) {
+                listener.treeExpanded(event);
+            } else {
+                listener.treeCollapsed(event);
+            }
+        }
+    }
+
+    /**
+     * Add a menu listener on {@link ITimeGraphEntry}s
+     * @param listener
+     *            The listener to add
+     * @since 1.2
+     */
+    public void addTimeGraphEntryMenuListener(MenuDetectListener listener) {
+        if (!fTimeGraphEntryMenuListeners.contains(listener)) {
+            fTimeGraphEntryMenuListeners.add(listener);
+        }
+    }
+
+    /**
+     * Remove a menu listener on {@link ITimeGraphEntry}s
+     *
+     * @param listener
+     *            The listener to remove
+     * @since 1.2
+     */
+    public void removeTimeGraphEntryMenuListener(MenuDetectListener listener) {
+        if (fTimeGraphEntryMenuListeners.contains(listener)) {
+            fTimeGraphEntryMenuListeners.remove(listener);
+        }
+    }
+
+    /**
+     * Menu event callback on {@link ITimeGraphEntry}s
+     *
+     * @param event
+     *            The MenuDetectEvent, with field {@link TypedEvent#data} set to the selected {@link ITimeGraphEntry}
+     */
+    private void fireMenuEventOnTimeGraphEntry(MenuDetectEvent event) {
+        for (MenuDetectListener listener : fTimeGraphEntryMenuListeners) {
+            listener.menuDetected(event);
+        }
+    }
+
+    /**
+     * Add a menu listener on {@link ITimeEvent}s
+     *
+     * @param listener
+     *            The listener to add
+     * @since 1.2
+     */
+    public void addTimeEventMenuListener(MenuDetectListener listener) {
+        if (!fTimeEventMenuListeners.contains(listener)) {
+            fTimeEventMenuListeners.add(listener);
+        }
+    }
+
+    /**
+     * Remove a menu listener on {@link ITimeEvent}s
+     *
+     * @param listener
+     *            The listener to remove
+     * @since 1.2
+     */
+    public void removeTimeEventMenuListener(MenuDetectListener listener) {
+        if (fTimeEventMenuListeners.contains(listener)) {
+            fTimeEventMenuListeners.remove(listener);
+        }
+    }
+
+    /**
+     * Menu event callback on {@link ITimeEvent}s
+     *
+     * @param event
+     *            The MenuDetectEvent, with field {@link TypedEvent#data} set to the selected {@link ITimeEvent}
+     */
+    private void fireMenuEventOnTimeEvent(MenuDetectEvent event) {
+        for (MenuDetectListener listener : fTimeEventMenuListeners) {
+            listener.menuDetected(event);
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    @Override
+    public ISelection getSelection() {
+        TimeGraphSelection sel = new TimeGraphSelection();
+        ITimeGraphEntry trace = getSelectedTrace();
+        if (null != trace && null != fTimeProvider) {
+            long selectedTime;
+            if (fTimeProvider instanceof ITimeDataProvider2) {
+                selectedTime = ((ITimeDataProvider2) fTimeProvider).getSelectionBegin();
+            } else {
+                selectedTime = fTimeProvider.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 < fItemData.fExpandedItems.length; i++) {
+            Item item = fItemData.fExpandedItems[i];
+            if (item.fSelected) {
+                lastSelection = i;
+                if ((1 == n) && (i < fItemData.fExpandedItems.length - 1)) {
+                    item.fSelected = false;
+                    item = fItemData.fExpandedItems[i + 1];
+                    item.fSelected = true;
+                    changed = true;
+                } else if ((-1 == n) && (i > 0)) {
+                    item.fSelected = false;
+                    item = fItemData.fExpandedItems[i - 1];
+                    item.fSelected = true;
+                    changed = true;
+                }
+                break;
+            }
+        }
+
+        if (lastSelection < 0 && fItemData.fExpandedItems.length > 0) {
+            Item item = fItemData.fExpandedItems[0];
+            item.fSelected = 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 == fTimeProvider) {
+            return;
+        }
+        ITimeGraphEntry trace = getSelectedTrace();
+        if (trace == null) {
+            return;
+        }
+        long selectedTime;
+        if (fTimeProvider instanceof ITimeDataProvider2) {
+            selectedTime = ((ITimeDataProvider2) fTimeProvider).getSelectionBegin();
+        } else {
+            @SuppressWarnings("deprecation")
+            long time = fTimeProvider.getSelectedTime();
+            selectedTime = time;
+        }
+        long endTime = fTimeProvider.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 && nextEvent.getTime() + nextEvent.getDuration() < selectedTime) {
+                // for previous event go to its end time unless we were already there
+                nextTime = nextEvent.getTime() + nextEvent.getDuration();
+            }
+            fTimeProvider.setSelectedTimeNotify(nextTime, true);
+            fireSelectionChanged();
+        } else if (1 == n) {
+            fTimeProvider.setSelectedTimeNotify(endTime, true);
+            fireSelectionChanged();
+        }
+    }
+
+    /**
+     * Select the next event
+     */
+    public void selectNextEvent() {
+        selectEvent(1);
+        // Notify if visible time window has been adjusted
+        fTimeProvider.setStartFinishTimeNotify(fTimeProvider.getTime0(), fTimeProvider.getTime1());
+    }
+
+    /**
+     * Select the previous event
+     */
+    public void selectPrevEvent() {
+        selectEvent(-1);
+        // Notify if visible time window has been adjusted
+        fTimeProvider.setStartFinishTimeNotify(fTimeProvider.getTime0(), fTimeProvider.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 = fTimeProvider.getNameSpace();
+        int timeSpace = fTimeProvider.getTimeSpace();
+        int xPos = Math.max(nameSpace, Math.min(nameSpace + timeSpace, p.x));
+        long time0 = fTimeProvider.getTime0();
+        long time1 = fTimeProvider.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 * ZOOM_IN_FACTOR), fTimeProvider.getMinTimeInterval());
+        } else {
+            newInterval = (long) Math.ceil(interval * ZOOM_OUT_FACTOR);
+        }
+        long center = time0 + Math.round(((double) (xPos - nameSpace) / timeSpace * interval));
+        long newTime0 = center - Math.round((double) newInterval * (center - time0) / interval);
+        long newTime1 = newTime0 + newInterval;
+        fTimeProvider.setStartFinishTime(newTime0, newTime1);
+        synchronized (fMouseScrollNotifierLock) {
+            if (fMouseScrollNotifier == null) {
+                fMouseScrollNotifier = new MouseScrollNotifier();
+                fMouseScrollNotifier.start();
+            }
+            fMouseScrollNotifier.mouseScrolled();
+        }
+    }
+
+    /**
+     * zoom in using single click
+     */
+    public void zoomIn() {
+        long prevTime0 = fTimeProvider.getTime0();
+        long prevTime1 = fTimeProvider.getTime1();
+        long prevRange = prevTime1 - prevTime0;
+        if (prevRange == 0) {
+            return;
+        }
+        long selTime;
+        if (fTimeProvider instanceof ITimeDataProvider2) {
+            ITimeDataProvider2 provider = ((ITimeDataProvider2) fTimeProvider);
+            selTime = (provider.getSelectionEnd() + provider.getSelectionBegin()) / 2;
+        } else {
+            @SuppressWarnings("deprecation")
+            long selectedTime = fTimeProvider.getSelectedTime();
+            selTime = selectedTime;
+        }
+        if (selTime <= prevTime0 || selTime >= prevTime1) {
+            selTime = (prevTime0 + prevTime1) / 2;
+        }
+        long time0 = selTime - (long) ((selTime - prevTime0) / ZOOM_FACTOR);
+        long time1 = selTime + (long) ((prevTime1 - selTime) / ZOOM_FACTOR);
+
+        long inaccuracy = (fTimeProvider.getMaxTime() - fTimeProvider.getMinTime()) - (time1 - time0);
+
+        if (inaccuracy > 0 && inaccuracy < 100) {
+            fTimeProvider.setStartFinishTimeNotify(fTimeProvider.getMinTime(), fTimeProvider.getMaxTime());
+            return;
+        }
+
+        long min = fTimeProvider.getMinTimeInterval();
+        if ((time1 - time0) < min) {
+            time0 = selTime - (selTime - prevTime0) * min / prevRange;
+            time1 = time0 + min;
+        }
+
+        fTimeProvider.setStartFinishTimeNotify(time0, time1);
+    }
+
+    /**
+     * zoom out using single click
+     */
+    public void zoomOut() {
+        long prevTime0 = fTimeProvider.getTime0();
+        long prevTime1 = fTimeProvider.getTime1();
+        long selTime;
+        if (fTimeProvider instanceof ITimeDataProvider2) {
+            ITimeDataProvider2 provider = ((ITimeDataProvider2) fTimeProvider);
+            selTime = (provider.getSelectionEnd() + provider.getSelectionBegin()) / 2;
+        } else {
+            @SuppressWarnings("deprecation")
+            long selectedTime = fTimeProvider.getSelectedTime();
+            selTime = selectedTime;
+        }
+        if (selTime <= prevTime0 || selTime >= prevTime1) {
+            selTime = (prevTime0 + prevTime1) / 2;
+        }
+        long time0 = (long) (selTime - (selTime - prevTime0) * ZOOM_FACTOR);
+        long time1 = (long) (selTime + (prevTime1 - selTime) * ZOOM_FACTOR);
+
+        long inaccuracy = (fTimeProvider.getMaxTime() - fTimeProvider.getMinTime()) - (time1 - time0);
+        if (inaccuracy > 0 && inaccuracy < 100) {
+            fTimeProvider.setStartFinishTimeNotify(fTimeProvider.getMinTime(), fTimeProvider.getMaxTime());
+            return;
+        }
+
+        fTimeProvider.setStartFinishTimeNotify(time0, time1);
+    }
+
+    /**
+     * Hide arrows
+     *
+     * @param hideArrows true to hide arrows
+     *
+     * @since 2.1
+     */
+    public void hideArrows(boolean hideArrows) {
+        fHideArrows = hideArrows;
+    }
+
+    /**
+     * Follow the arrow forward
+     *
+     * @since 2.1
+     */
+    public void followArrowFwd() {
+        ITimeGraphEntry trace = getSelectedTrace();
+        if (trace == null) {
+            return;
+        }
+        long selectedTime = ((ITimeDataProvider2) fTimeProvider).getSelectionBegin();
+        for (ILinkEvent link : fItemData.fLinks) {
+            if (link.getEntry() == trace && link.getTime() == selectedTime) {
+                selectItem(link.getDestinationEntry(), false);
+                if (link.getDuration() != 0) {
+                    fTimeProvider.setSelectedTimeNotify(link.getTime() + link.getDuration(), true);
+                    // Notify if visible time window has been adjusted
+                    fTimeProvider.setStartFinishTimeNotify(fTimeProvider.getTime0(), fTimeProvider.getTime1());
+                }
+                fireSelectionChanged();
+                return;
+            }
+        }
+        selectNextEvent();
+    }
+
+    /**
+     * Follow the arrow backward
+     *
+     * @since 2.1
+     */
+    public void followArrowBwd() {
+        ITimeGraphEntry trace = getSelectedTrace();
+        if (trace == null) {
+            return;
+        }
+        long selectedTime = ((ITimeDataProvider2) fTimeProvider).getSelectionBegin();
+        for (ILinkEvent link : fItemData.fLinks) {
+            if (link.getDestinationEntry() == trace && link.getTime() + link.getDuration() == selectedTime) {
+                selectItem(link.getEntry(), false);
+                if (link.getDuration() != 0) {
+                    fTimeProvider.setSelectedTimeNotify(link.getTime(), true);
+                    // Notify if visible time window has been adjusted
+                    fTimeProvider.setStartFinishTimeNotify(fTimeProvider.getTime0(), fTimeProvider.getTime1());
+                }
+                fireSelectionChanged();
+                return;
+            }
+        }
+        selectPrevEvent();
+    }
+
+    /**
+     * Return the currently selected trace
+     *
+     * @return The entry matching the trace
+     */
+    public ITimeGraphEntry getSelectedTrace() {
+        ITimeGraphEntry trace = null;
+        int idx = getSelectedIndex();
+        if (idx >= 0) {
+            trace = fItemData.fExpandedItems[idx].fTrace;
+        }
+        return trace;
+    }
+
+    /**
+     * Retrieve the index of the currently selected item
+     *
+     * @return The index
+     */
+    public int getSelectedIndex() {
+        int idx = -1;
+        for (int i = 0; i < fItemData.fExpandedItems.length; i++) {
+            Item item = fItemData.fExpandedItems[i];
+            if (item.fSelected) {
+                idx = i;
+                break;
+            }
+        }
+        return idx;
+    }
+
+    boolean toggle(int idx) {
+        boolean toggled = false;
+        if (idx >= 0 && idx < fItemData.fExpandedItems.length) {
+            Item item = fItemData.fExpandedItems[idx];
+            if (item.fHasChildren) {
+                item.fExpanded = !item.fExpanded;
+                fItemData.updateExpandedItems();
+                adjustScrolls();
+                redraw();
+                toggled = true;
+                fireTreeEvent(item.fTrace, item.fExpanded);
+            }
+        }
+        return toggled;
+    }
+
+    int getItemIndexAtY(int y) {
+        if (y < 0) {
+            return -1;
+        }
+        int ySum = 0;
+        for (int idx = fTopIndex; idx < fItemData.fExpandedItems.length; idx++) {
+            ySum += fItemData.fExpandedItems[idx].fItemHeight;
+            if (y < ySum) {
+                return idx;
+            }
+        }
+        return -1;
+    }
+
+    boolean isOverSplitLine(int x) {
+        if (x < 0 || null == fTimeProvider) {
+            return false;
+        }
+        int nameWidth = fTimeProvider.getNameSpace();
+        return Math.abs(x - nameWidth) < SNAP_WIDTH;
+    }
+
+    ITimeGraphEntry getEntry(Point pt) {
+        int idx = getItemIndexAtY(pt.y);
+        return idx >= 0 ? fItemData.fExpandedItems[idx].fTrace : null;
+    }
+
+    /**
+     * @since 2.0
+     */
+    @Override
+    public int getXForTime(long time) {
+        if (null == fTimeProvider) {
+            return -1;
+        }
+        long time0 = fTimeProvider.getTime0();
+        long time1 = fTimeProvider.getTime1();
+        int width = getCtrlSize().x;
+        int nameSpace = fTimeProvider.getNameSpace();
+        double pixelsPerNanoSec = (width - nameSpace <= RIGHT_MARGIN) ? 0 : (double) (width - nameSpace - RIGHT_MARGIN) / (time1 - time0);
+        int x = getBounds().x + nameSpace + (int) ((time - time0) * pixelsPerNanoSec);
+        return x;
+    }
+
+    /**
+     * @since 2.0
+     */
+    @Override
+    public long getTimeAtX(int coord) {
+        if (null == fTimeProvider) {
+            return -1;
+        }
+        long hitTime = -1;
+        Point size = getCtrlSize();
+        long time0 = fTimeProvider.getTime0();
+        long time1 = fTimeProvider.getTime1();
+        int nameWidth = fTimeProvider.getNameSpace();
+        final int x = coord - 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 < fItemData.fExpandedItems.length) {
+                Item item = fItemData.fExpandedItems[idx];
+                changed = !item.fSelected;
+                item.fSelected = true;
+            }
+        } else {
+            for (int i = 0; i < fItemData.fExpandedItems.length; i++) {
+                Item item = fItemData.fExpandedItems[i];
+                if ((i == idx && !item.fSelected) || (idx == -1 && item.fSelected)) {
+                    changed = true;
+                }
+                item.fSelected = 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 = fItemData.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;
+        int ySum = 0;
+        for (int idx = fTopIndex; idx < fItemData.fExpandedItems.length; idx++) {
+            ySum += fItemData.fExpandedItems[idx].fItemHeight;
+            if (ySum >= height) {
+                return count;
+            }
+            count++;
+        }
+        for (int idx = fTopIndex - 1; idx >= 0; idx--) {
+            ySum += fItemData.fExpandedItems[idx].fItemHeight;
+            if (ySum >= height) {
+                return count;
+            }
+            count++;
+        }
+        return count;
+    }
+
+    /**
+     * Get the index of the top element
+     *
+     * @return The index
+     */
+    public int getTopIndex() {
+        return fTopIndex;
+    }
+
+    /**
+     * Get the number of expanded items
+     *
+     * @return The count of expanded items
+     */
+    public int getExpandedElementCount() {
+        return fItemData.fExpandedItems.length;
+    }
+
+    /**
+     * Get an array of all expanded elements
+     *
+     * @return The expanded elements
+     */
+    public ITimeGraphEntry[] getExpandedElements() {
+        ArrayList<ITimeGraphEntry> elements = new ArrayList<>();
+        for (Item item : fItemData.fExpandedItems) {
+            elements.add(item.fTrace);
+        }
+        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) {
+        Rectangle rect = getStatesRect(bound, idx, nameWidth);
+        rect.x = bound.x;
+        rect.width = nameWidth;
+        return rect;
+    }
+
+    Rectangle getStatesRect(Rectangle bound, int idx, int nameWidth) {
+        int x = bound.x + nameWidth;
+        int width = bound.width - x;
+        int ySum = 0;
+        if (idx >= fTopIndex) {
+            for (int i = fTopIndex; i < idx; i++) {
+                ySum += fItemData.fExpandedItems[i].fItemHeight;
+            }
+        } else {
+            for (int i = fTopIndex - 1; i >= idx; i--) {
+                ySum -= fItemData.fExpandedItems[i].fItemHeight;
+            }
+        }
+        int y = bound.y + ySum;
+        int height = fItemData.fExpandedItems[idx].fItemHeight;
+        return new Rectangle(x, y, width, height);
+    }
+
+    @Override
+    void paint(Rectangle bounds, PaintEvent e) {
+        GC gc = e.gc;
+        gc.setBackground(getColorScheme().getColor(TimeGraphColorScheme.BACKGROUND));
+        drawBackground(gc, bounds.x, bounds.y, bounds.width, bounds.height);
+
+        if (bounds.width < 2 || bounds.height < 2 || null == fTimeProvider) {
+            return;
+        }
+
+        fIdealNameSpace = 0;
+        int nameSpace = fTimeProvider.getNameSpace();
+
+        // draw empty name space background
+        gc.setBackground(getColorScheme().getBkColor(false, false, true));
+        drawBackground(gc, bounds.x, bounds.y, nameSpace, bounds.height);
+
+        // draw items
+        drawItems(bounds, fTimeProvider, fItemData.fExpandedItems, fTopIndex, nameSpace, gc);
+        drawLinks(bounds, fTimeProvider, fItemData.fLinks, nameSpace, gc);
+        fTimeGraphProvider.postDrawControl(bounds, gc);
+
+        int alpha = gc.getAlpha();
+        gc.setAlpha(100);
+
+        long time0 = fTimeProvider.getTime0();
+        long time1 = fTimeProvider.getTime1();
+        long selectionBegin;
+        long selectionEnd;
+        if (fTimeProvider instanceof ITimeDataProvider2) {
+            selectionBegin = ((ITimeDataProvider2) fTimeProvider).getSelectionBegin();
+            selectionEnd = ((ITimeDataProvider2) fTimeProvider).getSelectionEnd();
+        } else {
+            @SuppressWarnings("deprecation")
+            long selectedTime = fTimeProvider.getSelectedTime();
+            selectionBegin = selectedTime;
+            selectionEnd = selectedTime;
+        }
+        double pixelsPerNanoSec = (bounds.width - nameSpace <= RIGHT_MARGIN) ? 0 : (double) (bounds.width - nameSpace - RIGHT_MARGIN) / (time1 - time0);
+        int x0 = bounds.x + nameSpace + (int) ((selectionBegin - time0) * pixelsPerNanoSec);
+        int x1 = bounds.x + nameSpace + (int) ((selectionEnd - time0) * pixelsPerNanoSec);
+
+        // draw selection lines
+        if (fDragState != DRAG_SELECTION) {
+            gc.setForeground(getColorScheme().getColor(TimeGraphColorScheme.SELECTED_TIME));
+            if (x0 >= nameSpace && x0 < bounds.x + bounds.width) {
+                gc.drawLine(x0, bounds.y, x0, bounds.y + bounds.height);
+            }
+            if (x1 != x0) {
+                if (x1 >= nameSpace && x1 < bounds.x + bounds.width) {
+                    gc.drawLine(x1, bounds.y, x1, bounds.y + bounds.height);
+                }
+            }
+        }
+
+        // draw selection background
+        if (selectionBegin != 0 && selectionEnd != 0 && fDragState != DRAG_SELECTION) {
+            x0 = Math.max(nameSpace, Math.min(bounds.x + bounds.width, x0));
+            x1 = Math.max(nameSpace, Math.min(bounds.x + bounds.width, x1));
+            gc.setBackground(getColorScheme().getBkColor(false, false, true));
+            if (x1 - x0 > 1) {
+                gc.fillRectangle(new Rectangle(x0 + 1, bounds.y, x1 - x0 - 1, bounds.height));
+            } else if (x0 - x1 > 1) {
+                gc.fillRectangle(new Rectangle(x1 + 1, bounds.y, x0 - x1 - 1, bounds.height));
+            }
+        }
+
+        // draw drag selection background
+        if (fDragState == DRAG_ZOOM || fDragState == DRAG_SELECTION) {
+            gc.setBackground(getColorScheme().getBkColor(false, false, true));
+            if (fDragX0 < fDragX) {
+                gc.fillRectangle(new Rectangle(fDragX0, bounds.y, fDragX - fDragX0, bounds.height));
+            } else if (fDragX0 > fDragX) {
+                gc.fillRectangle(new Rectangle(fDragX, bounds.y, fDragX0 - fDragX, bounds.height));
+            }
+        }
+
+        // draw drag line
+        if (DRAG_SPLIT_LINE == fDragState) {
+            gc.setForeground(getColorScheme().getColor(TimeGraphColorScheme.BLACK));
+            gc.drawLine(bounds.x + nameSpace, bounds.y, bounds.x + nameSpace, bounds.y + bounds.height - 1);
+        } else if (DRAG_ZOOM == fDragState && Math.max(fDragX, fDragX0) > nameSpace) {
+            gc.setForeground(getColorScheme().getColor(TimeGraphColorScheme.TOOL_FOREGROUND));
+            gc.drawLine(fDragX0, bounds.y, fDragX0, bounds.y + bounds.height - 1);
+            if (fDragX != fDragX0) {
+                gc.drawLine(fDragX, bounds.y, fDragX, bounds.y + bounds.height - 1);
+            }
+        } else if (DRAG_SELECTION == fDragState && Math.max(fDragX, fDragX0) > nameSpace) {
+            gc.setForeground(getColorScheme().getColor(TimeGraphColorScheme.SELECTED_TIME));
+            gc.drawLine(fDragX0, bounds.y, fDragX0, bounds.y + bounds.height - 1);
+            if (fDragX != fDragX0) {
+                gc.drawLine(fDragX, bounds.y, fDragX, bounds.y + bounds.height - 1);
+            }
+        } else if (DRAG_NONE == fDragState && fMouseOverSplitLine && fTimeProvider.getNameSpace() > 0) {
+            gc.setForeground(getColorScheme().getColor(TimeGraphColorScheme.RED));
+            gc.drawLine(bounds.x + nameSpace, bounds.y, bounds.x + nameSpace, bounds.y + bounds.height - 1);
+        }
+
+        gc.setAlpha(alpha);
+    }
+
+    /**
+     * 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);
+        }
+    }
+
+    /**
+     * Draws the item
+     *
+     * @param item the item to draw
+     * @param bounds the container rectangle
+     * @param timeProvider Time provider
+     * @param i the item index
+     * @param nameSpace the name space
+     * @param gc Graphics context
+     */
+    protected void drawItem(Item item, Rectangle bounds, ITimeDataProvider timeProvider, int i, int nameSpace, GC gc) {
+        ITimeGraphEntry entry = item.fTrace;
+        long time0 = timeProvider.getTime0();
+        long time1 = timeProvider.getTime1();
+        long selectedTime;
+        if (fTimeProvider instanceof ITimeDataProvider2) {
+            selectedTime = ((ITimeDataProvider2) fTimeProvider).getSelectionBegin();
+        } else {
+            @SuppressWarnings("deprecation")
+            long time = fTimeProvider.getSelectedTime();
+            selectedTime = time;
+        }
+
+        Rectangle nameRect = getNameRect(bounds, i, nameSpace);
+        if (nameRect.y >= bounds.y + bounds.height) {
+            return;
+        }
+
+        if (! item.fTrace.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(getColorScheme().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.fSelected;
+        // K pixels per second
+        double pixelsPerNanoSec = (rect.width <= RIGHT_MARGIN) ? 0 : (double) (rect.width - RIGHT_MARGIN) / (time1 - time0);
+
+        if (item.fTrace.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;
+                    }
+                }
+                boolean timeSelected = selectedTime >= event.getTime() && selectedTime < event.getTime() + event.getDuration();
+                if (drawState(getColorScheme(), event, stateRect, gc, selected, timeSelected)) {
+                    lastX = x;
+                }
+            }
+        }
+        fTimeGraphProvider.postDrawEntry(entry, rect, gc);
+    }
+
+    /**
+     * Draw the links
+     *
+     * @param bounds
+     *            The rectangle of the area
+     * @param timeProvider
+     *            The time provider
+     * @param links
+     *            The array items to draw
+     * @param nameSpace
+     *            The width reserved for the names
+     * @param gc
+     *            Reference to the SWT GC object
+     * @since 2.1
+     */
+    public void drawLinks(Rectangle bounds, ITimeDataProvider timeProvider,
+            List<ILinkEvent> links, int nameSpace, GC gc) {
+        if (fHideArrows) {
+            return;
+        }
+        for (ILinkEvent event : links) {
+            drawLink(event, bounds, timeProvider, nameSpace, gc);
+        }
+    }
+
+    /**
+     * Draws the link type events of this item
+     *
+     * @param event
+     *            the item to draw
+     * @param bounds
+     *            the container rectangle
+     * @param timeProvider
+     *            Time provider
+     * @param nameSpace
+     *            the name space
+     * @param gc
+     *            Graphics context
+     * @since 2.1
+     */
+    protected void drawLink(ILinkEvent event, Rectangle bounds, ITimeDataProvider timeProvider, int nameSpace, GC gc) {
+        int srcIndex = fItemData.findItemIndex(event.getEntry());
+        int destIndex = fItemData.findItemIndex(event.getDestinationEntry());
+
+        if ((srcIndex == -1) || (destIndex == -1)) {
+            return;
+        }
+
+        Rectangle src = getStatesRect(bounds, srcIndex, nameSpace);
+        Rectangle dst = getStatesRect(bounds, destIndex, nameSpace);
+
+        int x0 = getXForTime(event.getTime());
+        int x1 = getXForTime(event.getTime() + event.getDuration());
+
+        // limit the x-coordinates to prevent integer overflow in calculations
+        // and also GC.drawLine doesn't draw properly with large coordinates
+        final int limit = Integer.MAX_VALUE / 1024;
+        x0 = Math.max(-limit, Math.min(x0, limit));
+        x1 = Math.max(-limit, Math.min(x1, limit));
+
+        int y0 = src.y + src.height / 2;
+        int y1 = dst.y + dst.height / 2;
+        drawArrow(getColorScheme(), event, new Rectangle(x0, y0, x1 - x0, y1 - y0), gc);
+    }
+
+    /**
+     * Draw the state (color fill)
+     *
+     * @param colors
+     *            Color scheme
+     * @param event
+     *            Time event for which we're drawing the state
+     * @param rect
+     *            Where to draw
+     * @param gc
+     *            Graphics context
+     * @return true if the state was drawn
+     * @since 2.1
+     */
+    protected boolean drawArrow(TimeGraphColorScheme colors, ITimeEvent event,
+            Rectangle rect, GC gc) {
+
+        int colorIdx = fTimeGraphProvider.getStateTableIndex(event);
+        if (colorIdx < 0) {
+            return false;
+        }
+        boolean visible = ((rect.height == 0) && (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);
+            }
+
+            gc.setForeground(stateColor);
+            gc.setBackground(stateColor);
+
+            /* Draw the arrow */
+            gc.drawLine(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height);
+            drawArrowHead(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height, gc);
+
+        }
+        fTimeGraphProvider.postDrawEvent(event, rect, gc);
+        return visible;
+    }
+
+    /*
+     * @author Francis Giraldeau
+     *
+     * Inspiration:
+     * http://stackoverflow.com/questions/3010803/draw-arrow-on-line-algorithm
+     *
+     * The algorithm was taken from this site, not the code itself
+     */
+    private static void drawArrowHead(int x0, int y0, int x1, int y1, GC gc)
+    {
+        int factor = 10;
+        double cos = 0.9510;
+        double sin = 0.3090;
+        long lenx = x1 - x0;
+        long leny = y1 - y0;
+        double len = Math.sqrt(lenx * lenx + leny * leny);
+
+        double dx = factor * lenx / len;
+        double dy = factor * leny / len;
+        int end1X = (int) Math.round((x1 - (dx * cos + dy * -sin)));
+        int end1Y = (int) Math.round((y1 - (dx * sin + dy * cos)));
+        int end2X = (int) Math.round((x1 - (dx * cos + dy * sin)));
+        int end2Y = (int) Math.round((y1 - (dx * -sin + dy * cos)));
+        int[] arrow = new int[] { x1, y1, end1X, end1Y, end2X, end2Y, x1, y1 };
+        gc.fillPolygon(arrow);
+    }
+
+    /**
+     * Draw the name of an item.
+     *
+     * @param item
+     *            Item object
+     * @param bounds
+     *            Where to draw the name
+     * @param gc
+     *            Graphics context
+     */
+    protected void drawName(Item item, Rectangle bounds, GC gc) {
+        boolean hasTimeEvents = item.fTrace.hasTimeEvents();
+        if (! hasTimeEvents) {
+            gc.setBackground(getColorScheme().getBkColorGroup(item.fSelected, fIsInFocus));
+            gc.fillRectangle(bounds);
+            if (item.fSelected && fIsInFocus) {
+                gc.setForeground(getColorScheme().getBkColor(item.fSelected, fIsInFocus, false));
+                gc.drawRectangle(bounds.x, bounds.y, bounds.width - 1, bounds.height - 1);
+            }
+        } else {
+            gc.setBackground(getColorScheme().getBkColor(item.fSelected, fIsInFocus, true));
+            gc.setForeground(getColorScheme().getFgColor(item.fSelected, fIsInFocus));
+            gc.fillRectangle(bounds);
+        }
+
+        // No name to be drawn
+        if (fTimeProvider.getNameSpace() == 0) {
+            return;
+        }
+
+        int leftMargin = MARGIN + item.fLevel * EXPAND_SIZE;
+        if (item.fHasChildren) {
+            gc.setForeground(getColorScheme().getFgColorGroup(false, false));
+            gc.setBackground(getColorScheme().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.fExpanded) {
+                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.fTrace);
+        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.fName;
+        Point size = gc.stringExtent(name);
+        if (fIdealNameSpace < leftMargin + size.x + MARGIN) {
+            fIdealNameSpace = 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(getColorScheme().getFgColor(item.fSelected, fIsInFocus));
+            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(getColorScheme().getColor(TimeGraphColorScheme.MID_LINE));
+                gc.drawLine(x, midy, x + width, midy);
+            }
+        }
+    }
+
+    /**
+     * Draw the state (color fill)
+     *
+     * @param colors
+     *            Color scheme
+     * @param event
+     *            Time event for which we're drawing the state
+     * @param rect
+     *            Where to draw
+     * @param gc
+     *            Graphics context
+     * @param selected
+     *            Is this time event currently selected (so it appears
+     *            highlighted)
+     * @param timeSelected
+     *            Is the timestamp currently selected
+     * @return true if the state was drawn
+     * @since 2.0
+     */
+    protected boolean drawState(TimeGraphColorScheme colors, ITimeEvent event,
+            Rectangle rect, GC gc, boolean selected, boolean timeSelected) {
+
+        int colorIdx = fTimeGraphProvider.getStateTableIndex(event);
+        if (colorIdx < 0 && colorIdx != ITimeGraphPresentationProvider.TRANSPARENT) {
+            return false;
+        }
+        boolean visible = rect.width == 0 ? false : true;
+        Color black =  Display.getDefault().getSystemColor(SWT.COLOR_BLACK);
+        gc.setForeground(black);
+
+        if (visible) {
+            if (colorIdx == ITimeGraphPresentationProvider.TRANSPARENT) {
+                // Only draw the top and bottom borders
+                gc.drawLine(rect.x, rect.y, rect.x + rect.width - 1, rect.y);
+                gc.drawLine(rect.x, rect.y + rect.height - 1, rect.x + rect.width - 1, rect.y + rect.height - 1);
+                if (rect.width == 1) {
+                    gc.drawPoint(rect.x, rect.y - 2);
+                }
+                return false;
+            }
+            Color stateColor = null;
+            if (colorIdx < fEventColorMap.length) {
+                stateColor = fEventColorMap[colorIdx];
+            } else {
+                stateColor = black;
+            }
+
+            boolean reallySelected = timeSelected && selected;
+            // fill all rect area
+            gc.setBackground(stateColor);
+            gc.fillRectangle(rect);
+
+            if (reallySelected) {
+                gc.drawLine(rect.x, rect.y - 1, rect.x + rect.width - 1, rect.y - 1);
+                gc.drawLine(rect.x, rect.y + rect.height, rect.x + rect.width - 1, rect.y + rect.height);
+            }
+        } else {
+            gc.drawPoint(rect.x, rect.y - 2);
+        }
+        fTimeGraphProvider.postDrawEvent(event, rect, gc);
+        return visible;
+    }
+
+    /**
+     * Fill the space between two contiguous time events
+     *
+     * @param rect
+     *            Rectangle to fill
+     * @param gc
+     *            Graphics context
+     * @param selected
+     *            Is this time event selected or not
+     */
+    protected void fillSpace(Rectangle rect, GC gc, boolean selected) {
+        gc.setBackground(getColorScheme().getBkColor(selected, fIsInFocus, false));
+        gc.fillRectangle(rect);
+        if (fDragState == DRAG_ZOOM) {
+            gc.setBackground(getColorScheme().getBkColor(selected, fIsInFocus, true));
+            if (fDragX0 < fDragX) {
+                gc.fillRectangle(new Rectangle(fDragX0, rect.y, fDragX - fDragX0, rect.height));
+            } else if (fDragX0 > fDragX) {
+                gc.fillRectangle(new Rectangle(fDragX, rect.y, fDragX0 - fDragX, rect.height));
+            }
+        }
+        // draw middle line
+        gc.setForeground(getColorScheme().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 (fItemData.fExpandedItems.length == 0) {
+            return;
+        }
+        if (SWT.HOME == e.keyCode) {
+            idx = 0;
+        } else if (SWT.END == e.keyCode) {
+            idx = fItemData.fExpandedItems.length - 1;
+        } else if (SWT.ARROW_DOWN == e.keyCode) {
+            idx = getSelectedIndex();
+            if (idx < 0) {
+                idx = 0;
+            } else if (idx < fItemData.fExpandedItems.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 >= fItemData.fExpandedItems.length) {
+                idx = fItemData.fExpandedItems.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 (fItemData.fExpandedItems[idx].fHasChildren) {
+                    toggle(idx);
+                } else {
+                    fireDefaultSelection();
+                }
+            }
+            idx = -1;
+        }
+        if (idx >= 0) {
+            selectItem(idx, false);
+            fireSelectionChanged();
+        }
+        int x = toControl(e.display.getCursorLocation()).x;
+        updateCursor(x, e.stateMask | e.keyCode);
+    }
+
+    @Override
+    public void keyReleased(KeyEvent e) {
+        int x = toControl(e.display.getCursorLocation()).x;
+        updateCursor(x, e.stateMask & ~e.keyCode);
+    }
+
+    @Override
+    public void focusGained(FocusEvent e) {
+        fIsInFocus = true;
+        if (fMouseScrollFilterListener == null) {
+            fMouseScrollFilterListener = 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, fMouseScrollFilterListener);
+        }
+        redraw();
+        updateStatusLine(NO_STATUS);
+    }
+
+    @Override
+    public void focusLost(FocusEvent e) {
+        fIsInFocus = false;
+        if (fMouseScrollFilterListener != null) {
+            getDisplay().removeFilter(SWT.MouseWheel, fMouseScrollFilterListener);
+            fMouseScrollFilterListener = null;
+        }
+        if (DRAG_NONE != fDragState) {
+            setCapture(false);
+            fDragState = DRAG_NONE;
+        }
+        redraw();
+        updateStatusLine(NO_STATUS);
+    }
+
+    /**
+     * @return If the current view is focused
+     */
+    public boolean isInFocus() {
+        return fIsInFocus;
+    }
+
+    /**
+     * 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(fWaitCursor);
+        } else {
+            setCursor(null);
+        }
+    }
+
+    private void updateCursor(int x, int stateMask) {
+        // if Wait cursor not active, check for the need to change the cursor
+        if (getCursor() == fWaitCursor) {
+            return;
+        }
+        Cursor cursor = null;
+        if (fDragState == DRAG_SPLIT_LINE) {
+        } else if (fDragState == DRAG_SELECTION) {
+            cursor = fResizeCursor;
+        } else if (fDragState == DRAG_TRACE_ITEM) {
+            cursor = fDragCursor;
+        } else if (fDragState == DRAG_ZOOM) {
+            cursor = fZoomCursor;
+        } else if ((stateMask & SWT.MODIFIER_MASK) == SWT.CTRL) {
+            cursor = fDragCursor;
+        } else if ((stateMask & SWT.MODIFIER_MASK) == SWT.SHIFT) {
+            cursor = fResizeCursor;
+        } else {
+            if (!isOverSplitLine(x) &&fTimeProvider instanceof ITimeDataProvider2) {
+                long selectionBegin = ((ITimeDataProvider2) fTimeProvider).getSelectionBegin();
+                long selectionEnd = ((ITimeDataProvider2) fTimeProvider).getSelectionEnd();
+                int xBegin = getXForTime(selectionBegin);
+                int xEnd = getXForTime(selectionEnd);
+                if (Math.abs(x - xBegin) < SNAP_WIDTH || Math.abs(x - xEnd) < SNAP_WIDTH) {
+                    cursor = fResizeCursor;
+                }
+            }
+        }
+        if (getCursor() != cursor) {
+            setCursor(cursor);
+        }
+    }
+
+    private void updateStatusLine(int x) {
+        if (fStatusLineManager == null || null == fTimeProvider ||
+                fTimeProvider.getTime0() == fTimeProvider.getTime1()) {
+            return;
+        }
+        StringBuilder message = new StringBuilder();
+        if (x >= 0 && fDragState == DRAG_NONE) {
+            long time = getTimeAtX(x);
+            if (time >= 0) {
+                message.append("T: "); //$NON-NLS-1$
+                message.append(new TmfNanoTimestamp(time).toString());
+                message.append("     T1: "); //$NON-NLS-1$
+                if (fTimeProvider instanceof ITimeDataProvider2) {
+                    long selectionBegin = ((ITimeDataProvider2) fTimeProvider).getSelectionBegin();
+                    long selectionEnd = ((ITimeDataProvider2) fTimeProvider).getSelectionEnd();
+                    message.append(new TmfNanoTimestamp(Math.min(selectionBegin, selectionEnd)).toString());
+                    if (selectionBegin != selectionEnd) {
+                        message.append("     T2: "); //$NON-NLS-1$
+                        message.append(new TmfNanoTimestamp(Math.max(selectionBegin, selectionEnd)).toString());
+                        message.append("     \u0394: "); //$NON-NLS-1$
+                        message.append(new TmfTimestampDelta(Math.abs(selectionBegin - selectionEnd), ITmfTimestamp.NANOSECOND_SCALE));
+                    }
+                } else {
+                    @SuppressWarnings("deprecation")
+                    long selectedTime = fTimeProvider.getSelectedTime();
+                    message.append(new TmfNanoTimestamp(selectedTime));
+                }
+            }
+        } else if (fDragState == DRAG_SELECTION || fDragState == DRAG_ZOOM) {
+            long time0 = fDragTime0;
+            long time = getTimeAtX(fDragX);
+            message.append("T1: "); //$NON-NLS-1$
+            message.append(new TmfNanoTimestamp(Math.min(time, time0)).toString());
+            if (time != time0) {
+                message.append("     T2: "); //$NON-NLS-1$
+                message.append(new TmfNanoTimestamp(Math.max(time, time0)).toString());
+                message.append("     \u0394: "); //$NON-NLS-1$
+                message.append(new TmfTimestampDelta(Math.abs(time - time0), ITmfTimestamp.NANOSECOND_SCALE));
+            }
+        }
+        fStatusLineManager.setMessage(message.toString());
+    }
+
+    @Override
+    public void mouseMove(MouseEvent e) {
+        if (null == fTimeProvider) {
+            return;
+        }
+        Point size = getCtrlSize();
+        if (DRAG_TRACE_ITEM == fDragState) {
+            int nameWidth = fTimeProvider.getNameSpace();
+            if (e.x > nameWidth && size.x > nameWidth && fDragX != e.x) {
+                fDragX = e.x;
+                double pixelsPerNanoSec = (size.x - nameWidth <= RIGHT_MARGIN) ? 0 : (double) (size.x - nameWidth - RIGHT_MARGIN) / (fTime1bak - fTime0bak);
+                long timeDelta = (long) ((pixelsPerNanoSec == 0) ? 0 : ((fDragX - fDragX0) / pixelsPerNanoSec));
+                long time1 = fTime1bak - timeDelta;
+                long maxTime = fTimeProvider.getMaxTime();
+                if (time1 > maxTime) {
+                    time1 = maxTime;
+                }
+                long time0 = time1 - (fTime1bak - fTime0bak);
+                if (time0 < fTimeProvider.getMinTime()) {
+                    time0 = fTimeProvider.getMinTime();
+                    time1 = time0 + (fTime1bak - fTime0bak);
+                }
+                fTimeProvider.setStartFinishTime(time0, time1);
+            }
+        } else if (DRAG_SPLIT_LINE == fDragState) {
+            fDragX = e.x;
+            fTimeProvider.setNameSpace(e.x);
+        } else if (DRAG_SELECTION == fDragState) {
+            if (fTimeProvider instanceof ITimeDataProvider2) {
+                fDragX = Math.min(Math.max(e.x, fTimeProvider.getNameSpace()), size.x - RIGHT_MARGIN);
+                redraw();
+                fTimeGraphScale.setDragRange(fDragX0, fDragX);
+            }
+        } else if (DRAG_ZOOM == fDragState) {
+            fDragX = Math.min(Math.max(e.x, fTimeProvider.getNameSpace()), size.x - RIGHT_MARGIN);
+            redraw();
+            fTimeGraphScale.setDragRange(fDragX0, fDragX);
+        } else if (DRAG_NONE == fDragState) {
+            boolean mouseOverSplitLine = isOverSplitLine(e.x);
+            if (fMouseOverSplitLine != mouseOverSplitLine) {
+                redraw();
+            }
+            fMouseOverSplitLine = mouseOverSplitLine;
+        }
+        updateCursor(e.x, e.stateMask);
+        updateStatusLine(e.x);
+    }
+
+    @Override
+    public void mouseDoubleClick(MouseEvent e) {
+        if (null == fTimeProvider) {
+            return;
+        }
+        if (1 == e.button && (e.stateMask & SWT.BUTTON_MASK) == 0) {
+            if (isOverSplitLine(e.x) && fTimeProvider.getNameSpace() != 0) {
+                fTimeProvider.setNameSpace(fIdealNameSpace);
+                boolean mouseOverSplitLine = isOverSplitLine(e.x);
+                if (fMouseOverSplitLine != mouseOverSplitLine) {
+                    redraw();
+                }
+                fMouseOverSplitLine = mouseOverSplitLine;
+                return;
+            }
+            int idx = getItemIndexAtY(e.y);
+            if (idx >= 0) {
+                selectItem(idx, false);
+                fireDefaultSelection();
+            }
+        }
+    }
+
+    @Override
+    public void mouseDown(MouseEvent e) {
+        if (fDragState != DRAG_NONE || null == fTimeProvider ||
+                fTimeProvider.getTime0() == fTimeProvider.getTime1() ||
+                getCtrlSize().x - fTimeProvider.getNameSpace() <= 0) {
+            return;
+        }
+        int idx;
+        if (1 == e.button && (e.stateMask & SWT.MODIFIER_MASK) == 0) {
+            int nameSpace = fTimeProvider.getNameSpace();
+            if (nameSpace != 0 && isOverSplitLine(e.x)) {
+                fDragState = DRAG_SPLIT_LINE;
+                fDragButton = e.button;
+                fDragX = e.x;
+                fDragX0 = fDragX;
+                fTime0bak = fTimeProvider.getTime0();
+                fTime1bak = fTimeProvider.getTime1();
+                redraw();
+                updateCursor(e.x, e.stateMask);
+                return;
+            }
+        }
+        if (1 == e.button && ((e.stateMask & SWT.MODIFIER_MASK) == 0 || (e.stateMask & SWT.MODIFIER_MASK) == SWT.SHIFT)) {
+            int nameSpace = fTimeProvider.getNameSpace();
+            idx = getItemIndexAtY(e.y);
+            if (idx >= 0) {
+                Item item = fItemData.fExpandedItems[idx];
+                if (item.fHasChildren && e.x < nameSpace && e.x < MARGIN + (item.fLevel + 1) * EXPAND_SIZE) {
+                    toggle(idx);
+                    return;
+                }
+                selectItem(idx, false);
+                fireSelectionChanged();
+            } else {
+                selectItem(idx, false); // clear selection
+                fireSelectionChanged();
+            }
+            long hitTime = getTimeAtX(e.x);
+            if (hitTime >= 0) {
+                setCapture(true);
+
+                fDragState = DRAG_SELECTION;
+                fDragButton = e.button;
+                fDragX = e.x;
+                fDragX0 = fDragX;
+                fDragTime0 = getTimeAtX(fDragX0);
+                if (fTimeProvider instanceof ITimeDataProvider2) {
+                    long selectionBegin = ((ITimeDataProvider2) fTimeProvider).getSelectionBegin();
+                    long selectionEnd = ((ITimeDataProvider2) fTimeProvider).getSelectionEnd();
+                    int xBegin = getXForTime(selectionBegin);
+                    int xEnd = getXForTime(selectionEnd);
+                    if ((e.stateMask & SWT.MODIFIER_MASK) == SWT.SHIFT) {
+                        long time = getTimeAtX(e.x);
+                        if (Math.abs(time - selectionBegin) < Math.abs(time - selectionEnd)) {
+                            fDragX0 = xEnd;
+                            fDragTime0 = selectionEnd;
+                        } else {
+                            fDragX0 = xBegin;
+                            fDragTime0 = selectionBegin;
+                        }
+                    } else {
+                        long time = getTimeAtX(e.x);
+                        if (Math.abs(e.x - xBegin) < SNAP_WIDTH && Math.abs(time - selectionBegin) <= Math.abs(time - selectionEnd)) {
+                            fDragX0 = xEnd;
+                            fDragTime0 = selectionEnd;
+                        } else if (Math.abs(e.x - xEnd) < SNAP_WIDTH && Math.abs(time - selectionEnd) <= Math.abs(time - selectionBegin)) {
+                            fDragX0 = xBegin;
+                            fDragTime0 = selectionBegin;
+                        }
+                    }
+                }
+                fTime0bak = fTimeProvider.getTime0();
+                fTime1bak = fTimeProvider.getTime1();
+                redraw();
+                updateCursor(e.x, e.stateMask);
+                fTimeGraphScale.setDragRange(fDragX0, fDragX);
+            }
+        } else if (2 == e.button || (1 == e.button && (e.stateMask & SWT.MODIFIER_MASK) == SWT.CTRL)) {
+            long hitTime = getTimeAtX(e.x);
+            if (hitTime > 0) {
+                setCapture(true);
+                fDragState = DRAG_TRACE_ITEM;
+                fDragButton = e.button;
+                fDragX = e.x;
+                fDragX0 = fDragX;
+                fTime0bak = fTimeProvider.getTime0();
+                fTime1bak = fTimeProvider.getTime1();
+                updateCursor(e.x, e.stateMask);
+            }
+        } else if (3 == e.button) {
+            setCapture(true);
+            fDragX = Math.min(Math.max(e.x, fTimeProvider.getNameSpace()), getCtrlSize().x - RIGHT_MARGIN);
+            fDragX0 = fDragX;
+            fDragState = DRAG_ZOOM;
+            fDragButton = e.button;
+            redraw();
+            updateCursor(e.x, e.stateMask);
+            fTimeGraphScale.setDragRange(fDragX0, fDragX);
+        }
+    }
+
+    @Override
+    public void mouseUp(MouseEvent e) {
+        if (fPendingMenuDetectEvent != null && e.button == 3) {
+            menuDetected(fPendingMenuDetectEvent);
+        }
+        if (DRAG_NONE != fDragState) {
+            setCapture(false);
+            if (e.button == fDragButton && DRAG_TRACE_ITEM == fDragState) {
+                if (fDragX != fDragX0) {
+                    fTimeProvider.notifyStartFinishTime();
+                }
+                fDragState = DRAG_NONE;
+            } else if (e.button == fDragButton && DRAG_SPLIT_LINE == fDragState) {
+                fDragState = DRAG_NONE;
+                redraw();
+            }  else if (e.button == fDragButton && DRAG_SELECTION == fDragState) {
+                if (fDragX == fDragX0) { // click without selecting anything
+                    long time = getTimeAtX(e.x);
+                    fTimeProvider.setSelectedTimeNotify(time, false);
+                } else {
+                    long time0 = fDragTime0;
+                    long time1 = getTimeAtX(fDragX);
+                    if (time0 <= time1) {
+                        ((ITimeDataProvider2) fTimeProvider).setSelectionRangeNotify(time0, time1);
+                    } else {
+                        ((ITimeDataProvider2) fTimeProvider).setSelectionRangeNotify(time1, time0);
+                    }
+                }
+                fDragState = DRAG_NONE;
+                redraw();
+                fTimeGraphScale.setDragRange(-1, -1);
+            } else if (e.button == fDragButton && DRAG_ZOOM == fDragState) {
+                int nameWidth = fTimeProvider.getNameSpace();
+                if (Math.max(fDragX, fDragX0) > nameWidth && fDragX != fDragX0) {
+                    long time0 = getTimeAtX(fDragX0);
+                    long time1 = getTimeAtX(fDragX);
+                    if (time0 < time1) {
+                        fTimeProvider.setStartFinishTimeNotify(time0, time1);
+                    } else {
+                        fTimeProvider.setStartFinishTimeNotify(time1, time0);
+                    }
+                } else {
+                    redraw();
+                }
+                fDragState = DRAG_NONE;
+                fTimeGraphScale.setDragRange(-1, -1);
+            }
+        }
+        updateCursor(e.x, e.stateMask);
+        updateStatusLine(e.x);
+    }
+
+    @Override
+    public void mouseEnter(MouseEvent e) {
+    }
+
+    @Override
+    public void mouseExit(MouseEvent e) {
+        if (fMouseOverSplitLine) {
+            fMouseOverSplitLine = false;
+            redraw();
+        }
+        updateStatusLine(NO_STATUS);
+    }
+
+    @Override
+    public void mouseHover(MouseEvent e) {
+    }
+
+    @Override
+    public void mouseScrolled(MouseEvent e) {
+        if ((fMouseScrollFilterListener == null) || fDragState != 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 < fTimeProvider.getNameSpace()) {
+                // over the name space
+                zoomScroll = false;
+            } else {
+                zoomScroll = true;
+            }
+        }
+        if (zoomScroll && fTimeProvider.getTime0() != fTimeProvider.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 != fTimeProvider) {
+            int start = getHorizontalBar().getSelection();
+            long time0 = fTimeProvider.getTime0();
+            long time1 = fTimeProvider.getTime1();
+            long timeMin = fTimeProvider.getMinTime();
+            long timeMax = fTimeProvider.getMaxTime();
+            long delta = timeMax - timeMin;
+
+            long range = time1 - time0;
+            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) {
+                fTimeProvider.setStartFinishTime(time0, time1);
+            } else {
+                fTimeProvider.setStartFinishTimeNotify(time0, time1);
+            }
+        }
+    }
+
+    @Override
+    public int getBorderWidth() {
+        return fBorderWidth;
+    }
+
+    /**
+     * Set the border width
+     *
+     * @param borderWidth
+     *            The width
+     */
+    public void setBorderWidth(int borderWidth) {
+        this.fBorderWidth = borderWidth;
+    }
+
+    /**
+     * @return The current height of the header row
+     */
+    public int getHeaderHeight() {
+        return fHeaderHeight;
+    }
+
+    /**
+     * Set the height of the header row
+     *
+     * @param headerHeight
+     *            The height
+     */
+    public void setHeaderHeight(int headerHeight) {
+        this.fHeaderHeight = headerHeight;
+    }
+
+    /**
+     * @return The default height of regular item rows
+     */
+    public int getItemHeight() {
+        return fGlobalItemHeight;
+    }
+
+    /**
+     * Set the default height of regular item rows.
+     *
+     * @param rowHeight
+     *            The height
+     */
+    public void setItemHeight(int rowHeight) {
+        this.fGlobalItemHeight = rowHeight;
+    }
+
+    /**
+     * Set the height of a specific item. Overrides the default item height.
+     *
+     * @param entry
+     *            A time graph entry
+     * @param rowHeight
+     *            The height
+     * @return true if the height is successfully stored, false otherwise
+     *
+     * @since 2.1
+     */
+    public boolean setItemHeight(ITimeGraphEntry entry, int rowHeight) {
+        Item item = fItemData.findItem(entry);
+        if (item != null) {
+            item.fItemHeight = rowHeight;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Set the minimum item width
+     *
+     * @param width The minimum width
+     */
+    public void setMinimumItemWidth(int width) {
+        this.fMinimumItemWidth = width;
+    }
+
+    /**
+     * @return The minimum item width
+     */
+    public int getMinimumItemWidth() {
+        return fMinimumItemWidth;
+    }
+
+    /**
+     * @return The entries that are currently filtered out
+     *
+     * @since 2.0
+     */
+    public List<ITimeGraphEntry> getFilteredOut() {
+        return fItemData.getFilteredOut();
+    }
+
+    @Override
+    public void addSelectionChangedListener(ISelectionChangedListener listener) {
+        if (listener != null && !fSelectionChangedListeners.contains(listener)) {
+            fSelectionChangedListeners.add(listener);
+        }
+    }
+
+    @Override
+    public void removeSelectionChangedListener(ISelectionChangedListener listener) {
+        if (listener != null) {
+            fSelectionChangedListeners.remove(listener);
+        }
+    }
+
+    @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);
+            }
+        }
+
+    }
+
+    /**
+     * @param filter The filter object to be attached to the view
+     * @since 2.0
+     */
+    public void addFilter(ViewerFilter filter) {
+        if (!fFilters.contains(filter)) {
+            fFilters.add(filter);
+        }
+    }
+
+    /**
+     * @param filter The filter object to be attached to the view
+     * @since 2.0
+     */
+    public void removeFilter(ViewerFilter filter) {
+        fFilters.remove(filter);
+    }
+
+    /**
+     * @since 3.0
+     */
+    @Override
+    public void colorSettingsChanged(StateItem[] stateItems) {
+        /* Destroy previous colors from the resource manager */
+        if (fEventColorMap != null) {
+            for (Color color : fEventColorMap) {
+                fResourceManager.destroyColor(color.getRGB());
+            }
+        }
+        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[] { };
+        }
+        redraw();
+    }
+
+    private class ItemData {
+        private Item[] fExpandedItems = new Item[0];
+        private Item[] fItems = new Item[0];
+        private ITimeGraphEntry fTraces[] = new ITimeGraphEntry[0];
+        private List<ILinkEvent> fLinks = new ArrayList<>();
+        private boolean fTraceFilter[] = new boolean[0];
+        private final ArrayList<ITimeGraphEntry> fFilteredOut = new ArrayList<>();
+
+        public ItemData() {
+        }
+
+        Item findItem(ITimeGraphEntry entry) {
+            if (entry == null) {
+                return null;
+            }
+
+            for (int i = 0; i < fItems.length; i++) {
+                Item item = fItems[i];
+                if (item.fTrace == entry) {
+                    return item;
+                }
+            }
+
+            return null;
+        }
+
+        int findItemIndex(ITimeGraphEntry trace) {
+            if (trace == null) {
+                return -1;
+            }
+
+            for (int i = 0; i < fExpandedItems.length; i++) {
+                Item item = fExpandedItems[i];
+                if (item.fTrace == trace) {
+                    return i;
+                }
+            }
+
+            return -1;
+        }
+
+        public void refreshData() {
+            List<Item> itemList = new ArrayList<>();
+            fFilteredOut.clear();
+            ITimeGraphEntry selection = getSelectedTrace();
+            for (int i = 0; i < fTraces.length; i++) {
+                ITimeGraphEntry entry = fTraces[i];
+                refreshData(itemList, null, 0, entry);
+            }
+            fItems = itemList.toArray(new Item[0]);
+            updateExpandedItems();
+            if (selection != null) {
+                for (Item item : fExpandedItems) {
+                    if (item.fTrace == selection) {
+                        item.fSelected = true;
+                        break;
+                    }
+                }
+            }
+        }
+
+        private void refreshData(List<Item> itemList, Item parent, int level, ITimeGraphEntry entry) {
+            Item item = new Item(entry, entry.getName(), level);
+            if (parent != null) {
+                parent.fChildren.add(item);
+            }
+            if (fGlobalItemHeight == CUSTOM_ITEM_HEIGHT) {
+                item.fItemHeight = fTimeGraphProvider.getItemHeight(entry);
+            } else {
+                item.fItemHeight = fGlobalItemHeight;
+            }
+            itemList.add(item);
+            if (entry.hasChildren()) {
+                item.fExpanded = true;
+                item.fHasChildren = true;
+                for (ITimeGraphEntry child : entry.getChildren()) {
+                    refreshData(itemList, item, level + 1, child);
+                }
+            }
+        }
+
+        public void updateExpandedItems() {
+            List<Item> expandedItemList = new ArrayList<>();
+            for (int i = 0; i < fTraces.length; i++) {
+                ITimeGraphEntry entry = fTraces[i];
+                Item item = findItem(entry);
+                refreshExpanded(expandedItemList, item);
+            }
+            fExpandedItems = expandedItemList.toArray(new Item[0]);
+            fTopIndex = Math.min(fTopIndex, Math.max(0, fExpandedItems.length - 1));
+        }
+
+        private void refreshExpanded(List<Item> expandedItemList, Item item) {
+            // Check for filters
+            boolean display = true;
+            for (ViewerFilter filter : fFilters) {
+                if (!filter.select(null, item.fTrace.getParent(), item.fTrace)) {
+                    display = false;
+                    break;
+                }
+            }
+            if (display) {
+                expandedItemList.add(item);
+                if (item.fHasChildren && item.fExpanded) {
+                    for (Item child : item.fChildren) {
+                        refreshExpanded(expandedItemList, child);
+                    }
+                }
+            }
+        }
+
+        public void refreshData(ITimeGraphEntry traces[]) {
+            if (traces == null) {
+                fTraceFilter = null;
+                fTraces = null;
+            } else {
+                if (traces.length == 0) {
+                    fTraceFilter = null;
+                } else if (fTraceFilter == null || traces.length != fTraceFilter.length) {
+                    fTraceFilter = new boolean[traces.length];
+                    java.util.Arrays.fill(fTraceFilter, true);
+                }
+                fTraces = Arrays.copyOf(traces, traces.length);
+            }
+
+            refreshData();
+        }
+
+        public void refreshArrows(List<ILinkEvent> events) {
+            /* If links are null, reset the list */
+            if (events != null) {
+                fLinks = events;
+            } else {
+                fLinks = new ArrayList<>();
+            }
+        }
+
+        public ITimeGraphEntry[] getTraces() {
+            return fTraces;
+        }
+
+        public boolean[] getTraceFilter() {
+            return fTraceFilter;
+        }
+
+        public List<ITimeGraphEntry> getFilteredOut() {
+            return fFilteredOut;
+        }
+    }
+
+    private class Item {
+        private boolean fExpanded;
+        private boolean fSelected;
+        private boolean fHasChildren;
+        private int fItemHeight;
+        private int fLevel;
+        private List<Item> fChildren;
+        private String fName;
+        private ITimeGraphEntry fTrace;
+
+        public Item(ITimeGraphEntry trace, String name, int level) {
+            this.fTrace = trace;
+            this.fName = name;
+            this.fLevel = level;
+            this.fChildren = new ArrayList<>();
+        }
+
+        @Override
+        public String toString() {
+            return fName;
+        }
+    }
+
+    /**
+     * @since 1.2
+     */
+    @Override
+    public void menuDetected(MenuDetectEvent e) {
+        if (null == fTimeProvider) {
+            return;
+        }
+        if (e.detail == SWT.MENU_MOUSE) {
+            if (fPendingMenuDetectEvent == null) {
+                /* Feature in Linux. The MenuDetectEvent is received before mouseDown.
+                 * Store the event and trigger it later just before handling mouseUp.
+                 * This allows for the method to detect if mouse is used to drag zoom.
+                 */
+                fPendingMenuDetectEvent = e;
+                return;
+            }
+            fPendingMenuDetectEvent = null;
+            if (fDragState != DRAG_ZOOM || fDragX != fDragX0) {
+                return;
+            }
+        } else {
+            if (fDragState != DRAG_NONE) {
+                return;
+            }
+        }
+        Point p = toControl(e.x, e.y);
+        int idx = getItemIndexAtY(p.y);
+        if (idx >= 0 && idx < fItemData.fExpandedItems.length) {
+            Item item = fItemData.fExpandedItems[idx];
+            ITimeGraphEntry entry = item.fTrace;
+            if (entry.hasTimeEvents()) {
+                ITimeEvent event = Utils.findEvent(entry, getTimeAtX(p.x), 2);
+                if (event != null) {
+                    e.data = event;
+                    fireMenuEventOnTimeEvent(e);
+                    return;
+                }
+            }
+            e.data = entry;
+            fireMenuEventOnTimeGraphEntry(e);
+        }
+    }
+
+}
+
+
This page took 0.069489 seconds and 5 git commands to generate.