tmf: Show closest event tool tip when hovering over blank space
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.ui / src / org / eclipse / tracecompass / tmf / ui / widgets / timegraph / widgets / TimeGraphControl.java
index ef18c1c60947b818e61839b8adeb0a3ff2d111d7..13438c859bb5988f436a8425747a0b4aa3555e21 100644 (file)
@@ -23,11 +23,13 @@ package org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 
+import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jface.action.IStatusLineManager;
 import org.eclipse.jface.resource.JFaceResources;
 import org.eclipse.jface.resource.LocalResourceManager;
@@ -56,6 +58,8 @@ 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.Font;
+import org.eclipse.swt.graphics.FontData;
 import org.eclipse.swt.graphics.GC;
 import org.eclipse.swt.graphics.Image;
 import org.eclipse.swt.graphics.Point;
@@ -77,6 +81,7 @@ import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.StateItem;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphTimeEvent;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphTreeExpansionEvent;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ILinkEvent;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.IMarkerEvent;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeEvent;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.Utils.Resolution;
@@ -116,6 +121,11 @@ public class TimeGraphControl extends TimeGraphBaseControl
     private static final int NO_STATUS = -1;
     private static final int STATUS_WITHOUT_CURSOR_TIME = -2;
 
+    private static final int MAX_LABEL_LENGTH = 256;
+
+    private static final int PPI = 72; // points per inch
+    private static final int DPI = Display.getDefault().getDPI().y;
+
     /** Resource manager */
     private LocalResourceManager fResourceManager = new LocalResourceManager(JFaceResources.getResources());
 
@@ -129,6 +139,8 @@ public class TimeGraphControl extends TimeGraphBaseControl
     private boolean fIsInFocus = false;
     private boolean fMouseOverSplitLine = false;
     private int fGlobalItemHeight = CUSTOM_ITEM_HEIGHT;
+    private int fHeightAdjustment = 0;
+    private Map<Integer, Font> fFonts = new HashMap<>();
     private boolean fBlendSubPixelEvents = false;
     private int fMinimumItemWidth = 0;
     private int fTopIndex = 0;
@@ -143,6 +155,8 @@ public class TimeGraphControl extends TimeGraphBaseControl
     private long fTime1bak;
     private ITimeGraphPresentationProvider fTimeGraphProvider = null;
     private ItemData fItemData = null;
+    private List<IMarkerEvent> fMarkers = null;
+    private boolean fMarkersVisible = true;
     private List<SelectionListener> fSelectionListeners;
     private List<ITimeGraphTimeListener> fDragSelectionListeners;
     private final List<ISelectionChangedListener> fSelectionChangedListeners = new ArrayList<>();
@@ -153,7 +167,7 @@ public class TimeGraphControl extends TimeGraphBaseControl
     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 final List<@NonNull ViewerFilter> fFilters = new ArrayList<>();
     private MenuDetectEvent fPendingMenuDetectEvent = null;
     private boolean fGridLinesVisible = true;
     private Color fGridLineColor = Display.getDefault().getSystemColor(SWT.COLOR_GRAY);
