tmf: Fill label background with marker color in time graph marker axis
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.ui / src / org / eclipse / tracecompass / tmf / ui / widgets / timegraph / widgets / TimeGraphMarkerAxis.java
index 14fd4da441053413a439c79d3e062c27f8724e94..53f151b5ba60c1f7c8086734267cf9255e4cd743 100644 (file)
@@ -14,7 +14,9 @@ package org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets;
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Set;
 
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.swt.SWT;
@@ -23,10 +25,13 @@ import org.eclipse.swt.events.MouseEvent;
 import org.eclipse.swt.events.PaintEvent;
 import org.eclipse.swt.graphics.Color;
 import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
 import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.RGB;
 import org.eclipse.swt.graphics.Rectangle;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Display;
+import org.eclipse.tracecompass.internal.tmf.ui.Activator;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.IMarkerEvent;
 
 import com.google.common.collect.LinkedHashMultimap;
@@ -39,6 +44,11 @@ import com.google.common.collect.Multimap;
  */
 public class TimeGraphMarkerAxis extends TimeGraphBaseControl {
 
+    private static final Image COLLAPSED = Activator.getDefault().getImageFromPath("icons/ovr16/collapsed_ovr.gif"); //$NON-NLS-1$
+    private static final Image EXPANDED = Activator.getDefault().getImageFromPath("icons/ovr16/expanded_ovr.gif"); //$NON-NLS-1$
+    private static final Image HIDE = Activator.getDefault().getImageFromPath("icons/etool16/hide.gif"); //$NON-NLS-1$
+    private static final int HIDE_BORDER = 4; // transparent border of the hide icon
+
     private static final int HEIGHT;
     static {
         GC gc = new GC(Display.getDefault());
@@ -53,8 +63,10 @@ public class TimeGraphMarkerAxis extends TimeGraphBaseControl {
     private static final int X_LIMIT = Integer.MAX_VALUE / 256;
 
     private @NonNull ITimeDataProvider fTimeProvider;
+    private final Set<IMarkerAxisListener> fListeners = new LinkedHashSet<>();
     private Multimap<String, IMarkerEvent> fMarkers = LinkedHashMultimap.create();
     private @NonNull List<String> fCategories = Collections.EMPTY_LIST;
+    private boolean fCollapsed = false;
 
     /**
      * Contructor
@@ -72,10 +84,9 @@ public class TimeGraphMarkerAxis extends TimeGraphBaseControl {
         addMouseListener(new MouseAdapter() {
             @Override
             public void mouseDown(MouseEvent e) {
-                IMarkerEvent marker = getMarkerForEvent(e);
-                if (marker != null) {
-                    fTimeProvider.setSelectionRangeNotify(marker.getTime(), marker.getTime() + marker.getDuration(), false);
-                }
+                Point size = getSize();
+                Rectangle bounds = new Rectangle(0, 0, size.x, size.y);
+                TimeGraphMarkerAxis.this.mouseDown(e, bounds, fTimeProvider.getNameSpace());
             }
         });
     }
@@ -84,11 +95,35 @@ public class TimeGraphMarkerAxis extends TimeGraphBaseControl {
     public Point computeSize(int wHint, int hHint, boolean changed) {
         int height = 0;
         if (!fMarkers.isEmpty() && fTimeProvider.getTime0() != fTimeProvider.getTime1()) {
-            height = TOP_MARGIN + fMarkers.keySet().size() * HEIGHT;
+            if (fCollapsed) {
+                height = COLLAPSED.getBounds().height;
+            } else {
+                height = TOP_MARGIN + fMarkers.keySet().size() * HEIGHT;
+            }
         }
         return super.computeSize(wHint, height, changed);
     }
 
+    /**
+     * Add a marker axis listener.
+     *
+     * @param listener
+     *            the listener
+     */
+    public void addMarkerAxisListener(IMarkerAxisListener listener) {
+        fListeners.add(listener);
+    }
+
+    /**
+     * Remove a marker axis listener.
+     *
+     * @param listener
+     *            the listener
+     */
+    public void removeMarkerAxisListener(IMarkerAxisListener listener) {
+        fListeners.remove(listener);
+    }
+
     /**
      * Set the time provider
      *
@@ -113,6 +148,41 @@ public class TimeGraphMarkerAxis extends TimeGraphBaseControl {
         }
     }
 
+    /**
+     * Handle a mouseDown event.
+     *
+     * @param e
+     *            the mouse event
+     * @param bounds
+     *            the bounds of the marker axis in the mouse event's coordinates
+     * @param nameSpace
+     *            the width of the marker name area
+     */
+    public void mouseDown(MouseEvent e, Rectangle bounds, int nameSpace) {
+        if (bounds.isEmpty()) {
+            return;
+        }
+        if (fCollapsed || (e.x < bounds.x + Math.min(nameSpace, EXPANDED.getBounds().width))) {
+            fCollapsed = !fCollapsed;
+            getParent().layout();
+            redraw();
+            return;
+        }
+        if (e.x < bounds.x + nameSpace) {
+            String category = getHiddenCategoryForEvent(e, bounds);
+            if (category != null) {
+                for (IMarkerAxisListener listener : fListeners) {
+                    listener.setMarkerCategoryVisible(category, false);
+                }
+            }
+            return;
+        }
+        IMarkerEvent marker = getMarkerForEvent(e);
+        if (marker != null) {
+            fTimeProvider.setSelectionRangeNotify(marker.getTime(), marker.getTime() + marker.getDuration(), false);
+        }
+    }
+
     /**
      * Set the markers list.
      *
@@ -125,6 +195,9 @@ public class TimeGraphMarkerAxis extends TimeGraphBaseControl {
             map.put(marker.getCategory(), marker);
         }
         Display.getDefault().asyncExec(() -> {
+            if (isDisposed()) {
+                return;
+            }
             fMarkers = map;
             getParent().layout();
             redraw();
@@ -147,21 +220,28 @@ public class TimeGraphMarkerAxis extends TimeGraphBaseControl {
      *            the GC instance
      */
     protected void drawMarkerAxis(Rectangle bounds, int nameSpace, GC gc) {
+        if (bounds.isEmpty()) {
+            return;
+        }
         // draw background
         gc.fillRectangle(bounds);
 
-        Rectangle rect = new Rectangle(bounds.x, bounds.y + TOP_MARGIN, bounds.width, HEIGHT);
-        List<String> categories = new ArrayList<>(fCategories);
-        categories.retainAll(fMarkers.keySet());
-        for (String category : categories) {
-            rect.x = bounds.x;
-            rect.width = nameSpace;
-            drawMarkerCategory(category, rect, gc);
-            rect.x = nameSpace;
-            rect.width = bounds.width - nameSpace;
-            drawMarkerLabels(category, rect, gc);
-            rect.y += HEIGHT;
+        if (!fCollapsed) {
+            Rectangle rect = new Rectangle(bounds.x, bounds.y + TOP_MARGIN, bounds.width, HEIGHT);
+            for (String category : getVisibleCategories()) {
+                rect.x = bounds.x;
+                rect.width = nameSpace;
+                drawMarkerCategory(category, rect, gc);
+                rect.x = nameSpace;
+                rect.width = bounds.width - nameSpace;
+                drawMarkerLabels(category, rect, gc);
+                rect.y += HEIGHT;
+            }
         }
+
+        Rectangle rect = new Rectangle(bounds.x, bounds.y, nameSpace, bounds.height);
+        gc.setClipping(rect);
+        drawToolbar(rect, nameSpace, gc);
     }
 
     /**
@@ -182,7 +262,8 @@ public class TimeGraphMarkerAxis extends TimeGraphBaseControl {
         gc.setForeground(gc.getDevice().getSystemColor(SWT.COLOR_WIDGET_FOREGROUND));
         gc.setClipping(rect);
         int width = gc.textExtent(category).x + TEXT_MARGIN;
-        gc.drawText(category, Math.max(rect.x, rect.x + rect.width - width), rect.y, true);
+        int x = rect.x + EXPANDED.getBounds().width + HIDE.getBounds().width;
+        gc.drawText(category, Math.max(x, rect.x + rect.width - width), rect.y, true);
     }
 
     /**
@@ -212,6 +293,7 @@ public class TimeGraphMarkerAxis extends TimeGraphBaseControl {
         for (IMarkerEvent markerEvent : fMarkers.get(category)) {
             Color color = getColorScheme().getColor(markerEvent.getColor());
             gc.setForeground(color);
+            gc.setBackground(color);
             int x1 = getXForTime(rect, time0, pixelsPerNanoSec, markerEvent.getTime());
             if (x1 > rect.x + rect.width) {
                 return;
@@ -232,12 +314,13 @@ public class TimeGraphMarkerAxis extends TimeGraphBaseControl {
                     }
                 }
                 gc.fillRectangle(x1, rect.y, width, rect.height - 1);
-                Utils.drawText(gc, label, x1 + TEXT_MARGIN, rect.y, true);
                 gc.drawRectangle(x1, rect.y, width, rect.height - 1);
                 if (x2 > x1 + width) {
                     int y = rect.y + rect.height / 2;
                     gc.drawLine(x1 + width, y, x2, y);
                 }
+                gc.setForeground(getDistinctForeground(color.getRGB()));
+                Utils.drawText(gc, label, x1 + TEXT_MARGIN, rect.y, true);
             } else {
                 int y = rect.y + rect.height / 2;
                 gc.drawLine(x1, y, x2, y);
@@ -245,6 +328,45 @@ public class TimeGraphMarkerAxis extends TimeGraphBaseControl {
         }
     }
 
+    private static Color getDistinctForeground(RGB rgb) {
+        /* Calculate the relative luminance of the color, high value is bright */
+        final int luminanceThreshold = 128;
+        /* Relative luminance (Y) coefficients as defined in ITU.R Rec. 709 */
+        final double redCoefficient = 0.2126;
+        final double greenCoefficient = 0.7152;
+        final double blueCoefficient = 0.0722;
+        int luminance = (int) (redCoefficient * rgb.red + greenCoefficient * rgb.green + blueCoefficient * rgb.blue);
+        /* Use black over bright colors and white over dark colors */
+        return Display.getDefault().getSystemColor(
+                luminance > luminanceThreshold ? SWT.COLOR_BLACK : SWT.COLOR_WHITE);
+    }
+
+    /**
+     * Draw the toolbar
+     *
+     * @param bounds
+     *            the bounds of the marker axis
+     * @param nameSpace
+     *            the width of the marker name area
+     * @param gc
+     *            the GC instance
+     */
+    protected void drawToolbar(Rectangle bounds, int nameSpace, GC gc) {
+        if (bounds.isEmpty()) {
+            return;
+        }
+        if (fCollapsed) {
+            gc.drawImage(COLLAPSED, bounds.x, bounds.y);
+        } else {
+            gc.drawImage(EXPANDED, bounds.x, bounds.y);
+            int x = bounds.x + EXPANDED.getBounds().width;
+            for (int i = 0; i < fMarkers.keySet().size(); i++) {
+                int y = bounds.y + TOP_MARGIN + i * HEIGHT;
+                gc.drawImage(HIDE, x, y);
+            }
+        }
+    }
+
     private static String getTrimmedLabel(IMarkerEvent marker) {
         String label = marker.getLabel();
         if (label == null) {
@@ -269,9 +391,7 @@ public class TimeGraphMarkerAxis extends TimeGraphBaseControl {
             (double) (timeSpace - RIGHT_MARGIN) / (time1 - time0);
 
         int categoryIndex = Math.max((event.y - TOP_MARGIN) / HEIGHT, 0);
-        List<String> categories = new ArrayList<>(fCategories);
-        categories.retainAll(fMarkers.keySet());
-        String category = categories.get(categoryIndex);
+        String category = getVisibleCategories().get(categoryIndex);
 
         IMarkerEvent marker = null;
         GC gc = new GC(Display.getDefault());
@@ -303,4 +423,26 @@ public class TimeGraphMarkerAxis extends TimeGraphBaseControl {
         gc.dispose();
         return marker;
     }
+
+    private String getHiddenCategoryForEvent(MouseEvent e, Rectangle bounds) {
+        List<String> categories = getVisibleCategories();
+        Rectangle rect = HIDE.getBounds();
+        rect.x += bounds.x + EXPANDED.getBounds().width + HIDE_BORDER;
+        rect.y += bounds.y + TOP_MARGIN + HIDE_BORDER;
+        rect.width -= 2 * HIDE_BORDER;
+        rect.height -= 2 * HIDE_BORDER;
+        for (int i = 0; i < categories.size(); i++) {
+            if (rect.contains(e.x, e.y)) {
+                return categories.get(i);
+            }
+            rect.y += HEIGHT;
+        }
+        return null;
+    }
+
+    private List<String> getVisibleCategories() {
+        List<String> categories = new ArrayList<>(fCategories);
+        categories.retainAll(fMarkers.keySet());
+        return categories;
+    }
 }
This page took 0.064057 seconds and 5 git commands to generate.