Commit | Line | Data |
---|---|---|
c392540b | 1 | /******************************************************************************* |
b6d74e0b | 2 | * Copyright (c) 2011, 2015 Ericsson |
20ff3b75 | 3 | * |
c392540b FC |
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 | |
20ff3b75 | 8 | * |
c392540b FC |
9 | * Contributors: |
10 | * Francois Chouinard - Initial API and implementation | |
fbd124dd | 11 | * Bernd Hufmann - Changed to updated histogram data model |
f8177ba2 | 12 | * Francois Chouinard - Reformat histogram labels on format change |
0fcf3b09 | 13 | * Patrick Tasse - Support selection range |
2fc582d2 | 14 | * Xavier Raynaud - Support multi-trace coloring |
c392540b FC |
15 | *******************************************************************************/ |
16 | ||
2bdf0193 | 17 | package org.eclipse.tracecompass.tmf.ui.views.histogram; |
c392540b | 18 | |
720d67cb | 19 | import org.eclipse.osgi.util.NLS; |
c392540b FC |
20 | import org.eclipse.swt.SWT; |
21 | import org.eclipse.swt.events.ControlEvent; | |
22 | import org.eclipse.swt.events.ControlListener; | |
2fea16bc MAL |
23 | import org.eclipse.swt.events.DisposeEvent; |
24 | import org.eclipse.swt.events.DisposeListener; | |
c392540b FC |
25 | import org.eclipse.swt.events.KeyEvent; |
26 | import org.eclipse.swt.events.KeyListener; | |
27 | import org.eclipse.swt.events.MouseEvent; | |
28 | import org.eclipse.swt.events.MouseListener; | |
f888477a | 29 | import org.eclipse.swt.events.MouseMoveListener; |
c392540b | 30 | import org.eclipse.swt.events.MouseTrackListener; |
65cdf787 | 31 | import org.eclipse.swt.events.MouseWheelListener; |
c392540b FC |
32 | import org.eclipse.swt.events.PaintEvent; |
33 | import org.eclipse.swt.events.PaintListener; | |
34 | import org.eclipse.swt.graphics.Color; | |
35 | import org.eclipse.swt.graphics.Font; | |
36 | import org.eclipse.swt.graphics.FontData; | |
37 | import org.eclipse.swt.graphics.GC; | |
38 | import org.eclipse.swt.graphics.Image; | |
e8c79054 | 39 | import org.eclipse.swt.graphics.Point; |
e60df94a | 40 | import org.eclipse.swt.layout.FillLayout; |
c392540b FC |
41 | import org.eclipse.swt.layout.GridData; |
42 | import org.eclipse.swt.layout.GridLayout; | |
43 | import org.eclipse.swt.widgets.Canvas; | |
44 | import org.eclipse.swt.widgets.Composite; | |
45 | import org.eclipse.swt.widgets.Display; | |
65cdf787 | 46 | import org.eclipse.swt.widgets.Label; |
2bdf0193 AM |
47 | import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler; |
48 | import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager; | |
49 | import org.eclipse.tracecompass.tmf.core.signal.TmfTimestampFormatUpdateSignal; | |
50 | import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp; | |
51 | import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp; | |
52 | import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestampDelta; | |
53 | import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestampFormat; | |
e8c79054 BH |
54 | import org.eclipse.tracecompass.tmf.ui.signal.TmfTimeViewAlignmentSignal; |
55 | import org.eclipse.tracecompass.tmf.ui.views.ITmfTimeAligned; | |
2bdf0193 | 56 | import org.eclipse.tracecompass.tmf.ui.views.TmfView; |
c392540b FC |
57 | |
58 | /** | |
b544077e | 59 | * Re-usable histogram widget. |
20ff3b75 | 60 | * |
b544077e | 61 | * It has the following features: |
c392540b FC |
62 | * <ul> |
63 | * <li>Y-axis labels displaying min/max count values | |
64 | * <li>X-axis labels displaying time range | |
65 | * <li>a histogram displaying the distribution of values over time (note that | |
66 | * the histogram might not necessarily fill the whole canvas) | |
67 | * </ul> | |
224c38ef | 68 | * The widget also has 1 'marker' to identify: |
c392540b | 69 | * <ul> |
224c38ef | 70 | * <li>a blue dashed line over the bar that contains the currently selected event |
c392540b FC |
71 | * </ul> |
72 | * Clicking on the histogram will select the current event at the mouse | |
73 | * location. | |
74 | * <p> | |
75 | * Once the histogram is selected, there is some limited keyboard support: | |
76 | * <ul> | |
77 | * <li>Home: go to the first histogram bar | |
78 | * <li>End: go to the last histogram bar | |
79 | * <li>Left: go to the previous histogram | |
80 | * <li>Right: go to the next histogram bar | |
81 | * </ul> | |
82 | * Finally, when the mouse hovers over the histogram, a tool tip showing the | |
83 | * following information about the corresponding histogram bar time range: | |
84 | * <ul> | |
85 | * <li>start of the time range | |
86 | * <li>end of the time range | |
87 | * <li>number of events in that time range | |
88 | * </ul> | |
20ff3b75 | 89 | * |
f8177ba2 | 90 | * @version 1.1 |
b544077e | 91 | * @author Francois Chouinard |
c392540b | 92 | */ |
f888477a | 93 | public abstract class Histogram implements ControlListener, PaintListener, KeyListener, MouseListener, MouseMoveListener, MouseTrackListener, IHistogramModelListener { |
c392540b FC |
94 | |
95 | // ------------------------------------------------------------------------ | |
96 | // Constants | |
97 | // ------------------------------------------------------------------------ | |
98 | ||
c392540b | 99 | // Histogram colors |
2fea16bc MAL |
100 | |
101 | // System colors, they do not need to be disposed | |
c392540b | 102 | private final Color fBackgroundColor = Display.getCurrent().getSystemColor(SWT.COLOR_WHITE); |
0fcf3b09 PT |
103 | private final Color fSelectionForegroundColor = Display.getCurrent().getSystemColor(SWT.COLOR_BLUE); |
104 | private final Color fSelectionBackgroundColor = Display.getCurrent().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND); | |
2fea16bc MAL |
105 | |
106 | // Application colors, they need to be disposed | |
0271030d | 107 | private final Color[] fHistoBarColors = new Color[] { new Color(Display.getDefault(), 90, 90, 255), // blue |
2fc582d2 XR |
108 | new Color(Display.getDefault(), 0, 240, 0), // green |
109 | new Color(Display.getDefault(), 255, 0, 0), // red | |
110 | new Color(Display.getDefault(), 0, 255, 255), // cyan | |
111 | new Color(Display.getDefault(), 255, 80, 255), // magenta | |
112 | new Color(Display.getDefault(), 200, 200, 0), // yellow | |
113 | new Color(Display.getDefault(), 200, 150, 0), // brown | |
114 | new Color(Display.getDefault(), 150, 255, 150), // light green | |
115 | new Color(Display.getDefault(), 200, 80, 80), // dark red | |
116 | new Color(Display.getDefault(), 30, 150, 150), // dark cyan | |
117 | new Color(Display.getDefault(), 200, 200, 255), // light blue | |
118 | new Color(Display.getDefault(), 0, 120, 0), // dark green | |
119 | new Color(Display.getDefault(), 255, 150, 150), // lighter red | |
120 | new Color(Display.getDefault(), 140, 80, 140), // dark magenta | |
121 | new Color(Display.getDefault(), 150, 100, 50), // brown | |
122 | new Color(Display.getDefault(), 255, 80, 80), // light red | |
123 | new Color(Display.getDefault(), 200, 200, 200), // light grey | |
124 | new Color(Display.getDefault(), 255, 200, 80), // orange | |
125 | new Color(Display.getDefault(), 255, 255, 80), // pale yellow | |
126 | new Color(Display.getDefault(), 255, 200, 200), // pale red | |
127 | new Color(Display.getDefault(), 255, 200, 255), // pale magenta | |
128 | new Color(Display.getDefault(), 255, 255, 200), // pale pale yellow | |
129 | new Color(Display.getDefault(), 200, 255, 255), // pale pale blue | |
130 | }; | |
79d60771 | 131 | private final Color fTimeRangeColor = new Color(Display.getCurrent(), 255, 128, 0); |
2fea16bc | 132 | private final Color fLostEventColor = new Color(Display.getCurrent(), 208, 62, 120); |
c392540b | 133 | |
f888477a PT |
134 | // Drag states |
135 | /** | |
136 | * No drag in progress | |
f888477a PT |
137 | */ |
138 | protected final int DRAG_NONE = 0; | |
139 | /** | |
140 | * Drag the selection | |
f888477a PT |
141 | */ |
142 | protected final int DRAG_SELECTION = 1; | |
143 | /** | |
144 | * Drag the time range | |
f888477a PT |
145 | */ |
146 | protected final int DRAG_RANGE = 2; | |
79d60771 PT |
147 | /** |
148 | * Drag the zoom range | |
79d60771 PT |
149 | */ |
150 | protected final int DRAG_ZOOM = 3; | |
f888477a | 151 | |
c392540b FC |
152 | // ------------------------------------------------------------------------ |
153 | // Attributes | |
154 | // ------------------------------------------------------------------------ | |
155 | ||
b544077e BH |
156 | /** |
157 | * The parent TMF view. | |
158 | */ | |
c392540b FC |
159 | protected TmfView fParentView; |
160 | ||
da7bdcbc | 161 | private Composite fComposite; |
f8177ba2 FC |
162 | private Font fFont; |
163 | ||
c392540b | 164 | // Histogram text fields |
3311a6ca PT |
165 | private Label fMaxNbEventsLabel; |
166 | private Label fMinNbEventsLabel; | |
167 | private Label fTimeRangeStartLabel; | |
168 | private Label fTimeRangeEndLabel; | |
c392540b | 169 | |
b544077e | 170 | /** |
95aa81ef | 171 | * Histogram drawing area |
b544077e | 172 | */ |
c392540b | 173 | protected Canvas fCanvas; |
f8177ba2 | 174 | |
e8c79054 BH |
175 | private Composite canvasComposite; |
176 | ||
b544077e | 177 | /** |
95aa81ef | 178 | * The histogram data model. |
b544077e | 179 | */ |
c392540b | 180 | protected final HistogramDataModel fDataModel; |
f8177ba2 | 181 | |
b544077e | 182 | /** |
20ff3b75 | 183 | * The histogram data model scaled to current resolution and screen width. |
b544077e | 184 | */ |
c392540b FC |
185 | protected HistogramScaledData fScaledData; |
186 | ||
f8177ba2 FC |
187 | /** |
188 | * The current event value | |
189 | */ | |
190 | protected long fCurrentEventTime = 0L; | |
c392540b | 191 | |
0fcf3b09 PT |
192 | /** |
193 | * The current selection begin time | |
194 | */ | |
195 | private long fSelectionBegin = 0L; | |
196 | ||
197 | /** | |
198 | * The current selection end time | |
199 | */ | |
200 | private long fSelectionEnd = 0L; | |
201 | ||
f888477a PT |
202 | /** |
203 | * The drag state | |
0271030d | 204 | * |
f888477a PT |
205 | * @see #DRAG_NONE |
206 | * @see #DRAG_SELECTION | |
207 | * @see #DRAG_RANGE | |
79d60771 | 208 | * @see #DRAG_ZOOM |
f888477a PT |
209 | */ |
210 | protected int fDragState = DRAG_NONE; | |
211 | ||
212 | /** | |
213 | * The button that started a mouse drag, or 0 if no drag in progress | |
f888477a PT |
214 | */ |
215 | protected int fDragButton = 0; | |
216 | ||
cc817e65 PT |
217 | /** |
218 | * The bucket display offset | |
219 | */ | |
220 | private int fOffset = 0; | |
221 | ||
2fc582d2 XR |
222 | /** |
223 | * show the traces or not | |
2fc582d2 XR |
224 | */ |
225 | static boolean showTraces = true; | |
226 | ||
e8c79054 BH |
227 | |
228 | private boolean fSendTimeAlignSignals = false; | |
229 | ||
c392540b FC |
230 | // ------------------------------------------------------------------------ |
231 | // Construction | |
232 | // ------------------------------------------------------------------------ | |
233 | ||
b544077e | 234 | /** |
e8c79054 | 235 | * Constructor. |
20ff3b75 | 236 | * |
0271030d MK |
237 | * @param view |
238 | * A reference to the parent TMF view. | |
239 | * @param parent | |
240 | * A parent composite | |
b544077e | 241 | */ |
09e86496 | 242 | public Histogram(final TmfView view, final Composite parent) { |
e8c79054 BH |
243 | this(view, parent, false); |
244 | } | |
c392540b | 245 | |
e8c79054 BH |
246 | /** |
247 | * Full constructor. | |
248 | * | |
249 | * @param view | |
250 | * A reference to the parent TMF view. | |
251 | * @param parent | |
252 | * A parent composite | |
253 | * @param sendTimeAlignSignals | |
254 | * Flag to send time alignment signals or not | |
255 | * @since 1.0 | |
256 | */ | |
257 | public Histogram(final TmfView view, final Composite parent, final boolean sendTimeAlignSignals) { | |
258 | fParentView = view; | |
259 | fSendTimeAlignSignals = sendTimeAlignSignals; | |
da7bdcbc | 260 | fComposite = createWidget(parent); |
c392540b | 261 | fDataModel = new HistogramDataModel(); |
fbd124dd | 262 | fDataModel.addHistogramListener(this); |
c392540b FC |
263 | clear(); |
264 | ||
265 | fCanvas.addControlListener(this); | |
266 | fCanvas.addPaintListener(this); | |
267 | fCanvas.addKeyListener(this); | |
268 | fCanvas.addMouseListener(this); | |
269 | fCanvas.addMouseTrackListener(this); | |
f888477a | 270 | fCanvas.addMouseMoveListener(this); |
f8177ba2 FC |
271 | |
272 | TmfSignalManager.register(this); | |
c392540b FC |
273 | } |
274 | ||
b544077e | 275 | /** |
f8177ba2 | 276 | * Dispose resources and unregisters listeners. |
b544077e | 277 | */ |
c392540b | 278 | public void dispose() { |
f8177ba2 | 279 | TmfSignalManager.deregister(this); |
2fea16bc | 280 | fLostEventColor.dispose(); |
2fc582d2 XR |
281 | for (Color c : fHistoBarColors) { |
282 | c.dispose(); | |
283 | } | |
79d60771 | 284 | fTimeRangeColor.dispose(); |
2fea16bc | 285 | fFont.dispose(); |
fbd124dd | 286 | fDataModel.removeHistogramListener(this); |
c53a7992 | 287 | fDataModel.dispose(); |
c392540b FC |
288 | } |
289 | ||
da7bdcbc | 290 | private Composite createWidget(final Composite parent) { |
c392540b | 291 | |
f8177ba2 | 292 | fFont = adjustFont(parent); |
c392540b FC |
293 | |
294 | final int initalWidth = 10; | |
295 | ||
296 | // -------------------------------------------------------------------- | |
297 | // Define the histogram | |
298 | // -------------------------------------------------------------------- | |
299 | ||
09e86496 | 300 | final GridLayout gridLayout = new GridLayout(); |
c392540b FC |
301 | gridLayout.numColumns = 3; |
302 | gridLayout.marginHeight = 0; | |
303 | gridLayout.marginWidth = 0; | |
304 | gridLayout.marginTop = 0; | |
305 | gridLayout.horizontalSpacing = 0; | |
306 | gridLayout.verticalSpacing = 0; | |
307 | gridLayout.marginLeft = 0; | |
308 | gridLayout.marginRight = 0; | |
09e86496 | 309 | final Composite composite = new Composite(parent, SWT.FILL); |
c392540b FC |
310 | composite.setLayout(gridLayout); |
311 | ||
312 | // Use all the horizontal space | |
313 | GridData gridData = new GridData(); | |
314 | gridData.horizontalAlignment = SWT.FILL; | |
315 | gridData.verticalAlignment = SWT.FILL; | |
316 | gridData.grabExcessHorizontalSpace = true; | |
3a790c10 | 317 | gridData.grabExcessVerticalSpace = true; |
c392540b FC |
318 | composite.setLayoutData(gridData); |
319 | ||
320 | // Y-axis max event | |
321 | gridData = new GridData(); | |
322 | gridData.horizontalAlignment = SWT.RIGHT; | |
323 | gridData.verticalAlignment = SWT.TOP; | |
3311a6ca PT |
324 | fMaxNbEventsLabel = new Label(composite, SWT.RIGHT); |
325 | fMaxNbEventsLabel.setFont(fFont); | |
326 | fMaxNbEventsLabel.setText("0"); //$NON-NLS-1$ | |
327 | fMaxNbEventsLabel.setLayoutData(gridData); | |
c392540b FC |
328 | |
329 | // Histogram itself | |
e8c79054 | 330 | canvasComposite = new Composite(composite, SWT.BORDER); |
c392540b FC |
331 | gridData = new GridData(); |
332 | gridData.horizontalSpan = 2; | |
333 | gridData.verticalSpan = 2; | |
334 | gridData.horizontalAlignment = SWT.FILL; | |
335 | gridData.verticalAlignment = SWT.FILL; | |
3a790c10 PT |
336 | gridData.heightHint = 0; |
337 | gridData.widthHint = 0; | |
c392540b | 338 | gridData.grabExcessHorizontalSpace = true; |
3a790c10 | 339 | gridData.grabExcessVerticalSpace = true; |
e60df94a PT |
340 | canvasComposite.setLayoutData(gridData); |
341 | canvasComposite.setLayout(new FillLayout()); | |
342 | fCanvas = new Canvas(canvasComposite, SWT.DOUBLE_BUFFERED); | |
2fea16bc MAL |
343 | fCanvas.addDisposeListener(new DisposeListener() { |
344 | @Override | |
345 | public void widgetDisposed(DisposeEvent e) { | |
346 | Object image = fCanvas.getData(IMAGE_KEY); | |
347 | if (image instanceof Image) { | |
348 | ((Image) image).dispose(); | |
349 | } | |
350 | } | |
351 | }); | |
c392540b FC |
352 | |
353 | // Y-axis min event (always 0...) | |
354 | gridData = new GridData(); | |
355 | gridData.horizontalAlignment = SWT.RIGHT; | |
356 | gridData.verticalAlignment = SWT.BOTTOM; | |
3311a6ca PT |
357 | fMinNbEventsLabel = new Label(composite, SWT.RIGHT); |
358 | fMinNbEventsLabel.setFont(fFont); | |
359 | fMinNbEventsLabel.setText("0"); //$NON-NLS-1$ | |
360 | fMinNbEventsLabel.setLayoutData(gridData); | |
c392540b FC |
361 | |
362 | // Dummy cell | |
363 | gridData = new GridData(initalWidth, SWT.DEFAULT); | |
364 | gridData.horizontalAlignment = SWT.RIGHT; | |
365 | gridData.verticalAlignment = SWT.BOTTOM; | |
65cdf787 PT |
366 | final Label dummyLabel = new Label(composite, SWT.NONE); |
367 | dummyLabel.setLayoutData(gridData); | |
c392540b FC |
368 | |
369 | // Window range start time | |
370 | gridData = new GridData(); | |
371 | gridData.horizontalAlignment = SWT.LEFT; | |
372 | gridData.verticalAlignment = SWT.BOTTOM; | |
3311a6ca PT |
373 | fTimeRangeStartLabel = new Label(composite, SWT.NONE); |
374 | fTimeRangeStartLabel.setFont(fFont); | |
375 | fTimeRangeStartLabel.setLayoutData(gridData); | |
c392540b FC |
376 | |
377 | // Window range end time | |
378 | gridData = new GridData(); | |
379 | gridData.horizontalAlignment = SWT.RIGHT; | |
380 | gridData.verticalAlignment = SWT.BOTTOM; | |
3311a6ca PT |
381 | fTimeRangeEndLabel = new Label(composite, SWT.NONE); |
382 | fTimeRangeEndLabel.setFont(fFont); | |
383 | fTimeRangeEndLabel.setLayoutData(gridData); | |
65cdf787 | 384 | |
da7bdcbc | 385 | return composite; |
c392540b FC |
386 | } |
387 | ||
abbdd66a | 388 | private static Font adjustFont(final Composite composite) { |
c392540b | 389 | // Reduce font size for a more pleasing rendering |
09e86496 FC |
390 | final int fontSizeAdjustment = -2; |
391 | final Font font = composite.getFont(); | |
392 | final FontData fontData = font.getFontData()[0]; | |
c392540b FC |
393 | return new Font(font.getDevice(), fontData.getName(), fontData.getHeight() + fontSizeAdjustment, fontData.getStyle()); |
394 | } | |
395 | ||
396 | // ------------------------------------------------------------------------ | |
397 | // Accessors | |
398 | // ------------------------------------------------------------------------ | |
399 | ||
b544077e | 400 | /** |
f8177ba2 | 401 | * Returns the start time (equal first bucket time) |
0271030d | 402 | * |
b544077e BH |
403 | * @return the start time. |
404 | */ | |
c392540b | 405 | public long getStartTime() { |
fbd124dd | 406 | return fDataModel.getFirstBucketTime(); |
c392540b FC |
407 | } |
408 | ||
b544077e BH |
409 | /** |
410 | * Returns the end time. | |
0271030d | 411 | * |
b544077e BH |
412 | * @return the end time. |
413 | */ | |
c392540b FC |
414 | public long getEndTime() { |
415 | return fDataModel.getEndTime(); | |
416 | } | |
417 | ||
b544077e BH |
418 | /** |
419 | * Returns the time limit (end of last bucket) | |
0271030d | 420 | * |
b544077e BH |
421 | * @return the time limit. |
422 | */ | |
c392540b FC |
423 | public long getTimeLimit() { |
424 | return fDataModel.getTimeLimit(); | |
425 | } | |
09e86496 | 426 | |
20ff3b75 AM |
427 | /** |
428 | * Returns a data model reference. | |
0271030d | 429 | * |
b544077e BH |
430 | * @return data model. |
431 | */ | |
fbd124dd BH |
432 | public HistogramDataModel getDataModel() { |
433 | return fDataModel; | |
434 | } | |
c392540b | 435 | |
95aa81ef | 436 | /** |
3311a6ca | 437 | * Set the max number events to be displayed |
95aa81ef | 438 | * |
3311a6ca PT |
439 | * @param maxNbEvents |
440 | * the maximum number of events | |
95aa81ef | 441 | */ |
3311a6ca PT |
442 | void setMaxNbEvents(long maxNbEvents) { |
443 | fMaxNbEventsLabel.setText(Long.toString(maxNbEvents)); | |
444 | fMaxNbEventsLabel.getParent().layout(); | |
445 | fCanvas.redraw(); | |
95aa81ef JCK |
446 | } |
447 | ||
2fc582d2 | 448 | /** |
0271030d MK |
449 | * Return <code>true</code> if the traces must be displayed in the |
450 | * histogram, <code>false</code> otherwise. | |
451 | * | |
2fc582d2 | 452 | * @return whether the traces should be displayed |
2fc582d2 XR |
453 | */ |
454 | public boolean showTraces() { | |
455 | return showTraces && fDataModel.getNbTraces() < getMaxNbTraces(); | |
456 | } | |
457 | ||
458 | /** | |
0271030d MK |
459 | * Returns the maximum number of traces the histogram can display with |
460 | * separate colors. If there is more traces, histogram will use only one | |
461 | * color to display them. | |
462 | * | |
2fc582d2 | 463 | * @return the maximum number of traces the histogram can display. |
2fc582d2 XR |
464 | */ |
465 | public int getMaxNbTraces() { | |
466 | return fHistoBarColors.length; | |
467 | } | |
468 | ||
469 | /** | |
470 | * Returns the color used to display the trace at the given index. | |
0271030d MK |
471 | * |
472 | * @param traceIndex | |
473 | * a trace index | |
2fc582d2 | 474 | * @return a {@link Color} |
2fc582d2 XR |
475 | */ |
476 | public Color getTraceColor(int traceIndex) { | |
477 | return fHistoBarColors[traceIndex % fHistoBarColors.length]; | |
478 | } | |
479 | ||
c392540b FC |
480 | // ------------------------------------------------------------------------ |
481 | // Operations | |
482 | // ------------------------------------------------------------------------ | |
b544077e | 483 | /** |
20ff3b75 | 484 | * Updates the time range. |
0271030d MK |
485 | * |
486 | * @param startTime | |
487 | * A start time | |
488 | * @param endTime | |
489 | * A end time. | |
b544077e | 490 | */ |
f888477a PT |
491 | public void updateTimeRange(long startTime, long endTime) { |
492 | if (fDragState == DRAG_NONE) { | |
493 | ((HistogramView) fParentView).updateTimeRange(startTime, endTime); | |
494 | } | |
495 | } | |
c392540b FC |
496 | |
497 | /** | |
498 | * Clear the histogram and reset the data | |
499 | */ | |
500 | public void clear() { | |
501 | fDataModel.clear(); | |
06fcc8fe PT |
502 | if (fDragState == DRAG_SELECTION) { |
503 | updateSelectionTime(); | |
504 | } | |
80c930fa PT |
505 | fDragState = DRAG_NONE; |
506 | fDragButton = 0; | |
dcb3cda5 BH |
507 | synchronized (fDataModel) { |
508 | fScaledData = null; | |
509 | } | |
c392540b FC |
510 | } |
511 | ||
0fcf3b09 PT |
512 | /** |
513 | * Sets the current selection time range and refresh the display | |
514 | * | |
0271030d MK |
515 | * @param beginTime |
516 | * The begin time of the current selection | |
517 | * @param endTime | |
518 | * The end time of the current selection | |
0fcf3b09 PT |
519 | */ |
520 | public void setSelection(final long beginTime, final long endTime) { | |
521 | fSelectionBegin = (beginTime > 0) ? beginTime : 0; | |
522 | fSelectionEnd = (endTime > 0) ? endTime : 0; | |
523 | fDataModel.setSelectionNotifyListeners(beginTime, endTime); | |
c392540b FC |
524 | } |
525 | ||
526 | /** | |
527 | * Computes the timestamp of the bucket at [offset] | |
20ff3b75 | 528 | * |
0271030d MK |
529 | * @param offset |
530 | * offset from the left on the histogram | |
c392540b FC |
531 | * @return the start timestamp of the corresponding bucket |
532 | */ | |
09e86496 | 533 | public synchronized long getTimestamp(final int offset) { |
c392540b FC |
534 | assert offset > 0 && offset < fScaledData.fWidth; |
535 | try { | |
224c38ef | 536 | return fScaledData.fFirstBucketTime + (long) (fScaledData.fBucketDuration * offset); |
09e86496 | 537 | } catch (final Exception e) { |
c392540b FC |
538 | return 0; // TODO: Fix that racing condition (NPE) |
539 | } | |
540 | } | |
541 | ||
542 | /** | |
543 | * Computes the offset of the timestamp in the histogram | |
20ff3b75 | 544 | * |
0271030d MK |
545 | * @param timestamp |
546 | * the timestamp | |
c392540b FC |
547 | * @return the offset of the corresponding bucket (-1 if invalid) |
548 | */ | |
09e86496 | 549 | public synchronized int getOffset(final long timestamp) { |
20ff3b75 | 550 | if (timestamp < fDataModel.getFirstBucketTime() || timestamp > fDataModel.getEndTime()) { |
c392540b | 551 | return -1; |
20ff3b75 | 552 | } |
fbd124dd | 553 | return (int) ((timestamp - fDataModel.getFirstBucketTime()) / fScaledData.fBucketDuration); |
c392540b FC |
554 | } |
555 | ||
cc817e65 PT |
556 | /** |
557 | * Set the bucket display offset | |
558 | * | |
559 | * @param offset | |
560 | * the bucket display offset | |
cc817e65 PT |
561 | */ |
562 | protected void setOffset(final int offset) { | |
563 | fOffset = offset; | |
564 | } | |
565 | ||
c392540b FC |
566 | /** |
567 | * Move the currently selected bar cursor to a non-empty bucket. | |
20ff3b75 | 568 | * |
0271030d MK |
569 | * @param keyCode |
570 | * the SWT key code | |
c392540b | 571 | */ |
09e86496 | 572 | protected void moveCursor(final int keyCode) { |
c392540b | 573 | |
c392540b FC |
574 | int index; |
575 | switch (keyCode) { | |
576 | ||
95aa81ef JCK |
577 | case SWT.HOME: |
578 | index = 0; | |
2fc582d2 | 579 | while (index < fScaledData.fLastBucket && fScaledData.fData[index].isEmpty()) { |
95aa81ef JCK |
580 | index++; |
581 | } | |
582 | if (index < fScaledData.fLastBucket) { | |
583 | fScaledData.fSelectionBeginBucket = index; | |
584 | } | |
585 | break; | |
c392540b | 586 | |
95aa81ef JCK |
587 | case SWT.ARROW_RIGHT: |
588 | index = Math.max(0, fScaledData.fSelectionBeginBucket + 1); | |
2fc582d2 | 589 | while (index < fScaledData.fWidth && fScaledData.fData[index].isEmpty()) { |
95aa81ef JCK |
590 | index++; |
591 | } | |
592 | if (index < fScaledData.fLastBucket) { | |
593 | fScaledData.fSelectionBeginBucket = index; | |
594 | } | |
595 | break; | |
c392540b | 596 | |
95aa81ef JCK |
597 | case SWT.END: |
598 | index = fScaledData.fLastBucket; | |
2fc582d2 | 599 | while (index >= 0 && fScaledData.fData[index].isEmpty()) { |
95aa81ef JCK |
600 | index--; |
601 | } | |
602 | if (index >= 0) { | |
603 | fScaledData.fSelectionBeginBucket = index; | |
604 | } | |
605 | break; | |
c392540b | 606 | |
95aa81ef JCK |
607 | case SWT.ARROW_LEFT: |
608 | index = Math.min(fScaledData.fLastBucket - 1, fScaledData.fSelectionBeginBucket - 1); | |
2fc582d2 | 609 | while (index >= 0 && fScaledData.fData[index].isEmpty()) { |
95aa81ef JCK |
610 | index--; |
611 | } | |
612 | if (index >= 0) { | |
613 | fScaledData.fSelectionBeginBucket = index; | |
614 | } | |
615 | break; | |
c392540b | 616 | |
95aa81ef JCK |
617 | default: |
618 | return; | |
c392540b FC |
619 | } |
620 | ||
0fcf3b09 PT |
621 | fScaledData.fSelectionEndBucket = fScaledData.fSelectionBeginBucket; |
622 | fSelectionBegin = getTimestamp(fScaledData.fSelectionBeginBucket); | |
623 | fSelectionEnd = fSelectionBegin; | |
624 | updateSelectionTime(); | |
c392540b FC |
625 | } |
626 | ||
627 | /** | |
628 | * Refresh the histogram display | |
629 | */ | |
fbd124dd BH |
630 | @Override |
631 | public void modelUpdated() { | |
20ff3b75 | 632 | if (!fCanvas.isDisposed() && fCanvas.getDisplay() != null) { |
c392540b FC |
633 | fCanvas.getDisplay().asyncExec(new Runnable() { |
634 | @Override | |
635 | public void run() { | |
636 | if (!fCanvas.isDisposed()) { | |
637 | // Retrieve and normalize the data | |
09e86496 FC |
638 | final int canvasWidth = fCanvas.getBounds().width; |
639 | final int canvasHeight = fCanvas.getBounds().height; | |
20ff3b75 | 640 | if (canvasWidth <= 0 || canvasHeight <= 0) { |
40890fec | 641 | return; |
20ff3b75 | 642 | } |
0fcf3b09 | 643 | fDataModel.setSelection(fSelectionBegin, fSelectionEnd); |
f8177ba2 | 644 | fScaledData = fDataModel.scaleTo(canvasWidth, canvasHeight, 1); |
95aa81ef | 645 | synchronized (fDataModel) { |
0316808c FC |
646 | if (fScaledData != null) { |
647 | fCanvas.redraw(); | |
d418423b PT |
648 | // Display histogram and update X-,Y-axis labels |
649 | updateRangeTextControls(); | |
95aa81ef | 650 | long maxNbEvents = HistogramScaledData.hideLostEvents ? fScaledData.fMaxValue : fScaledData.fMaxCombinedValue; |
e8c79054 | 651 | String old = fMaxNbEventsLabel.getText(); |
3311a6ca | 652 | fMaxNbEventsLabel.setText(Long.toString(maxNbEvents)); |
0316808c | 653 | // The Y-axis area might need to be re-sized |
3311a6ca PT |
654 | GridData gd = (GridData) fMaxNbEventsLabel.getLayoutData(); |
655 | gd.widthHint = Math.max(gd.widthHint, fMaxNbEventsLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT).x); | |
656 | fMaxNbEventsLabel.getParent().layout(); | |
e8c79054 BH |
657 | if (old.length() < fMaxNbEventsLabel.getText().length()) { |
658 | if ((fSendTimeAlignSignals) && (fParentView instanceof ITmfTimeAligned)) { | |
659 | TmfSignalManager.dispatchSignal(new TmfTimeViewAlignmentSignal(this, ((ITmfTimeAligned) fParentView).getTimeViewAlignmentInfo(), true)); | |
660 | } | |
661 | } | |
0316808c | 662 | } |
09e86496 | 663 | } |
c392540b FC |
664 | } |
665 | } | |
666 | }); | |
20ff3b75 | 667 | } |
c392540b FC |
668 | } |
669 | ||
65cdf787 PT |
670 | /** |
671 | * Add a mouse wheel listener to the histogram | |
0271030d MK |
672 | * |
673 | * @param listener | |
674 | * the mouse wheel listener | |
65cdf787 PT |
675 | */ |
676 | public void addMouseWheelListener(MouseWheelListener listener) { | |
677 | fCanvas.addMouseWheelListener(listener); | |
678 | } | |
679 | ||
680 | /** | |
681 | * Remove a mouse wheel listener from the histogram | |
0271030d MK |
682 | * |
683 | * @param listener | |
684 | * the mouse wheel listener | |
65cdf787 PT |
685 | */ |
686 | public void removeMouseWheelListener(MouseWheelListener listener) { | |
687 | fCanvas.removeMouseWheelListener(listener); | |
688 | } | |
689 | ||
ba1f5c20 PT |
690 | /** |
691 | * Add a key listener to the histogram | |
0271030d MK |
692 | * |
693 | * @param listener | |
694 | * the key listener | |
ba1f5c20 PT |
695 | */ |
696 | public void addKeyListener(KeyListener listener) { | |
697 | fCanvas.addKeyListener(listener); | |
698 | } | |
699 | ||
700 | /** | |
701 | * Remove a key listener from the histogram | |
0271030d MK |
702 | * |
703 | * @param listener | |
704 | * the key listener | |
ba1f5c20 PT |
705 | */ |
706 | public void removeKeyListener(KeyListener listener) { | |
707 | fCanvas.removeKeyListener(listener); | |
708 | } | |
709 | ||
c392540b FC |
710 | // ------------------------------------------------------------------------ |
711 | // Helper functions | |
712 | // ------------------------------------------------------------------------ | |
713 | ||
0fcf3b09 | 714 | private void updateSelectionTime() { |
720d67cb PT |
715 | if (fSelectionBegin > fSelectionEnd) { |
716 | long end = fSelectionBegin; | |
717 | fSelectionBegin = fSelectionEnd; | |
718 | fSelectionEnd = end; | |
719 | } | |
0fcf3b09 | 720 | ((HistogramView) fParentView).updateSelectionTime(fSelectionBegin, fSelectionEnd); |
c392540b FC |
721 | } |
722 | ||
d418423b PT |
723 | /** |
724 | * Update the range text controls | |
725 | */ | |
726 | private void updateRangeTextControls() { | |
b6d74e0b | 727 | if (fDataModel.getNbEvents() != 0) { |
3311a6ca PT |
728 | fTimeRangeStartLabel.setText(TmfTimestampFormat.getDefaulTimeFormat().format(fDataModel.getStartTime())); |
729 | fTimeRangeEndLabel.setText(TmfTimestampFormat.getDefaulTimeFormat().format(fDataModel.getEndTime())); | |
d418423b | 730 | } else { |
3311a6ca PT |
731 | fTimeRangeStartLabel.setText(""); //$NON-NLS-1$ |
732 | fTimeRangeEndLabel.setText(""); //$NON-NLS-1$ | |
d418423b PT |
733 | } |
734 | } | |
735 | ||
c392540b FC |
736 | // ------------------------------------------------------------------------ |
737 | // PaintListener | |
738 | // ------------------------------------------------------------------------ | |
b544077e BH |
739 | /** |
740 | * Image key string for the canvas. | |
741 | */ | |
c392540b FC |
742 | protected final String IMAGE_KEY = "double-buffer-image"; //$NON-NLS-1$ |
743 | ||
744 | @Override | |
09e86496 | 745 | public void paintControl(final PaintEvent event) { |
c392540b FC |
746 | |
747 | // Get the geometry | |
09e86496 FC |
748 | final int canvasWidth = fCanvas.getBounds().width; |
749 | final int canvasHeight = fCanvas.getBounds().height; | |
c392540b FC |
750 | |
751 | // Make sure we have something to draw upon | |
20ff3b75 | 752 | if (canvasWidth <= 0 || canvasHeight <= 0) { |
c392540b | 753 | return; |
20ff3b75 | 754 | } |
c392540b FC |
755 | |
756 | // Retrieve image; re-create only if necessary | |
757 | Image image = (Image) fCanvas.getData(IMAGE_KEY); | |
758 | if (image == null || image.getBounds().width != canvasWidth || image.getBounds().height != canvasHeight) { | |
2fea16bc MAL |
759 | if (image != null) { |
760 | image.dispose(); | |
761 | } | |
c392540b FC |
762 | image = new Image(event.display, canvasWidth, canvasHeight); |
763 | fCanvas.setData(IMAGE_KEY, image); | |
764 | } | |
765 | ||
766 | // Draw the histogram on its canvas | |
09e86496 | 767 | final GC imageGC = new GC(image); |
c392540b FC |
768 | formatImage(imageGC, image); |
769 | event.gc.drawImage(image, 0, 0); | |
770 | imageGC.dispose(); | |
771 | } | |
772 | ||
09e86496 | 773 | private void formatImage(final GC imageGC, final Image image) { |
c392540b | 774 | |
20ff3b75 | 775 | if (fScaledData == null) { |
c392540b | 776 | return; |
20ff3b75 | 777 | } |
c392540b | 778 | |
09e86496 | 779 | final HistogramScaledData scaledData = new HistogramScaledData(fScaledData); |
c392540b FC |
780 | |
781 | try { | |
09e86496 | 782 | final int height = image.getBounds().height; |
c392540b FC |
783 | |
784 | // Clear the drawing area | |
785 | imageGC.setBackground(fBackgroundColor); | |
786 | imageGC.fillRectangle(0, 0, image.getBounds().width + 1, image.getBounds().height + 1); | |
787 | ||
788 | // Draw the histogram bars | |
224c38ef | 789 | final int limit = scaledData.fWidth; |
95aa81ef | 790 | double factor = HistogramScaledData.hideLostEvents ? scaledData.fScalingFactor : scaledData.fScalingFactorCombined; |
2fc582d2 | 791 | final boolean showTracesColors = showTraces(); |
e6953950 | 792 | for (int i = 0; i < limit; i++) { |
2fc582d2 XR |
793 | HistogramBucket hb = scaledData.fData[i]; |
794 | int totalNbEvents = hb.getNbEvents(); | |
795 | int value = (int) Math.ceil(totalNbEvents * factor); | |
cc817e65 | 796 | int x = i + fOffset; |
95aa81ef | 797 | |
0271030d MK |
798 | /* |
799 | * in Linux, the last pixel in a line is not drawn, so draw lost | |
800 | * events first, one pixel too far | |
801 | */ | |
95aa81ef JCK |
802 | if (!HistogramScaledData.hideLostEvents) { |
803 | imageGC.setForeground(fLostEventColor); | |
804 | final int lostEventValue = (int) Math.ceil(scaledData.fLostEventsData[i] * factor); | |
805 | if (lostEventValue != 0) { | |
0271030d MK |
806 | /* |
807 | * drawing a line is inclusive, so we should remove 1 | |
808 | * from y2 but we don't because Linux | |
809 | */ | |
b10913b2 | 810 | imageGC.drawLine(x, height - value - lostEventValue, x, height - value); |
95aa81ef JCK |
811 | } |
812 | } | |
b10913b2 PT |
813 | |
814 | // then draw normal events second, to overwrite that extra pixel | |
2fc582d2 XR |
815 | if (!hb.isEmpty()) { |
816 | if (showTracesColors) { | |
817 | for (int traceIndex = 0; traceIndex < hb.getNbTraces(); traceIndex++) { | |
818 | int nbEventsForTrace = hb.getNbEvent(traceIndex); | |
819 | if (nbEventsForTrace > 0) { | |
820 | Color c = fHistoBarColors[traceIndex % fHistoBarColors.length]; | |
821 | imageGC.setForeground(c); | |
822 | imageGC.drawLine(x, height - value, x, height); | |
823 | totalNbEvents -= nbEventsForTrace; | |
824 | value = (int) Math.ceil(totalNbEvents * scaledData.fScalingFactor); | |
825 | } | |
826 | } | |
827 | } else { | |
828 | Color c = fHistoBarColors[0]; | |
829 | imageGC.setForeground(c); | |
830 | imageGC.drawLine(x, height - value, x, height); | |
831 | } | |
832 | } | |
c392540b FC |
833 | } |
834 | ||
0fcf3b09 PT |
835 | // Draw the selection bars |
836 | int alpha = imageGC.getAlpha(); | |
837 | imageGC.setAlpha(100); | |
838 | imageGC.setForeground(fSelectionForegroundColor); | |
839 | imageGC.setBackground(fSelectionBackgroundColor); | |
cc817e65 | 840 | final int beginBucket = scaledData.fSelectionBeginBucket + fOffset; |
0fcf3b09 PT |
841 | if (beginBucket >= 0 && beginBucket < limit) { |
842 | imageGC.drawLine(beginBucket, 0, beginBucket, height); | |
843 | } | |
cc817e65 | 844 | final int endBucket = scaledData.fSelectionEndBucket + fOffset; |
0fcf3b09 PT |
845 | if (endBucket >= 0 && endBucket < limit && endBucket != beginBucket) { |
846 | imageGC.drawLine(endBucket, 0, endBucket, height); | |
847 | } | |
f888477a PT |
848 | if (Math.abs(endBucket - beginBucket) > 1) { |
849 | if (endBucket > beginBucket) { | |
850 | imageGC.fillRectangle(beginBucket + 1, 0, endBucket - beginBucket - 1, height); | |
851 | } else { | |
852 | imageGC.fillRectangle(endBucket + 1, 0, beginBucket - endBucket - 1, height); | |
853 | } | |
0fcf3b09 PT |
854 | } |
855 | imageGC.setAlpha(alpha); | |
09e86496 | 856 | } catch (final Exception e) { |
c392540b FC |
857 | // Do nothing |
858 | } | |
859 | } | |
860 | ||
79d60771 PT |
861 | /** |
862 | * Draw a time range window | |
863 | * | |
864 | * @param imageGC | |
865 | * the GC | |
866 | * @param rangeStartTime | |
867 | * the range start time | |
868 | * @param rangeDuration | |
869 | * the range duration | |
79d60771 PT |
870 | */ |
871 | protected void drawTimeRangeWindow(GC imageGC, long rangeStartTime, long rangeDuration) { | |
872 | ||
647640df BH |
873 | if (fScaledData == null) { |
874 | return; | |
875 | } | |
876 | ||
79d60771 | 877 | // Map times to histogram coordinates |
224c38ef | 878 | double bucketSpan = fScaledData.fBucketDuration; |
79d60771 | 879 | long startTime = Math.min(rangeStartTime, rangeStartTime + rangeDuration); |
224c38ef | 880 | double rangeWidth = (Math.abs(rangeDuration) / bucketSpan); |
79d60771 PT |
881 | |
882 | int left = (int) ((startTime - fDataModel.getFirstBucketTime()) / bucketSpan); | |
224c38ef | 883 | int right = (int) (left + rangeWidth); |
79d60771 PT |
884 | int center = (left + right) / 2; |
885 | int height = fCanvas.getSize().y; | |
224c38ef | 886 | int arc = Math.min(15, (int) rangeWidth); |
79d60771 PT |
887 | |
888 | // Draw the selection window | |
889 | imageGC.setForeground(fTimeRangeColor); | |
890 | imageGC.setLineWidth(1); | |
891 | imageGC.setLineStyle(SWT.LINE_SOLID); | |
224c38ef | 892 | imageGC.drawRoundRectangle(left, 0, (int) rangeWidth, height - 1, arc, arc); |
79d60771 PT |
893 | |
894 | // Fill the selection window | |
895 | imageGC.setBackground(fTimeRangeColor); | |
896 | imageGC.setAlpha(35); | |
224c38ef | 897 | imageGC.fillRoundRectangle(left + 1, 1, (int) rangeWidth - 1, height - 2, arc, arc); |
79d60771 PT |
898 | imageGC.setAlpha(255); |
899 | ||
900 | // Draw the cross hair | |
901 | imageGC.setForeground(fTimeRangeColor); | |
902 | imageGC.setLineWidth(1); | |
903 | imageGC.setLineStyle(SWT.LINE_SOLID); | |
904 | ||
224c38ef | 905 | int chHalfWidth = ((rangeWidth < 60) ? (int) ((rangeWidth * 2) / 3) : 40) / 2; |
79d60771 PT |
906 | imageGC.drawLine(center - chHalfWidth, height / 2, center + chHalfWidth, height / 2); |
907 | imageGC.drawLine(center, (height / 2) - chHalfWidth, center, (height / 2) + chHalfWidth); | |
908 | } | |
909 | ||
e8c79054 BH |
910 | /** |
911 | * Get the offset of the point area, relative to the histogram canvas | |
912 | * We consider the point area to be from where the first point could | |
913 | * be drawn to where the last point could be drawn. | |
914 | * | |
915 | * @return the offset in pixels | |
916 | * | |
917 | * @since 1.0 | |
918 | */ | |
919 | public int getPointAreaOffset() { | |
920 | Point absCanvas = fCanvas.toDisplay(0, 0); | |
921 | Point viewPoint = fComposite.getParent().toDisplay(0, 0); | |
922 | return absCanvas.x - viewPoint.x; | |
923 | } | |
924 | ||
925 | /** | |
926 | * Get the width of the point area. We consider the point area to be from | |
927 | * where the first point could be drawn to where the last point could be | |
928 | * drawn. The point area differs from the plot area because there might be a | |
929 | * gap between where the plot area start and where the fist point is drawn. | |
930 | * This also matches the width that the use can select. | |
931 | * | |
932 | * @return the width in pixels | |
933 | * | |
934 | * @since 1.0 | |
935 | */ | |
936 | public int getPointAreaWidth() { | |
937 | if (!fCanvas.isDisposed()) { | |
938 | // Retrieve and normalize the data | |
939 | return fComposite.getBounds().width; | |
940 | } | |
941 | return 0; | |
942 | } | |
943 | ||
c392540b FC |
944 | // ------------------------------------------------------------------------ |
945 | // KeyListener | |
946 | // ------------------------------------------------------------------------ | |
947 | ||
948 | @Override | |
09e86496 | 949 | public void keyPressed(final KeyEvent event) { |
c392540b FC |
950 | moveCursor(event.keyCode); |
951 | } | |
952 | ||
953 | @Override | |
09e86496 | 954 | public void keyReleased(final KeyEvent event) { |
c392540b FC |
955 | } |
956 | ||
957 | // ------------------------------------------------------------------------ | |
958 | // MouseListener | |
959 | // ------------------------------------------------------------------------ | |
960 | ||
961 | @Override | |
09e86496 | 962 | public void mouseDoubleClick(final MouseEvent event) { |
c392540b FC |
963 | } |
964 | ||
965 | @Override | |
09e86496 | 966 | public void mouseDown(final MouseEvent event) { |
31d6440d | 967 | if (fScaledData != null && event.button == 1 && fDragState == DRAG_NONE && fDataModel.getStartTime() < fDataModel.getEndTime()) { |
f888477a PT |
968 | fDragState = DRAG_SELECTION; |
969 | fDragButton = event.button; | |
970 | if ((event.stateMask & SWT.MODIFIER_MASK) == SWT.SHIFT) { | |
971 | if (Math.abs(event.x - fScaledData.fSelectionBeginBucket) < Math.abs(event.x - fScaledData.fSelectionEndBucket)) { | |
972 | fScaledData.fSelectionBeginBucket = fScaledData.fSelectionEndBucket; | |
973 | fSelectionBegin = fSelectionEnd; | |
0fcf3b09 | 974 | } |
224c38ef | 975 | fSelectionEnd = getTimestamp(event.x); |
f888477a PT |
976 | fScaledData.fSelectionEndBucket = (int) ((fSelectionEnd - fScaledData.fFirstBucketTime) / fScaledData.fBucketDuration); |
977 | } else { | |
978 | fSelectionBegin = Math.min(getTimestamp(event.x), getEndTime()); | |
979 | fScaledData.fSelectionBeginBucket = (int) ((fSelectionBegin - fScaledData.fFirstBucketTime) / fScaledData.fBucketDuration); | |
980 | fSelectionEnd = fSelectionBegin; | |
981 | fScaledData.fSelectionEndBucket = fScaledData.fSelectionBeginBucket; | |
0fcf3b09 | 982 | } |
f888477a | 983 | fCanvas.redraw(); |
c392540b FC |
984 | } |
985 | } | |
986 | ||
987 | @Override | |
09e86496 | 988 | public void mouseUp(final MouseEvent event) { |
f888477a PT |
989 | if (fDragState == DRAG_SELECTION && event.button == fDragButton) { |
990 | fDragState = DRAG_NONE; | |
991 | fDragButton = 0; | |
992 | updateSelectionTime(); | |
993 | } | |
994 | } | |
995 | ||
996 | // ------------------------------------------------------------------------ | |
997 | // MouseMoveListener | |
998 | // ------------------------------------------------------------------------ | |
999 | ||
f888477a PT |
1000 | @Override |
1001 | public void mouseMove(MouseEvent event) { | |
31d6440d | 1002 | if (fDragState == DRAG_SELECTION && fDataModel.getStartTime() < fDataModel.getEndTime()) { |
f888477a PT |
1003 | fSelectionEnd = Math.max(getStartTime(), Math.min(getEndTime(), getTimestamp(event.x))); |
1004 | fScaledData.fSelectionEndBucket = (int) ((fSelectionEnd - fScaledData.fFirstBucketTime) / fScaledData.fBucketDuration); | |
1005 | fCanvas.redraw(); | |
1006 | } | |
c392540b FC |
1007 | } |
1008 | ||
1009 | // ------------------------------------------------------------------------ | |
1010 | // MouseTrackListener | |
1011 | // ------------------------------------------------------------------------ | |
1012 | ||
1013 | @Override | |
09e86496 | 1014 | public void mouseEnter(final MouseEvent event) { |
c392540b FC |
1015 | } |
1016 | ||
1017 | @Override | |
09e86496 | 1018 | public void mouseExit(final MouseEvent event) { |
c392540b FC |
1019 | } |
1020 | ||
1021 | @Override | |
09e86496 | 1022 | public void mouseHover(final MouseEvent event) { |
b6d74e0b | 1023 | if (fDataModel.getNbEvents() != 0 && fScaledData != null) { |
224c38ef MK |
1024 | final String tooltip = formatToolTipLabel(event.x - fOffset); |
1025 | fCanvas.setToolTipText(tooltip); | |
1026 | return; | |
c392540b | 1027 | } |
cc817e65 | 1028 | fCanvas.setToolTipText(null); |
c392540b FC |
1029 | } |
1030 | ||
09e86496 | 1031 | private String formatToolTipLabel(final int index) { |
466857f6 | 1032 | long startTime = fScaledData.getBucketStartTime(index); |
0271030d MK |
1033 | /* |
1034 | * negative values are possible if time values came into the model in | |
1035 | * decreasing order | |
1036 | */ | |
20ff3b75 | 1037 | if (startTime < 0) { |
fbd124dd | 1038 | startTime = 0; |
20ff3b75 | 1039 | } |
466857f6 | 1040 | final long endTime = fScaledData.getBucketEndTime(index); |
2fc582d2 | 1041 | final int nbEvents = (index >= 0) ? fScaledData.fData[index].getNbEvents() : 0; |
95aa81ef | 1042 | final String newLine = System.getProperty("line.separator"); //$NON-NLS-1$ |
09e86496 | 1043 | final StringBuffer buffer = new StringBuffer(); |
720d67cb PT |
1044 | int selectionBeginBucket = Math.min(fScaledData.fSelectionBeginBucket, fScaledData.fSelectionEndBucket); |
1045 | int selectionEndBucket = Math.max(fScaledData.fSelectionBeginBucket, fScaledData.fSelectionEndBucket); | |
1046 | if (selectionBeginBucket <= index && index <= selectionEndBucket && fSelectionBegin != fSelectionEnd) { | |
1047 | TmfTimestampDelta delta = new TmfTimestampDelta(Math.abs(fSelectionEnd - fSelectionBegin), ITmfTimestamp.NANOSECOND_SCALE); | |
1048 | buffer.append(NLS.bind(Messages.Histogram_selectionSpanToolTip, delta.toString())); | |
1049 | buffer.append(newLine); | |
1050 | } | |
1051 | buffer.append(NLS.bind(Messages.Histogram_bucketRangeToolTip, | |
1052 | new TmfTimestamp(startTime, ITmfTimestamp.NANOSECOND_SCALE).toString(), | |
1053 | new TmfTimestamp(endTime, ITmfTimestamp.NANOSECOND_SCALE).toString())); | |
95aa81ef | 1054 | buffer.append(newLine); |
720d67cb | 1055 | buffer.append(NLS.bind(Messages.Histogram_eventCountToolTip, nbEvents)); |
95aa81ef JCK |
1056 | if (!HistogramScaledData.hideLostEvents) { |
1057 | final int nbLostEvents = (index >= 0) ? fScaledData.fLostEventsData[index] : 0; | |
1058 | buffer.append(newLine); | |
720d67cb | 1059 | buffer.append(NLS.bind(Messages.Histogram_lostEventCountToolTip, nbLostEvents)); |
95aa81ef | 1060 | } |
c392540b FC |
1061 | return buffer.toString(); |
1062 | } | |
1063 | ||
1064 | // ------------------------------------------------------------------------ | |
1065 | // ControlListener | |
1066 | // ------------------------------------------------------------------------ | |
1067 | ||
1068 | @Override | |
09e86496 | 1069 | public void controlMoved(final ControlEvent event) { |
fbd124dd | 1070 | fDataModel.complete(); |
c392540b FC |
1071 | } |
1072 | ||
1073 | @Override | |
09e86496 | 1074 | public void controlResized(final ControlEvent event) { |
fbd124dd | 1075 | fDataModel.complete(); |
c392540b | 1076 | } |
f8177ba2 FC |
1077 | |
1078 | // ------------------------------------------------------------------------ | |
1079 | // Signal Handlers | |
1080 | // ------------------------------------------------------------------------ | |
1081 | ||
1082 | /** | |
1083 | * Format the timestamp and update the display | |
1084 | * | |
95aa81ef JCK |
1085 | * @param signal |
1086 | * the incoming signal | |
f8177ba2 FC |
1087 | */ |
1088 | @TmfSignalHandler | |
1089 | public void timestampFormatUpdated(TmfTimestampFormatUpdateSignal signal) { | |
d418423b | 1090 | updateRangeTextControls(); |
f8177ba2 | 1091 | |
da7bdcbc | 1092 | fComposite.layout(); |
f8177ba2 FC |
1093 | } |
1094 | ||
c392540b | 1095 | } |