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