@@ -192,6 +206,9 @@ public class TimeGraphControl extends TimeGraphBaseControl
     public void dispose() {
         super.dispose();
         fResourceManager.dispose();
+        for (Font font : fFonts.values()) {
+            font.dispose();
+        }
     }
 
     /**
@@ -415,7 +432,7 @@ public class TimeGraphControl extends TimeGraphBaseControl
      *
      * @return The unmodifiable link event list
      *
-     * @since 2.0
+     * @since 1.1
      */
     public List<ILinkEvent> getArrows() {
         return Collections.unmodifiableList(fItemData.fLinks);
@@ -502,7 +519,7 @@ public class TimeGraphControl extends TimeGraphBaseControl
      * @param entry
      *            The entry
      * @return true if the entry is expanded, false if collapsed
-     * @since 2.0
+     * @since 1.1
      */
     public boolean getExpandedState(ITimeGraphEntry entry) {
         Item item = fItemData.fItemMap.get(entry);
@@ -783,14 +800,14 @@ public class TimeGraphControl extends TimeGraphBaseControl
                 nextTime = nextEvent.getTime() + nextEvent.getDuration();
             }
             if (extend) {
-                fTimeProvider.setSelectionRangeNotify(fTimeProvider.getSelectionBegin(), nextTime);
+                fTimeProvider.setSelectionRangeNotify(fTimeProvider.getSelectionBegin(), nextTime, true);
             } else {
                 fTimeProvider.setSelectedTimeNotify(nextTime, true);
             }
             fireSelectionChanged();
         } else if (n == 1) {
             if (extend) {
-                fTimeProvider.setSelectionRangeNotify(fTimeProvider.getSelectionBegin(), endTime);
+                fTimeProvider.setSelectionRangeNotify(fTimeProvider.getSelectionBegin(), endTime, true);
             } else {
                 fTimeProvider.setSelectedTimeNotify(endTime, true);
             }
@@ -951,6 +968,34 @@ public class TimeGraphControl extends TimeGraphBaseControl
         fTimeProvider.setStartFinishTimeNotify(time0, time1);
     }
 
