tmf : Update the histogram to handle lost events correctly
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.ui / src / org / eclipse / linuxtools / tmf / ui / views / histogram / Histogram.java
1 /*******************************************************************************
2 * Copyright (c) 2011, 2013 Ericsson
3 *
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
8 *
9 * Contributors:
10 * Francois Chouinard - Initial API and implementation
11 * Bernd Hufmann - Changed to updated histogram data model
12 * Francois Chouinard - Reformat histogram labels on format change
13 * Patrick Tasse - Support selection range
14 *******************************************************************************/
15
16 package org.eclipse.linuxtools.tmf.ui.views.histogram;
17
18 import org.eclipse.linuxtools.tmf.core.signal.TmfSignalHandler;
19 import org.eclipse.linuxtools.tmf.core.signal.TmfSignalManager;
20 import org.eclipse.linuxtools.tmf.core.signal.TmfTimestampFormatUpdateSignal;
21 import org.eclipse.linuxtools.tmf.core.timestamp.ITmfTimestamp;
22 import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimestamp;
23 import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimestampFormat;
24 import org.eclipse.linuxtools.tmf.ui.views.TmfView;
25 import org.eclipse.swt.SWT;
26 import org.eclipse.swt.events.ControlEvent;
27 import org.eclipse.swt.events.ControlListener;
28 import org.eclipse.swt.events.FocusAdapter;
29 import org.eclipse.swt.events.FocusEvent;
30 import org.eclipse.swt.events.FocusListener;
31 import org.eclipse.swt.events.KeyEvent;
32 import org.eclipse.swt.events.KeyListener;
33 import org.eclipse.swt.events.MouseEvent;
34 import org.eclipse.swt.events.MouseListener;
35 import org.eclipse.swt.events.MouseTrackListener;
36 import org.eclipse.swt.events.MouseWheelListener;
37 import org.eclipse.swt.events.PaintEvent;
38 import org.eclipse.swt.events.PaintListener;
39 import org.eclipse.swt.graphics.Color;
40 import org.eclipse.swt.graphics.Font;
41 import org.eclipse.swt.graphics.FontData;
42 import org.eclipse.swt.graphics.GC;
43 import org.eclipse.swt.graphics.Image;
44 import org.eclipse.swt.layout.FillLayout;
45 import org.eclipse.swt.layout.GridData;
46 import org.eclipse.swt.layout.GridLayout;
47 import org.eclipse.swt.widgets.Canvas;
48 import org.eclipse.swt.widgets.Composite;
49 import org.eclipse.swt.widgets.Display;
50 import org.eclipse.swt.widgets.Label;
51 import org.eclipse.swt.widgets.Text;
52
53 /**
54 * Re-usable histogram widget.
55 *
56 * It has the following features:
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>
86 *
87 * @version 1.1
88 * @author Francois Chouinard
89 */
90 public abstract class Histogram implements ControlListener, PaintListener, KeyListener, MouseListener, MouseTrackListener, IHistogramModelListener {
91
92 // ------------------------------------------------------------------------
93 // Constants
94 // ------------------------------------------------------------------------
95
96 // Histogram colors
97 private final Color fBackgroundColor = Display.getCurrent().getSystemColor(SWT.COLOR_WHITE);
98 private final Color fSelectionForegroundColor = Display.getCurrent().getSystemColor(SWT.COLOR_BLUE);
99 private final Color fSelectionBackgroundColor = Display.getCurrent().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
100 private final Color fLastEventColor = Display.getCurrent().getSystemColor(SWT.COLOR_DARK_RED);
101 private final Color fHistoBarColor = new Color(Display.getDefault(), 74, 112, 139);
102 private final Color fLostEventColor = new Color(Display.getCurrent(), 208, 62, 120);
103
104 // ------------------------------------------------------------------------
105 // Attributes
106 // ------------------------------------------------------------------------
107
108 /**
109 * The parent TMF view.
110 */
111 protected TmfView fParentView;
112
113 private Composite fComposite;
114 private Font fFont;
115
116 // Histogram text fields
117 private Text fMaxNbEventsText;
118 private Text fMinNbEventsText;
119 private Text fTimeRangeStartText;
120 private Text fTimeRangeEndText;
121
122 /**
123 * Histogram drawing area
124 */
125 protected Canvas fCanvas;
126
127 /**
128 * The histogram data model.
129 */
130 protected final HistogramDataModel fDataModel;
131
132 /**
133 * The histogram data model scaled to current resolution and screen width.
134 */
135 protected HistogramScaledData fScaledData;
136
137 /**
138 * The current event value
139 */
140 protected long fCurrentEventTime = 0L;
141
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
152 // ------------------------------------------------------------------------
153 // Construction
154 // ------------------------------------------------------------------------
155
156 /**
157 * Full constructor.
158 *
159 * @param view A reference to the parent TMF view.
160 * @param parent A parent composite
161 */
162 public Histogram(final TmfView view, final Composite parent) {
163 fParentView = view;
164
165 fComposite = createWidget(parent);
166 fDataModel = new HistogramDataModel();
167 fDataModel.addHistogramListener(this);
168 clear();
169
170 fCanvas.addControlListener(this);
171 fCanvas.addPaintListener(this);
172 fCanvas.addKeyListener(this);
173 fCanvas.addMouseListener(this);
174 fCanvas.addMouseTrackListener(this);
175
176 TmfSignalManager.register(this);
177 }
178
179 /**
180 * Dispose resources and unregisters listeners.
181 */
182 public void dispose() {
183 TmfSignalManager.deregister(this);
184
185 fHistoBarColor.dispose();
186 fLastEventColor.dispose();
187 fDataModel.removeHistogramListener(this);
188 }
189
190 private Composite createWidget(final Composite parent) {
191
192 final Color labelColor = parent.getBackground();
193 fFont = adjustFont(parent);
194
195 final int initalWidth = 10;
196
197 // --------------------------------------------------------------------
198 // Define the histogram
199 // --------------------------------------------------------------------
200
201 final GridLayout gridLayout = new GridLayout();
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;
210 final Composite composite = new Composite(parent, SWT.FILL);
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
232 Composite canvasComposite = new Composite(composite, SWT.BORDER);
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;
239 canvasComposite.setLayoutData(gridData);
240 canvasComposite.setLayout(new FillLayout());
241 fCanvas = new Canvas(canvasComposite, SWT.DOUBLE_BUFFERED);
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;
258 final Label dummyLabel = new Label(composite, SWT.NONE);
259 dummyLabel.setLayoutData(gridData);
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);
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);
277 fTimeRangeEndText.setLayoutData(gridData);
278
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
290 return composite;
291 }
292
293 private static Font adjustFont(final Composite composite) {
294 // Reduce font size for a more pleasing rendering
295 final int fontSizeAdjustment = -2;
296 final Font font = composite.getFont();
297 final FontData fontData = font.getFontData()[0];
298 return new Font(font.getDevice(), fontData.getName(), fontData.getHeight() + fontSizeAdjustment, fontData.getStyle());
299 }
300
301 // ------------------------------------------------------------------------
302 // Accessors
303 // ------------------------------------------------------------------------
304
305 /**
306 * Returns the start time (equal first bucket time)
307 * @return the start time.
308 */
309 public long getStartTime() {
310 return fDataModel.getFirstBucketTime();
311 }
312
313 /**
314 * Returns the end time.
315 * @return the end time.
316 */
317 public long getEndTime() {
318 return fDataModel.getEndTime();
319 }
320
321 /**
322 * Returns the time limit (end of last bucket)
323 * @return the time limit.
324 */
325 public long getTimeLimit() {
326 return fDataModel.getTimeLimit();
327 }
328
329 /**
330 * Returns a data model reference.
331 * @return data model.
332 */
333 public HistogramDataModel getDataModel() {
334 return fDataModel;
335 }
336
337 /**
338 * Returns the text control for the maximum of events in one bar
339 *
340 * @return the text control
341 * @since 2.1
342 */
343 public Text getMaxNbEventsText() {
344 return fMaxNbEventsText;
345 }
346
347 // ------------------------------------------------------------------------
348 // Operations
349 // ------------------------------------------------------------------------
350 /**
351 * Updates the time range.
352 * @param startTime A start time
353 * @param endTime A end time.
354 */
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();
362 synchronized (fDataModel) {
363 fScaledData = null;
364 }
365 }
366
367 /**
368 * Increase the histogram bucket corresponding to [timestamp]
369 *
370 * @param eventCount The new event count
371 * @param timestamp The latest timestamp
372 */
373 public void countEvent(final long eventCount, final long timestamp) {
374 fDataModel.countEvent(eventCount, timestamp);
375 }
376
377 /**
378 * Sets the current event time and refresh the display
379 *
380 * @param timestamp The time of the current event
381 * @deprecated As of 2.1, use {@link #setSelection(long, long)}
382 */
383 @Deprecated
384 public void setCurrentEvent(final long timestamp) {
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 *
393 * @param beginTime The begin time of the current selection
394 * @param endTime The end time of the current selection
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);
401 }
402
403 /**
404 * Computes the timestamp of the bucket at [offset]
405 *
406 * @param offset offset from the left on the histogram
407 * @return the start timestamp of the corresponding bucket
408 */
409 public synchronized long getTimestamp(final int offset) {
410 assert offset > 0 && offset < fScaledData.fWidth;
411 try {
412 return fDataModel.getFirstBucketTime() + fScaledData.fBucketDuration * offset;
413 } catch (final Exception e) {
414 return 0; // TODO: Fix that racing condition (NPE)
415 }
416 }
417
418 /**
419 * Computes the offset of the timestamp in the histogram
420 *
421 * @param timestamp the timestamp
422 * @return the offset of the corresponding bucket (-1 if invalid)
423 */
424 public synchronized int getOffset(final long timestamp) {
425 if (timestamp < fDataModel.getFirstBucketTime() || timestamp > fDataModel.getEndTime()) {
426 return -1;
427 }
428 return (int) ((timestamp - fDataModel.getFirstBucketTime()) / fScaledData.fBucketDuration);
429 }
430
431 /**
432 * Move the currently selected bar cursor to a non-empty bucket.
433 *
434 * @param keyCode the SWT key code
435 */
436 protected void moveCursor(final int keyCode) {
437
438 int index;
439 switch (keyCode) {
440
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;
450
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;
460
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;
470
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;
480
481 default:
482 return;
483 }
484
485 fScaledData.fSelectionEndBucket = fScaledData.fSelectionBeginBucket;
486 fSelectionBegin = getTimestamp(fScaledData.fSelectionBeginBucket);
487 fSelectionEnd = fSelectionBegin;
488 updateSelectionTime();
489 }
490
491 /**
492 * Refresh the histogram display
493 */
494 @Override
495 public void modelUpdated() {
496 if (!fCanvas.isDisposed() && fCanvas.getDisplay() != null) {
497 fCanvas.getDisplay().asyncExec(new Runnable() {
498 @Override
499 public void run() {
500 if (!fCanvas.isDisposed()) {
501 // Retrieve and normalize the data
502 final int canvasWidth = fCanvas.getBounds().width;
503 final int canvasHeight = fCanvas.getBounds().height;
504 if (canvasWidth <= 0 || canvasHeight <= 0) {
505 return;
506 }
507 fDataModel.setSelection(fSelectionBegin, fSelectionEnd);
508 fScaledData = fDataModel.scaleTo(canvasWidth, canvasHeight, 1);
509 synchronized (fDataModel) {
510 if (fScaledData != null) {
511 fCanvas.redraw();
512 if (fDataModel.getNbEvents() != 0) {
513 // Display histogram and update X-,Y-axis
514 // labels
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 }
521 long maxNbEvents = HistogramScaledData.hideLostEvents ? fScaledData.fMaxValue : fScaledData.fMaxCombinedValue;
522 fMaxNbEventsText.setText(Long.toString(maxNbEvents));
523 // The Y-axis area might need to be re-sized
524 fMaxNbEventsText.getParent().layout();
525 }
526 }
527 }
528 }
529 });
530 }
531 }
532
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
551 // ------------------------------------------------------------------------
552 // Helper functions
553 // ------------------------------------------------------------------------
554
555 private void updateSelectionTime() {
556 ((HistogramView) fParentView).updateSelectionTime(fSelectionBegin, fSelectionEnd);
557 }
558
559 // ------------------------------------------------------------------------
560 // PaintListener
561 // ------------------------------------------------------------------------
562 /**
563 * Image key string for the canvas.
564 */
565 protected final String IMAGE_KEY = "double-buffer-image"; //$NON-NLS-1$
566
567 @Override
568 public void paintControl(final PaintEvent event) {
569
570 // Get the geometry
571 final int canvasWidth = fCanvas.getBounds().width;
572 final int canvasHeight = fCanvas.getBounds().height;
573
574 // Make sure we have something to draw upon
575 if (canvasWidth <= 0 || canvasHeight <= 0) {
576 return;
577 }
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
587 final GC imageGC = new GC(image);
588 formatImage(imageGC, image);
589 event.gc.drawImage(image, 0, 0);
590 imageGC.dispose();
591 }
592
593 private void formatImage(final GC imageGC, final Image image) {
594
595 if (fScaledData == null) {
596 return;
597 }
598
599 final HistogramScaledData scaledData = new HistogramScaledData(fScaledData);
600
601 try {
602 // Get drawing boundaries
603 final int width = image.getBounds().width;
604 final int height = image.getBounds().height;
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
611 final int limit = width < scaledData.fWidth ? width : scaledData.fWidth;
612 double factor = HistogramScaledData.hideLostEvents ? scaledData.fScalingFactor : scaledData.fScalingFactorCombined;
613 for (int i = 0; i < limit; i++) {
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 }
631 }
632
633 // Add a dashed line as a delimiter (at the right of the last bar)
634 int lastEventIndex = limit - 1;
635 while (lastEventIndex >= 0 && scaledData.fData[lastEventIndex] == 0) {
636 lastEventIndex--;
637 }
638 lastEventIndex += (lastEventIndex < limit - 1) ? 1 : 0;
639 drawDelimiter(imageGC, fLastEventColor, height, lastEventIndex);
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);
658 } catch (final Exception e) {
659 // Do nothing
660 }
661 }
662
663 private static void drawDelimiter(final GC imageGC, final Color color,
664 final int height, final int index) {
665 imageGC.setBackground(color);
666 final int dash = height / 4;
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
678 public void keyPressed(final KeyEvent event) {
679 moveCursor(event.keyCode);
680 }
681
682 @Override
683 public void keyReleased(final KeyEvent event) {
684 }
685
686 // ------------------------------------------------------------------------
687 // MouseListener
688 // ------------------------------------------------------------------------
689
690 @Override
691 public void mouseDoubleClick(final MouseEvent event) {
692 }
693
694 @Override
695 public void mouseDown(final MouseEvent event) {
696 if (fDataModel.getNbEvents() > 0 && fScaledData.fLastBucket >= event.x) {
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();
720 }
721 }
722
723 @Override
724 public void mouseUp(final MouseEvent event) {
725 }
726
727 // ------------------------------------------------------------------------
728 // MouseTrackListener
729 // ------------------------------------------------------------------------
730
731 @Override
732 public void mouseEnter(final MouseEvent event) {
733 }
734
735 @Override
736 public void mouseExit(final MouseEvent event) {
737 }
738
739 @Override
740 public void mouseHover(final MouseEvent event) {
741 if (fDataModel.getNbEvents() > 0 && fScaledData != null && fScaledData.fLastBucket >= event.x) {
742 final String tooltip = formatToolTipLabel(event.x);
743 fCanvas.setToolTipText(tooltip);
744 } else {
745 fCanvas.setToolTipText(null);
746 }
747 }
748
749 private String formatToolTipLabel(final int index) {
750 long startTime = fScaledData.getBucketStartTime(index);
751 // negative values are possible if time values came into the model in decreasing order
752 if (startTime < 0) {
753 startTime = 0;
754 }
755 final long endTime = fScaledData.getBucketEndTime(index);
756 final int nbEvents = (index >= 0) ? fScaledData.fData[index] : 0;
757 final String newLine = System.getProperty("line.separator"); //$NON-NLS-1$
758 final StringBuffer buffer = new StringBuffer();
759 buffer.append("Range = ["); //$NON-NLS-1$
760 buffer.append(new TmfTimestamp(startTime, ITmfTimestamp.NANOSECOND_SCALE).toString());
761 buffer.append(',');
762 buffer.append(new TmfTimestamp(endTime, ITmfTimestamp.NANOSECOND_SCALE).toString());
763 buffer.append(')');
764 buffer.append(newLine);
765 buffer.append("Event count = "); //$NON-NLS-1$
766 buffer.append(nbEvents);
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 }
773 return buffer.toString();
774 }
775
776 // ------------------------------------------------------------------------
777 // ControlListener
778 // ------------------------------------------------------------------------
779
780 @Override
781 public void controlMoved(final ControlEvent event) {
782 fDataModel.complete();
783 }
784
785 @Override
786 public void controlResized(final ControlEvent event) {
787 fDataModel.complete();
788 }
789
790 // ------------------------------------------------------------------------
791 // Signal Handlers
792 // ------------------------------------------------------------------------
793
794 /**
795 * Format the timestamp and update the display
796 *
797 * @param signal
798 * the incoming signal
799 * @since 2.0
800 */
801 @TmfSignalHandler
802 public void timestampFormatUpdated(TmfTimestampFormatUpdateSignal signal) {
803 if (fDataModel.getNbEvents() == 0) {
804 return;
805 }
806
807 String newTS = TmfTimestampFormat.getDefaulTimeFormat().format(fDataModel.getFirstBucketTime());
808 fTimeRangeStartText.setText(newTS);
809
810 newTS = TmfTimestampFormat.getDefaulTimeFormat().format(fDataModel.getEndTime());
811 fTimeRangeEndText.setText(newTS);
812
813 fComposite.layout();
814 }
815
816 }
This page took 0.052969 seconds and 5 git commands to generate.