1 /*******************************************************************************
2 * Copyright (c) 2011, 2013 Ericsson
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
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 *******************************************************************************/
16 package org
.eclipse
.linuxtools
.tmf
.ui
.views
.histogram
;
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
;
54 * Re-usable histogram widget.
56 * It has the following features:
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)
63 * The widget also has 2 'markers' to identify:
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)
69 * Clicking on the histogram will select the current event at the mouse
72 * Once the histogram is selected, there is some limited keyboard support:
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
79 * Finally, when the mouse hovers over the histogram, a tool tip showing the
80 * following information about the corresponding histogram bar time range:
82 * <li>start of the time range
83 * <li>end of the time range
84 * <li>number of events in that time range
88 * @author Francois Chouinard
90 public abstract class Histogram
implements ControlListener
, PaintListener
, KeyListener
, MouseListener
, MouseTrackListener
, IHistogramModelListener
{
92 // ------------------------------------------------------------------------
94 // ------------------------------------------------------------------------
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
);
105 // ------------------------------------------------------------------------
107 // ------------------------------------------------------------------------
110 * The parent TMF view.
112 protected TmfView fParentView
;
114 private Composite fComposite
;
117 // Histogram text fields
118 private Text fMaxNbEventsText
;
119 private Text fMinNbEventsText
;
120 private Text fTimeRangeStartText
;
121 private Text fTimeRangeEndText
;
124 * Histogram drawing area
126 protected Canvas fCanvas
;
129 * The histogram data model.
131 protected final HistogramDataModel fDataModel
;
134 * The histogram data model scaled to current resolution and screen width.
136 protected HistogramScaledData fScaledData
;
139 * The current event value
141 protected long fCurrentEventTime
= 0L;
144 * The current selection begin time
146 private long fSelectionBegin
= 0L;
149 * The current selection end time
151 private long fSelectionEnd
= 0L;
153 // ------------------------------------------------------------------------
155 // ------------------------------------------------------------------------
160 * @param view A reference to the parent TMF view.
161 * @param parent A parent composite
163 public Histogram(final TmfView view
, final Composite parent
) {
166 fComposite
= createWidget(parent
);
167 fDataModel
= new HistogramDataModel();
168 fDataModel
.addHistogramListener(this);
171 fCanvas
.addControlListener(this);
172 fCanvas
.addPaintListener(this);
173 fCanvas
.addKeyListener(this);
174 fCanvas
.addMouseListener(this);
175 fCanvas
.addMouseTrackListener(this);
177 TmfSignalManager
.register(this);
181 * Dispose resources and unregisters listeners.
183 public void dispose() {
184 TmfSignalManager
.deregister(this);
186 fHistoBarColor
.dispose();
187 fLastEventColor
.dispose();
188 fDataModel
.removeHistogramListener(this);
191 private Composite
createWidget(final Composite parent
) {
193 final Color labelColor
= parent
.getBackground();
194 fFont
= adjustFont(parent
);
196 final int initalWidth
= 10;
198 // --------------------------------------------------------------------
199 // Define the histogram
200 // --------------------------------------------------------------------
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
280 FocusListener listener
= new FocusAdapter() {
282 public void focusGained(FocusEvent e
) {
286 fMaxNbEventsText
.addFocusListener(listener
);
287 fMinNbEventsText
.addFocusListener(listener
);
288 fTimeRangeStartText
.addFocusListener(listener
);
289 fTimeRangeEndText
.addFocusListener(listener
);
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());
302 // ------------------------------------------------------------------------
304 // ------------------------------------------------------------------------
307 * Returns the start time (equal first bucket time)
308 * @return the start time.
310 public long getStartTime() {
311 return fDataModel
.getFirstBucketTime();
315 * Returns the end time.
316 * @return the end time.
318 public long getEndTime() {
319 return fDataModel
.getEndTime();
323 * Returns the time limit (end of last bucket)
324 * @return the time limit.
326 public long getTimeLimit() {
327 return fDataModel
.getTimeLimit();
331 * Returns a data model reference.
332 * @return data model.
334 public HistogramDataModel
getDataModel() {
339 * Returns the text control for the maximum of events in one bar
341 * @return the text control
344 public Text
getMaxNbEventsText() {
345 return fMaxNbEventsText
;
348 // ------------------------------------------------------------------------
350 // ------------------------------------------------------------------------
352 * Updates the time range.
353 * @param startTime A start time
354 * @param endTime A end time.
356 public abstract void updateTimeRange(long startTime
, long endTime
);
359 * Clear the histogram and reset the data
361 public void clear() {
363 synchronized (fDataModel
) {
369 * Increase the histogram bucket corresponding to [timestamp]
371 * @param eventCount The new event count
372 * @param timestamp The latest timestamp
374 public void countEvent(final long eventCount
, final long timestamp
) {
375 fDataModel
.countEvent(eventCount
, timestamp
);
379 * Sets the current event time and refresh the display
381 * @param timestamp The time of the current event
382 * @deprecated As of 2.1, use {@link #setSelection(long, long)}
385 public void setCurrentEvent(final long timestamp
) {
386 fSelectionBegin
= (timestamp
> 0) ? timestamp
: 0;
387 fSelectionEnd
= (timestamp
> 0) ? timestamp
: 0;
388 fDataModel
.setSelectionNotifyListeners(timestamp
, timestamp
);
392 * Sets the current selection time range and refresh the display
394 * @param beginTime The begin time of the current selection
395 * @param endTime The end time of the current selection
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
);
405 * Computes the timestamp of the bucket at [offset]
407 * @param offset offset from the left on the histogram
408 * @return the start timestamp of the corresponding bucket
410 public synchronized long getTimestamp(final int offset
) {
411 assert offset
> 0 && offset
< fScaledData
.fWidth
;
413 return fDataModel
.getFirstBucketTime() + fScaledData
.fBucketDuration
* offset
;
414 } catch (final Exception e
) {
415 return 0; // TODO: Fix that racing condition (NPE)
420 * Computes the offset of the timestamp in the histogram
422 * @param timestamp the timestamp
423 * @return the offset of the corresponding bucket (-1 if invalid)
425 public synchronized int getOffset(final long timestamp
) {
426 if (timestamp
< fDataModel
.getFirstBucketTime() || timestamp
> fDataModel
.getEndTime()) {
429 return (int) ((timestamp
- fDataModel
.getFirstBucketTime()) / fScaledData
.fBucketDuration
);
433 * Move the currently selected bar cursor to a non-empty bucket.
435 * @param keyCode the SWT key code
437 protected void moveCursor(final int keyCode
) {
444 while (index
< fScaledData
.fLastBucket
&& fScaledData
.fData
[index
] == 0) {
447 if (index
< fScaledData
.fLastBucket
) {
448 fScaledData
.fSelectionBeginBucket
= index
;
452 case SWT
.ARROW_RIGHT
:
453 index
= Math
.max(0, fScaledData
.fSelectionBeginBucket
+ 1);
454 while (index
< fScaledData
.fWidth
&& fScaledData
.fData
[index
] == 0) {
457 if (index
< fScaledData
.fLastBucket
) {
458 fScaledData
.fSelectionBeginBucket
= index
;
463 index
= fScaledData
.fLastBucket
;
464 while (index
>= 0 && fScaledData
.fData
[index
] == 0) {
468 fScaledData
.fSelectionBeginBucket
= index
;
473 index
= Math
.min(fScaledData
.fLastBucket
- 1, fScaledData
.fSelectionBeginBucket
- 1);
474 while (index
>= 0 && fScaledData
.fData
[index
] == 0) {
478 fScaledData
.fSelectionBeginBucket
= index
;
486 fScaledData
.fSelectionEndBucket
= fScaledData
.fSelectionBeginBucket
;
487 fSelectionBegin
= getTimestamp(fScaledData
.fSelectionBeginBucket
);
488 fSelectionEnd
= fSelectionBegin
;
489 updateSelectionTime();
493 * Refresh the histogram display
496 public void modelUpdated() {
497 if (!fCanvas
.isDisposed() && fCanvas
.getDisplay() != null) {
498 fCanvas
.getDisplay().asyncExec(new Runnable() {
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) {
508 fDataModel
.setSelection(fSelectionBegin
, fSelectionEnd
);
509 fScaledData
= fDataModel
.scaleTo(canvasWidth
, canvasHeight
, 1);
510 synchronized (fDataModel
) {
511 if (fScaledData
!= null) {
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();
528 * Add a mouse wheel listener to the histogram
529 * @param listener the mouse wheel listener
532 public void addMouseWheelListener(MouseWheelListener listener
) {
533 fCanvas
.addMouseWheelListener(listener
);
537 * Remove a mouse wheel listener from the histogram
538 * @param listener the mouse wheel listener
541 public void removeMouseWheelListener(MouseWheelListener listener
) {
542 fCanvas
.removeMouseWheelListener(listener
);
545 // ------------------------------------------------------------------------
547 // ------------------------------------------------------------------------
549 private void updateSelectionTime() {
550 fSelectionBegin
= Math
.min(fSelectionBegin
, fDataModel
.getEndTime());
551 fSelectionEnd
= Math
.min(fSelectionEnd
, fDataModel
.getEndTime());
552 ((HistogramView
) fParentView
).updateSelectionTime(fSelectionBegin
, fSelectionEnd
);
556 * Update the range text controls
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()));
563 fTimeRangeStartText
.setText(""); //$NON-NLS-1$
564 fTimeRangeEndText
.setText(""); //$NON-NLS-1$
568 // ------------------------------------------------------------------------
570 // ------------------------------------------------------------------------
572 * Image key string for the canvas.
574 protected final String IMAGE_KEY
= "double-buffer-image"; //$NON-NLS-1$
577 public void paintControl(final PaintEvent event
) {
580 final int canvasWidth
= fCanvas
.getBounds().width
;
581 final int canvasHeight
= fCanvas
.getBounds().height
;
583 // Make sure we have something to draw upon
584 if (canvasWidth
<= 0 || canvasHeight
<= 0) {
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
);
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);
602 private void formatImage(final GC imageGC
, final Image image
) {
604 if (fScaledData
== null) {
608 final HistogramScaledData scaledData
= new HistogramScaledData(fScaledData
);
611 // Get drawing boundaries
612 final int width
= image
.getBounds().width
;
613 final int height
= image
.getBounds().height
;
615 // Clear the drawing area
616 imageGC
.setBackground(fBackgroundColor
);
617 imageGC
.fillRectangle(0, 0, image
.getBounds().width
+ 1, image
.getBounds().height
+ 1);
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
);
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);
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);
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
);
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
);
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
);
659 final int endBucket
= scaledData
.fSelectionEndBucket
;
660 if (endBucket
>= 0 && endBucket
< limit
&& endBucket
!= beginBucket
) {
661 imageGC
.drawLine(endBucket
, 0, endBucket
, height
);
663 if (endBucket
- beginBucket
> 1) {
664 imageGC
.fillRectangle(beginBucket
+ 1, 0, endBucket
- beginBucket
- 1, height
);
666 imageGC
.setAlpha(alpha
);
667 } catch (final Exception e
) {
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
);
682 // ------------------------------------------------------------------------
684 // ------------------------------------------------------------------------
687 public void keyPressed(final KeyEvent event
) {
688 moveCursor(event
.keyCode
);
692 public void keyReleased(final KeyEvent event
) {
695 // ------------------------------------------------------------------------
697 // ------------------------------------------------------------------------
700 public void mouseDoubleClick(final MouseEvent event
) {
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
);
717 fScaledData
.fSelectionEndBucket
= event
.x
;
718 fSelectionEnd
= getTimestamp(event
.x
);
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
);
724 fScaledData
.fSelectionEndBucket
= event
.x
;
725 fSelectionEnd
= getTimestamp(event
.x
);
728 updateSelectionTime();
733 public void mouseUp(final MouseEvent event
) {
736 // ------------------------------------------------------------------------
737 // MouseTrackListener
738 // ------------------------------------------------------------------------
741 public void mouseEnter(final MouseEvent event
) {
745 public void mouseExit(final MouseEvent event
) {
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
);
754 fCanvas
.setToolTipText(null);
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
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());
771 buffer
.append(new TmfTimestamp(endTime
, ITmfTimestamp
.NANOSECOND_SCALE
).toString());
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
);
782 return buffer
.toString();
785 // ------------------------------------------------------------------------
787 // ------------------------------------------------------------------------
790 public void controlMoved(final ControlEvent event
) {
791 fDataModel
.complete();
795 public void controlResized(final ControlEvent event
) {
796 fDataModel
.complete();
799 // ------------------------------------------------------------------------
801 // ------------------------------------------------------------------------
804 * Format the timestamp and update the display
807 * the incoming signal
811 public void timestampFormatUpdated(TmfTimestampFormatUpdateSignal signal
) {
812 if (fDataModel
.getNbEvents() == 0) {
816 updateRangeTextControls();