+    /**
+     * Zoom vertically.
+     *
+     * @param zoomIn
+     *            true to zoom in, false to zoom out
+     * @since 2.0
+     */
+    public void verticalZoom(boolean zoomIn) {
+        if (zoomIn) {
+            fHeightAdjustment++;
+        } else {
+            fHeightAdjustment--;
+        }
+        fItemData.refreshData();
+        redraw();
+    }
+
+    /**
+     * Reset the vertical zoom to default.
+     *
+     * @since 2.0
+     */
+    public void resetVerticalZoom() {
+        fHeightAdjustment = 0;
+        fItemData.refreshData();
+        redraw();
+    }
+
     /**
      * Set the grid lines visibility. The default is true.
      *
@@ -993,6 +1038,50 @@ public class TimeGraphControl extends TimeGraphBaseControl
         return fGridLineColor;
     }
 
+    /**
+     * Set the markers list.
+     *
+     * @param markers
+     *            The markers list, or null
+     * @since 2.0
+     */
+    public void setMarkers(List<IMarkerEvent> markers) {
+        fMarkers = markers;
+        fTimeGraphScale.setMarkers(markers);
+    }
+
+    /**
+     * Get the markers list.
+     *
+     * @return The markers list, or null
+     * @since 2.0
+     */
+    public List<IMarkerEvent> getMarkers() {
+        return fMarkers;
+    }
+
+    /**
+     * Set the markers visibility. The default is true.
+     *
+     * @param visible
+     *            true to show the markers, false otherwise
+     * @since 2.0
+     */
+    public void setMarkersVisible(boolean visible) {
+        fMarkersVisible = visible;
+        fTimeGraphScale.setMarkersVisible(visible);
+    }
+
+    /**
+     * Get the markers visibility.
+     *
+     * @return true if the markers are visible, false otherwise
+     * @since 2.0
+     */
+    public boolean getMarkersVisible() {
+        return fMarkersVisible;
+    }
+
     /**
      * Hide arrows
      *
@@ -1020,7 +1109,7 @@ public class TimeGraphControl extends TimeGraphBaseControl
                 selectItem(link.getDestinationEntry(), false);
                 if (link.getDuration() != 0) {
                     if (extend) {
-                        fTimeProvider.setSelectionRangeNotify(fTimeProvider.getSelectionBegin(), link.getTime() + link.getDuration());
+                        fTimeProvider.setSelectionRangeNotify(fTimeProvider.getSelectionBegin(), link.getTime() + link.getDuration(), true);
                     } else {
                         fTimeProvider.setSelectedTimeNotify(link.getTime() + link.getDuration(), true);
                     }
@@ -1053,7 +1142,7 @@ public class TimeGraphControl extends TimeGraphBaseControl
                 selectItem(link.getEntry(), false);
                 if (link.getDuration() != 0) {
                     if (extend) {
-                        fTimeProvider.setSelectionRangeNotify(fTimeProvider.getSelectionBegin(), link.getTime());
+                        fTimeProvider.setSelectionRangeNotify(fTimeProvider.getSelectionBegin(), link.getTime(), true);
                     } else {
                         fTimeProvider.setSelectedTimeNotify(link.getTime(), true);
                     }
@@ -1361,14 +1450,24 @@ public class TimeGraphControl extends TimeGraphBaseControl
         fIdealNameSpace = 0;
         int nameSpace = fTimeProvider.getNameSpace();
 
+        // draw the background layer
         drawBackground(bounds, nameSpace, gc);
 
         // draw the grid lines
         drawGridLines(bounds, gc);
 
-        // draw items
+        // draw the background markers
+        drawMarkers(bounds, fTimeProvider, fMarkers, false, nameSpace, gc);
+
+        // draw the items
         drawItems(bounds, fTimeProvider, fItemData.fExpandedItems, fTopIndex, nameSpace, gc);
+
+        // draw the foreground markers
+        drawMarkers(bounds, fTimeProvider, fMarkers, true, nameSpace, gc);
+
+        // draw the links (arrows)
         drawLinks(bounds, fTimeProvider, fItemData.fLinks, nameSpace, gc);
+
         fTimeGraphProvider.postDrawControl(bounds, gc);
 
         int alpha = gc.getAlpha();
@@ -1442,14 +1541,16 @@ public class TimeGraphControl extends TimeGraphBaseControl
     }
 
     /**
-     * Draw the background
+     * Draw the background layer. Fills the background of the control's name
+     * space and states space, updates the background of items if necessary,
+     * and draws the item's name text and middle line.
      *
      * @param bounds
-     *            The rectangle of the area
+     *            The bounds of the control
      * @param nameSpace
-     *            The width reserved for the names
+     *            The name space width
      * @param gc
-     *            Reference to the SWT GC object
+     *            Graphics context
      * @since 2.0
      */
     protected void drawBackground(Rectangle bounds, int nameSpace, GC gc) {
@@ -1461,13 +1562,13 @@ public class TimeGraphControl extends TimeGraphBaseControl
         gc.setBackground(getColorScheme().getColor(TimeGraphColorScheme.BACKGROUND));
         drawBackground(gc, bounds.x + nameSpace, bounds.y, bounds.width - nameSpace, bounds.height);
 
-        // draw the background of selected item and items with no time events
         for (int i = fTopIndex; i < fItemData.fExpandedItems.length; i++) {
             Rectangle itemRect = getItemRect(bounds, i);
             if (itemRect.y >= bounds.y + bounds.height) {
                 break;
             }
             Item item = fItemData.fExpandedItems[i];
+            // draw the background of selected item and items with no time events
             if (! item.fEntry.hasTimeEvents()) {
                 gc.setBackground(getColorScheme().getBkColorGroup(item.fSelected, fIsInFocus));
                 gc.fillRectangle(itemRect);
@@ -1477,6 +1578,15 @@ public class TimeGraphControl extends TimeGraphBaseControl
                 gc.setBackground(getColorScheme().getBkColor(true, fIsInFocus, false));
                 gc.fillRectangle(nameSpace, itemRect.y, itemRect.width - nameSpace, itemRect.height);
             }
+            // draw the name and middle line
+            if (! item.fEntry.hasTimeEvents()) {
+                drawName(item, itemRect, gc);
+            } else {
+                Rectangle nameRect = new Rectangle(itemRect.x, itemRect.y, nameSpace, itemRect.height);
+                drawName(item, nameRect, gc);
+                Rectangle rect = new Rectangle(nameSpace, itemRect.y, itemRect.width - nameSpace, itemRect.height);
+                drawMidLine(rect, gc);
+            }
         }
     }
 
