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