tmf: Update histogram text controls
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.ui / src / org / eclipse / linuxtools / tmf / ui / views / histogram / Histogram.java
CommitLineData
c392540b 1/*******************************************************************************
c8422608 2 * Copyright (c) 2011, 2013 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
0fcf3b09 13 * Patrick Tasse - Support selection range
c392540b
FC
14 *******************************************************************************/
15
e0752744 16package org.eclipse.linuxtools.tmf.ui.views.histogram;
c392540b 17
f8177ba2
FC
18import org.eclipse.linuxtools.tmf.core.signal.TmfSignalHandler;
19import org.eclipse.linuxtools.tmf.core.signal.TmfSignalManager;
20import org.eclipse.linuxtools.tmf.core.signal.TmfTimestampFormatUpdateSignal;
3bd46eef
AM
21import org.eclipse.linuxtools.tmf.core.timestamp.ITmfTimestamp;
22import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimestamp;
720d67cb 23import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimestampDelta;
3bd46eef 24import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimestampFormat;
c392540b 25import org.eclipse.linuxtools.tmf.ui.views.TmfView;
720d67cb 26import org.eclipse.osgi.util.NLS;
c392540b
FC
27import org.eclipse.swt.SWT;
28import org.eclipse.swt.events.ControlEvent;
29import org.eclipse.swt.events.ControlListener;
65cdf787
PT
30import org.eclipse.swt.events.FocusAdapter;
31import org.eclipse.swt.events.FocusEvent;
32import org.eclipse.swt.events.FocusListener;
c392540b
FC
33import org.eclipse.swt.events.KeyEvent;
34import org.eclipse.swt.events.KeyListener;
35import org.eclipse.swt.events.MouseEvent;
36import org.eclipse.swt.events.MouseListener;
f888477a 37import org.eclipse.swt.events.MouseMoveListener;
c392540b 38import org.eclipse.swt.events.MouseTrackListener;
65cdf787 39import org.eclipse.swt.events.MouseWheelListener;
c392540b
FC
40import org.eclipse.swt.events.PaintEvent;
41import org.eclipse.swt.events.PaintListener;
42import org.eclipse.swt.graphics.Color;
43import org.eclipse.swt.graphics.Font;
44import org.eclipse.swt.graphics.FontData;
45import org.eclipse.swt.graphics.GC;
46import org.eclipse.swt.graphics.Image;
e60df94a 47import org.eclipse.swt.layout.FillLayout;
c392540b
FC
48import org.eclipse.swt.layout.GridData;
49import org.eclipse.swt.layout.GridLayout;
50import org.eclipse.swt.widgets.Canvas;
51import org.eclipse.swt.widgets.Composite;
52import org.eclipse.swt.widgets.Display;
65cdf787 53import org.eclipse.swt.widgets.Label;
c392540b
FC
54import org.eclipse.swt.widgets.Text;
55
56/**
b544077e 57 * Re-usable histogram widget.
20ff3b75 58 *
b544077e 59 * It has the following features:
c392540b
FC
60 * <ul>
61 * <li>Y-axis labels displaying min/max count values
62 * <li>X-axis labels displaying time range
63 * <li>a histogram displaying the distribution of values over time (note that
64 * the histogram might not necessarily fill the whole canvas)
65 * </ul>
66 * The widget also has 2 'markers' to identify:
67 * <ul>
68 * <li>a red dashed line over the bar that contains the currently selected event
69 * <li>a dark red dashed line that delimits the right end of the histogram (if
70 * it doesn't fill the canvas)
71 * </ul>
72 * Clicking on the histogram will select the current event at the mouse
73 * location.
74 * <p>
75 * Once the histogram is selected, there is some limited keyboard support:
76 * <ul>
77 * <li>Home: go to the first histogram bar
78 * <li>End: go to the last histogram bar
79 * <li>Left: go to the previous histogram
80 * <li>Right: go to the next histogram bar
81 * </ul>
82 * Finally, when the mouse hovers over the histogram, a tool tip showing the
83 * following information about the corresponding histogram bar time range:
84 * <ul>
85 * <li>start of the time range
86 * <li>end of the time range
87 * <li>number of events in that time range
88 * </ul>
20ff3b75 89 *
f8177ba2 90 * @version 1.1
b544077e 91 * @author Francois Chouinard
c392540b 92 */
f888477a 93public abstract class Histogram implements ControlListener, PaintListener, KeyListener, MouseListener, MouseMoveListener, MouseTrackListener, IHistogramModelListener {
c392540b
FC
94
95 // ------------------------------------------------------------------------
96 // Constants
97 // ------------------------------------------------------------------------
98
c392540b
FC
99 // Histogram colors
100 private final Color fBackgroundColor = Display.getCurrent().getSystemColor(SWT.COLOR_WHITE);
0fcf3b09
PT
101 private final Color fSelectionForegroundColor = Display.getCurrent().getSystemColor(SWT.COLOR_BLUE);
102 private final Color fSelectionBackgroundColor = Display.getCurrent().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
c392540b
FC
103 private final Color fLastEventColor = Display.getCurrent().getSystemColor(SWT.COLOR_DARK_RED);
104 private final Color fHistoBarColor = new Color(Display.getDefault(), 74, 112, 139);
95aa81ef 105 private final Color fLostEventColor = new Color(Display.getCurrent(), 208, 62, 120);
d418423b 106 private final Color fFillColor = Display.getCurrent().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
79d60771 107 private final Color fTimeRangeColor = new Color(Display.getCurrent(), 255, 128, 0);
c392540b 108
f888477a
PT
109 // Drag states
110 /**
111 * No drag in progress
112 * @since 2.2
113 */
114 protected final int DRAG_NONE = 0;
115 /**
116 * Drag the selection
117 * @since 2.2
118 */
119 protected final int DRAG_SELECTION = 1;
120 /**
121 * Drag the time range
122 * @since 2.2
123 */
124 protected final int DRAG_RANGE = 2;
79d60771
PT
125 /**
126 * Drag the zoom range
127 * @since 2.2
128 */
129 protected final int DRAG_ZOOM = 3;
f888477a 130
c392540b
FC
131 // ------------------------------------------------------------------------
132 // Attributes
133 // ------------------------------------------------------------------------
134
b544077e
BH
135 /**
136 * The parent TMF view.
137 */
c392540b
FC
138 protected TmfView fParentView;
139
da7bdcbc 140 private Composite fComposite;
f8177ba2
FC
141 private Font fFont;
142
c392540b
FC
143 // Histogram text fields
144 private Text fMaxNbEventsText;
145 private Text fMinNbEventsText;
146 private Text fTimeRangeStartText;
147 private Text fTimeRangeEndText;
148
b544077e 149 /**
95aa81ef 150 * Histogram drawing area
b544077e 151 */
c392540b 152 protected Canvas fCanvas;
f8177ba2 153
b544077e 154 /**
95aa81ef 155 * The histogram data model.
b544077e 156 */
c392540b 157 protected final HistogramDataModel fDataModel;
f8177ba2 158
b544077e 159 /**
20ff3b75 160 * The histogram data model scaled to current resolution and screen width.
b544077e 161 */
c392540b
FC
162 protected HistogramScaledData fScaledData;
163
f8177ba2
FC
164 /**
165 * The current event value
166 */
167 protected long fCurrentEventTime = 0L;
c392540b 168
0fcf3b09
PT
169 /**
170 * The current selection begin time
171 */
172 private long fSelectionBegin = 0L;
173
174 /**
175 * The current selection end time
176 */
177 private long fSelectionEnd = 0L;
178
f888477a
PT
179 /**
180 * The drag state
181 * @see #DRAG_NONE
182 * @see #DRAG_SELECTION
183 * @see #DRAG_RANGE
79d60771 184 * @see #DRAG_ZOOM
f888477a
PT
185 * @since 2.2
186 */
187 protected int fDragState = DRAG_NONE;
188
189 /**
190 * The button that started a mouse drag, or 0 if no drag in progress
191 * @since 2.2
192 */
193 protected int fDragButton = 0;
194
cc817e65
PT
195 /**
196 * The bucket display offset
197 */
198 private int fOffset = 0;
199
c392540b
FC
200 // ------------------------------------------------------------------------
201 // Construction
202 // ------------------------------------------------------------------------
203
b544077e 204 /**
f8177ba2 205 * Full constructor.
20ff3b75 206 *
b544077e
BH
207 * @param view A reference to the parent TMF view.
208 * @param parent A parent composite
209 */
09e86496 210 public Histogram(final TmfView view, final Composite parent) {
c392540b
FC
211 fParentView = view;
212
da7bdcbc 213 fComposite = createWidget(parent);
c392540b 214 fDataModel = new HistogramDataModel();
fbd124dd 215 fDataModel.addHistogramListener(this);
c392540b
FC
216 clear();
217
218 fCanvas.addControlListener(this);
219 fCanvas.addPaintListener(this);
220 fCanvas.addKeyListener(this);
221 fCanvas.addMouseListener(this);
222 fCanvas.addMouseTrackListener(this);
f888477a 223 fCanvas.addMouseMoveListener(this);
f8177ba2
FC
224
225 TmfSignalManager.register(this);
c392540b
FC
226 }
227
b544077e 228 /**
f8177ba2 229 * Dispose resources and unregisters listeners.
b544077e 230 */
c392540b 231 public void dispose() {
f8177ba2
FC
232 TmfSignalManager.deregister(this);
233
c392540b 234 fHistoBarColor.dispose();
95aa81ef 235 fLastEventColor.dispose();
79d60771 236 fTimeRangeColor.dispose();
fbd124dd 237 fDataModel.removeHistogramListener(this);
c392540b
FC
238 }
239
da7bdcbc 240 private Composite createWidget(final Composite parent) {
c392540b 241
0301dc43 242 final Color labelColor = parent.getBackground();
f8177ba2 243 fFont = adjustFont(parent);
c392540b
FC
244
245 final int initalWidth = 10;
246
247 // --------------------------------------------------------------------
248 // Define the histogram
249 // --------------------------------------------------------------------
250
09e86496 251 final GridLayout gridLayout = new GridLayout();
c392540b
FC
252 gridLayout.numColumns = 3;
253 gridLayout.marginHeight = 0;
254 gridLayout.marginWidth = 0;
255 gridLayout.marginTop = 0;
256 gridLayout.horizontalSpacing = 0;
257 gridLayout.verticalSpacing = 0;
258 gridLayout.marginLeft = 0;
259 gridLayout.marginRight = 0;
09e86496 260 final Composite composite = new Composite(parent, SWT.FILL);
c392540b
FC
261 composite.setLayout(gridLayout);
262
263 // Use all the horizontal space
264 GridData gridData = new GridData();
265 gridData.horizontalAlignment = SWT.FILL;
266 gridData.verticalAlignment = SWT.FILL;
267 gridData.grabExcessHorizontalSpace = true;
3a790c10 268 gridData.grabExcessVerticalSpace = true;
c392540b
FC
269 composite.setLayoutData(gridData);
270
271 // Y-axis max event
272 gridData = new GridData();
273 gridData.horizontalAlignment = SWT.RIGHT;
274 gridData.verticalAlignment = SWT.TOP;
275 fMaxNbEventsText = new Text(composite, SWT.READ_ONLY | SWT.RIGHT);
276 fMaxNbEventsText.setFont(fFont);
277 fMaxNbEventsText.setBackground(labelColor);
278 fMaxNbEventsText.setEditable(false);
279 fMaxNbEventsText.setText("0"); //$NON-NLS-1$
280 fMaxNbEventsText.setLayoutData(gridData);
281
282 // Histogram itself
e60df94a 283 Composite canvasComposite = new Composite(composite, SWT.BORDER);
c392540b
FC
284 gridData = new GridData();
285 gridData.horizontalSpan = 2;
286 gridData.verticalSpan = 2;
287 gridData.horizontalAlignment = SWT.FILL;
288 gridData.verticalAlignment = SWT.FILL;
3a790c10
PT
289 gridData.heightHint = 0;
290 gridData.widthHint = 0;
c392540b 291 gridData.grabExcessHorizontalSpace = true;
3a790c10 292 gridData.grabExcessVerticalSpace = true;
e60df94a
PT
293 canvasComposite.setLayoutData(gridData);
294 canvasComposite.setLayout(new FillLayout());
295 fCanvas = new Canvas(canvasComposite, SWT.DOUBLE_BUFFERED);
c392540b
FC
296
297 // Y-axis min event (always 0...)
298 gridData = new GridData();
299 gridData.horizontalAlignment = SWT.RIGHT;
300 gridData.verticalAlignment = SWT.BOTTOM;
301 fMinNbEventsText = new Text(composite, SWT.READ_ONLY | SWT.RIGHT);
302 fMinNbEventsText.setFont(fFont);
303 fMinNbEventsText.setBackground(labelColor);
304 fMinNbEventsText.setEditable(false);
305 fMinNbEventsText.setText("0"); //$NON-NLS-1$
306 fMinNbEventsText.setLayoutData(gridData);
307
308 // Dummy cell
309 gridData = new GridData(initalWidth, SWT.DEFAULT);
310 gridData.horizontalAlignment = SWT.RIGHT;
311 gridData.verticalAlignment = SWT.BOTTOM;
65cdf787
PT
312 final Label dummyLabel = new Label(composite, SWT.NONE);
313 dummyLabel.setLayoutData(gridData);
c392540b
FC
314
315 // Window range start time
316 gridData = new GridData();
317 gridData.horizontalAlignment = SWT.LEFT;
318 gridData.verticalAlignment = SWT.BOTTOM;
319 fTimeRangeStartText = new Text(composite, SWT.READ_ONLY);
320 fTimeRangeStartText.setFont(fFont);
321 fTimeRangeStartText.setBackground(labelColor);
c392540b
FC
322 fTimeRangeStartText.setLayoutData(gridData);
323
324 // Window range end time
325 gridData = new GridData();
326 gridData.horizontalAlignment = SWT.RIGHT;
327 gridData.verticalAlignment = SWT.BOTTOM;
328 fTimeRangeEndText = new Text(composite, SWT.READ_ONLY);
329 fTimeRangeEndText.setFont(fFont);
330 fTimeRangeEndText.setBackground(labelColor);
c392540b 331 fTimeRangeEndText.setLayoutData(gridData);
da7bdcbc 332
65cdf787
PT
333 FocusListener listener = new FocusAdapter() {
334 @Override
335 public void focusGained(FocusEvent e) {
336 fCanvas.setFocus();
337 }
338 };
339 fMaxNbEventsText.addFocusListener(listener);
340 fMinNbEventsText.addFocusListener(listener);
341 fTimeRangeStartText.addFocusListener(listener);
342 fTimeRangeEndText.addFocusListener(listener);
343
da7bdcbc 344 return composite;
c392540b
FC
345 }
346
abbdd66a 347 private static Font adjustFont(final Composite composite) {
c392540b 348 // Reduce font size for a more pleasing rendering
09e86496
FC
349 final int fontSizeAdjustment = -2;
350 final Font font = composite.getFont();
351 final FontData fontData = font.getFontData()[0];
c392540b
FC
352 return new Font(font.getDevice(), fontData.getName(), fontData.getHeight() + fontSizeAdjustment, fontData.getStyle());
353 }
354
355 // ------------------------------------------------------------------------
356 // Accessors
357 // ------------------------------------------------------------------------
358
b544077e 359 /**
f8177ba2 360 * Returns the start time (equal first bucket time)
b544077e
BH
361 * @return the start time.
362 */
c392540b 363 public long getStartTime() {
fbd124dd 364 return fDataModel.getFirstBucketTime();
c392540b
FC
365 }
366
b544077e
BH
367 /**
368 * Returns the end time.
369 * @return the end time.
370 */
c392540b
FC
371 public long getEndTime() {
372 return fDataModel.getEndTime();
373 }
374
b544077e
BH
375 /**
376 * Returns the time limit (end of last bucket)
377 * @return the time limit.
378 */
c392540b
FC
379 public long getTimeLimit() {
380 return fDataModel.getTimeLimit();
381 }
09e86496 382
20ff3b75
AM
383 /**
384 * Returns a data model reference.
b544077e
BH
385 * @return data model.
386 */
fbd124dd
BH
387 public HistogramDataModel getDataModel() {
388 return fDataModel;
389 }
c392540b 390
95aa81ef
JCK
391 /**
392 * Returns the text control for the maximum of events in one bar
393 *
394 * @return the text control
46a59db7 395 * @since 2.2
95aa81ef
JCK
396 */
397 public Text getMaxNbEventsText() {
398 return fMaxNbEventsText;
399 }
400
c392540b
FC
401 // ------------------------------------------------------------------------
402 // Operations
403 // ------------------------------------------------------------------------
b544077e 404 /**
20ff3b75 405 * Updates the time range.
b544077e
BH
406 * @param startTime A start time
407 * @param endTime A end time.
408 */
f888477a
PT
409 public void updateTimeRange(long startTime, long endTime) {
410 if (fDragState == DRAG_NONE) {
411 ((HistogramView) fParentView).updateTimeRange(startTime, endTime);
412 }
413 }
c392540b
FC
414
415 /**
416 * Clear the histogram and reset the data
417 */
418 public void clear() {
419 fDataModel.clear();
dcb3cda5
BH
420 synchronized (fDataModel) {
421 fScaledData = null;
422 }
c392540b
FC
423 }
424
425 /**
426 * Increase the histogram bucket corresponding to [timestamp]
20ff3b75 427 *
95aa81ef
JCK
428 * @param eventCount The new event count
429 * @param timestamp The latest timestamp
c392540b 430 */
09e86496 431 public void countEvent(final long eventCount, final long timestamp) {
fbd124dd 432 fDataModel.countEvent(eventCount, timestamp);
c392540b
FC
433 }
434
435 /**
436 * Sets the current event time and refresh the display
20ff3b75 437 *
95aa81ef 438 * @param timestamp The time of the current event
0fcf3b09 439 * @deprecated As of 2.1, use {@link #setSelection(long, long)}
c392540b 440 */
0fcf3b09 441 @Deprecated
09e86496 442 public void setCurrentEvent(final long timestamp) {
0fcf3b09
PT
443 fSelectionBegin = (timestamp > 0) ? timestamp : 0;
444 fSelectionEnd = (timestamp > 0) ? timestamp : 0;
445 fDataModel.setSelectionNotifyListeners(timestamp, timestamp);
446 }
447
448 /**
449 * Sets the current selection time range and refresh the display
450 *
95aa81ef
JCK
451 * @param beginTime The begin time of the current selection
452 * @param endTime The end time of the current selection
0fcf3b09
PT
453 * @since 2.1
454 */
455 public void setSelection(final long beginTime, final long endTime) {
456 fSelectionBegin = (beginTime > 0) ? beginTime : 0;
457 fSelectionEnd = (endTime > 0) ? endTime : 0;
458 fDataModel.setSelectionNotifyListeners(beginTime, endTime);
c392540b
FC
459 }
460
461 /**
462 * Computes the timestamp of the bucket at [offset]
20ff3b75 463 *
c392540b
FC
464 * @param offset offset from the left on the histogram
465 * @return the start timestamp of the corresponding bucket
466 */
09e86496 467 public synchronized long getTimestamp(final int offset) {
c392540b
FC
468 assert offset > 0 && offset < fScaledData.fWidth;
469 try {
cc817e65 470 return fScaledData.fFirstBucketTime + fScaledData.fBucketDuration * offset;
09e86496 471 } catch (final Exception e) {
c392540b
FC
472 return 0; // TODO: Fix that racing condition (NPE)
473 }
474 }
475
476 /**
477 * Computes the offset of the timestamp in the histogram
20ff3b75 478 *
c392540b
FC
479 * @param timestamp the timestamp
480 * @return the offset of the corresponding bucket (-1 if invalid)
481 */
09e86496 482 public synchronized int getOffset(final long timestamp) {
20ff3b75 483 if (timestamp < fDataModel.getFirstBucketTime() || timestamp > fDataModel.getEndTime()) {
c392540b 484 return -1;
20ff3b75 485 }
fbd124dd 486 return (int) ((timestamp - fDataModel.getFirstBucketTime()) / fScaledData.fBucketDuration);
c392540b
FC
487 }
488
cc817e65
PT
489 /**
490 * Set the bucket display offset
491 *
492 * @param offset
493 * the bucket display offset
494 * @since 2.2
495 */
496 protected void setOffset(final int offset) {
497 fOffset = offset;
498 }
499
c392540b
FC
500 /**
501 * Move the currently selected bar cursor to a non-empty bucket.
20ff3b75 502 *
c392540b
FC
503 * @param keyCode the SWT key code
504 */
09e86496 505 protected void moveCursor(final int keyCode) {
c392540b 506
c392540b
FC
507 int index;
508 switch (keyCode) {
509
95aa81ef
JCK
510 case SWT.HOME:
511 index = 0;
512 while (index < fScaledData.fLastBucket && fScaledData.fData[index] == 0) {
513 index++;
514 }
515 if (index < fScaledData.fLastBucket) {
516 fScaledData.fSelectionBeginBucket = index;
517 }
518 break;
c392540b 519
95aa81ef
JCK
520 case SWT.ARROW_RIGHT:
521 index = Math.max(0, fScaledData.fSelectionBeginBucket + 1);
522 while (index < fScaledData.fWidth && fScaledData.fData[index] == 0) {
523 index++;
524 }
525 if (index < fScaledData.fLastBucket) {
526 fScaledData.fSelectionBeginBucket = index;
527 }
528 break;
c392540b 529
95aa81ef
JCK
530 case SWT.END:
531 index = fScaledData.fLastBucket;
532 while (index >= 0 && fScaledData.fData[index] == 0) {
533 index--;
534 }
535 if (index >= 0) {
536 fScaledData.fSelectionBeginBucket = index;
537 }
538 break;
c392540b 539
95aa81ef
JCK
540 case SWT.ARROW_LEFT:
541 index = Math.min(fScaledData.fLastBucket - 1, fScaledData.fSelectionBeginBucket - 1);
542 while (index >= 0 && fScaledData.fData[index] == 0) {
543 index--;
544 }
545 if (index >= 0) {
546 fScaledData.fSelectionBeginBucket = index;
547 }
548 break;
c392540b 549
95aa81ef
JCK
550 default:
551 return;
c392540b
FC
552 }
553
0fcf3b09
PT
554 fScaledData.fSelectionEndBucket = fScaledData.fSelectionBeginBucket;
555 fSelectionBegin = getTimestamp(fScaledData.fSelectionBeginBucket);
556 fSelectionEnd = fSelectionBegin;
557 updateSelectionTime();
c392540b
FC
558 }
559
560 /**
561 * Refresh the histogram display
562 */
fbd124dd
BH
563 @Override
564 public void modelUpdated() {
20ff3b75 565 if (!fCanvas.isDisposed() && fCanvas.getDisplay() != null) {
c392540b
FC
566 fCanvas.getDisplay().asyncExec(new Runnable() {
567 @Override
568 public void run() {
569 if (!fCanvas.isDisposed()) {
570 // Retrieve and normalize the data
09e86496
FC
571 final int canvasWidth = fCanvas.getBounds().width;
572 final int canvasHeight = fCanvas.getBounds().height;
20ff3b75 573 if (canvasWidth <= 0 || canvasHeight <= 0) {
40890fec 574 return;
20ff3b75 575 }
0fcf3b09 576 fDataModel.setSelection(fSelectionBegin, fSelectionEnd);
f8177ba2 577 fScaledData = fDataModel.scaleTo(canvasWidth, canvasHeight, 1);
95aa81ef 578 synchronized (fDataModel) {
0316808c
FC
579 if (fScaledData != null) {
580 fCanvas.redraw();
d418423b
PT
581 // Display histogram and update X-,Y-axis labels
582 updateRangeTextControls();
95aa81ef
JCK
583 long maxNbEvents = HistogramScaledData.hideLostEvents ? fScaledData.fMaxValue : fScaledData.fMaxCombinedValue;
584 fMaxNbEventsText.setText(Long.toString(maxNbEvents));
0316808c 585 // The Y-axis area might need to be re-sized
3a790c10
PT
586 GridData gd = (GridData) fMaxNbEventsText.getLayoutData();
587 gd.widthHint = Math.max(gd.widthHint, fMaxNbEventsText.computeSize(SWT.DEFAULT, SWT.DEFAULT).x);
0316808c
FC
588 fMaxNbEventsText.getParent().layout();
589 }
09e86496 590 }
c392540b
FC
591 }
592 }
593 });
20ff3b75 594 }
c392540b
FC
595 }
596
65cdf787
PT
597 /**
598 * Add a mouse wheel listener to the histogram
599 * @param listener the mouse wheel listener
600 * @since 2.0
601 */
602 public void addMouseWheelListener(MouseWheelListener listener) {
603 fCanvas.addMouseWheelListener(listener);
604 }
605
606 /**
607 * Remove a mouse wheel listener from the histogram
608 * @param listener the mouse wheel listener
609 * @since 2.0
610 */
611 public void removeMouseWheelListener(MouseWheelListener listener) {
612 fCanvas.removeMouseWheelListener(listener);
613 }
614
c392540b
FC
615 // ------------------------------------------------------------------------
616 // Helper functions
617 // ------------------------------------------------------------------------
618
0fcf3b09 619 private void updateSelectionTime() {
720d67cb
PT
620 if (fSelectionBegin > fSelectionEnd) {
621 long end = fSelectionBegin;
622 fSelectionBegin = fSelectionEnd;
623 fSelectionEnd = end;
624 }
0fcf3b09 625 ((HistogramView) fParentView).updateSelectionTime(fSelectionBegin, fSelectionEnd);
c392540b
FC
626 }
627
d418423b
PT
628 /**
629 * Update the range text controls
630 */
631 private void updateRangeTextControls() {
632 if (fDataModel != null && fDataModel.getStartTime() < fDataModel.getEndTime()) {
633 fTimeRangeStartText.setText(TmfTimestampFormat.getDefaulTimeFormat().format(fDataModel.getStartTime()));
634 fTimeRangeEndText.setText(TmfTimestampFormat.getDefaulTimeFormat().format(fDataModel.getEndTime()));
635 } else {
636 fTimeRangeStartText.setText(""); //$NON-NLS-1$
637 fTimeRangeEndText.setText(""); //$NON-NLS-1$
638 }
639 }
640
c392540b
FC
641 // ------------------------------------------------------------------------
642 // PaintListener
643 // ------------------------------------------------------------------------
b544077e
BH
644 /**
645 * Image key string for the canvas.
646 */
c392540b
FC
647 protected final String IMAGE_KEY = "double-buffer-image"; //$NON-NLS-1$
648
649 @Override
09e86496 650 public void paintControl(final PaintEvent event) {
c392540b
FC
651
652 // Get the geometry
09e86496
FC
653 final int canvasWidth = fCanvas.getBounds().width;
654 final int canvasHeight = fCanvas.getBounds().height;
c392540b
FC
655
656 // Make sure we have something to draw upon
20ff3b75 657 if (canvasWidth <= 0 || canvasHeight <= 0) {
c392540b 658 return;
20ff3b75 659 }
c392540b
FC
660
661 // Retrieve image; re-create only if necessary
662 Image image = (Image) fCanvas.getData(IMAGE_KEY);
663 if (image == null || image.getBounds().width != canvasWidth || image.getBounds().height != canvasHeight) {
664 image = new Image(event.display, canvasWidth, canvasHeight);
665 fCanvas.setData(IMAGE_KEY, image);
666 }
667
668 // Draw the histogram on its canvas
09e86496 669 final GC imageGC = new GC(image);
c392540b
FC
670 formatImage(imageGC, image);
671 event.gc.drawImage(image, 0, 0);
672 imageGC.dispose();
673 }
674
09e86496 675 private void formatImage(final GC imageGC, final Image image) {
c392540b 676
20ff3b75 677 if (fScaledData == null) {
c392540b 678 return;
20ff3b75 679 }
c392540b 680
09e86496 681 final HistogramScaledData scaledData = new HistogramScaledData(fScaledData);
c392540b
FC
682
683 try {
684 // Get drawing boundaries
09e86496
FC
685 final int width = image.getBounds().width;
686 final int height = image.getBounds().height;
c392540b
FC
687
688 // Clear the drawing area
689 imageGC.setBackground(fBackgroundColor);
690 imageGC.fillRectangle(0, 0, image.getBounds().width + 1, image.getBounds().height + 1);
691
692 // Draw the histogram bars
09e86496 693 final int limit = width < scaledData.fWidth ? width : scaledData.fWidth;
95aa81ef 694 double factor = HistogramScaledData.hideLostEvents ? scaledData.fScalingFactor : scaledData.fScalingFactorCombined;
e6953950 695 for (int i = 0; i < limit; i++) {
95aa81ef
JCK
696 imageGC.setForeground(fHistoBarColor);
697 final int value = (int) Math.ceil(scaledData.fData[i] * factor);
cc817e65
PT
698 int x = i + fOffset;
699 imageGC.drawLine(x, height - value, x, height);
95aa81ef
JCK
700
701 if (!HistogramScaledData.hideLostEvents) {
702 imageGC.setForeground(fLostEventColor);
703 final int lostEventValue = (int) Math.ceil(scaledData.fLostEventsData[i] * factor);
704 if (lostEventValue != 0) {
705 if (lostEventValue == 1) {
706 // in linux, a line from x to x is not drawn, in windows it is.
cc817e65 707 imageGC.drawPoint(x, height - value - 1);
95aa81ef
JCK
708 } else {
709 // drawing a line is inclusive, so we need to remove 1 from the destination to have the correct length
cc817e65 710 imageGC.drawLine(x, height - value - lostEventValue, x, height - value - 1);
95aa81ef
JCK
711 }
712 }
713 }
c392540b
FC
714 }
715
0fcf3b09
PT
716 // Draw the selection bars
717 int alpha = imageGC.getAlpha();
718 imageGC.setAlpha(100);
719 imageGC.setForeground(fSelectionForegroundColor);
720 imageGC.setBackground(fSelectionBackgroundColor);
cc817e65 721 final int beginBucket = scaledData.fSelectionBeginBucket + fOffset;
0fcf3b09
PT
722 if (beginBucket >= 0 && beginBucket < limit) {
723 imageGC.drawLine(beginBucket, 0, beginBucket, height);
724 }
cc817e65 725 final int endBucket = scaledData.fSelectionEndBucket + fOffset;
0fcf3b09
PT
726 if (endBucket >= 0 && endBucket < limit && endBucket != beginBucket) {
727 imageGC.drawLine(endBucket, 0, endBucket, height);
728 }
f888477a
PT
729 if (Math.abs(endBucket - beginBucket) > 1) {
730 if (endBucket > beginBucket) {
731 imageGC.fillRectangle(beginBucket + 1, 0, endBucket - beginBucket - 1, height);
732 } else {
733 imageGC.fillRectangle(endBucket + 1, 0, beginBucket - endBucket - 1, height);
734 }
0fcf3b09
PT
735 }
736 imageGC.setAlpha(alpha);
f888477a
PT
737
738 // Add a dashed line as a delimiter
739 int delimiterIndex = (int) ((getDataModel().getEndTime() - scaledData.getFirstBucketTime()) / scaledData.fBucketDuration) + 1;
740 drawDelimiter(imageGC, fLastEventColor, height, delimiterIndex);
741
742 // Fill the area to the right of delimiter with background color
743 imageGC.setBackground(fFillColor);
744 imageGC.fillRectangle(delimiterIndex + 1, 0, width - (delimiterIndex + 1), height);
745
09e86496 746 } catch (final Exception e) {
c392540b
FC
747 // Do nothing
748 }
749 }
750
abbdd66a
AM
751 private static void drawDelimiter(final GC imageGC, final Color color,
752 final int height, final int index) {
c392540b 753 imageGC.setBackground(color);
09e86496 754 final int dash = height / 4;
c392540b
FC
755 imageGC.fillRectangle(index, 0 * dash, 1, dash - 1);
756 imageGC.fillRectangle(index, 1 * dash, 1, dash - 1);
757 imageGC.fillRectangle(index, 2 * dash, 1, dash - 1);
758 imageGC.fillRectangle(index, 3 * dash, 1, height - 3 * dash);
759 }
760
79d60771
PT
761 /**
762 * Draw a time range window
763 *
764 * @param imageGC
765 * the GC
766 * @param rangeStartTime
767 * the range start time
768 * @param rangeDuration
769 * the range duration
770 * @since 2.2
771 */
772 protected void drawTimeRangeWindow(GC imageGC, long rangeStartTime, long rangeDuration) {
773
774 // Map times to histogram coordinates
775 long bucketSpan = Math.max(fScaledData.fBucketDuration, 1);
776 long startTime = Math.min(rangeStartTime, rangeStartTime + rangeDuration);
777 int rangeWidth = (int) (Math.abs(rangeDuration) / bucketSpan);
778
779 int left = (int) ((startTime - fDataModel.getFirstBucketTime()) / bucketSpan);
780 int right = left + rangeWidth;
781 int center = (left + right) / 2;
782 int height = fCanvas.getSize().y;
783
784 // Draw the selection window
785 imageGC.setForeground(fTimeRangeColor);
786 imageGC.setLineWidth(1);
787 imageGC.setLineStyle(SWT.LINE_SOLID);
788 imageGC.drawRoundRectangle(left, 0, rangeWidth, height - 1, 15, 15);
789
790 // Fill the selection window
791 imageGC.setBackground(fTimeRangeColor);
792 imageGC.setAlpha(35);
793 imageGC.fillRoundRectangle(left + 1, 1, rangeWidth - 1, height - 2, 15, 15);
794 imageGC.setAlpha(255);
795
796 // Draw the cross hair
797 imageGC.setForeground(fTimeRangeColor);
798 imageGC.setLineWidth(1);
799 imageGC.setLineStyle(SWT.LINE_SOLID);
800
801 int chHalfWidth = ((rangeWidth < 60) ? (rangeWidth * 2) / 3 : 40) / 2;
802 imageGC.drawLine(center - chHalfWidth, height / 2, center + chHalfWidth, height / 2);
803 imageGC.drawLine(center, (height / 2) - chHalfWidth, center, (height / 2) + chHalfWidth);
804 }
805
c392540b
FC
806 // ------------------------------------------------------------------------
807 // KeyListener
808 // ------------------------------------------------------------------------
809
810 @Override
09e86496 811 public void keyPressed(final KeyEvent event) {
c392540b
FC
812 moveCursor(event.keyCode);
813 }
814
815 @Override
09e86496 816 public void keyReleased(final KeyEvent event) {
c392540b
FC
817 }
818
819 // ------------------------------------------------------------------------
820 // MouseListener
821 // ------------------------------------------------------------------------
822
823 @Override
09e86496 824 public void mouseDoubleClick(final MouseEvent event) {
c392540b
FC
825 }
826
827 @Override
09e86496 828 public void mouseDown(final MouseEvent event) {
f888477a
PT
829 if (event.button == 1 && fDragState == DRAG_NONE && fDataModel.getNbEvents() != 0) {
830 fDragState = DRAG_SELECTION;
831 fDragButton = event.button;
832 if ((event.stateMask & SWT.MODIFIER_MASK) == SWT.SHIFT) {
833 if (Math.abs(event.x - fScaledData.fSelectionBeginBucket) < Math.abs(event.x - fScaledData.fSelectionEndBucket)) {
834 fScaledData.fSelectionBeginBucket = fScaledData.fSelectionEndBucket;
835 fSelectionBegin = fSelectionEnd;
0fcf3b09 836 }
f888477a
PT
837 fSelectionEnd = Math.min(getTimestamp(event.x), getEndTime());
838 fScaledData.fSelectionEndBucket = (int) ((fSelectionEnd - fScaledData.fFirstBucketTime) / fScaledData.fBucketDuration);
839 } else {
840 fSelectionBegin = Math.min(getTimestamp(event.x), getEndTime());
841 fScaledData.fSelectionBeginBucket = (int) ((fSelectionBegin - fScaledData.fFirstBucketTime) / fScaledData.fBucketDuration);
842 fSelectionEnd = fSelectionBegin;
843 fScaledData.fSelectionEndBucket = fScaledData.fSelectionBeginBucket;
0fcf3b09 844 }
f888477a 845 fCanvas.redraw();
c392540b
FC
846 }
847 }
848
849 @Override
09e86496 850 public void mouseUp(final MouseEvent event) {
f888477a
PT
851 if (fDragState == DRAG_SELECTION && event.button == fDragButton) {
852 fDragState = DRAG_NONE;
853 fDragButton = 0;
854 updateSelectionTime();
855 }
856 }
857
858 // ------------------------------------------------------------------------
859 // MouseMoveListener
860 // ------------------------------------------------------------------------
861
862 /**
863 * @since 2.2
864 */
865 @Override
866 public void mouseMove(MouseEvent event) {
867 if (fDragState == DRAG_SELECTION && fDataModel.getNbEvents() > 0) {
868 fSelectionEnd = Math.max(getStartTime(), Math.min(getEndTime(), getTimestamp(event.x)));
869 fScaledData.fSelectionEndBucket = (int) ((fSelectionEnd - fScaledData.fFirstBucketTime) / fScaledData.fBucketDuration);
870 fCanvas.redraw();
871 }
c392540b
FC
872 }
873
874 // ------------------------------------------------------------------------
875 // MouseTrackListener
876 // ------------------------------------------------------------------------
877
878 @Override
09e86496 879 public void mouseEnter(final MouseEvent event) {
c392540b
FC
880 }
881
882 @Override
09e86496 883 public void mouseExit(final MouseEvent event) {
c392540b
FC
884 }
885
886 @Override
09e86496 887 public void mouseHover(final MouseEvent event) {
cc817e65
PT
888 if (fDataModel.getNbEvents() > 0 && fScaledData != null) {
889 int delimiterIndex = (int) ((fDataModel.getEndTime() - fScaledData.getFirstBucketTime()) / fScaledData.fBucketDuration) + 1;
890 if (event.x < delimiterIndex) {
891 final String tooltip = formatToolTipLabel(event.x - fOffset);
892 fCanvas.setToolTipText(tooltip);
893 return;
894 }
c392540b 895 }
cc817e65 896 fCanvas.setToolTipText(null);
c392540b
FC
897 }
898
09e86496 899 private String formatToolTipLabel(final int index) {
466857f6 900 long startTime = fScaledData.getBucketStartTime(index);
09e86496 901 // negative values are possible if time values came into the model in decreasing order
20ff3b75 902 if (startTime < 0) {
fbd124dd 903 startTime = 0;
20ff3b75 904 }
466857f6 905 final long endTime = fScaledData.getBucketEndTime(index);
09e86496 906 final int nbEvents = (index >= 0) ? fScaledData.fData[index] : 0;
95aa81ef 907 final String newLine = System.getProperty("line.separator"); //$NON-NLS-1$
09e86496 908 final StringBuffer buffer = new StringBuffer();
720d67cb
PT
909 int selectionBeginBucket = Math.min(fScaledData.fSelectionBeginBucket, fScaledData.fSelectionEndBucket);
910 int selectionEndBucket = Math.max(fScaledData.fSelectionBeginBucket, fScaledData.fSelectionEndBucket);
911 if (selectionBeginBucket <= index && index <= selectionEndBucket && fSelectionBegin != fSelectionEnd) {
912 TmfTimestampDelta delta = new TmfTimestampDelta(Math.abs(fSelectionEnd - fSelectionBegin), ITmfTimestamp.NANOSECOND_SCALE);
913 buffer.append(NLS.bind(Messages.Histogram_selectionSpanToolTip, delta.toString()));
914 buffer.append(newLine);
915 }
916 buffer.append(NLS.bind(Messages.Histogram_bucketRangeToolTip,
917 new TmfTimestamp(startTime, ITmfTimestamp.NANOSECOND_SCALE).toString(),
918 new TmfTimestamp(endTime, ITmfTimestamp.NANOSECOND_SCALE).toString()));
95aa81ef 919 buffer.append(newLine);
720d67cb 920 buffer.append(NLS.bind(Messages.Histogram_eventCountToolTip, nbEvents));
95aa81ef
JCK
921 if (!HistogramScaledData.hideLostEvents) {
922 final int nbLostEvents = (index >= 0) ? fScaledData.fLostEventsData[index] : 0;
923 buffer.append(newLine);
720d67cb 924 buffer.append(NLS.bind(Messages.Histogram_lostEventCountToolTip, nbLostEvents));
95aa81ef 925 }
c392540b
FC
926 return buffer.toString();
927 }
928
929 // ------------------------------------------------------------------------
930 // ControlListener
931 // ------------------------------------------------------------------------
932
933 @Override
09e86496 934 public void controlMoved(final ControlEvent event) {
fbd124dd 935 fDataModel.complete();
c392540b
FC
936 }
937
938 @Override
09e86496 939 public void controlResized(final ControlEvent event) {
fbd124dd 940 fDataModel.complete();
c392540b 941 }
f8177ba2
FC
942
943 // ------------------------------------------------------------------------
944 // Signal Handlers
945 // ------------------------------------------------------------------------
946
947 /**
948 * Format the timestamp and update the display
949 *
95aa81ef
JCK
950 * @param signal
951 * the incoming signal
f8177ba2
FC
952 * @since 2.0
953 */
954 @TmfSignalHandler
955 public void timestampFormatUpdated(TmfTimestampFormatUpdateSignal signal) {
da7bdcbc
PT
956 if (fDataModel.getNbEvents() == 0) {
957 return;
958 }
959
d418423b 960 updateRangeTextControls();
f8177ba2 961
da7bdcbc 962 fComposite.layout();
f8177ba2
FC
963 }
964
c392540b 965}
This page took 0.095426 seconds and 5 git commands to generate.