@@ -1484,9 +1594,9 @@ public class TimeGraphControl extends TimeGraphBaseControl
      * Draw the grid lines
      *
      * @param bounds
-     *            The rectangle of the area
+     *            The bounds of the control
      * @param gc
-     *            Reference to the SWT GC object
+     *            Graphics context
      * @since 2.0
      */
     public void drawGridLines(Rectangle bounds, GC gc) {
@@ -1494,26 +1604,107 @@ public class TimeGraphControl extends TimeGraphBaseControl
             return;
         }
         gc.setForeground(fGridLineColor);
+        gc.setAlpha(fGridLineColor.getAlpha());
         for (int x : fTimeGraphScale.getTickList()) {
             gc.drawLine(x, bounds.y, x, bounds.y + bounds.height);
         }
+        gc.setAlpha(255);
     }
 
     /**
-     * Draw many items at once
+     * Draw the markers
      *
      * @param bounds
      *            The rectangle of the area
      * @param timeProvider
      *            The time provider
+     * @param markers
+     *            The list of markers
+     * @param foreground
+     *            true to draw the foreground markers, false otherwise
+     * @param nameSpace
+     *            The width reserved for the names
+     * @param gc
+     *            Reference to the SWT GC object
+     * @since 2.0
+     */
+    protected void drawMarkers(Rectangle bounds, ITimeDataProvider timeProvider, List<IMarkerEvent> markers, boolean foreground, int nameSpace, GC gc) {
+        if (!fMarkersVisible || markers == null || markers.isEmpty()) {
+            return;
+        }
+        gc.setClipping(new Rectangle(nameSpace, 0, bounds.width - nameSpace, bounds.height));
+        /* the list can grow concurrently but cannot shrink */
+        for (int i = 0; i < markers.size(); i++) {
+            IMarkerEvent marker = markers.get(i);
+            if (marker.isForeground() == foreground) {
+                drawMarker(marker, bounds, timeProvider, nameSpace, gc);
+            }
+        }
+        gc.setClipping((Rectangle) null);
+    }
+
+    /**
+     * Draw a single marker
+     *
+     * @param marker
+     *            The marker event
+     * @param bounds
+     *            The bounds of the control
+     * @param timeProvider
+     *            The time provider
+     * @param nameSpace
+     *            The width reserved for the name
+     * @param gc
+     *            Reference to the SWT GC object
+     * @since 2.0
+     */
+    protected void drawMarker(IMarkerEvent marker, Rectangle bounds, ITimeDataProvider timeProvider, int nameSpace, GC gc) {
+        Rectangle rect = Utils.clone(bounds);
+        if (marker.getEntry() != null) {
+            int index = fItemData.findItemIndex(marker.getEntry());
+            if (index == -1) {
+                return;
+            }
+            rect = getStatesRect(bounds, index, nameSpace);
+            if (rect.y < 0 || rect.y > bounds.height) {
+                return;
+            }
+        }
+        int x0 = getXForTime(marker.getTime());
+        int x1 = getXForTime(marker.getTime() + marker.getDuration());
+        if (x0 > bounds.width || x1 < nameSpace) {
+            return;
+        }
+        rect.x = Math.max(nameSpace, Math.min(bounds.width, x0));
+        rect.width = Math.max(1, Math.min(bounds.width, x1) - rect.x);
+
+        gc.setBackground(marker.getColor());
+        gc.setAlpha(marker.getColor().getAlpha());
+        gc.fillRectangle(rect);
+        gc.setAlpha(255);
+        String label = marker.getLabel();
+        if (label != null && marker.getEntry() != null) {
+            label = label.substring(0, Math.min(label.indexOf('\n') != -1 ? label.indexOf('\n') : label.length(), MAX_LABEL_LENGTH));
+            gc.setForeground(marker.getColor());
+            Utils.drawText(gc, label, rect.x - gc.textExtent(label).x, rect.y, true);
+        }
+    }
+
+    /**
+     * Draw many items at once
+     *
+     * @param bounds
+     *            The bounds of the control
+     * @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
+     *            The name space width
      * @param gc
-     *            Reference to the SWT GC object
+     *            Graphics context
      */
     public void drawItems(Rectangle bounds, ITimeDataProvider timeProvider,
             Item[] items, int topIndex, int nameSpace, GC gc) {
@@ -1526,12 +1717,18 @@ public class TimeGraphControl extends TimeGraphBaseControl
     /**
      * 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
+     * @param item
+     *            The item to draw
+     * @param bounds
+     *            The bounds of the control
+     * @param timeProvider
+     *            The time provider
+     * @param i
+     *            The expanded item index
+     * @param nameSpace
+     *            The name space width
+     * @param gc
+     *            Graphics context
      */
     protected void drawItem(Item item, Rectangle bounds, ITimeDataProvider timeProvider, int i, int nameSpace, GC gc) {
         Rectangle itemRect = getItemRect(bounds, i);
@@ -1544,20 +1741,8 @@ public class TimeGraphControl extends TimeGraphBaseControl
         long time1 = timeProvider.getTime1();
         long selectedTime = fTimeProvider.getSelectionEnd();
 
-        if (! item.fEntry.hasTimeEvents()) {
-            drawName(item, itemRect, gc);
-        } else {
-            Rectangle nameRect = new Rectangle(itemRect.x, itemRect.y, nameSpace, itemRect.height);
-            drawName(item, nameRect, gc);
-        }
         Rectangle rect = new Rectangle(nameSpace, itemRect.y, itemRect.width - nameSpace, itemRect.height);
-        if (rect.isEmpty()) {
-            fTimeGraphProvider.postDrawEntry(entry, rect, gc);
-            return;
-        }
-        if (time1 <= time0) {
-            gc.setBackground(getColorScheme().getBkColor(false, false, false));
-            gc.fillRectangle(rect);
+        if (rect.isEmpty() || (time1 <= time0)) {
             fTimeGraphProvider.postDrawEntry(entry, rect, gc);
             return;
         }
@@ -1569,14 +1754,14 @@ public class TimeGraphControl extends TimeGraphBaseControl
         if (item.fEntry.hasTimeEvents()) {
             gc.setClipping(new Rectangle(nameSpace, 0, bounds.width - nameSpace, bounds.height));
             fillSpace(rect, gc, selected);
-            /*
-             * State rectangle is smaller than item bounds. Use a margin height
-             * of 3 pixels, keep at least 3 pixels for the state, but not more
-             * than the item height. Favor the top margin for the remainder.
-             */
-            int height = Math.min(rect.height, Math.max(3, rect.height - 6));
-            int margin = (rect.height - height + 1) / 2;
-            Rectangle stateRect = new Rectangle(rect.x, rect.y + margin, rect.width, height);
+
+            int margins = getMarginForHeight(rect.height);
+            int height = rect.height - margins;
+            int topMargin = (margins + 1) / 2;
+            Rectangle stateRect = new Rectangle(rect.x, rect.y + topMargin, rect.width, height);
+
+            /* Set the font for this item */
+            setFontForHeight(height, gc);
 
             long maxDuration = (timeProvider.getTimeSpace() == 0) ? Long.MAX_VALUE : 1 * (time1 - time0) / timeProvider.getTimeSpace();
             Iterator<ITimeEvent> iterator = entry.getTimeEventsIterator(time0, time1, maxDuration);
@@ -1615,15 +1800,15 @@ public class TimeGraphControl extends TimeGraphBaseControl
      * Draw the links
      *
      * @param bounds
-     *            The rectangle of the area
+     *            The bounds of the control
      * @param timeProvider
      *            The time provider
      * @param links
      *            The list of link events
      * @param nameSpace
-     *            The width reserved for the names
+     *            The name space width
      * @param gc
-     *            Reference to the SWT GC object
+     *            Graphics context
      */
     public void drawLinks(Rectangle bounds, ITimeDataProvider timeProvider,
             List<ILinkEvent> links, int nameSpace, GC gc) {
@@ -1639,16 +1824,16 @@ public class TimeGraphControl extends TimeGraphBaseControl
     }
 
     /**
-     * Draws the link type events of this item
+     * Draws a link type event
      *
      * @param event
-     *            the item to draw
+     *            The link event to draw
      * @param bounds
-     *            the container rectangle
+     *            The bounds of the control
      * @param timeProvider
-     *            Time provider
+     *            The time provider
      * @param nameSpace
-     *            the name space
+     *            The name space width
      * @param gc
      *            Graphics context
      */
@@ -1682,17 +1867,17 @@ public class TimeGraphControl extends TimeGraphBaseControl
     }
 
     /**
-     * Draw the state (color fill)
+     * Draw an arrow
      *
      * @param colors
      *            Color scheme
      * @param event
-     *            Time event for which we're drawing the state
+     *            Time event for which we're drawing the arrow
      * @param rect
-     *            Where to draw
+     *            The arrow rectangle
      * @param gc
      *            Graphics context
-     * @return true if the state was drawn
+     * @return true if the arrow was drawn
      */
     protected boolean drawArrow(TimeGraphColorScheme colors, ITimeEvent event,
             Rectangle rect, GC gc) {
@@ -1759,7 +1944,7 @@ public class TimeGraphControl extends TimeGraphBaseControl
      * @param item
      *            Item object
      * @param bounds
-     *            Where to draw the name
+     *            The bounds of the item's name space
      * @param gc
      *            Graphics context
      */
@@ -1771,6 +1956,9 @@ public class TimeGraphControl extends TimeGraphBaseControl
             return;
         }
 
+        int height = bounds.height - getMarginForHeight(bounds.height);
+        setFontForHeight(height, gc);
+
         int leftMargin = MARGIN + item.fLevel * EXPAND_SIZE;
         if (item.fHasChildren) {
             gc.setForeground(getColorScheme().getFgColorGroup(false, false));
@@ -1828,15 +2016,13 @@ public class TimeGraphControl extends TimeGraphBaseControl
             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);
+                rect.x = bounds.x + leftMargin;
+                rect.y = bounds.y;
+                rect.width = bounds.width - rect.x;
+                drawMidLine(rect, gc);
             }
         }
     }
