~ubuntu-branches/ubuntu/trusty/eclipse-linuxtools/trusty

« back to all changes in this revision

Viewing changes to lttng/org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/tmf/ui/views/histogram/Histogram.java

  • Committer: Package Import Robot
  • Author(s): Jakub Adam
  • Date: 2012-06-29 12:07:30 UTC
  • Revision ID: package-import@ubuntu.com-20120629120730-bfri1xys1i71dpn6
Tags: upstream-1.0.0
ImportĀ upstreamĀ versionĀ 1.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*******************************************************************************
 
2
 * Copyright (c) 2011, 2012 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 - Initial API and implementation
 
13
 *******************************************************************************/
 
14
 
 
15
package org.eclipse.linuxtools.tmf.ui.views.histogram;
 
16
 
 
17
import org.eclipse.linuxtools.tmf.ui.views.TmfView;
 
18
import org.eclipse.swt.SWT;
 
19
import org.eclipse.swt.events.ControlEvent;
 
20
import org.eclipse.swt.events.ControlListener;
 
21
import org.eclipse.swt.events.KeyEvent;
 
22
import org.eclipse.swt.events.KeyListener;
 
23
import org.eclipse.swt.events.MouseEvent;
 
24
import org.eclipse.swt.events.MouseListener;
 
25
import org.eclipse.swt.events.MouseTrackListener;
 
26
import org.eclipse.swt.events.PaintEvent;
 
27
import org.eclipse.swt.events.PaintListener;
 
28
import org.eclipse.swt.graphics.Color;
 
29
import org.eclipse.swt.graphics.Font;
 
30
import org.eclipse.swt.graphics.FontData;
 
31
import org.eclipse.swt.graphics.GC;
 
32
import org.eclipse.swt.graphics.Image;
 
33
import org.eclipse.swt.layout.GridData;
 
34
import org.eclipse.swt.layout.GridLayout;
 
35
import org.eclipse.swt.widgets.Canvas;
 
36
import org.eclipse.swt.widgets.Composite;
 
37
import org.eclipse.swt.widgets.Display;
 
38
import org.eclipse.swt.widgets.Text;
 
39
 
 
40
/**
 
41
 * Re-usable histogram widget.
 
42
 * 
 
43
 * It has the following features:
 
44
 * <ul>
 
45
 * <li>Y-axis labels displaying min/max count values
 
46
 * <li>X-axis labels displaying time range
 
47
 * <li>a histogram displaying the distribution of values over time (note that
 
48
 * the histogram might not necessarily fill the whole canvas)
 
49
 * </ul>
 
50
 * The widget also has 2 'markers' to identify:
 
51
 * <ul>
 
52
 * <li>a red dashed line over the bar that contains the currently selected event
 
53
 * <li>a dark red dashed line that delimits the right end of the histogram (if
 
54
 * it doesn't fill the canvas)
 
55
 * </ul>
 
56
 * Clicking on the histogram will select the current event at the mouse
 
57
 * location.
 
58
 * <p>
 
59
 * Once the histogram is selected, there is some limited keyboard support:
 
60
 * <ul>
 
61
 * <li>Home: go to the first histogram bar
 
62
 * <li>End: go to the last histogram bar
 
63
 * <li>Left: go to the previous histogram
 
64
 * <li>Right: go to the next histogram bar
 
65
 * </ul>
 
66
 * Finally, when the mouse hovers over the histogram, a tool tip showing the
 
67
 * following information about the corresponding histogram bar time range:
 
68
 * <ul>
 
69
 * <li>start of the time range
 
70
 * <li>end of the time range
 
71
 * <li>number of events in that time range
 
72
 * </ul>
 
73
 * 
 
74
 * @version 1.0
 
75
 * @author Francois Chouinard
 
76
 */
 
