Do not act on user input when pinned
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.ui / src / org / eclipse / tracecompass / tmf / ui / widgets / timegraph / widgets / TimeGraphScale.java
1 /*****************************************************************************
2 * Copyright (c) 2007, 2016 Intel Corporation, Ericsson
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 * Intel Corporation - Initial API and implementation
10 * Ruslan A. Scherbakov, Intel - Initial API and implementation
11 * Alvaro Sanchez-Leon - Updated for TMF
12 * Patrick Tasse - Refactoring
13 * Marc-Andre Laperle - Add time zone preference
14 *****************************************************************************/
15
16 package org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets;
17
18 import java.text.NumberFormat;
19 import java.text.SimpleDateFormat;
20 import java.util.ArrayList;
21 import java.util.Calendar;
22 import java.util.Date;
23 import java.util.List;
24 import java.util.TimeZone;
25
26 import org.eclipse.swt.SWT;
27 import org.eclipse.swt.events.MouseEvent;
28 import org.eclipse.swt.events.MouseListener;
29 import org.eclipse.swt.events.MouseMoveListener;
30 import org.eclipse.swt.events.PaintEvent;
31 import org.eclipse.swt.graphics.GC;
32 import org.eclipse.swt.graphics.Point;
33 import org.eclipse.swt.graphics.Rectangle;
34 import org.eclipse.swt.widgets.Composite;
35 import org.eclipse.swt.widgets.Control;
36 import org.eclipse.tracecompass.common.core.math.SaturatedArithmetic;
37 import org.eclipse.tracecompass.internal.tmf.ui.Messages;
38 import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
39 import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager;
40 import org.eclipse.tracecompass.tmf.core.signal.TmfTimestampFormatUpdateSignal;
41 import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimePreferences;
42 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.Utils.Resolution;
43 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.Utils.TimeFormat;
44
45 import com.google.common.collect.ImmutableList;
46
47 /**
48 * Implementation of the scale for the time graph view.
49 *
50 * This goes above the "gantt chart" area.
51 *
52 * @version 1.0
53 * @author Alvaro Sanchez-Leon
54 * @author Patrick Tasse
55 */
56 public class TimeGraphScale extends TimeGraphBaseControl implements
57 MouseListener, MouseMoveListener {
58
59 private static final int BASE_10 = 10;
60 private static final int X_OFFSET = 4;
61 private static final int Y_OFFSET = 4;
62
63 private static final int MIN_SECOND_FACTOR = 20;
64 private static final int SECOND_FACTOR = 30;
65 private static final int MAX_SECOND_FACTOR = 30;
66
67 private static final int MIN_MINUTE_FACTOR = 10;
68 private static final int MINUTE_FACTOR = 15;
69 private static final int MAX_MINUTE_FACTOR = 30;
70
71 private static final int MIN_HOUR_FACTOR = 3;
72 private static final int HOUR_FACTOR = 6;
73 private static final int MAX_HOUR_FACTOR = 12;
74
75 private static final int MAX_DAY_FACTOR = 10;
76
77 private static final int MAX_MONTH_FACTOR = 6;
78 private static final int MONTH_FACTOR = 6;
79 private static final int MIN_MONTH_FACTOR = 3;
80
81 private static final long MICROSEC_IN_NS = 1000;
82 private static final long MILLISEC_IN_NS = 1000000;
83 private static final long SEC_IN_NS = 1000000000;
84 private static final long MIN_IN_NS = 60 * SEC_IN_NS;
85 private static final long HOUR_IN_NS = 60 * MIN_IN_NS;
86 private static final long DAY_IN_NS = 24 * HOUR_IN_NS;
87 private static final long MONTH_IN_NS = 31 * DAY_IN_NS; // upper limit
88 private static final long YEAR_IN_NS = 366 * DAY_IN_NS; // upper limit
89
90 private static final double LOG10_1 = Math.log10(1);
91 private static final double LOG10_2 = Math.log10(2);
92 private static final double LOG10_3 = Math.log10(3);
93 private static final double LOG10_5 = Math.log10(5);
94
95 private static final Calendar GREGORIAN_CALENDAR = Calendar.getInstance();
96
97 private static final TimeDraw TIMEDRAW_NANOSEC = new TimeDrawNanosec();
98 private static final TimeDraw TIMEDRAW_MICROSEC = new TimeDrawMicrosec();
99 private static final TimeDraw TIMEDRAW_MILLISEC = new TimeDrawMillisec();
100 private static final TimeDraw TIMEDRAW_SEC = new TimeDrawSec();
101 private static final TimeDraw TIMEDRAW_ABS_NANOSEC = new TimeDrawAbsNanoSec();
102 private static final TimeDraw TIMEDRAW_ABS_MICROSEC = new TimeDrawAbsMicroSec();
103 private static final TimeDraw TIMEDRAW_ABS_MILLISEC = new TimeDrawAbsMillisec();
104 private static final TimeDraw TIMEDRAW_ABS_SEC = new TimeDrawAbsSec();
105 private static final TimeDraw TIMEDRAW_ABS_MIN = new TimeDrawAbsMin();
106 private static final TimeDraw TIMEDRAW_ABS_HRS = new TimeDrawAbsHrs();
107 private static final TimeDraw TIMEDRAW_ABS_DAY = new TimeDrawAbsDay();
108 private static final TimeDraw TIMEDRAW_ABS_MONTH = new TimeDrawAbsMonth();
109 private static final TimeDraw TIMEDRAW_ABS_YEAR = new TimeDrawAbsYear();
110 private static final TimeDraw TIMEDRAW_NUMBER = new TimeDrawNumber();
111 private static final TimeDraw TIMEDRAW_CYCLES = new TimeDrawCycles();
112
113 private static final int DRAG_EXTERNAL = -1;
114 private static final int NO_BUTTON = 0;
115 private static final int LEFT_BUTTON = 1;
116
117 private ITimeDataProvider fTimeProvider;
118 private int fDragState = NO_BUTTON;
119 private int fDragX0 = 0;
120 private int fDragX = 0;
121 private long fTime0bak;
122 private long fTime1bak;
123 private boolean fIsInUpdate;
124 private int fHeight;
125 private List<Integer> fTickList = new ArrayList<>();
126
127 private boolean fIsUserInteractionEnabled = true;
128
129 /**
130 * Standard constructor
131 *
132 * @param parent
133 * The parent composite object
134 * @param colors
135 * The color scheme to use
136 */
137 public TimeGraphScale(Composite parent, TimeGraphColorScheme colors) {
138 super(parent, colors, SWT.NO_BACKGROUND | SWT.NO_FOCUS | SWT.DOUBLE_BUFFERED);
139 TmfSignalManager.register(this);
140 addMouseListener(this);
141 addMouseMoveListener(this);
142 TimeDraw.updateTimeZone();
143 addDisposeListener((e) -> {
144 TmfSignalManager.deregister(TimeGraphScale.this);
145 });
146 }
147
148 /**
149 * Assign the time provider for this scale
150 *
151 * @param timeProvider
152 * The provider to use
153 */
154 public void setTimeProvider(ITimeDataProvider timeProvider) {
155 fTimeProvider = timeProvider;
156 }
157
158 /**
159 * Get the time provider used by this scale
160 *
161 * @return The time provider
162 */
163 public ITimeDataProvider getTimeProvider() {
164 return fTimeProvider;
165 }
166
167 @Override
168 public Point computeSize(int wHint, int hHint, boolean changed) {
169 return super.computeSize(wHint, fHeight, changed);
170 }
171
172 /**
173 * Set the height of the scale
174 *
175 * @param height
176 * The height to use
177 */
178 public void setHeight(int height) {
179 if (fHeight != height) {
180 fHeight = height;
181 getParent().layout(new Control[] { this });
182 }
183 }
184
185 /**
186 * Set the drag range to paint decorators
187 *
188 * @param begin
189 * The begin x-coordinate
190 * @param end
191 * The end x-coordinate
192 */
193 public void setDragRange(int begin, int end) {
194 if (NO_BUTTON == fDragState || DRAG_EXTERNAL == fDragState) {
195 fDragX0 = begin - fTimeProvider.getNameSpace();
196 fDragX = end - fTimeProvider.getNameSpace();
197 if (begin >= 0 || end >= 0) {
198 fDragState = DRAG_EXTERNAL;
199 } else {
200 fDragState = NO_BUTTON;
201 }
202 }
203 redraw();
204 }
205
206 /**
207 * Get the list of visible ticks of the time axis.
208 *
209 * @return the list of visible tick x-coordinates
210 * @since 2.0
211 */
212 public List<Integer> getTickList() {
213 return fTickList;
214 }
215
216 private long calcTimeDelta(int width, double pixelsPerNanoSec) {
217 long timeDelta;
218 double minDelta = (pixelsPerNanoSec == 0) ? YEAR_IN_NS : width / pixelsPerNanoSec;
219 long unit = 1;
220 if (fTimeProvider != null && fTimeProvider.getTimeFormat() == TimeFormat.CALENDAR) {
221 if (minDelta > MAX_MONTH_FACTOR * MONTH_IN_NS) {
222 unit = YEAR_IN_NS;
223 } else if (minDelta > MIN_MONTH_FACTOR * MONTH_IN_NS) {
224 unit = MONTH_FACTOR * MONTH_IN_NS;
225 } else if (minDelta > MAX_DAY_FACTOR * DAY_IN_NS) {
226 unit = MONTH_IN_NS;
227 } else if (minDelta > MAX_HOUR_FACTOR * HOUR_IN_NS) {
228 unit = DAY_IN_NS;
229 } else if (minDelta > MIN_HOUR_FACTOR * HOUR_IN_NS) {
230 unit = HOUR_FACTOR * HOUR_IN_NS;
231 } else if (minDelta > MAX_MINUTE_FACTOR * MIN_IN_NS) {
232 unit = HOUR_IN_NS;
233 } else if (minDelta > MIN_MINUTE_FACTOR * MIN_IN_NS) {
234 unit = MINUTE_FACTOR * MIN_IN_NS;
235 } else if (minDelta > MAX_SECOND_FACTOR * SEC_IN_NS) {
236 unit = MIN_IN_NS;
237 } else if (minDelta > MIN_SECOND_FACTOR * SEC_IN_NS) {
238 unit = SECOND_FACTOR * SEC_IN_NS;
239 } else if (minDelta <= 1) {
240 timeDelta = 1;
241 return timeDelta;
242 }
243 }
244 double log = Math.log10(minDelta / unit);
245 long pow10 = (long) log;
246 double remainder = log - pow10;
247 if (remainder < LOG10_1) {
248 timeDelta = (long) Math.pow(BASE_10, pow10) * unit;
249 } else if (remainder < LOG10_2) {
250 timeDelta = 2 * (long) Math.pow(BASE_10, pow10) * unit;
251 } else if (remainder < LOG10_3 && unit >= HOUR_IN_NS && unit < YEAR_IN_NS) {
252 timeDelta = 3 * (long) Math.pow(BASE_10, pow10) * unit;
253 } else if (remainder < LOG10_5) {
254 timeDelta = 5 * (long) Math.pow(BASE_10, pow10) * unit;
255 } else {
256 timeDelta = 10 * (long) Math.pow(BASE_10, pow10) * unit;
257 }
258 if (timeDelta <= 0) {
259 timeDelta = 1;
260 }
261 return timeDelta;
262 }
263
264 TimeDraw getTimeDraw(long timeDelta) {
265 TimeDraw timeDraw;
266 if (fTimeProvider != null) {
267 switch (fTimeProvider.getTimeFormat()) {
268 case CALENDAR:
269 if (timeDelta >= YEAR_IN_NS) {
270 timeDraw = TIMEDRAW_ABS_YEAR;
271 } else if (timeDelta >= MONTH_IN_NS) {
272 timeDraw = TIMEDRAW_ABS_MONTH;
273 } else if (timeDelta >= DAY_IN_NS) {
274 timeDraw = TIMEDRAW_ABS_DAY;
275 } else if (timeDelta >= HOUR_IN_NS) {
276 timeDraw = TIMEDRAW_ABS_HRS;
277 } else if (timeDelta >= MIN_IN_NS) {
278 timeDraw = TIMEDRAW_ABS_MIN;
279 } else if (timeDelta >= SEC_IN_NS) {
280 timeDraw = TIMEDRAW_ABS_SEC;
281 } else if (timeDelta >= MILLISEC_IN_NS) {
282 timeDraw = TIMEDRAW_ABS_MILLISEC;
283 } else if (timeDelta >= MICROSEC_IN_NS) {
284 timeDraw = TIMEDRAW_ABS_MICROSEC;
285 } else {
286 timeDraw = TIMEDRAW_ABS_NANOSEC;
287 }
288 return timeDraw;
289 case NUMBER:
290 return TIMEDRAW_NUMBER;
291 case CYCLES:
292 return TIMEDRAW_CYCLES;
293 case RELATIVE:
294 default:
295 }
296
297 }
298 if (timeDelta >= SEC_IN_NS) {
299 timeDraw = TIMEDRAW_SEC;
300 } else if (timeDelta >= MILLISEC_IN_NS) {
301 timeDraw = TIMEDRAW_MILLISEC;
302 } else if (timeDelta >= MICROSEC_IN_NS) {
303 timeDraw = TIMEDRAW_MICROSEC;
304 } else {
305 timeDraw = TIMEDRAW_NANOSEC;
306 }
307 return timeDraw;
308 }
309
310 @Override
311 void paint(Rectangle rect, PaintEvent e) {
312
313 if (fIsInUpdate || null == fTimeProvider) {
314 return;
315 }
316
317 GC gc = e.gc;
318 gc.fillRectangle(rect);
319
320 long time0 = fTimeProvider.getTime0();
321 long time1 = fTimeProvider.getTime1();
322 int leftSpace = fTimeProvider.getNameSpace();
323 int timeSpace = fTimeProvider.getTimeSpace();
324
325 gc.setBackground(getColorScheme().getColor(TimeGraphColorScheme.TOOL_BACKGROUND));
326 gc.setForeground(getColorScheme().getColor(TimeGraphColorScheme.TOOL_FOREGROUND));
327 Rectangle rect0 = new Rectangle(0, 0, 0, 0);
328 Utils.init(rect0, rect);
329
330 // draw top left area
331 rect0.width = leftSpace;
332 rect0.x += X_OFFSET;
333 rect0.width -= X_OFFSET;
334 Rectangle absHeaderRect = new Rectangle(rect0.x, rect0.y, rect0.width, rect0.height);
335 rect0.x -= X_OFFSET;
336 rect0.width += X_OFFSET;
337
338 // prepare and draw right rect of the timescale
339 rect0.x += leftSpace;
340 rect0.width = rect.width - leftSpace;
341
342 // draw bottom border and erase all other area
343 gc.drawLine(rect.x, rect.y + rect.height - 1, rect.x + rect.width - 1,
344 rect.y + rect.height - 1);
345 rect0.height--;
346 gc.fillRectangle(rect0);
347
348 if (time1 <= time0 || timeSpace < 2) {
349 fTickList.clear();
350 return;
351 }
352
353 int numDigits = calculateDigits(time0, time1);
354
355 int labelWidth = gc.getCharWidth('0') * numDigits;
356 double pixelsPerNanoSec = (timeSpace <= RIGHT_MARGIN) ? 0 :
357 (double) (timeSpace - RIGHT_MARGIN) / (time1 - time0);
358 long timeDelta = calcTimeDelta(labelWidth, pixelsPerNanoSec);
359
360 TimeDraw timeDraw = getTimeDraw(timeDelta);
361
362 // draw range decorators
363 if (DRAG_EXTERNAL == fDragState) {
364 int x1 = leftSpace + fDragX0;
365 int x2 = leftSpace + fDragX;
366 drawRangeDecorators(rect0, gc, x1, x2);
367 } else {
368 int x1;
369 int x2;
370 long selectionBegin = fTimeProvider.getSelectionBegin();
371 long selectionEnd = fTimeProvider.getSelectionEnd();
372 x1 = SaturatedArithmetic.add(leftSpace, (int) ((selectionBegin - time0) * pixelsPerNanoSec));
373 x2 = SaturatedArithmetic.add(leftSpace, (int) ((selectionEnd - time0) * pixelsPerNanoSec));
374 drawRangeDecorators(rect0, gc, x1, x2);
375 }
376
377 if (rect0.isEmpty()) {
378 return;
379 }
380
381 // draw time scale ticks
382 rect0.y = rect.y;
383 rect0.height = rect.height - Y_OFFSET;
384 rect0.width = labelWidth;
385
386 long time;
387 if (fTimeProvider != null && fTimeProvider.getTimeFormat() == TimeFormat.CALENDAR) {
388 time = floorToCalendar(time0, timeDelta);
389 } else {
390 time = (time0 / timeDelta) * timeDelta;
391 if (time != time0) {
392 time += timeDelta;
393 }
394 }
395
396 int y = rect0.y + rect0.height;
397
398 if (fTimeProvider != null && fTimeProvider.getTimeFormat() == TimeFormat.CALENDAR) {
399 timeDraw.drawAbsHeader(gc, time, absHeaderRect);
400 }
401
402 List<Integer> tickList = new ArrayList<>();
403 while (true) {
404 int x = SaturatedArithmetic.add(rect.x + leftSpace, (int) (Math.floor((time - time0) * pixelsPerNanoSec)));
405 if (x >= rect.x + leftSpace + rect.width - rect0.width) {
406 break;
407 }
408 if (x >= rect.x + leftSpace) {
409 gc.drawLine(x, y, x, y + Y_OFFSET);
410 rect0.x = x;
411 if (x + rect0.width <= rect.x + rect.width) {
412 timeDraw.draw(gc, time, rect0);
413 }
414 tickList.add(x);
415 }
416 if (pixelsPerNanoSec == 0 || time > Long.MAX_VALUE - timeDelta || timeDelta == 0) {
417 break;
418 }
419 if (fTimeProvider != null && fTimeProvider.getTimeFormat() == TimeFormat.CALENDAR) {
420 if (timeDelta >= YEAR_IN_NS) {
421 long millis = time / MILLISEC_IN_NS;
422 GREGORIAN_CALENDAR.setTime(new Date(millis));
423 GREGORIAN_CALENDAR.add(Calendar.YEAR, (int) (timeDelta / YEAR_IN_NS));
424 millis = GREGORIAN_CALENDAR.getTimeInMillis();
425 time = millis * MILLISEC_IN_NS;
426 } else if (timeDelta >= MONTH_IN_NS) {
427 long millis = time / MILLISEC_IN_NS;
428 GREGORIAN_CALENDAR.setTime(new Date(millis));
429 GREGORIAN_CALENDAR.add(Calendar.MONTH, (int) (timeDelta / MONTH_IN_NS));
430 millis = GREGORIAN_CALENDAR.getTimeInMillis();
431 time = millis * MILLISEC_IN_NS;
432 } else if (timeDelta >= DAY_IN_NS) {
433 long millis = time / MILLISEC_IN_NS;
434 GREGORIAN_CALENDAR.setTime(new Date(millis));
435 GREGORIAN_CALENDAR.add(Calendar.DAY_OF_MONTH, (int) (timeDelta / DAY_IN_NS));
436 millis = GREGORIAN_CALENDAR.getTimeInMillis();
437 time = millis * MILLISEC_IN_NS;
438 } else {
439 time += timeDelta;
440 }
441 } else {
442 time += timeDelta;
443 }
444 }
445 fTickList = tickList;
446 }
447
448 private static void drawRangeDecorators(Rectangle rect, GC gc, int x1, int x2) {
449 int y1 = rect.y + rect.height - 9;
450 int y2 = rect.y + rect.height - 5;
451 int ym = (y1 + y2) / 2;
452 if (x1 >= rect.x) {
453 // T1
454 gc.drawLine(x1 - 2, y1, x1 - 2, y2);
455 gc.drawLine(x1 - 3, y1, x1 - 1, y1);
456 gc.drawLine(x1 + 1, y1, x1 + 1, y2);
457 }
458 if (x2 >= rect.x && Math.abs(x2 - x1 - 2) > 3) {
459 // T of T2
460 gc.drawLine(x2 - 2, y1, x2 - 2, y2);
461 gc.drawLine(x2 - 3, y1, x2 - 1, y1);
462 }
463 if (x2 >= rect.x && Math.abs(x2 - x1 + 3) > 3) {
464 // 2 of T2
465 gc.drawLine(x2 + 1, y1, x2 + 3, y1);
466 gc.drawLine(x2 + 3, y1, x2 + 3, ym);
467 gc.drawLine(x2 + 1, ym, x2 + 3, ym);
468 gc.drawLine(x2 + 1, ym, x2 + 1, y2);
469 gc.drawLine(x2 + 1, y2, x2 + 3, y2);
470 }
471 }
472
473 private static long floorToCalendar(long time, long timeDelta) {
474 long ret = time;
475
476 if (timeDelta >= YEAR_IN_NS) {
477 GREGORIAN_CALENDAR.setTime(new Date(ret / MILLISEC_IN_NS));
478 int year = GREGORIAN_CALENDAR.get(Calendar.YEAR);
479 int yearDelta = (int) (timeDelta / YEAR_IN_NS);
480 year = (year / yearDelta) * yearDelta;
481 GREGORIAN_CALENDAR.set(Calendar.YEAR, year);
482 GREGORIAN_CALENDAR.set(Calendar.MONTH, 0); // January 1st of year
483 GREGORIAN_CALENDAR.set(Calendar.DAY_OF_MONTH, 1);
484 GREGORIAN_CALENDAR.set(Calendar.HOUR_OF_DAY, 0);
485 GREGORIAN_CALENDAR.set(Calendar.MINUTE, 0);
486 GREGORIAN_CALENDAR.set(Calendar.SECOND, 0);
487 GREGORIAN_CALENDAR.set(Calendar.MILLISECOND, 0);
488 ret = GREGORIAN_CALENDAR.getTimeInMillis() * MILLISEC_IN_NS;
489 } else if (timeDelta >= MONTH_IN_NS) {
490 GREGORIAN_CALENDAR.setTime(new Date(ret / MILLISEC_IN_NS));
491 int month = GREGORIAN_CALENDAR.get(Calendar.MONTH);
492 int monthDelta = (int) (timeDelta / MONTH_IN_NS);
493 month = (month / monthDelta) * monthDelta;
494 GREGORIAN_CALENDAR.set(Calendar.MONTH, month);
495 GREGORIAN_CALENDAR.set(Calendar.DAY_OF_MONTH, 1); // 1st of month
496 GREGORIAN_CALENDAR.set(Calendar.HOUR_OF_DAY, 0);
497 GREGORIAN_CALENDAR.set(Calendar.MINUTE, 0);
498 GREGORIAN_CALENDAR.set(Calendar.SECOND, 0);
499 GREGORIAN_CALENDAR.set(Calendar.MILLISECOND, 0);
500 ret = GREGORIAN_CALENDAR.getTimeInMillis() * MILLISEC_IN_NS;
501 } else {
502 long offset = GREGORIAN_CALENDAR.getTimeZone().getOffset(ret / MILLISEC_IN_NS) * MILLISEC_IN_NS;
503 ret += offset;
504 ret = (ret / timeDelta) * timeDelta;
505 ret -= offset;
506 }
507 return ret;
508 }
509
510 private int calculateDigits(long time0, long time1) {
511 int numDigits = 5;
512 long timeRange = time1 - time0;
513 TimeFormat timeFormat = fTimeProvider.getTimeFormat();
514
515 if (timeFormat == TimeFormat.CALENDAR) {
516 // Calculate the number of digits to represent the minutes provided
517 // 11:222
518 // HH:mm:ss
519 numDigits += 8;
520 if (timeRange < 10000) {
521 // HH:11:222:333:444__
522 numDigits += 10;
523 } else if (timeRange < 10000000) {
524 // HH:11:222:333__
525 numDigits += 6;
526 }
527 } else {
528 long sec = time1 / SEC_IN_NS;
529 numDigits = Long.toString(sec).length();
530 int thousandGroups = (numDigits - 1) / 3;
531 numDigits += thousandGroups;
532 numDigits += 12; // .000 000 000
533 if (timeFormat == TimeFormat.CYCLES) {
534 numDigits += Messages.Utils_ClockCyclesUnit.length();
535 } else if (fTimeProvider.getTimeFormat() == TimeFormat.RELATIVE) {
536 numDigits += 2; // " s"
537 }
538 }
539
540 return numDigits;
541 }
542
543 @Override
544 public void mouseDown(MouseEvent e) {
545 getParent().setFocus();
546 if (fDragState == NO_BUTTON && null != fTimeProvider && fIsUserInteractionEnabled) {
547 int x = e.x - fTimeProvider.getNameSpace();
548 if (LEFT_BUTTON == e.button && x > 0) {
549 setCapture(true);
550 fDragState = LEFT_BUTTON;
551 }
552 if (x < 0) {
553 x = 0;
554 } else if (x > getSize().x - fTimeProvider.getNameSpace()) {
555 x = getSize().x - fTimeProvider.getNameSpace();
556 }
557 fDragX = x;
558 fDragX0 = x;
559 fTime0bak = fTimeProvider.getTime0();
560 fTime1bak = fTimeProvider.getTime1();
561 }
562 }
563
564 @Override
565 public void mouseUp(MouseEvent e) {
566 if (e.button == LEFT_BUTTON && fDragState == LEFT_BUTTON) {
567 setCapture(false);
568 fDragState = NO_BUTTON;
569
570 // Notify time provider to check the need for listener notification
571 if (fDragX != fDragX0 && fTimeProvider.getTime0() != fTimeProvider.getTime1()) {
572 fTimeProvider.setStartFinishTimeNotify(fTimeProvider.getTime0(), fTimeProvider.getTime1());
573 }
574 }
575 }
576
577 @Override
578 public void mouseMove(MouseEvent e) {
579 if (fDragX0 < 0 || fDragState == NO_BUTTON || fTimeProvider == null) {
580 return;
581 }
582 Point size = getSize();
583 int leftSpace = fTimeProvider.getNameSpace();
584 int x = e.x - leftSpace;
585 if (LEFT_BUTTON == fDragState) {
586 if (x > 0 && size.x > leftSpace && fDragX != x) {
587 fDragX = x;
588 if (fTimeProvider.getTime0() == fTimeProvider.getTime1()) {
589 return;
590 }
591 long interval = (long) ((fTime1bak - fTime0bak) * ((double) fDragX0 / fDragX));
592 if (interval == Long.MAX_VALUE) {
593 fTimeProvider.setStartFinishTimeNotify(fTime0bak, Long.MAX_VALUE);
594 } else {
595 long time1 = fTime0bak + (long) ((fTime1bak - fTime0bak) * ((double) fDragX0 / fDragX));
596 fTimeProvider.setStartFinishTimeNotify(fTime0bak, time1);
597 }
598 }
599 }
600 }
601
602 @Override
603 public void mouseDoubleClick(MouseEvent e) {
604 if (e.button == 1
605 && null != fTimeProvider
606 && fTimeProvider.getTime0() != fTimeProvider.getTime1()
607 && (e.stateMask & SWT.BUTTON_MASK) == 0
608 && fIsUserInteractionEnabled) {
609 fTimeProvider.resetStartFinishTime();
610 fTime0bak = fTimeProvider.getTime0();
611 fTime1bak = fTimeProvider.getTime1();
612 }
613 }
614
615 /**
616 * Update the display to use the updated timestamp format
617 *
618 * @param signal
619 * the incoming signal
620 */
621 @TmfSignalHandler
622 public void timestampFormatUpdated(TmfTimestampFormatUpdateSignal signal) {
623 TimeDraw.updateTimeZone();
624 Utils.updateTimeZone();
625 redraw();
626 }
627
628 @Override
629 public synchronized void setPinned(boolean pinned) {
630 /* When pinned disable user interaction */
631 boolean enabled = !pinned;
632 fIsUserInteractionEnabled = enabled;
633 }
634 }
635
636 abstract class TimeDraw {
637 protected static final long MICROSEC_IN_NS = 1000;
638 protected static final long MILLISEC_IN_NS = 1000000;
639 protected static final long MILLISEC_IN_US = 1000;
640 protected static final long SEC_IN_NS = 1000000000;
641 protected static final long SEC_IN_MS = 1000;
642 private static final String S = ""; //$NON-NLS-1$
643 private static final String S0 = "0"; //$NON-NLS-1$
644 private static final String S00 = "00"; //$NON-NLS-1$
645 protected static final long PAD_1000 = 1000;
646 protected static final SimpleDateFormat SEC_FORMAT_HEADER =
647 new SimpleDateFormat("yyyy MMM dd");//$NON-NLS-1$
648 protected static final SimpleDateFormat SEC_FORMAT =
649 new SimpleDateFormat("HH:mm:ss"); //$NON-NLS-1$
650 protected static final SimpleDateFormat MIN_FORMAT_HEADER =
651 new SimpleDateFormat("yyyy MMM dd"); //$NON-NLS-1$
652 protected static final SimpleDateFormat MIN_FORMAT =
653 new SimpleDateFormat("HH:mm"); //$NON-NLS-1$
654 protected static final SimpleDateFormat HOURS_FORMAT_HEADER =
655 new SimpleDateFormat("yyyy"); //$NON-NLS-1$
656 protected static final SimpleDateFormat HOURS_FORMAT =
657 new SimpleDateFormat("MMM dd HH:mm"); //$NON-NLS-1$
658 protected static final SimpleDateFormat DAY_FORMAT_HEADER =
659 new SimpleDateFormat("yyyy"); //$NON-NLS-1$
660 protected static final SimpleDateFormat DAY_FORMAT =
661 new SimpleDateFormat("MMM dd"); //$NON-NLS-1$
662 protected static final SimpleDateFormat MONTH_FORMAT =
663 new SimpleDateFormat("yyyy MMM"); //$NON-NLS-1$
664 protected static final SimpleDateFormat YEAR_FORMAT =
665 new SimpleDateFormat("yyyy"); //$NON-NLS-1$
666
667 protected static final List<SimpleDateFormat> formats;
668 static
669 {
670 ImmutableList.Builder<SimpleDateFormat> formatArrayBuilder = ImmutableList.<SimpleDateFormat> builder();
671 formatArrayBuilder.add(SEC_FORMAT);
672 formatArrayBuilder.add(SEC_FORMAT_HEADER);
673 formatArrayBuilder.add(MIN_FORMAT);
674 formatArrayBuilder.add(MIN_FORMAT_HEADER);
675 formatArrayBuilder.add(HOURS_FORMAT);
676 formatArrayBuilder.add(HOURS_FORMAT_HEADER);
677 formatArrayBuilder.add(DAY_FORMAT);
678 formatArrayBuilder.add(DAY_FORMAT_HEADER);
679 formatArrayBuilder.add(MONTH_FORMAT);
680 formatArrayBuilder.add(YEAR_FORMAT);
681 formats = formatArrayBuilder.build();
682 }
683
684 /**
685 * Updates the timezone using the preferences.
686 */
687 public static void updateTimeZone() {
688 final TimeZone timeZone = TmfTimePreferences.getTimeZone();
689 for (SimpleDateFormat sdf : formats) {
690 synchronized (sdf) {
691 sdf.setTimeZone(timeZone);
692 }
693 }
694 }
695
696 static String sep(long n) {
697 StringBuilder retVal = new StringBuilder();
698 String s = Long.toString(n);
699 for (int i = 0; i < s.length(); i++) {
700 int pos = s.length() - i - 1;
701 retVal.append(s.charAt(i));
702 if (pos % 3 == 0 && pos != 0) {
703 retVal.append(' ');
704 }
705 }
706 return retVal.toString();
707 }
708
709 static String pad(long n) {
710 String s;
711 if (n < 10) {
712 s = S00;
713 } else if (n < 100) {
714 s = S0;
715 } else {
716 s = S;
717 }
718 return s + n;
719 }
720
721 public abstract int draw(GC gc, long time, Rectangle rect);
722
723 /**
724 * Override to draw absolute time header. This is for the time information
725 * not shown in the draw of each tick
726 *
727 * @param gc
728 * Graphics context
729 * @param nanosec
730 * time in nanosec
731 * @param absHeaderRect
732 * Header rectangle
733 */
734 public void drawAbsHeader(GC gc, long nanosec, Rectangle absHeaderRect) {
735 }
736
737 protected void drawAbsHeader(GC gc, long nanosec, Rectangle rect, SimpleDateFormat dateFormat) {
738 String header;
739 synchronized (dateFormat) {
740 header = dateFormat.format(new Date(nanosec / MILLISEC_IN_NS));
741 }
742 int headerwidth = gc.stringExtent(header).x + 4;
743 if (headerwidth <= rect.width) {
744 rect.x += (rect.width - headerwidth);
745 Utils.drawText(gc, header, rect, true);
746 }
747 }
748 }
749
750 class TimeDrawSec extends TimeDraw {
751 @Override
752 public int draw(GC gc, long nanosec, Rectangle rect) {
753 long sec = nanosec / SEC_IN_NS;
754 return Utils.drawText(gc, sep(sec) + " s", rect, true); //$NON-NLS-1$
755 }
756 }
757
758 class TimeDrawMillisec extends TimeDraw {
759 @Override
760 public int draw(GC gc, long nanosec, Rectangle rect) {
761 long millisec = nanosec / MILLISEC_IN_NS;
762 long ms = millisec % PAD_1000;
763 long sec = millisec / SEC_IN_MS;
764 return Utils.drawText(gc, sep(sec) + "." + pad(ms) + " s", rect, true); //$NON-NLS-1$ //$NON-NLS-2$
765 }
766 }
767
768 class TimeDrawMicrosec extends TimeDraw {
769 @Override
770 public int draw(GC gc, long nanosec, Rectangle rect) {
771 long microsec = nanosec / MICROSEC_IN_NS;
772 long us = microsec % PAD_1000;
773 long millisec = microsec / MILLISEC_IN_US;
774 long ms = millisec % PAD_1000;
775 long sec = millisec / SEC_IN_MS;
776 return Utils.drawText(gc, sep(sec) + "." + pad(ms) + " " + pad(us) + " s", rect, true); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
777 }
778 }
779
780 class TimeDrawNanosec extends TimeDraw {
781 @Override
782 public int draw(GC gc, long nanosec, Rectangle rect) {
783 long ns = nanosec % PAD_1000;
784 long microsec = nanosec / MICROSEC_IN_NS;
785 long us = microsec % PAD_1000;
786 long millisec = microsec / MILLISEC_IN_US;
787 long ms = millisec % PAD_1000;
788 long sec = millisec / SEC_IN_MS;
789 return Utils.drawText(gc, sep(sec) + "." + pad(ms) + " " + pad(us) + " " + pad(ns) + " s", rect, true); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
790 }
791 }
792
793 class TimeDrawAbsYear extends TimeDraw {
794 @Override
795 public int draw(GC gc, long nanosec, Rectangle rect) {
796 String stime;
797 synchronized (YEAR_FORMAT) {
798 stime = YEAR_FORMAT.format(new Date(nanosec / MILLISEC_IN_NS));
799 }
800 return Utils.drawText(gc, stime, rect, true);
801 }
802 }
803
804 class TimeDrawAbsMonth extends TimeDraw {
805 @Override
806 public int draw(GC gc, long nanosec, Rectangle rect) {
807 String stime;
808 synchronized (MONTH_FORMAT) {
809 stime = MONTH_FORMAT.format(new Date(nanosec / MILLISEC_IN_NS));
810 }
811 return Utils.drawText(gc, stime, rect, true);
812 }
813 }
814
815 class TimeDrawAbsDay extends TimeDraw {
816 @Override
817 public int draw(GC gc, long nanosec, Rectangle rect) {
818 String stime;
819 synchronized (DAY_FORMAT) {
820 stime = DAY_FORMAT.format(new Date(nanosec / MILLISEC_IN_NS));
821 }
822 return Utils.drawText(gc, stime, rect, true);
823 }
824
825 @Override
826 public void drawAbsHeader(GC gc, long nanosec, Rectangle rect) {
827 drawAbsHeader(gc, nanosec, rect, DAY_FORMAT_HEADER);
828 }
829 }
830
831 class TimeDrawAbsHrs extends TimeDraw {
832 @Override
833 public int draw(GC gc, long nanosec, Rectangle rect) {
834 String stime;
835 synchronized (HOURS_FORMAT) {
836 stime = HOURS_FORMAT.format(new Date(nanosec / MILLISEC_IN_NS));
837 }
838 return Utils.drawText(gc, stime, rect, true);
839 }
840
841 @Override
842 public void drawAbsHeader(GC gc, long nanosec, Rectangle rect) {
843 drawAbsHeader(gc, nanosec, rect, HOURS_FORMAT_HEADER);
844 }
845 }
846
847 class TimeDrawAbsMin extends TimeDraw {
848 @Override
849 public int draw(GC gc, long nanosec, Rectangle rect) {
850 String stime;
851 synchronized (MIN_FORMAT) {
852 stime = MIN_FORMAT.format(new Date(nanosec / MILLISEC_IN_NS));
853 }
854 return Utils.drawText(gc, stime, rect, true);
855 }
856
857 @Override
858 public void drawAbsHeader(GC gc, long nanosec, Rectangle rect) {
859 drawAbsHeader(gc, nanosec, rect, MIN_FORMAT_HEADER);
860 }
861 }
862
863 class TimeDrawAbsSec extends TimeDraw {
864 @Override
865 public int draw(GC gc, long nanosec, Rectangle rect) {
866 String stime;
867 synchronized (SEC_FORMAT) {
868 stime = SEC_FORMAT.format(new Date(nanosec / MILLISEC_IN_NS));
869 }
870 return Utils.drawText(gc, stime, rect, true);
871 }
872
873 @Override
874 public void drawAbsHeader(GC gc, long nanosec, Rectangle rect) {
875 drawAbsHeader(gc, nanosec, rect, SEC_FORMAT_HEADER);
876 }
877 }
878
879 class TimeDrawAbsMillisec extends TimeDraw {
880 @Override
881 public int draw(GC gc, long nanosec, Rectangle rect) {
882 String stime;
883 synchronized (SEC_FORMAT) {
884 stime = SEC_FORMAT.format(new Date(nanosec / MILLISEC_IN_NS));
885 }
886 String ns = Utils.formatNs(nanosec, Resolution.MILLISEC);
887 return Utils.drawText(gc, stime + "." + ns, rect, true); //$NON-NLS-1$
888 }
889
890 @Override
891 public void drawAbsHeader(GC gc, long nanosec, Rectangle rect) {
892 drawAbsHeader(gc, nanosec, rect, SEC_FORMAT_HEADER);
893 }
894 }
895
896 class TimeDrawAbsMicroSec extends TimeDraw {
897 @Override
898 public int draw(GC gc, long nanosec, Rectangle rect) {
899 String stime;
900 synchronized (SEC_FORMAT) {
901 stime = SEC_FORMAT.format(new Date(nanosec / MILLISEC_IN_NS));
902 }
903 String micr = Utils.formatNs(nanosec, Resolution.MICROSEC);
904 return Utils.drawText(gc, stime + "." + micr, rect, true); //$NON-NLS-1$
905 }
906
907 @Override
908 public void drawAbsHeader(GC gc, long nanosec, Rectangle rect) {
909 drawAbsHeader(gc, nanosec, rect, SEC_FORMAT_HEADER);
910 }
911 }
912
913 class TimeDrawAbsNanoSec extends TimeDraw {
914 @Override
915 public int draw(GC gc, long nanosec, Rectangle rect) {
916 String stime;
917 synchronized (SEC_FORMAT) {
918 stime = SEC_FORMAT.format(new Date(nanosec / MILLISEC_IN_NS));
919 }
920 String ns = Utils.formatNs(nanosec, Resolution.NANOSEC);
921 return Utils.drawText(gc, stime + "." + ns, rect, true); //$NON-NLS-1$
922 }
923
924 @Override
925 public void drawAbsHeader(GC gc, long nanosec, Rectangle rect) {
926 drawAbsHeader(gc, nanosec, rect, SEC_FORMAT_HEADER);
927 }
928 }
929
930 class TimeDrawNumber extends TimeDraw {
931 @Override
932 public int draw(GC gc, long time, Rectangle rect) {
933 String stime = NumberFormat.getInstance().format(time);
934 return Utils.drawText(gc, stime, rect, true);
935 }
936 }
937
938 class TimeDrawCycles extends TimeDraw {
939 @Override
940 public int draw(GC gc, long time, Rectangle rect) {
941 String stime = Utils.formatTime(time, TimeFormat.CYCLES, Resolution.SECONDS);
942 return Utils.drawText(gc, stime, rect, true);
943 }
944 }
This page took 0.060854 seconds and 5 git commands to generate.