@@ -1849,7 +2035,7 @@ public class TimeGraphControl extends TimeGraphBaseControl
      * @param event
      *            Time event for which we're drawing the state
      * @param rect
-     *            Where to draw
+     *            The state rectangle
      * @param gc
      *            Graphics context
      * @param selected
@@ -1916,19 +2102,58 @@ public class TimeGraphControl extends TimeGraphBaseControl
      * Fill an item's states rectangle
      *
      * @param rect
-     *            Rectangle to fill
+     *            The states rectangle
      * @param gc
      *            Graphics context
      * @param selected
      *            true if the item is selected
      */
     protected void fillSpace(Rectangle rect, GC gc, boolean selected) {
-        // draw middle line
+        /* Nothing to draw */
+    }
+
+    /**
+     * Draw a line at the middle height of a rectangle
+     *
+     * @param rect
+     *            The rectangle
+     * @param gc
+     *            Graphics context
+     */
+    private void drawMidLine(Rectangle rect, GC gc) {
         gc.setForeground(getColorScheme().getColor(TimeGraphColorScheme.MID_LINE));
         int midy = rect.y + rect.height / 2;
         gc.drawLine(rect.x, midy, rect.x + rect.width, midy);
     }
 
+    private static int getMarginForHeight(int height) {
+        /*
+         * State rectangle is smaller than the item bounds when height is > 4.
+         * Don't use any margin if the height is below or equal that threshold.
+         * Use a maximum of 6 pixels for both margins, otherwise try to use 13
+         * pixels for the state height, but with a minimum margin of 1.
+         */
+        final int MARGIN_THRESHOLD = 4;
+        final int PREFERRED_HEIGHT = 13;
+        final int MIN_MARGIN = 1;
+        final int MAX_MARGIN = 6;
+        return height <= MARGIN_THRESHOLD ? 0 :
+            Math.max(Math.min(height - PREFERRED_HEIGHT, MAX_MARGIN), MIN_MARGIN);
+    }
+
+    private void setFontForHeight(int pixels, GC gc) {
+        /* convert font height from pixels to points */
+        int height = Math.max(pixels * PPI / DPI, 1);
+        Font font = fFonts.get(height);
+        if (font == null) {
+            FontData fontData = gc.getFont().getFontData()[0];
+            fontData.setHeight(height);
+            font = new Font(gc.getDevice(), fontData);
+            fFonts.put(height, font);
+        }
+        gc.setFont(font);
+    }
+
     @Override
     public void keyTraversed(TraverseEvent e) {
         if ((e.detail == SWT.TRAVERSE_TAB_NEXT) || (e.detail == SWT.TRAVERSE_TAB_PREVIOUS)) {
@@ -1996,6 +2221,12 @@ public class TimeGraphControl extends TimeGraphBaseControl
                 }
             }
             idx = -1;