77
public abstract class Histogram implements ControlListener, PaintListener, KeyListener, MouseListener, MouseTrackListener, IHistogramModelListener {
 
78
 
 
79
    // ------------------------------------------------------------------------
 
80
    // Constants
 
81
    // ------------------------------------------------------------------------
 
82
 
 
83
    // Histogram colors
 
84
    private final Color fBackgroundColor = Display.getCurrent().getSystemColor(SWT.COLOR_WHITE);
 
85
    private final Color fCurrentEventColor = Display.getCurrent().getSystemColor(SWT.COLOR_RED);
 
86
    private final Color fLastEventColor = Display.getCurrent().getSystemColor(SWT.COLOR_DARK_RED);
 
87
    private final Color fHistoBarColor = new Color(Display.getDefault(), 74, 112, 139);
 
88
 
 
89
    // Timestamp scale (nanosecond)
 
90
    /**
 
91
     * The time scale of the histogram (nano seconds)
 
92
     */
 
93
    public static final byte TIME_SCALE = -9;
 
94
 
 
95
    /**
 
96
     * The histogram bar width (number of pixels).
 
97
     */
 
98
    public static final int HISTOGRAM_BAR_WIDTH = 1;
 
99
 
 
100
    // ------------------------------------------------------------------------
 
101
    // Attributes
 
102
    // ------------------------------------------------------------------------
 
103
 
 
104
    // Owner view
 
105
    /**
 
106
     * The parent TMF view.
 
107
     */
 
108
    protected TmfView fParentView;
 
109
 
 
110
    // Histogram text fields
 
111
    private Text fMaxNbEventsText;
 
112
    private Text fMinNbEventsText;
 
113
    private Text fTimeRangeStartText;
 
114
    private Text fTimeRangeEndText;
 
115
 
 
116
    /**
 
117
     *  Histogram drawing area
 
118
     */
 
119
    protected Canvas fCanvas;
 
120
    /**
 
121
     *  The histogram data model.
 
122
     */
 
123
    protected final HistogramDataModel fDataModel;
 
124
    /**
 
125
     * The histogram data model scaled to current resolution and screen width. 
 
126
     */
 
127
    protected HistogramScaledData fScaledData;
 
128
 
 
129
    protected long fCurrentEventTime = 0;
 
130
 
 
131
    // ------------------------------------------------------------------------
 
132
    // Construction
 
133
    // ------------------------------------------------------------------------
 
134
 
 
135
    /**
 
136
     * Standard constructor.
 
137
     * 
 
138
     * @param view A reference to the parent TMF view.
 
139
     * @param parent A parent composite
 
140
     */
 
141
    public Histogram(final TmfView view, final Composite parent) {
 
142
        fParentView = view;
 
143
 
 
144
        createWidget(parent);
 
145
        fDataModel = new HistogramDataModel();
 
146
        fDataModel.addHistogramListener(this);
 
147
        clear();
 
148
 
 
149
        fCanvas.addControlListener(this);
 
150
        fCanvas.addPaintListener(this);
 
151
        fCanvas.addKeyListener(this);
 
152
        fCanvas.addMouseListener(this);
 
153
        fCanvas.addMouseTrackListener(this);
 
154
    }
 
155
 
 
156
    /**
 
157
     * Dispose resources and deregisters listeners.
 
158
     */
 
159
    public void dispose() {
 
160
        fHistoBarColor.dispose();
 
161
        fDataModel.removeHistogramListener(this);
 
162
    }
 
163
 
 
164
    private void createWidget(final Composite parent) {
 
165
 
 
166
        final Color labelColor = parent.getBackground();
 
167
        final Font fFont = adjustFont(parent);
 
168
 
 
169
        final int initalWidth = 10;
 
170
 
 
171
        // --------------------------------------------------------------------
 
172
        // Define the histogram
 
173
        // --------------------------------------------------------------------
 
174
 
 
175
        final GridLayout gridLayout = new GridLayout();
 
176
        gridLayout.numColumns = 3;
 
177
        gridLayout.marginHeight = 0;
 
178
        gridLayout.marginWidth = 0;
 
179
        gridLayout.marginTop = 0;
 
180
        gridLayout.horizontalSpacing = 0;
 
181
        gridLayout.verticalSpacing = 0;
 
182
        gridLayout.marginLeft = 0;
 
183
        gridLayout.marginRight = 0;
 
184
        final Composite composite = new Composite(parent, SWT.FILL);
 
185
        composite.setLayout(gridLayout);
 
186
 
 
187
        // Use all the horizontal space
 
188
        GridData gridData = new GridData();
 
189
        gridData.horizontalAlignment = SWT.FILL;
 
190
        gridData.verticalAlignment = SWT.FILL;
 
191
        gridData.grabExcessHorizontalSpace = true;
 
192
        composite.setLayoutData(gridData);
 
193
 
 
194
        // Y-axis max event
 
195
        gridData = new GridData();
 
196
        gridData.horizontalAlignment = SWT.RIGHT;
 
197
        gridData.verticalAlignment = SWT.TOP;
 
198
        fMaxNbEventsText = new Text(composite, SWT.READ_ONLY | SWT.RIGHT);
 
199
        fMaxNbEventsText.setFont(fFont);
 
200
        fMaxNbEventsText.setBackground(labelColor);
 
201
        fMaxNbEventsText.setEditable(false);
 
202
        fMaxNbEventsText.setText("0"); //$NON-NLS-1$
 
203
        fMaxNbEventsText.setLayoutData(gridData);
 
204
 
 
205
        // Histogram itself
 
206
        gridData = new GridData();
 
207
        gridData.horizontalSpan = 2;
 
208
        gridData.verticalSpan = 2;
 
209
        gridData.horizontalAlignment = SWT.FILL;
 
210
        gridData.verticalAlignment = SWT.FILL;
 
211
        gridData.grabExcessHorizontalSpace = true;
 
212
        fCanvas = new Canvas(composite, SWT.BORDER | SWT.DOUBLE_BUFFERED);
 
213
        fCanvas.setLayoutData(gridData);
 
214
 
 
215
        // Y-axis min event (always 0...)
 
216
        gridData = new GridData();
 
217
        gridData.horizontalAlignment = SWT.RIGHT;
 
218
        gridData.verticalAlignment = SWT.BOTTOM;
 
219
        fMinNbEventsText = new Text(composite, SWT.READ_ONLY | SWT.RIGHT);
 
220
        fMinNbEventsText.setFont(fFont);
 
221
        fMinNbEventsText.setBackground(labelColor);
 
222
        fMinNbEventsText.setEditable(false);
 
223
        fMinNbEventsText.setText("0"); //$NON-NLS-1$
 
224
        fMinNbEventsText.setLayoutData(gridData);
 
225
 
 
226
        // Dummy cell
 
227
        gridData = new GridData(initalWidth, SWT.DEFAULT);
 
228
        gridData.horizontalAlignment = SWT.RIGHT;
 
229
        gridData.verticalAlignment = SWT.BOTTOM;
 
230
        final Text dummyText = new Text(composite, SWT.READ_ONLY);
 
231
        dummyText.setFont(fFont);
 
232
        dummyText.setBackground(labelColor);
 
233
        dummyText.setEditable(false);
 
234
        dummyText.setText(""); //$NON-NLS-1$
 
235
        dummyText.setLayoutData(gridData);
 
236
 
 
237
        // Window range start time
 
238
        gridData = new GridData();
 
239
        gridData.horizontalAlignment = SWT.LEFT;
 
240
        gridData.verticalAlignment = SWT.BOTTOM;
 
241
        fTimeRangeStartText = new Text(composite, SWT.READ_ONLY);
 
242
        fTimeRangeStartText.setFont(fFont);
 
243
        fTimeRangeStartText.setBackground(labelColor);
 
244
        fTimeRangeStartText.setText(HistogramUtils.nanosecondsToString(0));
 
245
        fTimeRangeStartText.setLayoutData(gridData);
 
246
 
 
247
        // Window range end time
 
248
        gridData = new GridData();
 
249
        gridData.horizontalAlignment = SWT.RIGHT;
 
250
        gridData.verticalAlignment = SWT.BOTTOM;
 
251
        fTimeRangeEndText = new Text(composite, SWT.READ_ONLY);
 
252
        fTimeRangeEndText.setFont(fFont);
 
253
        fTimeRangeEndText.setBackground(labelColor);
 
254
        fTimeRangeEndText.setText(HistogramUtils.nanosecondsToString(0));
 
255
        fTimeRangeEndText.setLayoutData(gridData);
 
256
    }
 
257
 
 
258
    private Font adjustFont(final Composite composite) {
 
259
        // Reduce font size for a more pleasing rendering
 
260
        final int fontSizeAdjustment = -2;
 
261
        final Font font = composite.getFont();
 
262
        final FontData fontData = font.getFontData()[0];
 
263
        return new Font(font.getDevice(), fontData.getName(), fontData.getHeight() + fontSizeAdjustment, fontData.getStyle());
 
264
    }
 
265
 
 
266
    // ------------------------------------------------------------------------
 
267
    // Accessors
 
268
    // ------------------------------------------------------------------------
 
269
 
 
270
    /**
 
271
     * Returns the start time (equal first bucket time).
 
272
     * @return the start time.
 
273
     */
 
274
    public long getStartTime() {
 
275
        return fDataModel.getFirstBucketTime();
 
276
    }
 
277
 
 
278
    /**
 
279
     * Returns the end time.
 
280
     * @return the end time.
 
281
     */
 
282
    public long getEndTime() {
 
283
        return fDataModel.getEndTime();
 
284
    }
 
285
 
 
286
    /**
 
287
     * Returns the time limit (end of last bucket)
 
288
     * @return the time limit.
 
289
     */
 
290
    public long getTimeLimit() {
 
291
        return fDataModel.getTimeLimit();
 
292
    }
 
293
 
 
294
    /** 
 
295
     * Returns a data model reference. 
 
296
     * @return data model.
 
297
     */
 
298
    public HistogramDataModel getDataModel() {
 
299
        return fDataModel;
 
300
    }
 
301
 
 
302
    // ------------------------------------------------------------------------
 
303
    // Operations
 
304
    // ------------------------------------------------------------------------
 
305
    /**
 
306
     * Updates the time range. 
 
307
     * @param startTime A start time
 
308
     * @param endTime A end time.
 
309
     */
 
310
    public abstract void updateTimeRange(long startTime, long endTime);
 
311
 
 
312
    /**
 
313
     * Clear the histogram and reset the data
 
314
     */
 
315
    public void clear() {
 
316
        fDataModel.clear();
 
317
        fScaledData = null;
 
318
    }
 
319
 
 
320
    /**
 
321
     * Increase the histogram bucket corresponding to [timestamp]
 
322
     * 
 
323
     * @param timestamp
 
324
     */
 
325
    public void countEvent(final long eventCount, final long timestamp) {
 
326
        fDataModel.countEvent(eventCount, timestamp);
 
327
    }
 
328
 
 
329
    /**
 
330
     * Sets the current event time and refresh the display
 
331
     * 
 
332
     * @param timestamp
 
333
     */
 
334
    public void setCurrentEvent(final long timestamp) {
 
335
        fCurrentEventTime = (timestamp > 0) ? timestamp : 0;
 
336
        fDataModel.setCurrentEventNotifyListeners(timestamp);
 
337
    }
 
338
 
 
339
    /**
 
340
     * Computes the timestamp of the bucket at [offset]
 
341
     * 
 
342
     * @param offset offset from the left on the histogram
 
343
     * @return the start timestamp of the corresponding bucket
 
344
     */
 
345
    public synchronized long getTimestamp(final int offset) {
 
346
        assert offset > 0 && offset < fScaledData.fWidth;
 
347
        try {
 
348
            return fDataModel.getFirstBucketTime() + fScaledData.fBucketDuration * offset;
 
349
        } catch (final Exception e) {
 
350
            return 0; // TODO: Fix that racing condition (NPE)
 
351
        }
 
352
    }
 
353
 
 
354
    /**
 
355
     * Computes the offset of the timestamp in the histogram
 
356
     * 
 
357
     * @param timestamp the timestamp
 
358
     * @return the offset of the corresponding bucket (-1 if invalid)
 
359
     */
 
360
    public synchronized int getOffset(final long timestamp) {
 
361
        if (timestamp < fDataModel.getFirstBucketTime() || timestamp > fDataModel.getEndTime())
 
362
            return -1;
 
363
        return (int) ((timestamp - fDataModel.getFirstBucketTime()) / fScaledData.fBucketDuration);
 
364
    }
 
365
 
 
366
    /**
 
367
     * Move the currently selected bar cursor to a non-empty bucket.
 
368
     * 
 
369
     * @param keyCode the SWT key code
 
370
     */
 
371
    protected void moveCursor(final int keyCode) {
 
372
 
 
373
        if (fScaledData.fCurrentBucket == HistogramScaledData.OUT_OF_RANGE_BUCKET)
 
374
            return;
 
375
 
 
376
        int index;
 
377
        switch (keyCode) {
 
378
 
 
379
            case SWT.HOME:
 
380
                index = 0;
 
381
                while (index < fScaledData.fLastBucket && fScaledData.fData[index] == 0)
 
382
                    index++;
 
383
                if (index < fScaledData.fLastBucket)
 
384
                    fScaledData.fCurrentBucket = index;
 
385
                break;
 
386
 
 
387
            case SWT.ARROW_RIGHT:
 
388
                index = fScaledData.fCurrentBucket + 1;
 
389
                while (index < fScaledData.fWidth && fScaledData.fData[index] == 0)
 
390
                    index++;
 
391
                if (index < fScaledData.fLastBucket)
 
392
                    fScaledData.fCurrentBucket = index;
 
393
                break;
 
394
 
 
395
            case SWT.END:
 
396
                index = fScaledData.fLastBucket;
 
397
                while (index >= 0 && fScaledData.fData[index] == 0)
 
398
                    index--;
 
399
                if (index >= 0)
 
400
                    fScaledData.fCurrentBucket = index;
 
401
                break;
 
402
 
 
403
            case SWT.ARROW_LEFT:
 
404
                index = fScaledData.fCurrentBucket - 1;
 
405
                while (index >= 0 && fScaledData.fData[index] == 0)
 
406
                    index--;
 
407
                if (index >= 0)
 
408
                    fScaledData.fCurrentBucket = index;
 
409
                break;
 
410
 
 
411
            default:
 
412
                return;
 
413
        }
 
414
 
 
415
        updateCurrentEventTime();
 
416
    }
 
417
 
 
418
    /**
 
419
     * Refresh the histogram display
 
420
     */
 
421
    @Override
 
422
    public void modelUpdated() {
 
423
        if (!fCanvas.isDisposed() && fCanvas.getDisplay() != null)
 
424
            fCanvas.getDisplay().asyncExec(new Runnable() {
 
425
                @Override
 
426
                public void run() {
 
427
                    if (!fCanvas.isDisposed()) {
 
428
                        // Retrieve and normalize the data
 
429
                        final int canvasWidth = fCanvas.getBounds().width;
 
430
                        final int canvasHeight = fCanvas.getBounds().height;
 
431
                        if (canvasWidth <= 0 || canvasHeight <= 0)
 
432
                            return;
 
433
                        fDataModel.setCurrentEvent(fCurrentEventTime);
 
434
                        fScaledData = fDataModel.scaleTo(canvasWidth, canvasHeight, HISTOGRAM_BAR_WIDTH);
 
435
                        synchronized(fScaledData) {
 
436
                            if (fScaledData != null) {
 
437
                                fCanvas.redraw();
 
438
                                // Display histogram and update X-,Y-axis labels
 
439
                                fTimeRangeStartText.setText(HistogramUtils.nanosecondsToString(fDataModel.getFirstBucketTime()));
 
440
                                fTimeRangeEndText.setText(HistogramUtils.nanosecondsToString(fDataModel.getEndTime()));
 
441
                                fMaxNbEventsText.setText(Long.toString(fScaledData.fMaxValue));
 
442
                                // The Y-axis area might need to be re-sized
 
443
                                fMaxNbEventsText.getParent().layout();
 
444
                            }
 
445
                        }
 
446
                    }
 
447
                }
 
448
            });
 
449
    }
 
450
 
 
451
    // ------------------------------------------------------------------------
 
452
    // Helper functions
 
453
    // ------------------------------------------------------------------------
 
454
 
 
455
    private void updateCurrentEventTime() {
 
456
        final long bucketStartTime = getTimestamp(fScaledData.fCurrentBucket);
 
457
        ((HistogramView) fParentView).updateCurrentEventTime(bucketStartTime);
 
458
    }
 
459
 
 
460
    // ------------------------------------------------------------------------
 
461
    // PaintListener
 
462
    // ------------------------------------------------------------------------
 
463
    /**
 
464
     * Image key string for the canvas.
 
465
     */
 
466
    protected final String IMAGE_KEY = "double-buffer-image"; //$NON-NLS-1$
 
467
 
 
468
    @Override
 
469
    public void paintControl(final PaintEvent event) {
 
470
 
 
471
        // Get the geometry
 
472
        final int canvasWidth = fCanvas.getBounds().width;
 
473
        final int canvasHeight = fCanvas.getBounds().height;
 
474
 
 
475
        // Make sure we have something to draw upon
 
476
        if (canvasWidth <= 0 || canvasHeight <= 0)
 
477
            return;
 
478
 
 
479
        // Retrieve image; re-create only if necessary
 
480
        Image image = (Image) fCanvas.getData(IMAGE_KEY);
 
481
        if (image == null || image.getBounds().width != canvasWidth || image.getBounds().height != canvasHeight) {
 
482
            image = new Image(event.display, canvasWidth, canvasHeight);
 
483
            fCanvas.setData(IMAGE_KEY, image);
 
484
        }
 
485
 
 
486
        // Draw the histogram on its canvas
 
487
        final GC imageGC = new GC(image);
 
488
        formatImage(imageGC, image);
 
489
        event.gc.drawImage(image, 0, 0);
 
490
        imageGC.dispose();
 
491
    }
 
492
 
 
493
    private void formatImage(final GC imageGC, final Image image) {
 
494
 
 
495
        if (fScaledData == null)
 
496
            return;
 
497
 
 
498
        final HistogramScaledData scaledData = new HistogramScaledData(fScaledData);
 
499
 
 
500
        try {
 
501
            // Get drawing boundaries
 
502
            final int width = image.getBounds().width;
 
503
            final int height = image.getBounds().height;
 
504
 
 
505
            // Clear the drawing area
 
506
            imageGC.setBackground(fBackgroundColor);
 
507
            imageGC.fillRectangle(0, 0, image.getBounds().width + 1, image.getBounds().height + 1);
 
508
 
 
509
            // Draw the histogram bars
 
510
            imageGC.setBackground(fHistoBarColor);
 
511
            final int limit = width < scaledData.fWidth ? width : scaledData.fWidth;
 
512
            for (int i = 1; i < limit; i++) {
 
513
                final int value = (int) (scaledData.fData[i] * scaledData.fScalingFactor);
 
514
                imageGC.fillRectangle(i, height - value, 1, value);
 
515
            }
 
516
 
 
517
            // Draw the current event bar
 
518
            final int currentBucket = scaledData.fCurrentBucket;
 
519
            if (currentBucket >= 0 && currentBucket < limit)
 
520
                drawDelimiter(imageGC, fCurrentEventColor, height, currentBucket);
 
521
 
 
522
            // Add a dashed line as a delimiter (at the right of the last bar)
 
523
            int lastEventIndex = limit - 1;
 
524
            while (lastEventIndex >= 0 && scaledData.fData[lastEventIndex] == 0)
 
525
                lastEventIndex--;
 
526
            lastEventIndex += (lastEventIndex < limit - 1) ? 1 : 0;
 
527
            drawDelimiter(imageGC, fLastEventColor, height, lastEventIndex);
 
528
        } catch (final Exception e) {
 
529
            // Do nothing
 
530
        }
 
531
    }
 
532
 
 
533
    private void drawDelimiter(final GC imageGC, final Color color, final int height, final int index) {
 
534
        imageGC.setBackground(color);
 
535
        final int dash = height / 4;
 
536
        imageGC.fillRectangle(index, 0 * dash, 1, dash - 1);
 
537
        imageGC.fillRectangle(index, 1 * dash, 1, dash - 1);
 
538
        imageGC.fillRectangle(index, 2 * dash, 1, dash - 1);
 
539
        imageGC.fillRectangle(index, 3 * dash, 1, height - 3 * dash);
 
540
    }
 
541
 
 
542
    // ------------------------------------------------------------------------
 
543
    // KeyListener
 
544
    // ------------------------------------------------------------------------
 
545
 
 
546
    @Override
 
547
    public void keyPressed(final KeyEvent event) {
 
548
        moveCursor(event.keyCode);
 
549
    }
 
550
 
 
551
    @Override
 
552
    public void keyReleased(final KeyEvent event) {
 
553
    }
 
554
 
 
555
    // ------------------------------------------------------------------------
 
556
    // MouseListener
 
557
    // ------------------------------------------------------------------------
 
558
 
 
559
    @Override
 
560
    public void mouseDoubleClick(final MouseEvent event) {
 
561
    }
 
562
 
 
563
    @Override
 
564
    public void mouseDown(final MouseEvent event) {
 
565
        if (fDataModel.getNbEvents() > 0 && fScaledData.fLastBucket >= event.x) {
 
566
            fScaledData.fCurrentBucket = event.x;
 
567
            updateCurrentEventTime();
 
568
        }
 
569
    }
 
570
 
 
571
    @Override
 
572
    public void mouseUp(final MouseEvent event) {
 
573
    }
 
574
 
 
575
    // ------------------------------------------------------------------------
 
576
    // MouseTrackListener
 
577
    // ------------------------------------------------------------------------
 
578
 
 
579
    @Override
 
580
    public void mouseEnter(final MouseEvent event) {
 
581
    }
 
582
 
 
583
    @Override
 
584
    public void mouseExit(final MouseEvent event) {
 
585
    }
 
586
 
 
587
    @Override
 
588
    public void mouseHover(final MouseEvent event) {
 
589
        if (fDataModel.getNbEvents() > 0 && fScaledData != null && fScaledData.fLastBucket >= event.x) {
 
590
            final String tooltip = formatToolTipLabel(event.x);
 
591
            fCanvas.setToolTipText(tooltip);
 
592
        }
 
593
    }
 
594
 
 
595
    private String formatToolTipLabel(final int index) {
 
596
        long startTime = fScaledData.getBucketStartTime(fScaledData.fCurrentBucket);
 
597
        // negative values are possible if time values came into the model in decreasing order
 
598
        if (startTime < 0)
 
599
            startTime = 0;
 
600
        final long endTime = fScaledData.getBucketEndTime(fScaledData.fCurrentBucket);
 
601
        final int nbEvents = (index >= 0) ? fScaledData.fData[index] : 0;
 
602
 
 
603
        final StringBuffer buffer = new StringBuffer();
 
604
        buffer.append("Range = ["); //$NON-NLS-1$
 
605
        buffer.append(HistogramUtils.nanosecondsToString(startTime));
 
606
        buffer.append(","); //$NON-NLS-1$
 
607
        buffer.append(HistogramUtils.nanosecondsToString(endTime));
 
608
        buffer.append(")\n"); //$NON-NLS-1$
 
609
        buffer.append("Event count = "); //$NON-NLS-1$
 
610
        buffer.append(nbEvents);
 
611
        return buffer.toString();
 
612
    }
 
613
 
 
614
    // ------------------------------------------------------------------------
 
615
    // ControlListener
 
616
    // ------------------------------------------------------------------------
 
617
 
 
618
    @Override
 
619
    public void controlMoved(final ControlEvent event) {
 
620
        fDataModel.complete();
 
621
    }
 
622
 
 
623
    @Override
 
624
    public void controlResized(final ControlEvent event) {
 
625
        fDataModel.complete();
 
626
    }
 
627
}