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
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 *****************************************************************************/
16 package org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.widgets
;
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
;
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
.Color
;
32 import org
.eclipse
.swt
.graphics
.GC
;
33 import org
.eclipse
.swt
.graphics
.Point
;
34 import org
.eclipse
.swt
.graphics
.Rectangle
;
35 import org
.eclipse
.swt
.widgets
.Composite
;
36 import org
.eclipse
.swt
.widgets
.Control
;
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
.model
.IMarkerEvent
;
43 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.widgets
.Utils
.Resolution
;
44 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.widgets
.Utils
.TimeFormat
;
46 import com
.google
.common
.collect
.ImmutableList
;
49 * Implementation of the scale for the time graph view.
51 * This goes above the "gantt chart" area.
54 * @author Alvaro Sanchez-Leon
55 * @author Patrick Tasse
57 public class TimeGraphScale
extends TimeGraphBaseControl
implements
58 MouseListener
, MouseMoveListener
{
60 private static final int BASE_10
= 10;
61 private static final int X_OFFSET
= 4;
62 private static final int Y_OFFSET
= 4;
64 private static final int MIN_SECOND_FACTOR
= 20;
65 private static final int SECOND_FACTOR
= 30;
66 private static final int MAX_SECOND_FACTOR
= 30;
68 private static final int MIN_MINUTE_FACTOR
= 10;
69 private static final int MINUTE_FACTOR
= 15;
70 private static final int MAX_MINUTE_FACTOR
= 30;
72 private static final int MIN_HOUR_FACTOR
= 3;
73 private static final int HOUR_FACTOR
= 6;
74 private static final int MAX_HOUR_FACTOR
= 12;
76 private static final int MAX_DAY_FACTOR
= 10;
78 private static final int MAX_MONTH_FACTOR
= 6;
79 private static final int MONTH_FACTOR
= 6;
80 private static final int MIN_MONTH_FACTOR
= 3;
82 private static final long MICROSEC_IN_NS
= 1000;
83 private static final long MILLISEC_IN_NS
= 1000000;
84 private static final long SEC_IN_NS
= 1000000000;
85 private static final long MIN_IN_NS
= 60 * SEC_IN_NS
;
86 private static final long HOUR_IN_NS
= 60 * MIN_IN_NS
;
87 private static final long DAY_IN_NS
= 24 * HOUR_IN_NS
;
88 private static final long MONTH_IN_NS
= 31 * DAY_IN_NS
; // upper limit
89 private static final long YEAR_IN_NS
= 366 * DAY_IN_NS
; // upper limit
91 private static final double LOG10_1
= Math
.log10(1);
92 private static final double LOG10_2
= Math
.log10(2);
93 private static final double LOG10_3
= Math
.log10(3);
94 private static final double LOG10_5
= Math
.log10(5);
96 private static final Calendar GREGORIAN_CALENDAR
= Calendar
.getInstance();
98 private static final TimeDraw TIMEDRAW_NANOSEC
= new TimeDrawNanosec();
99 private static final TimeDraw TIMEDRAW_MICROSEC
= new TimeDrawMicrosec();
100 private static final TimeDraw TIMEDRAW_MILLISEC
= new TimeDrawMillisec();
101 private static final TimeDraw TIMEDRAW_SEC
= new TimeDrawSec();
102 private static final TimeDraw TIMEDRAW_ABS_NANOSEC
= new TimeDrawAbsNanoSec();
103 private static final TimeDraw TIMEDRAW_ABS_MICROSEC
= new TimeDrawAbsMicroSec();
104 private static final TimeDraw TIMEDRAW_ABS_MILLISEC
= new TimeDrawAbsMillisec();
105 private static final TimeDraw TIMEDRAW_ABS_SEC
= new TimeDrawAbsSec();
106 private static final TimeDraw TIMEDRAW_ABS_MIN
= new TimeDrawAbsMin();
107 private static final TimeDraw TIMEDRAW_ABS_HRS
= new TimeDrawAbsHrs();
108 private static final TimeDraw TIMEDRAW_ABS_DAY
= new TimeDrawAbsDay();
109 private static final TimeDraw TIMEDRAW_ABS_MONTH
= new TimeDrawAbsMonth();
110 private static final TimeDraw TIMEDRAW_ABS_YEAR
= new TimeDrawAbsYear();
111 private static final TimeDraw TIMEDRAW_NUMBER
= new TimeDrawNumber();
112 private static final TimeDraw TIMEDRAW_CYCLES
= new TimeDrawCycles();
114 private static final int DRAG_EXTERNAL
= -1;
115 private static final int NO_BUTTON
= 0;
116 private static final int LEFT_BUTTON
= 1;
118 private static final int MAX_LABEL_LENGTH
= 256;
120 private ITimeDataProvider fTimeProvider
;
121 private int fDragState
= NO_BUTTON
;
122 private int fDragX0
= 0;
123 private int fDragX
= 0;
124 private long fTime0bak
;
125 private long fTime1bak
;
126 private boolean fIsInUpdate
;
128 private List
<Integer
> fTickList
= new ArrayList
<>();
129 private List
<IMarkerEvent
> fMarkers
= null;
130 private boolean fMarkersVisible
= true;
133 * Standard constructor
136 * The parent composite object
138 * The color scheme to use
140 public TimeGraphScale(Composite parent
, TimeGraphColorScheme colors
) {
141 super(parent
, colors
, SWT
.NO_BACKGROUND
| SWT
.NO_FOCUS
| SWT
.DOUBLE_BUFFERED
);
142 TmfSignalManager
.register(this);
143 addMouseListener(this);
144 addMouseMoveListener(this);
145 TimeDraw
.updateTimeZone();
149 public void dispose() {
150 TmfSignalManager
.deregister(this);
155 * Assign the time provider for this scale
157 * @param timeProvider
158 * The provider to use
160 public void setTimeProvider(ITimeDataProvider timeProvider
) {
161 fTimeProvider
= timeProvider
;
165 * Get the time provider used by this scale
167 * @return The time provider
169 public ITimeDataProvider
getTimeProvider() {
170 return fTimeProvider
;
174 public Point
computeSize(int wHint
, int hHint
, boolean changed
) {
175 return super.computeSize(wHint
, fHeight
, changed
);
179 * Set the height of the scale
184 public void setHeight(int height
) {
185 if (fHeight
!= height
) {
187 getParent().layout(new Control
[] { this });
192 * Set the drag range to paint decorators
195 * The begin x-coordinate
197 * The end x-coordinate
199 public void setDragRange(int begin
, int end
) {
200 if (NO_BUTTON
== fDragState
|| DRAG_EXTERNAL
== fDragState
) {
201 fDragX0
= begin
- fTimeProvider
.getNameSpace();
202 fDragX
= end
- fTimeProvider
.getNameSpace();
203 if (begin
>= 0 || end
>= 0) {
204 fDragState
= DRAG_EXTERNAL
;
206 fDragState
= NO_BUTTON
;
213 * Get the list of visible ticks of the time axis.
215 * @return the list of visible tick x-coordinates
218 public List
<Integer
> getTickList() {
223 * Set the markers list.
226 * The markers list, or null
229 public void setMarkers(List
<IMarkerEvent
> markers
) {
234 * Set the markers visibility. The default is true.
237 * true to show the markers, false otherwise
240 public void setMarkersVisible(boolean visible
) {
241 fMarkersVisible
= visible
;
244 private long calcTimeDelta(int width
, double pixelsPerNanoSec
) {
246 double minDelta
= (pixelsPerNanoSec
== 0) ? YEAR_IN_NS
: width
/ pixelsPerNanoSec
;
248 if (fTimeProvider
!= null && fTimeProvider
.getTimeFormat() == TimeFormat
.CALENDAR
) {
249 if (minDelta
> MAX_MONTH_FACTOR
* MONTH_IN_NS
) {
251 } else if (minDelta
> MIN_MONTH_FACTOR
* MONTH_IN_NS
) {
252 unit
= MONTH_FACTOR
* MONTH_IN_NS
;
253 } else if (minDelta
> MAX_DAY_FACTOR
* DAY_IN_NS
) {
255 } else if (minDelta
> MAX_HOUR_FACTOR
* HOUR_IN_NS
) {
257 } else if (minDelta
> MIN_HOUR_FACTOR
* HOUR_IN_NS
) {
258 unit
= HOUR_FACTOR
* HOUR_IN_NS
;
259 } else if (minDelta
> MAX_MINUTE_FACTOR
* MIN_IN_NS
) {
261 } else if (minDelta
> MIN_MINUTE_FACTOR
* MIN_IN_NS
) {
262 unit
= MINUTE_FACTOR
* MIN_IN_NS
;
263 } else if (minDelta
> MAX_SECOND_FACTOR
* SEC_IN_NS
) {
265 } else if (minDelta
> MIN_SECOND_FACTOR
* SEC_IN_NS
) {
266 unit
= SECOND_FACTOR
* SEC_IN_NS
;
267 } else if (minDelta
<= 1) {
272 double log
= Math
.log10(minDelta
/ unit
);
273 long pow10
= (long) log
;
274 double remainder
= log
- pow10
;
275 if (remainder
< LOG10_1
) {
276 timeDelta
= (long) Math
.pow(BASE_10
, pow10
) * unit
;
277 } else if (remainder
< LOG10_2
) {
278 timeDelta
= 2 * (long) Math
.pow(BASE_10
, pow10
) * unit
;
279 } else if (remainder
< LOG10_3
&& unit
>= HOUR_IN_NS
&& unit
< YEAR_IN_NS
) {
280 timeDelta
= 3 * (long) Math
.pow(BASE_10
, pow10
) * unit
;
281 } else if (remainder
< LOG10_5
) {
282 timeDelta
= 5 * (long) Math
.pow(BASE_10
, pow10
) * unit
;
284 timeDelta
= 10 * (long) Math
.pow(BASE_10
, pow10
) * unit
;
286 if (timeDelta
<= 0) {
292 TimeDraw
getTimeDraw(long timeDelta
) {
294 if (fTimeProvider
!= null) {
295 switch (fTimeProvider
.getTimeFormat()) {
297 if (timeDelta
>= YEAR_IN_NS
) {
298 timeDraw
= TIMEDRAW_ABS_YEAR
;
299 } else if (timeDelta
>= MONTH_IN_NS
) {
300 timeDraw
= TIMEDRAW_ABS_MONTH
;
301 } else if (timeDelta
>= DAY_IN_NS
) {
302 timeDraw
= TIMEDRAW_ABS_DAY
;
303 } else if (timeDelta
>= HOUR_IN_NS
) {
304 timeDraw
= TIMEDRAW_ABS_HRS
;
305 } else if (timeDelta
>= MIN_IN_NS
) {
306 timeDraw
= TIMEDRAW_ABS_MIN
;
307 } else if (timeDelta
>= SEC_IN_NS
) {
308 timeDraw
= TIMEDRAW_ABS_SEC
;
309 } else if (timeDelta
>= MILLISEC_IN_NS
) {
310 timeDraw
= TIMEDRAW_ABS_MILLISEC
;
311 } else if (timeDelta
>= MICROSEC_IN_NS
) {
312 timeDraw
= TIMEDRAW_ABS_MICROSEC
;
314 timeDraw
= TIMEDRAW_ABS_NANOSEC
;
318 return TIMEDRAW_NUMBER
;
320 return TIMEDRAW_CYCLES
;
326 if (timeDelta
>= SEC_IN_NS
) {
327 timeDraw
= TIMEDRAW_SEC
;
328 } else if (timeDelta
>= MILLISEC_IN_NS
) {
329 timeDraw
= TIMEDRAW_MILLISEC
;
330 } else if (timeDelta
>= MICROSEC_IN_NS
) {
331 timeDraw
= TIMEDRAW_MICROSEC
;
333 timeDraw
= TIMEDRAW_NANOSEC
;
339 void paint(Rectangle rect
, PaintEvent e
) {
341 if (fIsInUpdate
|| null == fTimeProvider
) {
346 gc
.fillRectangle(rect
);
348 long time0
= fTimeProvider
.getTime0();
349 long time1
= fTimeProvider
.getTime1();
350 int leftSpace
= fTimeProvider
.getNameSpace();
351 int timeSpace
= fTimeProvider
.getTimeSpace();
353 gc
.setBackground(getColorScheme().getColor(TimeGraphColorScheme
.TOOL_BACKGROUND
));
354 gc
.setForeground(getColorScheme().getColor(TimeGraphColorScheme
.TOOL_FOREGROUND
));
355 Rectangle rect0
= new Rectangle(0, 0, 0, 0);
356 Utils
.init(rect0
, rect
);
358 // draw top left area
359 rect0
.width
= leftSpace
;
361 rect0
.width
-= X_OFFSET
;
362 Rectangle absHeaderRect
= new Rectangle(rect0
.x
, rect0
.y
, rect0
.width
, rect0
.height
);
364 rect0
.width
+= X_OFFSET
;
366 // prepare and draw right rect of the timescale
367 rect0
.x
+= leftSpace
;
368 rect0
.width
= rect
.width
- leftSpace
;
370 // draw bottom border and erase all other area
371 gc
.drawLine(rect
.x
, rect
.y
+ rect
.height
- 1, rect
.x
+ rect
.width
- 1,
372 rect
.y
+ rect
.height
- 1);
374 gc
.fillRectangle(rect0
);
376 if (time1
<= time0
|| timeSpace
< 2) {
381 int numDigits
= calculateDigits(time0
, time1
);
383 int labelWidth
= gc
.getCharWidth('0') * numDigits
;
384 double pixelsPerNanoSec
= (timeSpace
<= RIGHT_MARGIN
) ?
0 :
385 (double) (timeSpace
- RIGHT_MARGIN
) / (time1
- time0
);
386 long timeDelta
= calcTimeDelta(labelWidth
, pixelsPerNanoSec
);
388 TimeDraw timeDraw
= getTimeDraw(timeDelta
);
390 // draw range decorators
391 if (DRAG_EXTERNAL
== fDragState
) {
392 int x1
= leftSpace
+ fDragX0
;
393 int x2
= leftSpace
+ fDragX
;
394 drawRangeDecorators(rect0
, gc
, x1
, x2
);
398 long selectionBegin
= fTimeProvider
.getSelectionBegin();
399 long selectionEnd
= fTimeProvider
.getSelectionEnd();
400 x1
= leftSpace
+ (int) ((selectionBegin
- time0
) * pixelsPerNanoSec
);
401 x2
= leftSpace
+ (int) ((selectionEnd
- time0
) * pixelsPerNanoSec
);
402 drawRangeDecorators(rect0
, gc
, x1
, x2
);
405 if (rect0
.isEmpty()) {
409 // draw time scale ticks
411 rect0
.height
= rect
.height
- Y_OFFSET
;
412 rect0
.width
= labelWidth
;
415 if (fTimeProvider
!= null && fTimeProvider
.getTimeFormat() == TimeFormat
.CALENDAR
) {
416 time
= floorToCalendar(time0
, timeDelta
);
418 time
= (time0
/ timeDelta
) * timeDelta
;
424 int y
= rect0
.y
+ rect0
.height
;
426 if (fTimeProvider
!= null && fTimeProvider
.getTimeFormat() == TimeFormat
.CALENDAR
) {
427 timeDraw
.drawAbsHeader(gc
, time
, absHeaderRect
);
430 List
<Integer
> tickList
= new ArrayList
<>();
432 int x
= rect
.x
+ leftSpace
+ (int) (Math
.floor((time
- time0
) * pixelsPerNanoSec
));
433 if (x
>= rect
.x
+ leftSpace
+ rect
.width
- rect0
.width
) {
436 if (x
>= rect
.x
+ leftSpace
) {
437 gc
.drawLine(x
, y
, x
, y
+ Y_OFFSET
);
439 if (x
+ rect0
.width
<= rect
.x
+ rect
.width
) {
440 timeDraw
.draw(gc
, time
, rect0
);
444 if (pixelsPerNanoSec
== 0 || time
> Long
.MAX_VALUE
- timeDelta
|| timeDelta
== 0) {
447 if (fTimeProvider
!= null && fTimeProvider
.getTimeFormat() == TimeFormat
.CALENDAR
) {
448 if (timeDelta
>= YEAR_IN_NS
) {
449 long millis
= time
/ MILLISEC_IN_NS
;
450 GREGORIAN_CALENDAR
.setTime(new Date(millis
));
451 GREGORIAN_CALENDAR
.add(Calendar
.YEAR
, (int) (timeDelta
/ YEAR_IN_NS
));
452 millis
= GREGORIAN_CALENDAR
.getTimeInMillis();
453 time
= millis
* MILLISEC_IN_NS
;
454 } else if (timeDelta
>= MONTH_IN_NS
) {
455 long millis
= time
/ MILLISEC_IN_NS
;
456 GREGORIAN_CALENDAR
.setTime(new Date(millis
));
457 GREGORIAN_CALENDAR
.add(Calendar
.MONTH
, (int) (timeDelta
/ MONTH_IN_NS
));
458 millis
= GREGORIAN_CALENDAR
.getTimeInMillis();
459 time
= millis
* MILLISEC_IN_NS
;
460 } else if (timeDelta
>= DAY_IN_NS
) {
461 long millis
= time
/ MILLISEC_IN_NS
;
462 GREGORIAN_CALENDAR
.setTime(new Date(millis
));
463 GREGORIAN_CALENDAR
.add(Calendar
.DAY_OF_MONTH
, (int) (timeDelta
/ DAY_IN_NS
));
464 millis
= GREGORIAN_CALENDAR
.getTimeInMillis();
465 time
= millis
* MILLISEC_IN_NS
;
473 fTickList
= tickList
;
475 // draw marker labels
476 if (fMarkersVisible
) {
477 drawMarkerLabels(fMarkers
, rect
, gc
, time0
, leftSpace
, pixelsPerNanoSec
);
481 private void drawMarkerLabels(List
<IMarkerEvent
> markerEvents
, Rectangle rect
, GC gc
, long time0
, int leftSpace
, double pixelsPerNanoSec
) {
482 if (markerEvents
== null) {
485 for (IMarkerEvent markerEvent
: markerEvents
) {
486 String label
= markerEvent
.getLabel();
487 if (label
!= null && markerEvent
.getEntry() == null) {
488 label
= label
.substring(0, Math
.min(label
.indexOf('\n') != -1 ? label
.indexOf('\n') : label
.length(), MAX_LABEL_LENGTH
));
489 int x
= rect
.x
+ leftSpace
+ (int) (Math
.floor((markerEvent
.getTime() - time0
) * pixelsPerNanoSec
));
490 int y
= rect
.y
+ rect
.height
- gc
.stringExtent(" ").y
+ 2; //$NON-NLS-1$
491 Color color
= getColorScheme().getColor(markerEvent
.getColor());
492 gc
.setForeground(color
);
493 Utils
.drawText(gc
, label
, x
, y
, true);
498 private static void drawRangeDecorators(Rectangle rect
, GC gc
, int x1
, int x2
) {
499 int y1
= rect
.y
+ rect
.height
- 9;
500 int y2
= rect
.y
+ rect
.height
- 5;
501 int ym
= (y1
+ y2
) / 2;
504 gc
.drawLine(x1
- 2, y1
, x1
- 2, y2
);
505 gc
.drawLine(x1
- 3, y1
, x1
- 1, y1
);
506 gc
.drawLine(x1
+ 1, y1
, x1
+ 1, y2
);
508 if (x2
>= rect
.x
&& Math
.abs(x2
- x1
- 2) > 3) {
510 gc
.drawLine(x2
- 2, y1
, x2
- 2, y2
);
511 gc
.drawLine(x2
- 3, y1
, x2
- 1, y1
);
513 if (x2
>= rect
.x
&& Math
.abs(x2
- x1
+ 3) > 3) {
515 gc
.drawLine(x2
+ 1, y1
, x2
+ 3, y1
);
516 gc
.drawLine(x2
+ 3, y1
, x2
+ 3, ym
);
517 gc
.drawLine(x2
+ 1, ym
, x2
+ 3, ym
);
518 gc
.drawLine(x2
+ 1, ym
, x2
+ 1, y2
);
519 gc
.drawLine(x2
+ 1, y2
, x2
+ 3, y2
);
523 private static long floorToCalendar(long time
, long timeDelta
) {
526 if (timeDelta
>= YEAR_IN_NS
) {
527 GREGORIAN_CALENDAR
.setTime(new Date(ret
/ MILLISEC_IN_NS
));
528 int year
= GREGORIAN_CALENDAR
.get(Calendar
.YEAR
);
529 int yearDelta
= (int) (timeDelta
/ YEAR_IN_NS
);
530 year
= (year
/ yearDelta
) * yearDelta
;
531 GREGORIAN_CALENDAR
.set(Calendar
.YEAR
, year
);
532 GREGORIAN_CALENDAR
.set(Calendar
.MONTH
, 0); // January 1st of year
533 GREGORIAN_CALENDAR
.set(Calendar
.DAY_OF_MONTH
, 1);
534 GREGORIAN_CALENDAR
.set(Calendar
.HOUR_OF_DAY
, 0);
535 GREGORIAN_CALENDAR
.set(Calendar
.MINUTE
, 0);
536 GREGORIAN_CALENDAR
.set(Calendar
.SECOND
, 0);
537 GREGORIAN_CALENDAR
.set(Calendar
.MILLISECOND
, 0);
538 ret
= GREGORIAN_CALENDAR
.getTimeInMillis() * MILLISEC_IN_NS
;
539 } else if (timeDelta
>= MONTH_IN_NS
) {
540 GREGORIAN_CALENDAR
.setTime(new Date(ret
/ MILLISEC_IN_NS
));
541 int month
= GREGORIAN_CALENDAR
.get(Calendar
.MONTH
);
542 int monthDelta
= (int) (timeDelta
/ MONTH_IN_NS
);
543 month
= (month
/ monthDelta
) * monthDelta
;
544 GREGORIAN_CALENDAR
.set(Calendar
.MONTH
, month
);
545 GREGORIAN_CALENDAR
.set(Calendar
.DAY_OF_MONTH
, 1); // 1st of month
546 GREGORIAN_CALENDAR
.set(Calendar
.HOUR_OF_DAY
, 0);
547 GREGORIAN_CALENDAR
.set(Calendar
.MINUTE
, 0);
548 GREGORIAN_CALENDAR
.set(Calendar
.SECOND
, 0);
549 GREGORIAN_CALENDAR
.set(Calendar
.MILLISECOND
, 0);
550 ret
= GREGORIAN_CALENDAR
.getTimeInMillis() * MILLISEC_IN_NS
;
552 long offset
= GREGORIAN_CALENDAR
.getTimeZone().getOffset(ret
/ MILLISEC_IN_NS
) * MILLISEC_IN_NS
;
554 ret
= (ret
/ timeDelta
) * timeDelta
;
560 private int calculateDigits(long time0
, long time1
) {
562 long timeRange
= time1
- time0
;
564 if (fTimeProvider
.getTimeFormat() == TimeFormat
.CALENDAR
) {
565 // Calculate the number of digits to represent the minutes provided
569 if (timeRange
< 10000) {
570 // HH:11:222:333:444__
572 } else if (timeRange
< 10000000) {
577 long sec
= time1
/ SEC_IN_NS
;
578 numDigits
= Long
.toString(sec
).length();
579 int thousandGroups
= (numDigits
- 1) / 3;
580 numDigits
+= thousandGroups
;
581 numDigits
+= 12; // .000 000 000
582 if (fTimeProvider
.getTimeFormat() == TimeFormat
.CYCLES
) {
583 numDigits
+= Messages
.Utils_ClockCyclesUnit
.length();
591 public void mouseDown(MouseEvent e
) {
592 getParent().setFocus();
593 if (fDragState
== NO_BUTTON
&& null != fTimeProvider
) {
594 int x
= e
.x
- fTimeProvider
.getNameSpace();
595 if (LEFT_BUTTON
== e
.button
&& x
> 0) {
597 fDragState
= LEFT_BUTTON
;
601 } else if (x
> getSize().x
- fTimeProvider
.getNameSpace()) {
602 x
= getSize().x
- fTimeProvider
.getNameSpace();
606 fTime0bak
= fTimeProvider
.getTime0();
607 fTime1bak
= fTimeProvider
.getTime1();
612 public void mouseUp(MouseEvent e
) {
613 if (e
.button
== LEFT_BUTTON
&& fDragState
== LEFT_BUTTON
) {
615 fDragState
= NO_BUTTON
;
617 // Notify time provider to check the need for listener notification
618 if (fDragX
!= fDragX0
&& fTimeProvider
.getTime0() != fTimeProvider
.getTime1()) {
619 fTimeProvider
.setStartFinishTimeNotify(fTimeProvider
.getTime0(), fTimeProvider
.getTime1());
625 public void mouseMove(MouseEvent e
) {
626 if (fDragX0
< 0 || fDragState
== NO_BUTTON
|| fTimeProvider
== null) {
629 Point size
= getSize();
630 int leftSpace
= fTimeProvider
.getNameSpace();
631 int x
= e
.x
- leftSpace
;
632 if (LEFT_BUTTON
== fDragState
) {
633 if (x
> 0 && size
.x
> leftSpace
&& fDragX
!= x
) {
635 if (fTimeProvider
.getTime0() == fTimeProvider
.getTime1()) {
638 long interval
= (long) ((fTime1bak
- fTime0bak
) * ((double) fDragX0
/ fDragX
));
639 if (interval
== Long
.MAX_VALUE
) {
640 fTimeProvider
.setStartFinishTimeNotify(fTime0bak
, Long
.MAX_VALUE
);
642 long time1
= fTime0bak
+ (long) ((fTime1bak
- fTime0bak
) * ((double) fDragX0
/ fDragX
));
643 fTimeProvider
.setStartFinishTimeNotify(fTime0bak
, time1
);
650 public void mouseDoubleClick(MouseEvent e
) {
651 if (e
.button
== 1 && null != fTimeProvider
&& fTimeProvider
.getTime0() != fTimeProvider
.getTime1() && (e
.stateMask
& SWT
.BUTTON_MASK
) == 0) {
652 fTimeProvider
.resetStartFinishTime();
653 fTime0bak
= fTimeProvider
.getTime0();
654 fTime1bak
= fTimeProvider
.getTime1();
659 * Update the display to use the updated timestamp format
662 * the incoming signal
665 public void timestampFormatUpdated(TmfTimestampFormatUpdateSignal signal
) {
666 TimeDraw
.updateTimeZone();
667 Utils
.updateTimeZone();
672 abstract class TimeDraw
{
673 protected static final long MICROSEC_IN_NS
= 1000;
674 protected static final long MILLISEC_IN_NS
= 1000000;
675 protected static final long MILLISEC_IN_US
= 1000;
676 protected static final long SEC_IN_NS
= 1000000000;
677 protected static final long SEC_IN_MS
= 1000;
678 private static final String S
= ""; //$NON-NLS-1$
679 private static final String S0
= "0"; //$NON-NLS-1$
680 private static final String S00
= "00"; //$NON-NLS-1$
681 protected static final long PAD_1000
= 1000;
682 protected static final SimpleDateFormat SEC_FORMAT_HEADER
=
683 new SimpleDateFormat("yyyy MMM dd");//$NON-NLS-1$
684 protected static final SimpleDateFormat SEC_FORMAT
=
685 new SimpleDateFormat("HH:mm:ss"); //$NON-NLS-1$
686 protected static final SimpleDateFormat MIN_FORMAT_HEADER
=
687 new SimpleDateFormat("yyyy MMM dd"); //$NON-NLS-1$
688 protected static final SimpleDateFormat MIN_FORMAT
=
689 new SimpleDateFormat("HH:mm"); //$NON-NLS-1$
690 protected static final SimpleDateFormat HOURS_FORMAT_HEADER
=
691 new SimpleDateFormat("yyyy"); //$NON-NLS-1$
692 protected static final SimpleDateFormat HOURS_FORMAT
=
693 new SimpleDateFormat("MMM dd HH:mm"); //$NON-NLS-1$
694 protected static final SimpleDateFormat DAY_FORMAT_HEADER
=
695 new SimpleDateFormat("yyyy"); //$NON-NLS-1$
696 protected static final SimpleDateFormat DAY_FORMAT
=
697 new SimpleDateFormat("MMM dd"); //$NON-NLS-1$
698 protected static final SimpleDateFormat MONTH_FORMAT
=
699 new SimpleDateFormat("yyyy MMM"); //$NON-NLS-1$
700 protected static final SimpleDateFormat YEAR_FORMAT
=
701 new SimpleDateFormat("yyyy"); //$NON-NLS-1$
703 protected static final List
<SimpleDateFormat
> formats
;
706 ImmutableList
.Builder
<SimpleDateFormat
> formatArrayBuilder
= ImmutableList
.<SimpleDateFormat
> builder();
707 formatArrayBuilder
.add(SEC_FORMAT
);
708 formatArrayBuilder
.add(SEC_FORMAT_HEADER
);
709 formatArrayBuilder
.add(MIN_FORMAT
);
710 formatArrayBuilder
.add(MIN_FORMAT_HEADER
);
711 formatArrayBuilder
.add(HOURS_FORMAT
);
712 formatArrayBuilder
.add(HOURS_FORMAT_HEADER
);
713 formatArrayBuilder
.add(DAY_FORMAT
);
714 formatArrayBuilder
.add(DAY_FORMAT_HEADER
);
715 formatArrayBuilder
.add(MONTH_FORMAT
);
716 formatArrayBuilder
.add(YEAR_FORMAT
);
717 formats
= formatArrayBuilder
.build();
721 * Updates the timezone using the preferences.
723 public static void updateTimeZone() {
724 final TimeZone timeZone
= TmfTimePreferences
.getTimeZone();
725 for (SimpleDateFormat sdf
: formats
) {
727 sdf
.setTimeZone(timeZone
);
732 static String
sep(long n
) {
733 StringBuilder retVal
= new StringBuilder();
734 String s
= Long
.toString(n
);
735 for (int i
= 0; i
< s
.length(); i
++) {
736 int pos
= s
.length() - i
- 1;
737 retVal
.append(s
.charAt(i
));
738 if (pos
% 3 == 0 && pos
!= 0) {
742 return retVal
.toString();
745 static String
pad(long n
) {
749 } else if (n
< 100) {
757 public abstract int draw(GC gc
, long time
, Rectangle rect
);
760 * Override to draw absolute time header. This is for the time information
761 * not shown in the draw of each tick
767 * @param absHeaderRect
770 public void drawAbsHeader(GC gc
, long nanosec
, Rectangle absHeaderRect
) {
773 protected void drawAbsHeader(GC gc
, long nanosec
, Rectangle rect
, SimpleDateFormat dateFormat
) {
775 synchronized (dateFormat
) {
776 header
= dateFormat
.format(new Date(nanosec
/ MILLISEC_IN_NS
));
778 int headerwidth
= gc
.stringExtent(header
).x
+ 4;
779 if (headerwidth
<= rect
.width
) {
780 rect
.x
+= (rect
.width
- headerwidth
);
781 Utils
.drawText(gc
, header
, rect
, true);
786 class TimeDrawSec
extends TimeDraw
{
788 public int draw(GC gc
, long nanosec
, Rectangle rect
) {
789 long sec
= nanosec
/ SEC_IN_NS
;
790 return Utils
.drawText(gc
, sep(sec
), rect
, true);
794 class TimeDrawMillisec
extends TimeDraw
{
796 public int draw(GC gc
, long nanosec
, Rectangle rect
) {
797 long millisec
= nanosec
/ MILLISEC_IN_NS
;
798 long ms
= millisec
% PAD_1000
;
799 long sec
= millisec
/ SEC_IN_MS
;
800 return Utils
.drawText(gc
, sep(sec
) + "." + pad(ms
), rect
, true); //$NON-NLS-1$
804 class TimeDrawMicrosec
extends TimeDraw
{
806 public int draw(GC gc
, long nanosec
, Rectangle rect
) {
807 long microsec
= nanosec
/ MICROSEC_IN_NS
;
808 long us
= microsec
% PAD_1000
;
809 long millisec
= microsec
/ MILLISEC_IN_US
;
810 long ms
= millisec
% PAD_1000
;
811 long sec
= millisec
/ SEC_IN_MS
;
812 return Utils
.drawText(gc
, sep(sec
) + "." + pad(ms
) + " " + pad(us
), rect
, true); //$NON-NLS-1$ //$NON-NLS-2$
816 class TimeDrawNanosec
extends TimeDraw
{
818 public int draw(GC gc
, long nanosec
, Rectangle rect
) {
819 long ns
= nanosec
% PAD_1000
;
820 long microsec
= nanosec
/ MICROSEC_IN_NS
;
821 long us
= microsec
% PAD_1000
;
822 long millisec
= microsec
/ MILLISEC_IN_US
;
823 long ms
= millisec
% PAD_1000
;
824 long sec
= millisec
/ SEC_IN_MS
;
825 return Utils
.drawText(gc
, sep(sec
) + "." + pad(ms
) + " " + pad(us
) + " " + pad(ns
), rect
, true); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
829 class TimeDrawAbsYear
extends TimeDraw
{
831 public int draw(GC gc
, long nanosec
, Rectangle rect
) {
833 synchronized (YEAR_FORMAT
) {
834 stime
= YEAR_FORMAT
.format(new Date(nanosec
/ MILLISEC_IN_NS
));
836 return Utils
.drawText(gc
, stime
, rect
, true);
840 class TimeDrawAbsMonth
extends TimeDraw
{
842 public int draw(GC gc
, long nanosec
, Rectangle rect
) {
844 synchronized (MONTH_FORMAT
) {
845 stime
= MONTH_FORMAT
.format(new Date(nanosec
/ MILLISEC_IN_NS
));
847 return Utils
.drawText(gc
, stime
, rect
, true);
851 class TimeDrawAbsDay
extends TimeDraw
{
853 public int draw(GC gc
, long nanosec
, Rectangle rect
) {
855 synchronized (DAY_FORMAT
) {
856 stime
= DAY_FORMAT
.format(new Date(nanosec
/ MILLISEC_IN_NS
));
858 return Utils
.drawText(gc
, stime
, rect
, true);
862 public void drawAbsHeader(GC gc
, long nanosec
, Rectangle rect
) {
863 drawAbsHeader(gc
, nanosec
, rect
, DAY_FORMAT_HEADER
);
867 class TimeDrawAbsHrs
extends TimeDraw
{
869 public int draw(GC gc
, long nanosec
, Rectangle rect
) {
871 synchronized (HOURS_FORMAT
) {
872 stime
= HOURS_FORMAT
.format(new Date(nanosec
/ MILLISEC_IN_NS
));
874 return Utils
.drawText(gc
, stime
, rect
, true);
878 public void drawAbsHeader(GC gc
, long nanosec
, Rectangle rect
) {
879 drawAbsHeader(gc
, nanosec
, rect
, HOURS_FORMAT_HEADER
);
883 class TimeDrawAbsMin
extends TimeDraw
{
885 public int draw(GC gc
, long nanosec
, Rectangle rect
) {
887 synchronized (MIN_FORMAT
) {
888 stime
= MIN_FORMAT
.format(new Date(nanosec
/ MILLISEC_IN_NS
));
890 return Utils
.drawText(gc
, stime
, rect
, true);
894 public void drawAbsHeader(GC gc
, long nanosec
, Rectangle rect
) {
895 drawAbsHeader(gc
, nanosec
, rect
, MIN_FORMAT_HEADER
);
899 class TimeDrawAbsSec
extends TimeDraw
{
901 public int draw(GC gc
, long nanosec
, Rectangle rect
) {
903 synchronized (SEC_FORMAT
) {
904 stime
= SEC_FORMAT
.format(new Date(nanosec
/ MILLISEC_IN_NS
));
906 return Utils
.drawText(gc
, stime
, rect
, true);
910 public void drawAbsHeader(GC gc
, long nanosec
, Rectangle rect
) {
911 drawAbsHeader(gc
, nanosec
, rect
, SEC_FORMAT_HEADER
);
915 class TimeDrawAbsMillisec
extends TimeDraw
{
917 public int draw(GC gc
, long nanosec
, Rectangle rect
) {
919 synchronized (SEC_FORMAT
) {
920 stime
= SEC_FORMAT
.format(new Date(nanosec
/ MILLISEC_IN_NS
));
922 String ns
= Utils
.formatNs(nanosec
, Resolution
.MILLISEC
);
923 return Utils
.drawText(gc
, stime
+ "." + ns
, rect
, true); //$NON-NLS-1$
927 public void drawAbsHeader(GC gc
, long nanosec
, Rectangle rect
) {
928 drawAbsHeader(gc
, nanosec
, rect
, SEC_FORMAT_HEADER
);
932 class TimeDrawAbsMicroSec
extends TimeDraw
{
934 public int draw(GC gc
, long nanosec
, Rectangle rect
) {
936 synchronized (SEC_FORMAT
) {
937 stime
= SEC_FORMAT
.format(new Date(nanosec
/ MILLISEC_IN_NS
));
939 String micr
= Utils
.formatNs(nanosec
, Resolution
.MICROSEC
);
940 return Utils
.drawText(gc
, stime
+ "." + micr
, rect
, true); //$NON-NLS-1$
944 public void drawAbsHeader(GC gc
, long nanosec
, Rectangle rect
) {
945 drawAbsHeader(gc
, nanosec
, rect
, SEC_FORMAT_HEADER
);
949 class TimeDrawAbsNanoSec
extends TimeDraw
{
951 public int draw(GC gc
, long nanosec
, Rectangle rect
) {
953 synchronized (SEC_FORMAT
) {
954 stime
= SEC_FORMAT
.format(new Date(nanosec
/ MILLISEC_IN_NS
));
956 String ns
= Utils
.formatNs(nanosec
, Resolution
.NANOSEC
);
957 return Utils
.drawText(gc
, stime
+ "." + ns
, rect
, true); //$NON-NLS-1$
961 public void drawAbsHeader(GC gc
, long nanosec
, Rectangle rect
) {
962 drawAbsHeader(gc
, nanosec
, rect
, SEC_FORMAT_HEADER
);
966 class TimeDrawNumber
extends TimeDraw
{
968 public int draw(GC gc
, long time
, Rectangle rect
) {
969 String stime
= NumberFormat
.getInstance().format(time
);
970 return Utils
.drawText(gc
, stime
, rect
, true);
974 class TimeDrawCycles
extends TimeDraw
{
976 public int draw(GC gc
, long time
, Rectangle rect
) {
977 String stime
= Utils
.formatTime(time
, TimeFormat
.CYCLES
, Resolution
.SECONDS
);
978 return Utils
.drawText(gc
, stime
, rect
, true);