1 /*******************************************************************************
2 * Copyright (c) 2016 Ericsson
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * Patrick Tasse - Initial API and implementation
11 *******************************************************************************/
13 package org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.widgets
;
15 import java
.util
.ArrayList
;
16 import java
.util
.Collections
;
17 import java
.util
.List
;
19 import org
.eclipse
.jdt
.annotation
.NonNull
;
20 import org
.eclipse
.swt
.SWT
;
21 import org
.eclipse
.swt
.events
.MouseAdapter
;
22 import org
.eclipse
.swt
.events
.MouseEvent
;
23 import org
.eclipse
.swt
.events
.PaintEvent
;
24 import org
.eclipse
.swt
.graphics
.Color
;
25 import org
.eclipse
.swt
.graphics
.GC
;
26 import org
.eclipse
.swt
.graphics
.Point
;
27 import org
.eclipse
.swt
.graphics
.Rectangle
;
28 import org
.eclipse
.swt
.widgets
.Composite
;
29 import org
.eclipse
.swt
.widgets
.Display
;
30 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.model
.IMarkerEvent
;
32 import com
.google
.common
.collect
.LinkedHashMultimap
;
33 import com
.google
.common
.collect
.Multimap
;
36 * A control that shows marker labels on a time axis.
40 public class TimeGraphMarkerAxis
extends TimeGraphBaseControl
{
42 private static final int HEIGHT
;
44 GC gc
= new GC(Display
.getDefault());
45 HEIGHT
= gc
.getFontMetrics().getHeight() + 1;
49 private static final int TOP_MARGIN
= 1;
50 private static final int MAX_LABEL_LENGTH
= 256;
51 private static final int TEXT_MARGIN
= 2;
52 private static final int MAX_GAP
= 5;
53 private static final int X_LIMIT
= Integer
.MAX_VALUE
/ 256;
55 private @NonNull ITimeDataProvider fTimeProvider
;
56 private Multimap
<String
, IMarkerEvent
> fMarkers
= LinkedHashMultimap
.create();
57 private @NonNull List
<String
> fCategories
= Collections
.EMPTY_LIST
;
63 * The parent composite object
65 * The color scheme to use
67 * The time data provider
69 public TimeGraphMarkerAxis(Composite parent
, @NonNull TimeGraphColorScheme colorScheme
, @NonNull ITimeDataProvider timeProvider
) {
70 super(parent
, colorScheme
, SWT
.NO_BACKGROUND
| SWT
.NO_FOCUS
| SWT
.DOUBLE_BUFFERED
);
71 fTimeProvider
= timeProvider
;
72 addMouseListener(new MouseAdapter() {
74 public void mouseDown(MouseEvent e
) {
75 IMarkerEvent marker
= getMarkerForEvent(e
);
77 fTimeProvider
.setSelectionRangeNotify(marker
.getTime(), marker
.getTime() + marker
.getDuration(), false);
84 public Point
computeSize(int wHint
, int hHint
, boolean changed
) {
86 if (!fMarkers
.isEmpty() && fTimeProvider
.getTime0() != fTimeProvider
.getTime1()) {
87 height
= TOP_MARGIN
+ fMarkers
.keySet().size() * HEIGHT
;
89 return super.computeSize(wHint
, height
, changed
);
93 * Set the time provider
98 public void setTimeProvider(@NonNull ITimeDataProvider timeProvider
) {
99 fTimeProvider
= timeProvider
;
103 * Set the list of marker categories.
106 * The list of marker categories, or null
108 public void setMarkerCategories(List
<String
> categories
) {
109 if (categories
== null) {
110 fCategories
= Collections
.EMPTY_LIST
;
112 fCategories
= categories
;
117 * Set the markers list.
122 public void setMarkers(List
<IMarkerEvent
> markers
) {
123 Multimap
<String
, IMarkerEvent
> map
= LinkedHashMultimap
.create();
124 for (IMarkerEvent marker
: markers
) {
125 map
.put(marker
.getCategory(), marker
);
127 Display
.getDefault().asyncExec(() -> {
129 getParent().layout();
135 void paint(Rectangle bounds
, PaintEvent e
) {
136 drawMarkerAxis(bounds
, fTimeProvider
.getNameSpace(), e
.gc
);
140 * Draw the marker axis
143 * the bounds of the marker axis
145 * the width of the marker name area
149 protected void drawMarkerAxis(Rectangle bounds
, int nameSpace
, GC gc
) {
151 gc
.fillRectangle(bounds
);
153 Rectangle rect
= new Rectangle(bounds
.x
, bounds
.y
+ TOP_MARGIN
, bounds
.width
, HEIGHT
);
154 List
<String
> categories
= new ArrayList
<>(fCategories
);
155 categories
.retainAll(fMarkers
.keySet());
156 for (String category
: categories
) {
158 rect
.width
= nameSpace
;
159 drawMarkerCategory(category
, rect
, gc
);
161 rect
.width
= bounds
.width
- nameSpace
;
162 drawMarkerLabels(category
, rect
, gc
);
168 * Draw the marker category
173 * the bounds of the marker name area
177 protected void drawMarkerCategory(String category
, Rectangle rect
, GC gc
) {
178 if (rect
.isEmpty()) {
181 // draw marker category
182 gc
.setForeground(gc
.getDevice().getSystemColor(SWT
.COLOR_WIDGET_FOREGROUND
));
183 gc
.setClipping(rect
);
184 int width
= gc
.textExtent(category
).x
+ TEXT_MARGIN
;
185 gc
.drawText(category
, Math
.max(rect
.x
, rect
.x
+ rect
.width
- width
), rect
.y
, true);
189 * Draw the marker labels for the specified category
194 * the bounds of the marker time area
198 protected void drawMarkerLabels(String category
, Rectangle rect
, GC gc
) {
199 if (rect
.isEmpty()) {
202 long time0
= fTimeProvider
.getTime0();
203 long time1
= fTimeProvider
.getTime1();
204 if (time0
== time1
) {
207 int timeSpace
= fTimeProvider
.getTimeSpace();
208 double pixelsPerNanoSec
= (timeSpace
<= RIGHT_MARGIN
) ?
0 :
209 (double) (timeSpace
- RIGHT_MARGIN
) / (time1
- time0
);
211 gc
.setClipping(rect
);
212 for (IMarkerEvent markerEvent
: fMarkers
.get(category
)) {
213 Color color
= getColorScheme().getColor(markerEvent
.getColor());
214 gc
.setForeground(color
);
215 int x1
= getXForTime(rect
, time0
, pixelsPerNanoSec
, markerEvent
.getTime());
216 if (x1
> rect
.x
+ rect
.width
) {
219 if (markerEvent
.getEntry() != null) {
222 int x2
= getXForTime(rect
, time0
, pixelsPerNanoSec
, markerEvent
.getTime() + markerEvent
.getDuration()) - 1;
223 String label
= getTrimmedLabel(markerEvent
);
225 int width
= gc
.textExtent(label
).x
+ TEXT_MARGIN
;
226 if (x1
< rect
.x
&& x1
+ width
< x2
) {
227 int gap
= Math
.min(rect
.x
- x1
, MAX_GAP
);
228 x1
= Math
.min(rect
.x
+ gap
, x2
- width
);
230 int y
= rect
.y
+ rect
.height
/ 2;
231 gc
.drawLine(rect
.x
, y
, x1
, y
);
234 gc
.fillRectangle(x1
, rect
.y
, width
, rect
.height
- 1);
235 Utils
.drawText(gc
, label
, x1
+ TEXT_MARGIN
, rect
.y
, true);
236 gc
.drawRectangle(x1
, rect
.y
, width
, rect
.height
- 1);
237 if (x2
> x1
+ width
) {
238 int y
= rect
.y
+ rect
.height
/ 2;
239 gc
.drawLine(x1
+ width
, y
, x2
, y
);
242 int y
= rect
.y
+ rect
.height
/ 2;
243 gc
.drawLine(x1
, y
, x2
, y
);
248 private static String
getTrimmedLabel(IMarkerEvent marker
) {
249 String label
= marker
.getLabel();
253 return label
.substring(0, Math
.min(label
.indexOf(SWT
.LF
) != -1 ? label
.indexOf(SWT
.LF
) : label
.length(), MAX_LABEL_LENGTH
));
256 private static int getXForTime(Rectangle rect
, long time0
, double pixelsPerNanoSec
, long time
) {
257 int x
= rect
.x
+ (int) (Math
.floor((time
- time0
) * pixelsPerNanoSec
));
258 return Math
.min(Math
.max(x
, -X_LIMIT
), X_LIMIT
);
261 private IMarkerEvent
getMarkerForEvent(MouseEvent event
) {
262 long time0
= fTimeProvider
.getTime0();
263 long time1
= fTimeProvider
.getTime1();
264 if (time0
== time1
) {
267 int timeSpace
= fTimeProvider
.getTimeSpace();
268 double pixelsPerNanoSec
= (timeSpace
<= RIGHT_MARGIN
) ?
0 :
269 (double) (timeSpace
- RIGHT_MARGIN
) / (time1
- time0
);
271 int categoryIndex
= Math
.max((event
.y
- TOP_MARGIN
) / HEIGHT
, 0);
272 List
<String
> categories
= new ArrayList
<>(fCategories
);
273 categories
.retainAll(fMarkers
.keySet());
274 String category
= categories
.get(categoryIndex
);
276 IMarkerEvent marker
= null;
277 GC gc
= new GC(Display
.getDefault());
278 Rectangle rect
= getBounds();
279 rect
.x
+= fTimeProvider
.getNameSpace();
280 rect
.width
-= fTimeProvider
.getNameSpace();
282 for (IMarkerEvent markerEvent
: fMarkers
.get(category
)) {
283 String label
= getTrimmedLabel(markerEvent
);
284 if (markerEvent
.getEntry() == null) {
285 int x1
= getXForTime(rect
, time0
, pixelsPerNanoSec
, markerEvent
.getTime());
288 int width
= gc
.textExtent(label
).x
+ TEXT_MARGIN
;
289 if (event
.x
<= x1
+ width
) {
290 marker
= markerEvent
;
294 int x2
= getXForTime(rect
, time0
, pixelsPerNanoSec
, markerEvent
.getTime() + markerEvent
.getDuration()) - 1;
296 marker
= markerEvent
;