+        } else if ((e.character == '+' || e.character == '=') && ((e.stateMask & SWT.CTRL) != 0)) {
+            verticalZoom(true);
+        } else if (e.character == '-' && ((e.stateMask & SWT.CTRL) != 0)) {
+            verticalZoom(false);
+        } else if (e.character == '0' && ((e.stateMask & SWT.CTRL) != 0)) {
+            resetVerticalZoom();
         }
         if (idx >= 0) {
             selectItem(idx, false);
@@ -2082,6 +2313,15 @@ public class TimeGraphControl extends TimeGraphBaseControl
         }
     }
 
+    /**
+     * Update the status line following a change of selection.
+     *
+     * @since 2.0
+     */
+    public void updateStatusLine() {
+        updateStatusLine(STATUS_WITHOUT_CURSOR_TIME);
+    }
+
     private void updateStatusLine(int x) {
         // use the time provider of the time graph scale for the status line
         ITimeDataProvider tdp = fTimeGraphScale.getTimeProvider();
@@ -2343,7 +2583,7 @@ public class TimeGraphControl extends TimeGraphBaseControl
                 } else {
                     long time0 = fDragBeginMarker ? getTimeAtX(fDragX0) : fDragTime0;
                     long time1 = fDragBeginMarker ? fDragTime0 : getTimeAtX(fDragX);
-                    fTimeProvider.setSelectionRangeNotify(time0, time1);
+                    fTimeProvider.setSelectionRangeNotify(time0, time1, false);
                 }
                 fDragState = DRAG_NONE;
                 redraw();
