import org.eclipse.swt.events.SelectionEvent;
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;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.dialogs.ShowFilterDialogAction;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ILinkEvent;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.ITimeDataProvider;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.TimeGraphColorScheme;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.TimeGraphControl;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.TimeGraphMarkerAxis;
import com.google.common.collect.Iterables;
}
};
}
+
+ private class TimeGraphMarkerAxisExtension extends TimeGraphMarkerAxis {
+ private int fMargin = 0;
+
+ public TimeGraphMarkerAxisExtension(Composite parent, @NonNull TimeGraphColorScheme colorScheme, @NonNull ITimeDataProvider timeProvider) {
+ super(parent, colorScheme, timeProvider);
+ }
+
+ @Override
+ public Point computeSize(int wHint, int hHint, boolean changed) {
+ Point size = super.computeSize(wHint, hHint, changed);
+ if (size.y > 0) {
+ size.y += fMargin;
+ }
+ return size;
+ }
+
+ @Override
+ public void redraw() {
+ super.redraw();
+ fTreeViewer.getControl().redraw();
+ }
+
+ @Override
+ protected void drawMarkerAxis(Rectangle bounds, int nameSpace, GC gc) {
+ super.drawMarkerAxis(bounds, nameSpace, gc);
+ }
+
+ private Rectangle getAxisBounds() {
+ Tree tree = fTreeViewer.getTree();
+ Rectangle axisBounds = getBounds();
+ Rectangle treeClientArea = tree.getClientArea();
+ if (axisBounds.isEmpty()) {
+ treeClientArea.y += treeClientArea.height;
+ treeClientArea.height = 0;
+ return treeClientArea;
+ }
+ Composite axisParent = getParent();
+ Point axisDisplayCoordinates = axisParent.toDisplay(axisBounds.x, axisBounds.y);
+ Point axisTreeCoordinates = tree.toControl(axisDisplayCoordinates);
+ int height = treeClientArea.y + treeClientArea.height - axisTreeCoordinates.y;
+ int margin = Math.max(0, axisBounds.height - height);
+ if (fMargin != margin) {
+ fMargin = margin;
+ getParent().layout();
+ redraw();
+ axisTreeCoordinates.y -= margin;
+ height += margin;
+ }
+ return new Rectangle(treeClientArea.x, axisTreeCoordinates.y, treeClientArea.width, height);
+ }
+ }
+
+ @Override
+ protected TimeGraphMarkerAxis createTimeGraphMarkerAxis(Composite parent, @NonNull TimeGraphColorScheme colorScheme, @NonNull ITimeDataProvider timeProvider) {
+ TimeGraphMarkerAxisExtension timeGraphMarkerAxis = new TimeGraphMarkerAxisExtension(parent, colorScheme, timeProvider);
+ Tree tree = fTreeViewer.getTree();
+ tree.addPaintListener(new PaintListener() {
+ @Override
+ public void paintControl(PaintEvent e) {
+ /*
+ * Draw the marker axis over the tree. Only the name area
+ * will be drawn, since the time area has zero width.
+ */
+ Rectangle bounds = timeGraphMarkerAxis.getAxisBounds();
+ e.gc.setBackground(timeGraphMarkerAxis.getBackground());
+ timeGraphMarkerAxis.drawMarkerAxis(bounds, bounds.width, e.gc);
+ }
+ });
+ tree.getHorizontalBar().addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ tree.redraw();
+ }
+ });
+ return timeGraphMarkerAxis;
+ }
}
/**
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.TimeDataProviderCyclesConverter;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.TimeGraphColorScheme;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.TimeGraphControl;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.TimeGraphMarkerAxis;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.TimeGraphScale;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.TimeGraphTooltipHandler;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.Utils;
private TimeGraphControl fTimeGraphCtrl;
private TimeGraphScale fTimeScaleCtrl;
+ private TimeGraphMarkerAxis fMarkerAxisCtrl;
private Slider fHorizontalScrollBar;
private Slider fVerticalScrollBar;
- private TimeGraphColorScheme fColorScheme;
+ private @NonNull TimeGraphColorScheme fColorScheme = new TimeGraphColorScheme();
private Object fInputElement;
private ITimeGraphContentProvider fTimeGraphContentProvider;
private ITimeGraphPresentationProvider fTimeGraphProvider;
- private ITimeDataProvider fTimeDataProvider = this;
+ private @NonNull ITimeDataProvider fTimeDataProvider = this;
private TimeGraphTooltipHandler fToolTipHandler;
private List<ITimeGraphSelectionListener> fSelectionListeners = new ArrayList<>();
*/
protected Control createDataViewer(Composite parent, int style) {
loadOptions();
- fColorScheme = new TimeGraphColorScheme();
fDataViewer = new Composite(parent, style) {
@Override
public void redraw() {
fTimeScaleCtrl.redraw();
fTimeGraphCtrl.redraw();
+ fMarkerAxisCtrl.redraw();
super.redraw();
}
};
}
});
+ fMarkerAxisCtrl = createTimeGraphMarkerAxis(fTimeAlignedComposite, fColorScheme, this);
+ fMarkerAxisCtrl.setLayoutData(new GridData(SWT.FILL, SWT.DEFAULT, true, false));
+ fMarkerAxisCtrl.addMouseWheelListener(new MouseWheelListener() {
+ @Override
+ public void mouseScrolled(MouseEvent e) {
+ fTimeGraphCtrl.zoom(e.count > 0);
+ }
+ });
+
fVerticalScrollBar = new Slider(fDataViewer, SWT.VERTICAL | SWT.NO_FOCUS);
fVerticalScrollBar.setLayoutData(new GridData(SWT.DEFAULT, SWT.FILL, false, true, 1, 1));
fVerticalScrollBar.addSelectionListener(new SelectionAdapter() {
return new TimeGraphControl(parent, colors);
}
+ /**
+ * Create a new time graph marker axis.
+ *
+ * @param parent
+ * The parent composite object
+ * @param colorScheme
+ * The color scheme to use
+ * @param timeProvider
+ * The time data provider
+ * @return The new TimeGraphMarkerAxis
+ * @since 2.0
+ */
+ protected TimeGraphMarkerAxis createTimeGraphMarkerAxis(Composite parent,
+ @NonNull TimeGraphColorScheme colorScheme, @NonNull ITimeDataProvider timeProvider) {
+ return new TimeGraphMarkerAxis(parent, colorScheme, timeProvider);
+ }
+
/**
* Resize the controls
*/
}
fTimeGraphCtrl.refreshData(traces);
fTimeScaleCtrl.redraw();
+ fMarkerAxisCtrl.redraw();
updateMarkerActions();
adjustVerticalScrollBar();
}
}
fTimeGraphCtrl.redraw();
fTimeScaleCtrl.redraw();
+ fMarkerAxisCtrl.redraw();
+ /* force update the controls to keep them aligned */
+ fTimeScaleCtrl.update();
+ fMarkerAxisCtrl.update();
+ fTimeGraphCtrl.update();
}
@Override
adjustHorizontalScrollBar();
fTimeGraphCtrl.redraw();
fTimeScaleCtrl.redraw();
+ fMarkerAxisCtrl.redraw();
+ /* force update the controls to keep them aligned */
+ fTimeScaleCtrl.update();
+ fMarkerAxisCtrl.update();
+ fTimeGraphCtrl.update();
}
@Override
fTimeGraphCtrl.redraw();
fTimeScaleCtrl.redraw();
+ fMarkerAxisCtrl.redraw();
updateMarkerActions();
if ((time0 != fTime0) || (time1 != fTime1)) {
markers.addAll(fBookmarks);
}
Collections.sort(markers, new MarkerComparator());
- getTimeGraphControl().setMarkers(markers);
+ fTimeGraphCtrl.setMarkers(markers);
+ fMarkerAxisCtrl.setMarkers(markers);
}
private void adjustHorizontalScrollBar() {
*/
public void setMarkers(List<IMarkerEvent> markers) {
fMarkers = markers;
- fTimeGraphScale.setMarkers(markers);
}
/**
*/
public void setMarkersVisible(boolean visible) {
fMarkersVisible = visible;
- fTimeGraphScale.setMarkersVisible(visible);
}
/**
// over the time scale
horizontalZoom = true;
} else if (e.y >= getSize().y) {
- // over the horizontal scroll bar
- if ((e.stateMask & SWT.MODIFIER_MASK) == SWT.CTRL) {
- horizontalZoom = true;
- } else {
- horizontalScroll = true;
- }
+ // over the marker axis
+ horizontalZoom = true;
} else {
if ((e.stateMask & SWT.MODIFIER_MASK) == (SWT.SHIFT | SWT.CTRL)) {
verticalZoom = true;
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2016 Ericsson
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Patrick Tasse - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MouseAdapter;
+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.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.IMarkerEvent;
+
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
+
+/**
+ * A control that shows marker labels on a time axis.
+ *
+ * @since 2.0
+ */
+public class TimeGraphMarkerAxis extends TimeGraphBaseControl {
+
+ private static final int HEIGHT;
+ static {
+ GC gc = new GC(Display.getDefault());
+ HEIGHT = gc.getFontMetrics().getHeight() + 1;
+ gc.dispose();
+ }
+
+ private static final int TOP_MARGIN = 1;
+ private static final int MAX_LABEL_LENGTH = 256;
+ private static final int TEXT_MARGIN = 2;
+ private static final int MAX_GAP = 5;
+ private static final int X_LIMIT = Integer.MAX_VALUE / 256;
+
+ private @NonNull ITimeDataProvider fTimeProvider;
+ private Multimap<String, IMarkerEvent> fMarkers = LinkedHashMultimap.create();
+ private List<String> fCategories = Collections.EMPTY_LIST;
+
+ /**
+ * Contructor
+ *
+ * @param parent
+ * The parent composite object
+ * @param colorScheme
+ * The color scheme to use
+ * @param timeProvider
+ * The time data provider
+ */
+ public TimeGraphMarkerAxis(Composite parent, @NonNull TimeGraphColorScheme colorScheme, @NonNull ITimeDataProvider timeProvider) {
+ super(parent, colorScheme, SWT.NO_BACKGROUND | SWT.NO_FOCUS | SWT.DOUBLE_BUFFERED);
+ fTimeProvider = timeProvider;
+ 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);
+ }
+ }
+ });
+ }
+
+ @Override
+ 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;
+ }
+ return super.computeSize(wHint, height, changed);
+ }
+
+ /**
+ * Set the time provider
+ *
+ * @param timeProvider
+ * The provider to use
+ */
+ public void setTimeProvider(@NonNull ITimeDataProvider timeProvider) {
+ fTimeProvider = timeProvider;
+ }
+
+ /**
+ * Set the markers list.
+ *
+ * @param markers
+ * The markers list
+ */
+ public void setMarkers(List<IMarkerEvent> markers) {
+ Multimap<String, IMarkerEvent> map = LinkedHashMultimap.create();
+ for (IMarkerEvent marker : markers) {
+ map.put(marker.getCategory(), marker);
+ }
+ List<String> categories = Lists.newArrayList(map.keySet());
+ Collections.sort(categories);
+ Display.getDefault().asyncExec(() -> {
+ fMarkers = map;
+ fCategories = categories;
+ getParent().layout();
+ redraw();
+ });
+ }
+
+ @Override
+ void paint(Rectangle bounds, PaintEvent e) {
+ drawMarkerAxis(bounds, fTimeProvider.getNameSpace(), e.gc);
+ }
+
+ /**
+ * Draw the marker axis
+ *
+ * @param bounds
+ * the bounds of the marker axis
+ * @param nameSpace
+ * the width of the marker name area
+ * @param gc
+ * the GC instance
+ */
+ protected void drawMarkerAxis(Rectangle bounds, int nameSpace, GC gc) {
+ // draw background
+ gc.fillRectangle(bounds);
+
+ Rectangle rect = new Rectangle(bounds.x, bounds.y + TOP_MARGIN, bounds.width, HEIGHT);
+ for (String category : fCategories) {
+ 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;
+ }
+ }
+
+ /**
+ * Draw the marker category
+ *
+ * @param category
+ * the category
+ * @param rect
+ * the bounds of the marker name area
+ * @param gc
+ * the GC instance
+ */
+ protected void drawMarkerCategory(String category, Rectangle rect, GC gc) {
+ if (rect.isEmpty()) {
+ return;
+ }
+ // draw marker category
+ 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);
+ }
+
+ /**
+ * Draw the marker labels for the specified category
+ *
+ * @param category
+ * the category
+ * @param rect
+ * the bounds of the marker time area
+ * @param gc
+ * the GC instance
+ */
+ protected void drawMarkerLabels(String category, Rectangle rect, GC gc) {
+ if (rect.isEmpty()) {
+ return;
+ }
+ long time0 = fTimeProvider.getTime0();
+ long time1 = fTimeProvider.getTime1();
+ if (time0 == time1) {
+ return;
+ }
+ int timeSpace = fTimeProvider.getTimeSpace();
+ double pixelsPerNanoSec = (timeSpace <= RIGHT_MARGIN) ? 0 :
+ (double) (timeSpace - RIGHT_MARGIN) / (time1 - time0);
+
+ gc.setClipping(rect);
+ for (IMarkerEvent markerEvent : fMarkers.get(category)) {
+ Color color = getColorScheme().getColor(markerEvent.getColor());
+ gc.setForeground(color);
+ int x1 = getXForTime(rect, time0, pixelsPerNanoSec, markerEvent.getTime());
+ if (x1 > rect.x + rect.width) {
+ return;
+ }
+ if (markerEvent.getEntry() != null) {
+ continue;
+ }
+ int x2 = getXForTime(rect, time0, pixelsPerNanoSec, markerEvent.getTime() + markerEvent.getDuration()) - 1;
+ String label = getTrimmedLabel(markerEvent);
+ if (label != null) {
+ int width = gc.textExtent(label).x + TEXT_MARGIN;
+ if (x1 < rect.x && x1 + width < x2) {
+ int gap = Math.min(rect.x - x1, MAX_GAP);
+ x1 = Math.min(rect.x + gap, x2 - width);
+ if (x1 > rect.x) {
+ int y = rect.y + rect.height / 2;
+ gc.drawLine(rect.x, y, x1, y);
+ }
+ }
+ 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);
+ }
+ } else {
+ int y = rect.y + rect.height / 2;
+ gc.drawLine(x1, y, x2, y);
+ }
+ }
+ }
+
+ private static String getTrimmedLabel(IMarkerEvent marker) {
+ String label = marker.getLabel();
+ if (label == null) {
+ return null;
+ }
+ return label.substring(0, Math.min(label.indexOf(SWT.LF) != -1 ? label.indexOf(SWT.LF) : label.length(), MAX_LABEL_LENGTH));
+ }
+
+ private static int getXForTime(Rectangle rect, long time0, double pixelsPerNanoSec, long time) {
+ int x = rect.x + (int) (Math.floor((time - time0) * pixelsPerNanoSec));
+ return Math.min(Math.max(x, -X_LIMIT), X_LIMIT);
+ }
+
+ private IMarkerEvent getMarkerForEvent(MouseEvent event) {
+ long time0 = fTimeProvider.getTime0();
+ long time1 = fTimeProvider.getTime1();
+ if (time0 == time1) {
+ return null;
+ }
+ int timeSpace = fTimeProvider.getTimeSpace();
+ double pixelsPerNanoSec = (timeSpace <= RIGHT_MARGIN) ? 0 :
+ (double) (timeSpace - RIGHT_MARGIN) / (time1 - time0);
+
+ int categoryIndex = Math.max((event.y - TOP_MARGIN) / HEIGHT, 0);
+ String category = fCategories.get(categoryIndex);
+
+ IMarkerEvent marker = null;
+ GC gc = new GC(Display.getDefault());
+ Rectangle rect = getBounds();
+ rect.x += fTimeProvider.getNameSpace();
+ rect.width -= fTimeProvider.getNameSpace();
+
+ for (IMarkerEvent markerEvent : fMarkers.get(category)) {
+ String label = getTrimmedLabel(markerEvent);
+ if (markerEvent.getEntry() == null) {
+ int x1 = getXForTime(rect, time0, pixelsPerNanoSec, markerEvent.getTime());
+ if (x1 <= event.x) {
+ if (label != null) {
+ int width = gc.textExtent(label).x + TEXT_MARGIN;
+ if (event.x <= x1 + width) {
+ marker = markerEvent;
+ continue;
+ }
+ }
+ int x2 = getXForTime(rect, time0, pixelsPerNanoSec, markerEvent.getTime() + markerEvent.getDuration()) - 1;
+ if (event.x <= x2) {
+ marker = markerEvent;
+ }
+ } else {
+ break;
+ }
+ }
+ }
+ gc.dispose();
+ return marker;
+ }
+}
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.PaintEvent;
-import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager;
import org.eclipse.tracecompass.tmf.core.signal.TmfTimestampFormatUpdateSignal;
import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimePreferences;
-import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.IMarkerEvent;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.Utils.Resolution;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.Utils.TimeFormat;
private static final int NO_BUTTON = 0;
private static final int LEFT_BUTTON = 1;
- private static final int MAX_LABEL_LENGTH = 256;
-
private ITimeDataProvider fTimeProvider;
private int fDragState = NO_BUTTON;
private int fDragX0 = 0;
private boolean fIsInUpdate;
private int fHeight;
private List<Integer> fTickList = new ArrayList<>();
- private List<IMarkerEvent> fMarkers = null;
- private boolean fMarkersVisible = true;
/**
* Standard constructor
return fTickList;
}
- /**
- * Set the markers list.
- *
- * @param markers
- * The markers list, or null
- * @since 2.0
- */
- public void setMarkers(List<IMarkerEvent> markers) {
- fMarkers = markers;
- }
-
- /**
- * 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;
- }
-
private long calcTimeDelta(int width, double pixelsPerNanoSec) {
long timeDelta;
double minDelta = (pixelsPerNanoSec == 0) ? YEAR_IN_NS : width / pixelsPerNanoSec;
}
}
fTickList = tickList;
-
- // draw marker labels
- if (fMarkersVisible) {
- drawMarkerLabels(fMarkers, rect, gc, time0, leftSpace, pixelsPerNanoSec);
- }
- }
-
- private void drawMarkerLabels(List<IMarkerEvent> markerEvents, Rectangle rect, GC gc, long time0, int leftSpace, double pixelsPerNanoSec) {
- if (markerEvents == null) {
- return;
- }
- for (IMarkerEvent markerEvent : markerEvents) {
- String label = markerEvent.getLabel();
- if (label != null && markerEvent.getEntry() == null) {
- label = label.substring(0, Math.min(label.indexOf('\n') != -1 ? label.indexOf('\n') : label.length(), MAX_LABEL_LENGTH));
- int x = rect.x + leftSpace + (int) (Math.floor((markerEvent.getTime() - time0) * pixelsPerNanoSec));
- int y = rect.y + rect.height - gc.stringExtent(" ").y + 2; //$NON-NLS-1$
- Color color = getColorScheme().getColor(markerEvent.getColor());
- gc.setForeground(color);
- Utils.drawText(gc, label, x, y, true);
- }
- }
}
private static void drawRangeDecorators(Rectangle rect, GC gc, int x1, int x2) {