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