@@ -2391,42 +2631,53 @@ public class TimeGraphControl extends TimeGraphBaseControl
         if (fDragState != DRAG_NONE) {
             return;
         }
-        boolean zoomScroll = false;
+        boolean horizontalZoom = false;
         boolean horizontalScroll = false;
+        boolean verticalZoom = 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 > getSize().x) {
                 // over the vertical scroll bar
-                zoomScroll = false;
+                if ((e.stateMask & SWT.MODIFIER_MASK) == (SWT.SHIFT | SWT.CTRL)) {
+                    verticalZoom = true;
+                }
             } else if (e.y < 0) {
                 // over the time scale
-                zoomScroll = true;
+                horizontalZoom = true;
             } else if (e.y >= getSize().y) {
                 // over the horizontal scroll bar
                 if ((e.stateMask & SWT.MODIFIER_MASK) == SWT.CTRL) {
-                    zoomScroll = true;
+                    horizontalZoom = true;
                 } else {
                     horizontalScroll = true;
                 }
             } else {
-                if (e.x < fTimeProvider.getNameSpace()) {
+                if ((e.stateMask & SWT.MODIFIER_MASK) == (SWT.SHIFT | SWT.CTRL)) {
+                    verticalZoom = true;
+                } else if (e.x < fTimeProvider.getNameSpace()) {
                     // over the name space
-                    zoomScroll = false;
+                    horizontalZoom = false;
                 } else {
                     // over the state area
                     if ((e.stateMask & SWT.MODIFIER_MASK) == SWT.CTRL) {
                         // over the state area, CTRL pressed
-                        zoomScroll = true;
+                        horizontalZoom = true;
                     } else {
                         // over the state area, CTRL not pressed
-                        zoomScroll = false;
+                        horizontalZoom = false;
                     }
                 }
             }
         }
