tmf: remove setting of anti-aliasing in histogram
[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();
06fcc8fe
PT
420 if (fDragState == DRAG_SELECTION) {
421 updateSelectionTime();
422 }
80c930fa
PT
423 fDragState = DRAG_NONE;
424 fDragButton = 0;
dcb3cda5
BH
425 synchronized (fDataModel) {
426 fScaledData = null;
427 }
c392540b
FC
428 }
429
430 /**
431 * Increase the histogram bucket corresponding to [timestamp]
20ff3b75 432 *
95aa81ef
JCK
433 * @param eventCount The new event count
434 * @param timestamp The latest timestamp
c392540b 435 */
09e86496 436 public void countEvent(final long eventCount, final long timestamp) {
fbd124dd 437 fDataModel.countEvent(eventCount, timestamp);
c392540b
FC
438 }
439
440 /**
441 * Sets the current event time and refresh the display
20ff3b75 442 *
95aa81ef 443 * @param timestamp The time of the current event
0fcf3b09 444 * @deprecated As of 2.1, use {@link #setSelection(long, long)}
c392540b 445 */
0fcf3b09 446 @Deprecated
09e86496 447 public void setCurrentEvent(final long timestamp) {
0fcf3b09
PT
448 fSelectionBegin = (timestamp > 0) ? timestamp : 0;
449 fSelectionEnd = (timestamp > 0) ? timestamp : 0;
450 fDataModel.setSelectionNotifyListeners(timestamp, timestamp);
451 }
452
453 /**
454 * Sets the current selection time range and refresh the display
455 *
95aa81ef
JCK
456 * @param beginTime The begin time of the current selection
457 * @param endTime The end time of the current selection
0fcf3b09
PT
458 * @since 2.1
459 */
460 public void setSelection(final long beginTime, final long endTime) {
461 fSelectionBegin = (beginTime > 0) ? beginTime : 0;
462 fSelectionEnd = (endTime > 0) ? endTime : 0;
463 fDataModel.setSelectionNotifyListeners(beginTime, endTime);
c392540b
FC
464 }
465
466 /**
467 * Computes the timestamp of the bucket at [offset]
20ff3b75 468 *
c392540b
FC
469 * @param offset offset from the left on the histogram
470 * @return the start timestamp of the corresponding bucket
471 */
09e86496 472 public synchronized long getTimestamp(final int offset) {
c392540b
FC
473 assert offset > 0 && offset < fScaledData.fWidth;
474 try {
cc817e65 475 return fScaledData.fFirstBucketTime + fScaledData.fBucketDuration * offset;
09e86496 476 } catch (final Exception e) {
c392540b
FC
477 return 0; // TODO: Fix that racing condition (NPE)
478 }
479 }
480
481 /**
482 * Computes the offset of the timestamp in the histogram
20ff3b75 483 *
c392540b
FC
484 * @param timestamp the timestamp
485 * @return the offset of the corresponding bucket (-1 if invalid)
486 */
09e86496 487 public synchronized int getOffset(final long timestamp) {
20ff3b75 488 if (timestamp < fDataModel.getFirstBucketTime() || timestamp > fDataModel.getEndTime()) {
c392540b 489 return -1;
20ff3b75 490 }
fbd124dd 491 return (int) ((timestamp - fDataModel.getFirstBucketTime()) / fScaledData.fBucketDuration);
c392540b
FC
492 }
493
cc817e65
PT
494 /**
495 * Set the bucket display offset
496 *
497 * @param offset
498 * the bucket display offset
499 * @since 2.2
500 */
501 protected void setOffset(final int offset) {
502 fOffset = offset;
503 }
504
c392540b
FC
505 /**
506 * Move the currently selected bar cursor to a non-empty bucket.
20ff3b75 507 *
c392540b
FC
508 * @param keyCode the SWT key code
509 */
09e86496 510 protected void moveCursor(final int keyCode) {
c392540b 511
c392540b
FC
512 int index;
513 switch (keyCode) {
514
95aa81ef
JCK
515 case SWT.HOME:
516 index = 0;
517 while (index < fScaledData.fLastBucket && fScaledData.fData[index] == 0) {
518 index++;
519 }
520 if (index < fScaledData.fLastBucket) {
521 fScaledData.fSelectionBeginBucket = index;
522 }
523 break;
c392540b 524
95aa81ef
JCK
525 case SWT.ARROW_RIGHT:
526 index = Math.max(0, fScaledData.fSelectionBeginBucket + 1);
527 while (index < fScaledData.fWidth && fScaledData.fData[index] == 0) {
528 index++;
529 }
530 if (index < fScaledData.fLastBucket) {
531 fScaledData.fSelectionBeginBucket = index;
532 }
533 break;
c392540b 534
95aa81ef
JCK
535 case SWT.END:
536 index = fScaledData.fLastBucket;
537 while (index >= 0 && fScaledData.fData[index] == 0) {
538 index--;
539 }
540 if (index >= 0) {
541 fScaledData.fSelectionBeginBucket = index;
542 }
543 break;
c392540b 544
95aa81ef
JCK
545 case SWT.ARROW_LEFT:
546 index = Math.min(fScaledData.fLastBucket - 1, fScaledData.fSelectionBeginBucket - 1);
547 while (index >= 0 && fScaledData.fData[index] == 0) {
548 index--;
549 }
550 if (index >= 0) {
551 fScaledData.fSelectionBeginBucket = index;
552 }
553 break;
c392540b 554
95aa81ef
JCK
555 default:
556 return;
c392540b
FC
557 }
558
0fcf3b09
PT
559 fScaledData.fSelectionEndBucket = fScaledData.fSelectionBeginBucket;
560 fSelectionBegin = getTimestamp(fScaledData.fSelectionBeginBucket);
561 fSelectionEnd = fSelectionBegin;
562 updateSelectionTime();
c392540b
FC
563 }
564
565 /**
566 * Refresh the histogram display
567 */
fbd124dd
BH
568 @Override
569 public void modelUpdated() {
20ff3b75 570 if (!fCanvas.isDisposed() && fCanvas.getDisplay() != null) {
c392540b
FC
571 fCanvas.getDisplay().asyncExec(new Runnable() {
572 @Override
573 public void run() {
574 if (!fCanvas.isDisposed()) {
575 // Retrieve and normalize the data
09e86496
FC
576 final int canvasWidth = fCanvas.getBounds().width;
577 final int canvasHeight = fCanvas.getBounds().height;
20ff3b75 578 if (canvasWidth <= 0 || canvasHeight <= 0) {
40890fec 579 return;
20ff3b75 580 }
0fcf3b09 581 fDataModel.setSelection(fSelectionBegin, fSelectionEnd);
f8177ba2 582 fScaledData = fDataModel.scaleTo(canvasWidth, canvasHeight, 1);
95aa81ef 583 synchronized (fDataModel) {
0316808c
FC
584 if (fScaledData != null) {
585 fCanvas.redraw();
d418423b
PT
586 // Display histogram and update X-,Y-axis labels
587 updateRangeTextControls();
95aa81ef
JCK
588 long maxNbEvents = HistogramScaledData.hideLostEvents ? fScaledData.fMaxValue : fScaledData.fMaxCombinedValue;
589 fMaxNbEventsText.setText(Long.toString(maxNbEvents));
0316808c 590 // The Y-axis area might need to be re-sized
3a790c10
PT
591 GridData gd = (GridData) fMaxNbEventsText.getLayoutData();
592 gd.widthHint = Math.max(gd.widthHint, fMaxNbEventsText.computeSize(SWT.DEFAULT, SWT.DEFAULT).x);
0316808c
FC
593 fMaxNbEventsText.getParent().layout();
594 }
09e86496 595 }
c392540b
FC
596 }
597 }
598 });
20ff3b75 599 }
c392540b
FC
600 }
601
65cdf787
PT
602 /**
603 * Add a mouse wheel listener to the histogram
604 * @param listener the mouse wheel listener
605 * @since 2.0
606 */
607 public void addMouseWheelListener(MouseWheelListener listener) {
608 fCanvas.addMouseWheelListener(listener);
609 }
610
611 /**
612 * Remove a mouse wheel listener from the histogram
613 * @param listener the mouse wheel listener
614 * @since 2.0
615 */
616 public void removeMouseWheelListener(MouseWheelListener listener) {
617 fCanvas.removeMouseWheelListener(listener);
618 }
619
c392540b
FC
620 // ------------------------------------------------------------------------
621 // Helper functions
622 // ------------------------------------------------------------------------
623
0fcf3b09 624 private void updateSelectionTime() {
720d67cb
PT
625 if (fSelectionBegin > fSelectionEnd) {
626 long end = fSelectionBegin;
627 fSelectionBegin = fSelectionEnd;
628 fSelectionEnd = end;
629 }
0fcf3b09 630 ((HistogramView) fParentView).updateSelectionTime(fSelectionBegin, fSelectionEnd);
c392540b
FC
631 }
632
d418423b
PT
633 /**
634 * Update the range text controls
635 */
636 private void updateRangeTextControls() {
31d6440d 637 if (fDataModel.getStartTime() < fDataModel.getEndTime()) {
d418423b
PT
638 fTimeRangeStartText.setText(TmfTimestampFormat.getDefaulTimeFormat().format(fDataModel.getStartTime()));
639 fTimeRangeEndText.setText(TmfTimestampFormat.getDefaulTimeFormat().format(fDataModel.getEndTime()));
640 } else {
641 fTimeRangeStartText.setText(""); //$NON-NLS-1$
642 fTimeRangeEndText.setText(""); //$NON-NLS-1$
643 }
644 }
645
c392540b
FC
646 // ------------------------------------------------------------------------
647 // PaintListener
648 // ------------------------------------------------------------------------
b544077e
BH
649 /**
650 * Image key string for the canvas.
651 */
c392540b
FC
652 protected final String IMAGE_KEY = "double-buffer-image"; //$NON-NLS-1$
653
654 @Override
09e86496 655 public void paintControl(final PaintEvent event) {
c392540b
FC
656
657 // Get the geometry
09e86496
FC
658 final int canvasWidth = fCanvas.getBounds().width;
659 final int canvasHeight = fCanvas.getBounds().height;
c392540b
FC
660
661 // Make sure we have something to draw upon
20ff3b75 662 if (canvasWidth <= 0 || canvasHeight <= 0) {
c392540b 663 return;
20ff3b75 664 }
c392540b
FC
665
666 // Retrieve image; re-create only if necessary
667 Image image = (Image) fCanvas.getData(IMAGE_KEY);
668 if (image == null || image.getBounds().width != canvasWidth || image.getBounds().height != canvasHeight) {
669 image = new Image(event.display, canvasWidth, canvasHeight);
670 fCanvas.setData(IMAGE_KEY, image);
671 }
672
673 // Draw the histogram on its canvas
09e86496 674 final GC imageGC = new GC(image);
c392540b
FC
675 formatImage(imageGC, image);
676 event.gc.drawImage(image, 0, 0);
677 imageGC.dispose();
678 }
679
09e86496 680 private void formatImage(final GC imageGC, final Image image) {
c392540b 681
20ff3b75 682 if (fScaledData == null) {
c392540b 683 return;
20ff3b75 684 }
c392540b 685
09e86496 686 final HistogramScaledData scaledData = new HistogramScaledData(fScaledData);
c392540b
FC
687
688 try {
689 // Get drawing boundaries
09e86496
FC
690 final int width = image.getBounds().width;
691 final int height = image.getBounds().height;
c392540b
FC
692
693 // Clear the drawing area
694 imageGC.setBackground(fBackgroundColor);
695 imageGC.fillRectangle(0, 0, image.getBounds().width + 1, image.getBounds().height + 1);
696
697 // Draw the histogram bars
09e86496 698 final int limit = width < scaledData.fWidth ? width : scaledData.fWidth;
95aa81ef 699 double factor = HistogramScaledData.hideLostEvents ? scaledData.fScalingFactor : scaledData.fScalingFactorCombined;
e6953950 700 for (int i = 0; i < limit; i++) {
95aa81ef 701 final int value = (int) Math.ceil(scaledData.fData[i] * factor);
cc817e65 702 int x = i + fOffset;
95aa81ef 703
b10913b2
PT
704 // in Linux, the last pixel in a line is not drawn,
705 // so draw lost events first, one pixel too far
95aa81ef
JCK
706 if (!HistogramScaledData.hideLostEvents) {
707 imageGC.setForeground(fLostEventColor);
708 final int lostEventValue = (int) Math.ceil(scaledData.fLostEventsData[i] * factor);
709 if (lostEventValue != 0) {
b10913b2
PT
710 // drawing a line is inclusive, so we should remove 1 from y2
711 // but we don't because Linux
712 imageGC.drawLine(x, height - value - lostEventValue, x, height - value);
95aa81ef
JCK
713 }
714 }
b10913b2
PT
715
716 // then draw normal events second, to overwrite that extra pixel
717 imageGC.setForeground(fHistoBarColor);
718 imageGC.drawLine(x, height - value, x, height);
c392540b
FC
719 }
720
0fcf3b09
PT
721 // Draw the selection bars
722 int alpha = imageGC.getAlpha();
723 imageGC.setAlpha(100);
724 imageGC.setForeground(fSelectionForegroundColor);
725 imageGC.setBackground(fSelectionBackgroundColor);
cc817e65 726 final int beginBucket = scaledData.fSelectionBeginBucket + fOffset;
0fcf3b09
PT
727 if (beginBucket >= 0 && beginBucket < limit) {
728 imageGC.drawLine(beginBucket, 0, beginBucket, height);
729 }
cc817e65 730 final int endBucket = scaledData.fSelectionEndBucket + fOffset;
0fcf3b09
PT
731 if (endBucket >= 0 && endBucket < limit && endBucket != beginBucket) {
732 imageGC.drawLine(endBucket, 0, endBucket, height);
733 }
f888477a
PT
734 if (Math.abs(endBucket - beginBucket) > 1) {
735 if (endBucket > beginBucket) {
736 imageGC.fillRectangle(beginBucket + 1, 0, endBucket - beginBucket - 1, height);
737 } else {
738 imageGC.fillRectangle(endBucket + 1, 0, beginBucket - endBucket - 1, height);
739 }
0fcf3b09
PT
740 }
741 imageGC.setAlpha(alpha);
f888477a
PT
742
743 // Add a dashed line as a delimiter
744 int delimiterIndex = (int) ((getDataModel().getEndTime() - scaledData.getFirstBucketTime()) / scaledData.fBucketDuration) + 1;
745 drawDelimiter(imageGC, fLastEventColor, height, delimiterIndex);
746
747 // Fill the area to the right of delimiter with background color
748 imageGC.setBackground(fFillColor);
749 imageGC.fillRectangle(delimiterIndex + 1, 0, width - (delimiterIndex + 1), height);
750
09e86496 751 } catch (final Exception e) {
c392540b
FC
752 // Do nothing
753 }
754 }
755
abbdd66a
AM
756 private static void drawDelimiter(final GC imageGC, final Color color,
757 final int height, final int index) {
c392540b 758 imageGC.setBackground(color);
09e86496 759 final int dash = height / 4;
c392540b
FC
760 imageGC.fillRectangle(index, 0 * dash, 1, dash - 1);
761 imageGC.fillRectangle(index, 1 * dash, 1, dash - 1);
762 imageGC.fillRectangle(index, 2 * dash, 1, dash - 1);
763 imageGC.fillRectangle(index, 3 * dash, 1, height - 3 * dash);
764 }
765
79d60771
PT
766 /**
767 * Draw a time range window
768 *
769 * @param imageGC
770 * the GC
771 * @param rangeStartTime
772 * the range start time
773 * @param rangeDuration
774 * the range duration
775 * @since 2.2
776 */
777 protected void drawTimeRangeWindow(GC imageGC, long rangeStartTime, long rangeDuration) {
778
647640df
BH
779 if (fScaledData == null) {
780 return;
781 }
782
79d60771
PT
783 // Map times to histogram coordinates
784 long bucketSpan = Math.max(fScaledData.fBucketDuration, 1);
785 long startTime = Math.min(rangeStartTime, rangeStartTime + rangeDuration);
786 int rangeWidth = (int) (Math.abs(rangeDuration) / bucketSpan);
787
788 int left = (int) ((startTime - fDataModel.getFirstBucketTime()) / bucketSpan);
789 int right = left + rangeWidth;
790 int center = (left + right) / 2;
791 int height = fCanvas.getSize().y;
b10913b2 792 int arc = Math.min(15, rangeWidth);
79d60771
PT
793
794 // Draw the selection window
795 imageGC.setForeground(fTimeRangeColor);
796 imageGC.setLineWidth(1);
797 imageGC.setLineStyle(SWT.LINE_SOLID);
b10913b2 798 imageGC.drawRoundRectangle(left, 0, rangeWidth, height - 1, arc, arc);
79d60771
PT
799
800 // Fill the selection window
801 imageGC.setBackground(fTimeRangeColor);
802 imageGC.setAlpha(35);
b10913b2 803 imageGC.fillRoundRectangle(left + 1, 1, rangeWidth - 1, height - 2, arc, arc);
79d60771
PT
804 imageGC.setAlpha(255);
805
806 // Draw the cross hair
807 imageGC.setForeground(fTimeRangeColor);
808 imageGC.setLineWidth(1);
809 imageGC.setLineStyle(SWT.LINE_SOLID);
810
811 int chHalfWidth = ((rangeWidth < 60) ? (rangeWidth * 2) / 3 : 40) / 2;
812 imageGC.drawLine(center - chHalfWidth, height / 2, center + chHalfWidth, height / 2);
813 imageGC.drawLine(center, (height / 2) - chHalfWidth, center, (height / 2) + chHalfWidth);
814 }
815
c392540b
FC
816 // ------------------------------------------------------------------------
817 // KeyListener
818 // ------------------------------------------------------------------------
819
820 @Override
09e86496 821 public void keyPressed(final KeyEvent event) {
c392540b
FC
822 moveCursor(event.keyCode);
823 }
824
825 @Override
09e86496 826 public void keyReleased(final KeyEvent event) {
c392540b
FC
827 }
828
829 // ------------------------------------------------------------------------
830 // MouseListener
831 // ------------------------------------------------------------------------
832
833 @Override
09e86496 834 public void mouseDoubleClick(final MouseEvent event) {
c392540b
FC
835 }
836
837 @Override
09e86496 838 public void mouseDown(final MouseEvent event) {
31d6440d 839 if (fScaledData != null && event.button == 1 && fDragState == DRAG_NONE && fDataModel.getStartTime() < fDataModel.getEndTime()) {
f888477a
PT
840 fDragState = DRAG_SELECTION;
841 fDragButton = event.button;
842 if ((event.stateMask & SWT.MODIFIER_MASK) == SWT.SHIFT) {
843 if (Math.abs(event.x - fScaledData.fSelectionBeginBucket) < Math.abs(event.x - fScaledData.fSelectionEndBucket)) {
844 fScaledData.fSelectionBeginBucket = fScaledData.fSelectionEndBucket;
845 fSelectionBegin = fSelectionEnd;
0fcf3b09 846 }
f888477a
PT
847 fSelectionEnd = Math.min(getTimestamp(event.x), getEndTime());
848 fScaledData.fSelectionEndBucket = (int) ((fSelectionEnd - fScaledData.fFirstBucketTime) / fScaledData.fBucketDuration);
849 } else {
850 fSelectionBegin = Math.min(getTimestamp(event.x), getEndTime());
851 fScaledData.fSelectionBeginBucket = (int) ((fSelectionBegin - fScaledData.fFirstBucketTime) / fScaledData.fBucketDuration);
852 fSelectionEnd = fSelectionBegin;
853 fScaledData.fSelectionEndBucket = fScaledData.fSelectionBeginBucket;
0fcf3b09 854 }
f888477a 855 fCanvas.redraw();
c392540b
FC
856 }
857 }
858
859 @Override
09e86496 860 public void mouseUp(final MouseEvent event) {
f888477a
PT
861 if (fDragState == DRAG_SELECTION && event.button == fDragButton) {
862 fDragState = DRAG_NONE;
863 fDragButton = 0;
864 updateSelectionTime();
865 }
866 }
867
868 // ------------------------------------------------------------------------
869 // MouseMoveListener
870 // ------------------------------------------------------------------------
871
872 /**
873 * @since 2.2
874 */
875 @Override
876 public void mouseMove(MouseEvent event) {
31d6440d 877 if (fDragState == DRAG_SELECTION && fDataModel.getStartTime() < fDataModel.getEndTime()) {
f888477a
PT
878 fSelectionEnd = Math.max(getStartTime(), Math.min(getEndTime(), getTimestamp(event.x)));
879 fScaledData.fSelectionEndBucket = (int) ((fSelectionEnd - fScaledData.fFirstBucketTime) / fScaledData.fBucketDuration);
880 fCanvas.redraw();
881 }
c392540b
FC
882 }
883
884 // ------------------------------------------------------------------------
885 // MouseTrackListener
886 // ------------------------------------------------------------------------
887
888 @Override
09e86496 889 public void mouseEnter(final MouseEvent event) {
c392540b
FC
890 }
891
892 @Override
09e86496 893 public void mouseExit(final MouseEvent event) {
c392540b
FC
894 }
895
896 @Override
09e86496 897 public void mouseHover(final MouseEvent event) {
31d6440d 898 if (fDataModel.getStartTime() < fDataModel.getEndTime() && fScaledData != null) {
cc817e65
PT
899 int delimiterIndex = (int) ((fDataModel.getEndTime() - fScaledData.getFirstBucketTime()) / fScaledData.fBucketDuration) + 1;
900 if (event.x < delimiterIndex) {
901 final String tooltip = formatToolTipLabel(event.x - fOffset);
902 fCanvas.setToolTipText(tooltip);
903 return;
904 }
c392540b 905 }
cc817e65 906 fCanvas.setToolTipText(null);
c392540b
FC
907 }
908
09e86496 909 private String formatToolTipLabel(final int index) {
466857f6 910 long startTime = fScaledData.getBucketStartTime(index);
09e86496 911 // negative values are possible if time values came into the model in decreasing order
20ff3b75 912 if (startTime < 0) {
fbd124dd 913 startTime = 0;
20ff3b75 914 }
466857f6 915 final long endTime = fScaledData.getBucketEndTime(index);
09e86496 916 final int nbEvents = (index >= 0) ? fScaledData.fData[index] : 0;
95aa81ef 917 final String newLine = System.getProperty("line.separator"); //$NON-NLS-1$
09e86496 918 final StringBuffer buffer = new StringBuffer();
720d67cb
PT
919 int selectionBeginBucket = Math.min(fScaledData.fSelectionBeginBucket, fScaledData.fSelectionEndBucket);
920 int selectionEndBucket = Math.max(fScaledData.fSelectionBeginBucket, fScaledData.fSelectionEndBucket);
921 if (selectionBeginBucket <= index && index <= selectionEndBucket && fSelectionBegin != fSelectionEnd) {
922 TmfTimestampDelta delta = new TmfTimestampDelta(Math.abs(fSelectionEnd - fSelectionBegin), ITmfTimestamp.NANOSECOND_SCALE);
923 buffer.append(NLS.bind(Messages.Histogram_selectionSpanToolTip, delta.toString()));
924 buffer.append(newLine);
925 }
926 buffer.append(NLS.bind(Messages.Histogram_bucketRangeToolTip,
927 new TmfTimestamp(startTime, ITmfTimestamp.NANOSECOND_SCALE).toString(),
928 new TmfTimestamp(endTime, ITmfTimestamp.NANOSECOND_SCALE).toString()));
95aa81ef 929 buffer.append(newLine);
720d67cb 930 buffer.append(NLS.bind(Messages.Histogram_eventCountToolTip, nbEvents));
95aa81ef
JCK
931 if (!HistogramScaledData.hideLostEvents) {
932 final int nbLostEvents = (index >= 0) ? fScaledData.fLostEventsData[index] : 0;
933 buffer.append(newLine);
720d67cb 934 buffer.append(NLS.bind(Messages.Histogram_lostEventCountToolTip, nbLostEvents));
95aa81ef 935 }
c392540b
FC
936 return buffer.toString();
937 }
938
939 // ------------------------------------------------------------------------
940 // ControlListener
941 // ------------------------------------------------------------------------
942
943 @Override
09e86496 944 public void controlMoved(final ControlEvent event) {
fbd124dd 945 fDataModel.complete();
c392540b
FC
946 }
947
948 @Override
09e86496 949 public void controlResized(final ControlEvent event) {
fbd124dd 950 fDataModel.complete();
c392540b 951 }
f8177ba2
FC
952
953 // ------------------------------------------------------------------------
954 // Signal Handlers
955 // ------------------------------------------------------------------------
956
957 /**
958 * Format the timestamp and update the display
959 *
95aa81ef
JCK
960 * @param signal
961 * the incoming signal
f8177ba2
FC
962 * @since 2.0
963 */
964 @TmfSignalHandler
965 public void timestampFormatUpdated(TmfTimestampFormatUpdateSignal signal) {
d418423b 966 updateRangeTextControls();
f8177ba2 967
da7bdcbc 968 fComposite.layout();
f8177ba2
FC
969 }
970
c392540b 971}
This page took 0.09111 seconds and 5 git commands to generate.