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