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