Merge branch 'master' into lttng-luna
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.ui / src / org / eclipse / linuxtools / tmf / ui / views / histogram / Histogram.java
CommitLineData
c392540b 1/*******************************************************************************
c8422608 2 * Copyright (c) 2011, 2013 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
c392540b
FC
14 *******************************************************************************/
15
e0752744 16package org.eclipse.linuxtools.tmf.ui.views.histogram;
c392540b 17
f8177ba2
FC
18import org.eclipse.linuxtools.tmf.core.signal.TmfSignalHandler;
19import org.eclipse.linuxtools.tmf.core.signal.TmfSignalManager;
20import org.eclipse.linuxtools.tmf.core.signal.TmfTimestampFormatUpdateSignal;
3bd46eef
AM
21import org.eclipse.linuxtools.tmf.core.timestamp.ITmfTimestamp;
22import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimestamp;
23import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimestampFormat;
c392540b
FC
24import org.eclipse.linuxtools.tmf.ui.views.TmfView;
25import org.eclipse.swt.SWT;
26import org.eclipse.swt.events.ControlEvent;
27import org.eclipse.swt.events.ControlListener;
65cdf787
PT
28import org.eclipse.swt.events.FocusAdapter;
29import org.eclipse.swt.events.FocusEvent;
30import org.eclipse.swt.events.FocusListener;
c392540b
FC
31import org.eclipse.swt.events.KeyEvent;
32import org.eclipse.swt.events.KeyListener;
33import org.eclipse.swt.events.MouseEvent;
34import org.eclipse.swt.events.MouseListener;
35import org.eclipse.swt.events.MouseTrackListener;
65cdf787 36import org.eclipse.swt.events.MouseWheelListener;
c392540b
FC
37import org.eclipse.swt.events.PaintEvent;
38import org.eclipse.swt.events.PaintListener;
39import org.eclipse.swt.graphics.Color;
40import org.eclipse.swt.graphics.Font;
41import org.eclipse.swt.graphics.FontData;
42import org.eclipse.swt.graphics.GC;
43import org.eclipse.swt.graphics.Image;
e60df94a 44import org.eclipse.swt.layout.FillLayout;
c392540b
FC
45import org.eclipse.swt.layout.GridData;
46import org.eclipse.swt.layout.GridLayout;
47import org.eclipse.swt.widgets.Canvas;
48import org.eclipse.swt.widgets.Composite;
49import org.eclipse.swt.widgets.Display;
65cdf787 50import org.eclipse.swt.widgets.Label;
c392540b
FC
51import org.eclipse.swt.widgets.Text;
52
53/**
b544077e 54 * Re-usable histogram widget.
20ff3b75 55 *
b544077e 56 * It has the following features:
c392540b
FC
57 * <ul>
58 * <li>Y-axis labels displaying min/max count values
59 * <li>X-axis labels displaying time range
60 * <li>a histogram displaying the distribution of values over time (note that
61 * the histogram might not necessarily fill the whole canvas)
62 * </ul>
63 * The widget also has 2 'markers' to identify:
64 * <ul>
65 * <li>a red dashed line over the bar that contains the currently selected event
66 * <li>a dark red dashed line that delimits the right end of the histogram (if
67 * it doesn't fill the canvas)
68 * </ul>
69 * Clicking on the histogram will select the current event at the mouse
70 * location.
71 * <p>
72 * Once the histogram is selected, there is some limited keyboard support:
73 * <ul>
74 * <li>Home: go to the first histogram bar
75 * <li>End: go to the last histogram bar
76 * <li>Left: go to the previous histogram
77 * <li>Right: go to the next histogram bar
78 * </ul>
79 * Finally, when the mouse hovers over the histogram, a tool tip showing the
80 * following information about the corresponding histogram bar time range:
81 * <ul>
82 * <li>start of the time range
83 * <li>end of the time range
84 * <li>number of events in that time range
85 * </ul>
20ff3b75 86 *
f8177ba2 87 * @version 1.1
b544077e 88 * @author Francois Chouinard
c392540b 89 */
fbd124dd 90public abstract class Histogram implements ControlListener, PaintListener, KeyListener, MouseListener, MouseTrackListener, IHistogramModelListener {
c392540b
FC
91
92 // ------------------------------------------------------------------------
93 // Constants
94 // ------------------------------------------------------------------------
95
c392540b
FC
96 // Histogram colors
97 private final Color fBackgroundColor = Display.getCurrent().getSystemColor(SWT.COLOR_WHITE);
0fcf3b09
PT
98 private final Color fSelectionForegroundColor = Display.getCurrent().getSystemColor(SWT.COLOR_BLUE);
99 private final Color fSelectionBackgroundColor = Display.getCurrent().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
c392540b
FC
100 private final Color fLastEventColor = Display.getCurrent().getSystemColor(SWT.COLOR_DARK_RED);
101 private final Color fHistoBarColor = new Color(Display.getDefault(), 74, 112, 139);
95aa81ef 102 private final Color fLostEventColor = new Color(Display.getCurrent(), 208, 62, 120);
c392540b 103
c392540b
FC
104 // ------------------------------------------------------------------------
105 // Attributes
106 // ------------------------------------------------------------------------
107
b544077e
BH
108 /**
109 * The parent TMF view.
110 */
c392540b
FC
111 protected TmfView fParentView;
112
da7bdcbc 113 private Composite fComposite;
f8177ba2
FC
114 private Font fFont;
115
c392540b
FC
116 // Histogram text fields
117 private Text fMaxNbEventsText;
118 private Text fMinNbEventsText;
119 private Text fTimeRangeStartText;
120 private Text fTimeRangeEndText;
121
b544077e 122 /**
95aa81ef 123 * Histogram drawing area
b544077e 124 */
c392540b 125 protected Canvas fCanvas;
f8177ba2 126
b544077e 127 /**
95aa81ef 128 * The histogram data model.
b544077e 129 */
c392540b 130 protected final HistogramDataModel fDataModel;
f8177ba2 131
b544077e 132 /**
20ff3b75 133 * The histogram data model scaled to current resolution and screen width.
b544077e 134 */
c392540b
FC
135 protected HistogramScaledData fScaledData;
136
f8177ba2
FC
137 /**
138 * The current event value
139 */
140 protected long fCurrentEventTime = 0L;
c392540b 141
0fcf3b09
PT
142 /**
143 * The current selection begin time
144 */
145 private long fSelectionBegin = 0L;
146
147 /**
148 * The current selection end time
149 */
150 private long fSelectionEnd = 0L;
151
c392540b
FC
152 // ------------------------------------------------------------------------
153 // Construction
154 // ------------------------------------------------------------------------
155
b544077e 156 /**
f8177ba2 157 * Full constructor.
20ff3b75 158 *
b544077e
BH
159 * @param view A reference to the parent TMF view.
160 * @param parent A parent composite
161 */
09e86496 162 public Histogram(final TmfView view, final Composite parent) {
c392540b
FC
163 fParentView = view;
164
da7bdcbc 165 fComposite = createWidget(parent);
c392540b 166 fDataModel = new HistogramDataModel();
fbd124dd 167 fDataModel.addHistogramListener(this);
c392540b
FC
168 clear();
169
170 fCanvas.addControlListener(this);
171 fCanvas.addPaintListener(this);
172 fCanvas.addKeyListener(this);
173 fCanvas.addMouseListener(this);
174 fCanvas.addMouseTrackListener(this);
f8177ba2
FC
175
176 TmfSignalManager.register(this);
c392540b
FC
177 }
178
b544077e 179 /**
f8177ba2 180 * Dispose resources and unregisters listeners.
b544077e 181 */
c392540b 182 public void dispose() {
f8177ba2
FC
183 TmfSignalManager.deregister(this);
184
c392540b 185 fHistoBarColor.dispose();
95aa81ef 186 fLastEventColor.dispose();
fbd124dd 187 fDataModel.removeHistogramListener(this);
c392540b
FC
188 }
189
da7bdcbc 190 private Composite createWidget(final Composite parent) {
c392540b 191
0301dc43 192 final Color labelColor = parent.getBackground();
f8177ba2 193 fFont = adjustFont(parent);
c392540b
FC
194
195 final int initalWidth = 10;
196
197 // --------------------------------------------------------------------
198 // Define the histogram
199 // --------------------------------------------------------------------
200
09e86496 201 final GridLayout gridLayout = new GridLayout();
c392540b
FC
202 gridLayout.numColumns = 3;
203 gridLayout.marginHeight = 0;
204 gridLayout.marginWidth = 0;
205 gridLayout.marginTop = 0;
206 gridLayout.horizontalSpacing = 0;
207 gridLayout.verticalSpacing = 0;
208 gridLayout.marginLeft = 0;
209 gridLayout.marginRight = 0;
09e86496 210 final Composite composite = new Composite(parent, SWT.FILL);
c392540b
FC
211 composite.setLayout(gridLayout);
212
213 // Use all the horizontal space
214 GridData gridData = new GridData();
215 gridData.horizontalAlignment = SWT.FILL;
216 gridData.verticalAlignment = SWT.FILL;
217 gridData.grabExcessHorizontalSpace = true;
218 composite.setLayoutData(gridData);
219
220 // Y-axis max event
221 gridData = new GridData();
222 gridData.horizontalAlignment = SWT.RIGHT;
223 gridData.verticalAlignment = SWT.TOP;
224 fMaxNbEventsText = new Text(composite, SWT.READ_ONLY | SWT.RIGHT);
225 fMaxNbEventsText.setFont(fFont);
226 fMaxNbEventsText.setBackground(labelColor);
227 fMaxNbEventsText.setEditable(false);
228 fMaxNbEventsText.setText("0"); //$NON-NLS-1$
229 fMaxNbEventsText.setLayoutData(gridData);
230
231 // Histogram itself
e60df94a 232 Composite canvasComposite = new Composite(composite, SWT.BORDER);
c392540b
FC
233 gridData = new GridData();
234 gridData.horizontalSpan = 2;
235 gridData.verticalSpan = 2;
236 gridData.horizontalAlignment = SWT.FILL;
237 gridData.verticalAlignment = SWT.FILL;
238 gridData.grabExcessHorizontalSpace = true;
e60df94a
PT
239 canvasComposite.setLayoutData(gridData);
240 canvasComposite.setLayout(new FillLayout());
241 fCanvas = new Canvas(canvasComposite, SWT.DOUBLE_BUFFERED);
c392540b
FC
242
243 // Y-axis min event (always 0...)
244 gridData = new GridData();
245 gridData.horizontalAlignment = SWT.RIGHT;
246 gridData.verticalAlignment = SWT.BOTTOM;
247 fMinNbEventsText = new Text(composite, SWT.READ_ONLY | SWT.RIGHT);
248 fMinNbEventsText.setFont(fFont);
249 fMinNbEventsText.setBackground(labelColor);
250 fMinNbEventsText.setEditable(false);
251 fMinNbEventsText.setText("0"); //$NON-NLS-1$
252 fMinNbEventsText.setLayoutData(gridData);
253
254 // Dummy cell
255 gridData = new GridData(initalWidth, SWT.DEFAULT);
256 gridData.horizontalAlignment = SWT.RIGHT;
257 gridData.verticalAlignment = SWT.BOTTOM;
65cdf787
PT
258 final Label dummyLabel = new Label(composite, SWT.NONE);
259 dummyLabel.setLayoutData(gridData);
c392540b
FC
260
261 // Window range start time
262 gridData = new GridData();
263 gridData.horizontalAlignment = SWT.LEFT;
264 gridData.verticalAlignment = SWT.BOTTOM;
265 fTimeRangeStartText = new Text(composite, SWT.READ_ONLY);
266 fTimeRangeStartText.setFont(fFont);
267 fTimeRangeStartText.setBackground(labelColor);
c392540b
FC
268 fTimeRangeStartText.setLayoutData(gridData);
269
270 // Window range end time
271 gridData = new GridData();
272 gridData.horizontalAlignment = SWT.RIGHT;
273 gridData.verticalAlignment = SWT.BOTTOM;
274 fTimeRangeEndText = new Text(composite, SWT.READ_ONLY);
275 fTimeRangeEndText.setFont(fFont);
276 fTimeRangeEndText.setBackground(labelColor);
c392540b 277 fTimeRangeEndText.setLayoutData(gridData);
da7bdcbc 278
65cdf787
PT
279 FocusListener listener = new FocusAdapter() {
280 @Override
281 public void focusGained(FocusEvent e) {
282 fCanvas.setFocus();
283 }
284 };
285 fMaxNbEventsText.addFocusListener(listener);
286 fMinNbEventsText.addFocusListener(listener);
287 fTimeRangeStartText.addFocusListener(listener);
288 fTimeRangeEndText.addFocusListener(listener);
289
da7bdcbc 290 return composite;
c392540b
FC
291 }
292
abbdd66a 293 private static Font adjustFont(final Composite composite) {
c392540b 294 // Reduce font size for a more pleasing rendering
09e86496
FC
295 final int fontSizeAdjustment = -2;
296 final Font font = composite.getFont();
297 final FontData fontData = font.getFontData()[0];
c392540b
FC
298 return new Font(font.getDevice(), fontData.getName(), fontData.getHeight() + fontSizeAdjustment, fontData.getStyle());
299 }
300
301 // ------------------------------------------------------------------------
302 // Accessors
303 // ------------------------------------------------------------------------
304
b544077e 305 /**
f8177ba2 306 * Returns the start time (equal first bucket time)
b544077e
BH
307 * @return the start time.
308 */
c392540b 309 public long getStartTime() {
fbd124dd 310 return fDataModel.getFirstBucketTime();
c392540b
FC
311 }
312
b544077e
BH
313 /**
314 * Returns the end time.
315 * @return the end time.
316 */
c392540b
FC
317 public long getEndTime() {
318 return fDataModel.getEndTime();
319 }
320
b544077e
BH
321 /**
322 * Returns the time limit (end of last bucket)
323 * @return the time limit.
324 */
c392540b
FC
325 public long getTimeLimit() {
326 return fDataModel.getTimeLimit();
327 }
09e86496 328
20ff3b75
AM
329 /**
330 * Returns a data model reference.
b544077e
BH
331 * @return data model.
332 */
fbd124dd
BH
333 public HistogramDataModel getDataModel() {
334 return fDataModel;
335 }
c392540b 336
95aa81ef
JCK
337 /**
338 * Returns the text control for the maximum of events in one bar
339 *
340 * @return the text control
46a59db7 341 * @since 2.2
95aa81ef
JCK
342 */
343 public Text getMaxNbEventsText() {
344 return fMaxNbEventsText;
345 }
346
c392540b
FC
347 // ------------------------------------------------------------------------
348 // Operations
349 // ------------------------------------------------------------------------
b544077e 350 /**
20ff3b75 351 * Updates the time range.
b544077e
BH
352 * @param startTime A start time
353 * @param endTime A end time.
354 */
c392540b
FC
355 public abstract void updateTimeRange(long startTime, long endTime);
356
357 /**
358 * Clear the histogram and reset the data
359 */
360 public void clear() {
361 fDataModel.clear();
dcb3cda5
BH
362 synchronized (fDataModel) {
363 fScaledData = null;
364 }
c392540b
FC
365 }
366
367 /**
368 * Increase the histogram bucket corresponding to [timestamp]
20ff3b75 369 *
95aa81ef
JCK
370 * @param eventCount The new event count
371 * @param timestamp The latest timestamp
c392540b 372 */
09e86496 373 public void countEvent(final long eventCount, final long timestamp) {
fbd124dd 374 fDataModel.countEvent(eventCount, timestamp);
c392540b
FC
375 }
376
377 /**
378 * Sets the current event time and refresh the display
20ff3b75 379 *
95aa81ef 380 * @param timestamp The time of the current event
0fcf3b09 381 * @deprecated As of 2.1, use {@link #setSelection(long, long)}
c392540b 382 */
0fcf3b09 383 @Deprecated
09e86496 384 public void setCurrentEvent(final long timestamp) {
0fcf3b09
PT
385 fSelectionBegin = (timestamp > 0) ? timestamp : 0;
386 fSelectionEnd = (timestamp > 0) ? timestamp : 0;
387 fDataModel.setSelectionNotifyListeners(timestamp, timestamp);
388 }
389
390 /**
391 * Sets the current selection time range and refresh the display
392 *
95aa81ef
JCK
393 * @param beginTime The begin time of the current selection
394 * @param endTime The end time of the current selection
0fcf3b09
PT
395 * @since 2.1
396 */
397 public void setSelection(final long beginTime, final long endTime) {
398 fSelectionBegin = (beginTime > 0) ? beginTime : 0;
399 fSelectionEnd = (endTime > 0) ? endTime : 0;
400 fDataModel.setSelectionNotifyListeners(beginTime, endTime);
c392540b
FC
401 }
402
403 /**
404 * Computes the timestamp of the bucket at [offset]
20ff3b75 405 *
c392540b
FC
406 * @param offset offset from the left on the histogram
407 * @return the start timestamp of the corresponding bucket
408 */
09e86496 409 public synchronized long getTimestamp(final int offset) {
c392540b
FC
410 assert offset > 0 && offset < fScaledData.fWidth;
411 try {
fbd124dd 412 return fDataModel.getFirstBucketTime() + fScaledData.fBucketDuration * offset;
09e86496 413 } catch (final Exception e) {
c392540b
FC
414 return 0; // TODO: Fix that racing condition (NPE)
415 }
416 }
417
418 /**
419 * Computes the offset of the timestamp in the histogram
20ff3b75 420 *
c392540b
FC
421 * @param timestamp the timestamp
422 * @return the offset of the corresponding bucket (-1 if invalid)
423 */
09e86496 424 public synchronized int getOffset(final long timestamp) {
20ff3b75 425 if (timestamp < fDataModel.getFirstBucketTime() || timestamp > fDataModel.getEndTime()) {
c392540b 426 return -1;
20ff3b75 427 }
fbd124dd 428 return (int) ((timestamp - fDataModel.getFirstBucketTime()) / fScaledData.fBucketDuration);
c392540b
FC
429 }
430
431 /**
432 * Move the currently selected bar cursor to a non-empty bucket.
20ff3b75 433 *
c392540b
FC
434 * @param keyCode the SWT key code
435 */
09e86496 436 protected void moveCursor(final int keyCode) {
c392540b 437
c392540b
FC
438 int index;
439 switch (keyCode) {
440
95aa81ef
JCK
441 case SWT.HOME:
442 index = 0;
443 while (index < fScaledData.fLastBucket && fScaledData.fData[index] == 0) {
444 index++;
445 }
446 if (index < fScaledData.fLastBucket) {
447 fScaledData.fSelectionBeginBucket = index;
448 }
449 break;
c392540b 450
95aa81ef
JCK
451 case SWT.ARROW_RIGHT:
452 index = Math.max(0, fScaledData.fSelectionBeginBucket + 1);
453 while (index < fScaledData.fWidth && fScaledData.fData[index] == 0) {
454 index++;
455 }
456 if (index < fScaledData.fLastBucket) {
457 fScaledData.fSelectionBeginBucket = index;
458 }
459 break;
c392540b 460
95aa81ef
JCK
461 case SWT.END:
462 index = fScaledData.fLastBucket;
463 while (index >= 0 && fScaledData.fData[index] == 0) {
464 index--;
465 }
466 if (index >= 0) {
467 fScaledData.fSelectionBeginBucket = index;
468 }
469 break;
c392540b 470
95aa81ef
JCK
471 case SWT.ARROW_LEFT:
472 index = Math.min(fScaledData.fLastBucket - 1, fScaledData.fSelectionBeginBucket - 1);
473 while (index >= 0 && fScaledData.fData[index] == 0) {
474 index--;
475 }
476 if (index >= 0) {
477 fScaledData.fSelectionBeginBucket = index;
478 }
479 break;
c392540b 480
95aa81ef
JCK
481 default:
482 return;
c392540b
FC
483 }
484
0fcf3b09
PT
485 fScaledData.fSelectionEndBucket = fScaledData.fSelectionBeginBucket;
486 fSelectionBegin = getTimestamp(fScaledData.fSelectionBeginBucket);
487 fSelectionEnd = fSelectionBegin;
488 updateSelectionTime();
c392540b
FC
489 }
490
491 /**
492 * Refresh the histogram display
493 */
fbd124dd
BH
494 @Override
495 public void modelUpdated() {
20ff3b75 496 if (!fCanvas.isDisposed() && fCanvas.getDisplay() != null) {
c392540b
FC
497 fCanvas.getDisplay().asyncExec(new Runnable() {
498 @Override
499 public void run() {
500 if (!fCanvas.isDisposed()) {
501 // Retrieve and normalize the data
09e86496
FC
502 final int canvasWidth = fCanvas.getBounds().width;
503 final int canvasHeight = fCanvas.getBounds().height;
20ff3b75 504 if (canvasWidth <= 0 || canvasHeight <= 0) {
40890fec 505 return;
20ff3b75 506 }
0fcf3b09 507 fDataModel.setSelection(fSelectionBegin, fSelectionEnd);
f8177ba2 508 fScaledData = fDataModel.scaleTo(canvasWidth, canvasHeight, 1);
95aa81ef 509 synchronized (fDataModel) {
0316808c
FC
510 if (fScaledData != null) {
511 fCanvas.redraw();
da7bdcbc 512 if (fDataModel.getNbEvents() != 0) {
95aa81ef
JCK
513 // Display histogram and update X-,Y-axis
514 // labels
da7bdcbc
PT
515 fTimeRangeStartText.setText(TmfTimestampFormat.getDefaulTimeFormat().format(fDataModel.getFirstBucketTime()));
516 fTimeRangeEndText.setText(TmfTimestampFormat.getDefaulTimeFormat().format(fDataModel.getEndTime()));
517 } else {
518 fTimeRangeStartText.setText(""); //$NON-NLS-1$
519 fTimeRangeEndText.setText(""); //$NON-NLS-1$
520 }
95aa81ef
JCK
521 long maxNbEvents = HistogramScaledData.hideLostEvents ? fScaledData.fMaxValue : fScaledData.fMaxCombinedValue;
522 fMaxNbEventsText.setText(Long.toString(maxNbEvents));
0316808c
FC
523 // The Y-axis area might need to be re-sized
524 fMaxNbEventsText.getParent().layout();
525 }
09e86496 526 }
c392540b
FC
527 }
528 }
529 });
20ff3b75 530 }
c392540b
FC
531 }
532
65cdf787
PT
533 /**
534 * Add a mouse wheel listener to the histogram
535 * @param listener the mouse wheel listener
536 * @since 2.0
537 */
538 public void addMouseWheelListener(MouseWheelListener listener) {
539 fCanvas.addMouseWheelListener(listener);
540 }
541
542 /**
543 * Remove a mouse wheel listener from the histogram
544 * @param listener the mouse wheel listener
545 * @since 2.0
546 */
547 public void removeMouseWheelListener(MouseWheelListener listener) {
548 fCanvas.removeMouseWheelListener(listener);
549 }
550
c392540b
FC
551 // ------------------------------------------------------------------------
552 // Helper functions
553 // ------------------------------------------------------------------------
554
0fcf3b09
PT
555 private void updateSelectionTime() {
556 ((HistogramView) fParentView).updateSelectionTime(fSelectionBegin, fSelectionEnd);
c392540b
FC
557 }
558
559 // ------------------------------------------------------------------------
560 // PaintListener
561 // ------------------------------------------------------------------------
b544077e
BH
562 /**
563 * Image key string for the canvas.
564 */
c392540b
FC
565 protected final String IMAGE_KEY = "double-buffer-image"; //$NON-NLS-1$
566
567 @Override
09e86496 568 public void paintControl(final PaintEvent event) {
c392540b
FC
569
570 // Get the geometry
09e86496
FC
571 final int canvasWidth = fCanvas.getBounds().width;
572 final int canvasHeight = fCanvas.getBounds().height;
c392540b
FC
573
574 // Make sure we have something to draw upon
20ff3b75 575 if (canvasWidth <= 0 || canvasHeight <= 0) {
c392540b 576 return;
20ff3b75 577 }
c392540b
FC
578
579 // Retrieve image; re-create only if necessary
580 Image image = (Image) fCanvas.getData(IMAGE_KEY);
581 if (image == null || image.getBounds().width != canvasWidth || image.getBounds().height != canvasHeight) {
582 image = new Image(event.display, canvasWidth, canvasHeight);
583 fCanvas.setData(IMAGE_KEY, image);
584 }
585
586 // Draw the histogram on its canvas
09e86496 587 final GC imageGC = new GC(image);
c392540b
FC
588 formatImage(imageGC, image);
589 event.gc.drawImage(image, 0, 0);
590 imageGC.dispose();
591 }
592
09e86496 593 private void formatImage(final GC imageGC, final Image image) {
c392540b 594
20ff3b75 595 if (fScaledData == null) {
c392540b 596 return;
20ff3b75 597 }
c392540b 598
09e86496 599 final HistogramScaledData scaledData = new HistogramScaledData(fScaledData);
c392540b
FC
600
601 try {
602 // Get drawing boundaries
09e86496
FC
603 final int width = image.getBounds().width;
604 final int height = image.getBounds().height;
c392540b
FC
605
606 // Clear the drawing area
607 imageGC.setBackground(fBackgroundColor);
608 imageGC.fillRectangle(0, 0, image.getBounds().width + 1, image.getBounds().height + 1);
609
610 // Draw the histogram bars
09e86496 611 final int limit = width < scaledData.fWidth ? width : scaledData.fWidth;
95aa81ef 612 double factor = HistogramScaledData.hideLostEvents ? scaledData.fScalingFactor : scaledData.fScalingFactorCombined;
e6953950 613 for (int i = 0; i < limit; i++) {
95aa81ef
JCK
614 imageGC.setForeground(fHistoBarColor);
615 final int value = (int) Math.ceil(scaledData.fData[i] * factor);
616 imageGC.drawLine(i, height - value, i, height);
617
618 if (!HistogramScaledData.hideLostEvents) {
619 imageGC.setForeground(fLostEventColor);
620 final int lostEventValue = (int) Math.ceil(scaledData.fLostEventsData[i] * factor);
621 if (lostEventValue != 0) {
622 if (lostEventValue == 1) {
623 // in linux, a line from x to x is not drawn, in windows it is.
624 imageGC.drawPoint(i, height - value - 1);
625 } else {
626 // drawing a line is inclusive, so we need to remove 1 from the destination to have the correct length
627 imageGC.drawLine(i, height - value - lostEventValue, i, height - value - 1);
628 }
629 }
630 }
c392540b
FC
631 }
632
c392540b
FC
633 // Add a dashed line as a delimiter (at the right of the last bar)
634 int lastEventIndex = limit - 1;
20ff3b75 635 while (lastEventIndex >= 0 && scaledData.fData[lastEventIndex] == 0) {
c392540b 636 lastEventIndex--;
20ff3b75 637 }
c392540b
FC
638 lastEventIndex += (lastEventIndex < limit - 1) ? 1 : 0;
639 drawDelimiter(imageGC, fLastEventColor, height, lastEventIndex);
0fcf3b09
PT
640
641 // Draw the selection bars
642 int alpha = imageGC.getAlpha();
643 imageGC.setAlpha(100);
644 imageGC.setForeground(fSelectionForegroundColor);
645 imageGC.setBackground(fSelectionBackgroundColor);
646 final int beginBucket = scaledData.fSelectionBeginBucket;
647 if (beginBucket >= 0 && beginBucket < limit) {
648 imageGC.drawLine(beginBucket, 0, beginBucket, height);
649 }
650 final int endBucket = Math.min(lastEventIndex, scaledData.fSelectionEndBucket);
651 if (endBucket >= 0 && endBucket < limit && endBucket != beginBucket) {
652 imageGC.drawLine(endBucket, 0, endBucket, height);
653 }
654 if (endBucket - beginBucket > 1) {
655 imageGC.fillRectangle(beginBucket + 1, 0, endBucket - beginBucket - 1, height);
656 }
657 imageGC.setAlpha(alpha);
09e86496 658 } catch (final Exception e) {
c392540b
FC
659 // Do nothing
660 }
661 }
662
abbdd66a
AM
663 private static void drawDelimiter(final GC imageGC, final Color color,
664 final int height, final int index) {
c392540b 665 imageGC.setBackground(color);
09e86496 666 final int dash = height / 4;
c392540b
FC
667 imageGC.fillRectangle(index, 0 * dash, 1, dash - 1);
668 imageGC.fillRectangle(index, 1 * dash, 1, dash - 1);
669 imageGC.fillRectangle(index, 2 * dash, 1, dash - 1);
670 imageGC.fillRectangle(index, 3 * dash, 1, height - 3 * dash);
671 }
672
673 // ------------------------------------------------------------------------
674 // KeyListener
675 // ------------------------------------------------------------------------
676
677 @Override
09e86496 678 public void keyPressed(final KeyEvent event) {
c392540b
FC
679 moveCursor(event.keyCode);
680 }
681
682 @Override
09e86496 683 public void keyReleased(final KeyEvent event) {
c392540b
FC
684 }
685
686 // ------------------------------------------------------------------------
687 // MouseListener
688 // ------------------------------------------------------------------------
689
690 @Override
09e86496 691 public void mouseDoubleClick(final MouseEvent event) {
c392540b
FC
692 }
693
694 @Override
09e86496 695 public void mouseDown(final MouseEvent event) {
c392540b 696 if (fDataModel.getNbEvents() > 0 && fScaledData.fLastBucket >= event.x) {
0fcf3b09
PT
697 if ((event.stateMask & SWT.MODIFIER_MASK) == 0) {
698 fScaledData.fSelectionBeginBucket = event.x;
699 fScaledData.fSelectionEndBucket = event.x;
700 fSelectionBegin = getTimestamp(event.x);
701 fSelectionEnd = fSelectionBegin;
702 } else if ((event.stateMask & SWT.MODIFIER_MASK) == SWT.SHIFT) {
703 if (fSelectionBegin == fSelectionEnd) {
704 if (event.x < fScaledData.fSelectionBeginBucket) {
705 fScaledData.fSelectionBeginBucket = event.x;
706 fSelectionBegin = getTimestamp(event.x);
707 } else {
708 fScaledData.fSelectionEndBucket = event.x;
709 fSelectionEnd = getTimestamp(event.x);
710 }
711 } else if (Math.abs(event.x - fScaledData.fSelectionBeginBucket) <= Math.abs(event.x - fScaledData.fSelectionEndBucket)) {
712 fScaledData.fSelectionBeginBucket = event.x;
713 fSelectionBegin = getTimestamp(event.x);
714 } else {
715 fScaledData.fSelectionEndBucket = event.x;
716 fSelectionEnd = getTimestamp(event.x);
717 }
718 }
719 updateSelectionTime();
c392540b
FC
720 }
721 }
722
723 @Override
09e86496 724 public void mouseUp(final MouseEvent event) {
c392540b
FC
725 }
726
727 // ------------------------------------------------------------------------
728 // MouseTrackListener
729 // ------------------------------------------------------------------------
730
731 @Override
09e86496 732 public void mouseEnter(final MouseEvent event) {
c392540b
FC
733 }
734
735 @Override
09e86496 736 public void mouseExit(final MouseEvent event) {
c392540b
FC
737 }
738
739 @Override
09e86496 740 public void mouseHover(final MouseEvent event) {
74237cc3 741 if (fDataModel.getNbEvents() > 0 && fScaledData != null && fScaledData.fLastBucket >= event.x) {
09e86496 742 final String tooltip = formatToolTipLabel(event.x);
c392540b 743 fCanvas.setToolTipText(tooltip);
1be49d83
PT
744 } else {
745 fCanvas.setToolTipText(null);
c392540b
FC
746 }
747 }
748
09e86496 749 private String formatToolTipLabel(final int index) {
466857f6 750 long startTime = fScaledData.getBucketStartTime(index);
09e86496 751 // negative values are possible if time values came into the model in decreasing order
20ff3b75 752 if (startTime < 0) {
fbd124dd 753 startTime = 0;
20ff3b75 754 }
466857f6 755 final long endTime = fScaledData.getBucketEndTime(index);
09e86496 756 final int nbEvents = (index >= 0) ? fScaledData.fData[index] : 0;
95aa81ef 757 final String newLine = System.getProperty("line.separator"); //$NON-NLS-1$
09e86496 758 final StringBuffer buffer = new StringBuffer();
c392540b 759 buffer.append("Range = ["); //$NON-NLS-1$
f8177ba2 760 buffer.append(new TmfTimestamp(startTime, ITmfTimestamp.NANOSECOND_SCALE).toString());
95aa81ef 761 buffer.append(',');
f8177ba2 762 buffer.append(new TmfTimestamp(endTime, ITmfTimestamp.NANOSECOND_SCALE).toString());
95aa81ef
JCK
763 buffer.append(')');
764 buffer.append(newLine);
c392540b
FC
765 buffer.append("Event count = "); //$NON-NLS-1$
766 buffer.append(nbEvents);
95aa81ef
JCK
767 if (!HistogramScaledData.hideLostEvents) {
768 final int nbLostEvents = (index >= 0) ? fScaledData.fLostEventsData[index] : 0;
769 buffer.append(newLine);
770 buffer.append("Lost events count = "); //$NON-NLS-1$
771 buffer.append(nbLostEvents);
772 }
c392540b
FC
773 return buffer.toString();
774 }
775
776 // ------------------------------------------------------------------------
777 // ControlListener
778 // ------------------------------------------------------------------------
779
780 @Override
09e86496 781 public void controlMoved(final ControlEvent event) {
fbd124dd 782 fDataModel.complete();
c392540b
FC
783 }
784
785 @Override
09e86496 786 public void controlResized(final ControlEvent event) {
fbd124dd 787 fDataModel.complete();
c392540b 788 }
f8177ba2
FC
789
790 // ------------------------------------------------------------------------
791 // Signal Handlers
792 // ------------------------------------------------------------------------
793
794 /**
795 * Format the timestamp and update the display
796 *
95aa81ef
JCK
797 * @param signal
798 * the incoming signal
f8177ba2
FC
799 * @since 2.0
800 */
801 @TmfSignalHandler
802 public void timestampFormatUpdated(TmfTimestampFormatUpdateSignal signal) {
da7bdcbc
PT
803 if (fDataModel.getNbEvents() == 0) {
804 return;
805 }
806
f8177ba2 807 String newTS = TmfTimestampFormat.getDefaulTimeFormat().format(fDataModel.getFirstBucketTime());
f8177ba2
FC
808 fTimeRangeStartText.setText(newTS);
809
810 newTS = TmfTimestampFormat.getDefaulTimeFormat().format(fDataModel.getEndTime());
f8177ba2 811 fTimeRangeEndText.setText(newTS);
f8177ba2 812
da7bdcbc 813 fComposite.layout();
f8177ba2
FC
814 }
815
c392540b 816}
This page took 0.089099 seconds and 5 git commands to generate.