-        if (zoomScroll && fTimeProvider.getTime0() != fTimeProvider.getTime1()) {
+        if (verticalZoom) {
+            if (e.count > 0) {
+                verticalZoom(true);
+            } else if (e.count < 0) {
+                verticalZoom(false);
+            }
+        } else if (horizontalZoom && fTimeProvider.getTime0() != fTimeProvider.getTime1()) {
             if (e.count > 0) {
                 zoom(true);
             } else if (e.count < 0) {
@@ -2542,7 +2793,7 @@ public class TimeGraphControl extends TimeGraphBaseControl
      *
      * @param blend
      *            true if sub-pixel events should be blended, false otherwise.
-     * @since 2.0
+     * @since 1.1
      */
     public void setBlendSubPixelEvents(boolean blend) {
         fBlendSubPixelEvents = blend;
@@ -2578,7 +2829,7 @@ public class TimeGraphControl extends TimeGraphBaseControl
     /**
      * @param filter The filter object to be attached to the view
      */
-    public void addFilter(ViewerFilter filter) {
+    public void addFilter(@NonNull ViewerFilter filter) {
         if (!fFilters.contains(filter)) {
             fFilters.add(filter);
         }
@@ -2587,7 +2838,7 @@ public class TimeGraphControl extends TimeGraphBaseControl
     /**
      * @param filter The filter object to be attached to the view
      */
-    public void removeFilter(ViewerFilter filter) {
+    public void removeFilter(@NonNull ViewerFilter filter) {
         fFilters.remove(filter);
     }
 
@@ -2597,7 +2848,7 @@ public class TimeGraphControl extends TimeGraphBaseControl
      * @return an array of viewer filters
      * @since 2.0
      */
-    public ViewerFilter[] getFilters() {
+    public @NonNull ViewerFilter[] getFilters() {
         return Iterables.toArray(fFilters, ViewerFilter.class);
     }
 
@@ -2608,7 +2859,7 @@ public class TimeGraphControl extends TimeGraphBaseControl
      *            an array of viewer filters, or null
      * @since 2.0
      */
-    public void setFilters(ViewerFilter[] filters) {
+    public void setFilters(@NonNull ViewerFilter[] filters) {
         fFilters.clear();
         if (filters != null) {
             fFilters.addAll(Arrays.asList(filters));
@@ -2686,6 +2937,7 @@ public class TimeGraphControl extends TimeGraphBaseControl
             } else {
                 item.fItemHeight = fGlobalItemHeight;
             }
+            item.fItemHeight = Math.max(1, item.fItemHeight + fHeightAdjustment);
             itemMap.put(entry, item);
             if (entry.hasChildren()) {
                 Item oldItem = fItemMap.get(entry);
This page took 0.033639 seconds and 5 git commands to generate.