Commit | Line | Data |
---|---|---|
8e1f81f1 | 1 | /***************************************************************************** |
c8422608 | 2 | * Copyright (c) 2007, 2013 Intel Corporation, Ericsson |
8e1f81f1 PT |
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 | |
8e1f81f1 PT |
13 | *****************************************************************************/ |
14 | ||
15 | package org.eclipse.linuxtools.tmf.ui.widgets.timegraph.widgets; | |
16 | ||
026664b7 | 17 | import java.text.NumberFormat; |
8e1f81f1 PT |
18 | import java.text.SimpleDateFormat; |
19 | import java.util.Calendar; | |
20 | import java.util.Date; | |
21 | ||
22 | import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.widgets.Utils.Resolution; | |
026664b7 | 23 | import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.widgets.Utils.TimeFormat; |
8e1f81f1 PT |
24 | import org.eclipse.swt.SWT; |
25 | import org.eclipse.swt.events.MouseEvent; | |
26 | import org.eclipse.swt.events.MouseListener; | |
27 | import org.eclipse.swt.events.MouseMoveListener; | |
28 | import org.eclipse.swt.events.PaintEvent; | |
29 | import org.eclipse.swt.graphics.GC; | |
30 | import org.eclipse.swt.graphics.Point; | |
31 | import org.eclipse.swt.graphics.Rectangle; | |
32 | import org.eclipse.swt.widgets.Composite; | |
33 | ||
34 | /** | |
35 | * Implementation of the scale for the time graph view. | |
36 | * | |
37 | * This goes above the "gantt chart" area. | |
38 | * | |
39 | * @version 1.0 | |
40 | * @author Alvaro Sanchez-Leon | |
41 | * @author Patrick Tasse | |
42 | */ | |
43 | public class TimeGraphScale extends TimeGraphBaseControl implements | |
44 | MouseListener, MouseMoveListener { | |
45 | ||
f1fae91f PT |
46 | private static final long MICROSEC_IN_NS = 1000; |
47 | private static final long MILLISEC_IN_NS = 1000000; | |
8e1f81f1 PT |
48 | private static final long SEC_IN_NS = 1000000000; |
49 | private static final long MIN_IN_NS = 60 * SEC_IN_NS; | |
50 | private static final long HOUR_IN_NS = 60 * MIN_IN_NS; | |
51 | private static final long DAY_IN_NS = 24 * HOUR_IN_NS; | |
52 | private static final long MONTH_IN_NS = 31 * DAY_IN_NS; // upper limit | |
53 | private static final long YEAR_IN_NS = 366 * DAY_IN_NS; // upper limit | |
54 | ||
55 | private static final double LOG10_1 = Math.log10(1); | |
56 | private static final double LOG10_2 = Math.log10(2); | |
57 | private static final double LOG10_3 = Math.log10(3); | |
58 | private static final double LOG10_5 = Math.log10(5); | |
59 | ||
60 | private static final Calendar GREGORIAN_CALENDAR = Calendar.getInstance(); | |
61 | ||
f1fae91f PT |
62 | private static final TimeDraw TIMEDRAW_NANOSEC = new TimeDrawNanosec(); |
63 | private static final TimeDraw TIMEDRAW_MICROSEC = new TimeDrawMicrosec(); | |
64 | private static final TimeDraw TIMEDRAW_MILLISEC = new TimeDrawMillisec(); | |
65 | private static final TimeDraw TIMEDRAW_SEC = new TimeDrawSec(); | |
66 | private static final TimeDraw TIMEDRAW_ABS_NANOSEC = new TimeDrawAbsNanoSec(); | |
67 | private static final TimeDraw TIMEDRAW_ABS_MICROSEC = new TimeDrawAbsMicroSec(); | |
68 | private static final TimeDraw TIMEDRAW_ABS_MILLISEC = new TimeDrawAbsMillisec(); | |
69 | private static final TimeDraw TIMEDRAW_ABS_SEC = new TimeDrawAbsSec(); | |
70 | private static final TimeDraw TIMEDRAW_ABS_MIN = new TimeDrawAbsMin(); | |
71 | private static final TimeDraw TIMEDRAW_ABS_HRS = new TimeDrawAbsHrs(); | |
72 | private static final TimeDraw TIMEDRAW_ABS_DAY = new TimeDrawAbsDay(); | |
73 | private static final TimeDraw TIMEDRAW_ABS_MONTH = new TimeDrawAbsMonth(); | |
74 | private static final TimeDraw TIMEDRAW_ABS_YEAR = new TimeDrawAbsYear(); | |
75 | private static final TimeDraw TIMEDRAW_NUMBER = new TimeDrawNumber(); | |
76 | ||
77 | private static final int NO_BUTTON = 0; | |
78 | private static final int LEFT_BUTTON = 1; | |
79 | private static final int RIGHT_BUTTON = 3; | |
80 | ||
81 | private ITimeDataProvider fTimeProvider; | |
82 | private int fDragState = NO_BUTTON; | |
83 | private int fDragX0 = 0; | |
84 | private int fDragX = 0; | |
85 | private long fTime0bak; | |
86 | private long fTime1bak; | |
87 | private boolean fIsInUpdate; | |
88 | private int fHeight; | |
89 | ||
90 | /** | |
91 | * Standard constructor | |
92 | * | |
93 | * @param parent | |
94 | * The parent composite object | |
95 | * @param colors | |
96 | * The color scheme to use | |
97 | */ | |
98 | public TimeGraphScale(Composite parent, TimeGraphColorScheme colors) { | |
99 | super(parent, colors, SWT.NO_BACKGROUND | SWT.NO_FOCUS | SWT.DOUBLE_BUFFERED); | |
100 | addMouseListener(this); | |
101 | addMouseMoveListener(this); | |
102 | } | |
8e1f81f1 PT |
103 | |
104 | /** | |
105 | * Assign the time provider for this scale | |
106 | * | |
107 | * @param timeProvider | |
108 | * The provider to use | |
109 | */ | |
110 | public void setTimeProvider(ITimeDataProvider timeProvider) { | |
f1fae91f | 111 | fTimeProvider = timeProvider; |
8e1f81f1 PT |
112 | } |
113 | ||
8e1f81f1 PT |
114 | @Override |
115 | public Point computeSize(int wHint, int hHint, boolean changed) { | |
f1fae91f | 116 | return super.computeSize(wHint, fHeight, changed); |
8e1f81f1 PT |
117 | } |
118 | ||
119 | /** | |
120 | * Set the height of the scale | |
121 | * | |
122 | * @param height | |
123 | * The height to use | |
124 | */ | |
125 | public void setHeight(int height) { | |
f1fae91f | 126 | this.fHeight = height; |
8e1f81f1 PT |
127 | } |
128 | ||
f1fae91f PT |
129 | private long calcTimeDelta(int width, double pixelsPerNanoSec) { |
130 | long timeDelta; | |
8e1f81f1 PT |
131 | double minDelta = (pixelsPerNanoSec == 0) ? YEAR_IN_NS : width / pixelsPerNanoSec; |
132 | long unit = 1; | |
f1fae91f | 133 | if (fTimeProvider != null && fTimeProvider.getTimeFormat().equals(TimeFormat.CALENDAR)) { |
8e1f81f1 PT |
134 | if (minDelta > 6 * MONTH_IN_NS) { |
135 | unit = YEAR_IN_NS; | |
136 | } else if (minDelta > 3 * MONTH_IN_NS) { | |
137 | unit = 6 * MONTH_IN_NS; | |
138 | } else if (minDelta > 10 * DAY_IN_NS) { | |
139 | unit = MONTH_IN_NS; | |
140 | } else if (minDelta > 12 * HOUR_IN_NS) { | |
141 | unit = DAY_IN_NS; | |
142 | } else if (minDelta > 3 * HOUR_IN_NS) { | |
143 | unit = 6 * HOUR_IN_NS; | |
144 | } else if (minDelta > 30 * MIN_IN_NS) { | |
145 | unit = HOUR_IN_NS; | |
146 | } else if (minDelta > 10 * MIN_IN_NS) { | |
147 | unit = 15 * MIN_IN_NS; | |
148 | } else if (minDelta > 30 * SEC_IN_NS) { | |
149 | unit = MIN_IN_NS; | |
150 | } else if (minDelta > 20 * SEC_IN_NS) { | |
151 | unit = 30 * SEC_IN_NS; | |
152 | } else if (minDelta <= 1) { | |
f1fae91f PT |
153 | timeDelta = 1; |
154 | return timeDelta; | |
8e1f81f1 PT |
155 | } |
156 | } | |
157 | double log = Math.log10(minDelta / unit); | |
158 | long pow10 = (long) log; | |
159 | double remainder = log - pow10; | |
160 | if (remainder < LOG10_1) { | |
f1fae91f | 161 | timeDelta = (long) Math.pow(10, pow10) * unit; |
8e1f81f1 | 162 | } else if (remainder < LOG10_2) { |
f1fae91f | 163 | timeDelta = 2 * (long) Math.pow(10, pow10) * unit; |
8e1f81f1 | 164 | } else if (remainder < LOG10_3 && unit >= HOUR_IN_NS && unit < YEAR_IN_NS) { |
f1fae91f | 165 | timeDelta = 3 * (long) Math.pow(10, pow10) * unit; |
8e1f81f1 | 166 | } else if (remainder < LOG10_5) { |
f1fae91f | 167 | timeDelta = 5 * (long) Math.pow(10, pow10) * unit; |
8e1f81f1 | 168 | } else { |
f1fae91f | 169 | timeDelta = 10 * (long) Math.pow(10, pow10) * unit; |
8e1f81f1 | 170 | } |
f1fae91f PT |
171 | if (timeDelta <= 0) { |
172 | timeDelta = 1; | |
f85266f3 | 173 | } |
f1fae91f | 174 | return timeDelta; |
8e1f81f1 PT |
175 | } |
176 | ||
8e1f81f1 PT |
177 | TimeDraw getTimeDraw(long timeDelta) { |
178 | TimeDraw timeDraw; | |
f1fae91f | 179 | if (fTimeProvider != null) { |
026664b7 | 180 | |
f1fae91f | 181 | if (fTimeProvider.getTimeFormat() == TimeFormat.CALENDAR) { |
026664b7 XR |
182 | if (timeDelta >= YEAR_IN_NS) { |
183 | timeDraw = TIMEDRAW_ABS_YEAR; | |
184 | } else if (timeDelta >= MONTH_IN_NS) { | |
185 | timeDraw = TIMEDRAW_ABS_MONTH; | |
186 | } else if (timeDelta >= DAY_IN_NS) { | |
187 | timeDraw = TIMEDRAW_ABS_DAY; | |
188 | } else if (timeDelta >= HOUR_IN_NS) { | |
189 | timeDraw = TIMEDRAW_ABS_HRS; | |
190 | } else if (timeDelta >= MIN_IN_NS) { | |
191 | timeDraw = TIMEDRAW_ABS_MIN; | |
192 | } else if (timeDelta >= SEC_IN_NS) { | |
193 | timeDraw = TIMEDRAW_ABS_SEC; | |
f1fae91f | 194 | } else if (timeDelta >= MILLISEC_IN_NS) { |
026664b7 | 195 | timeDraw = TIMEDRAW_ABS_MILLISEC; |
f1fae91f | 196 | } else if (timeDelta >= MICROSEC_IN_NS) { |
026664b7 XR |
197 | timeDraw = TIMEDRAW_ABS_MICROSEC; |
198 | } else { | |
199 | timeDraw = TIMEDRAW_ABS_NANOSEC; | |
200 | } | |
201 | return timeDraw; | |
f1fae91f | 202 | } else if (fTimeProvider.getTimeFormat() == TimeFormat.NUMBER) { |
026664b7 XR |
203 | timeDraw = TIMEDRAW_NUMBER; |
204 | return timeDraw; | |
8e1f81f1 | 205 | } |
026664b7 | 206 | |
8e1f81f1 | 207 | } |
f1fae91f | 208 | if (timeDelta >= SEC_IN_NS) { |
8e1f81f1 | 209 | timeDraw = TIMEDRAW_SEC; |
f1fae91f | 210 | } else if (timeDelta >= MILLISEC_IN_NS) { |
8e1f81f1 | 211 | timeDraw = TIMEDRAW_MILLISEC; |
f1fae91f | 212 | } else if (timeDelta >= MICROSEC_IN_NS) { |
8e1f81f1 PT |
213 | timeDraw = TIMEDRAW_MICROSEC; |
214 | } else { | |
215 | timeDraw = TIMEDRAW_NANOSEC; | |
216 | } | |
217 | return timeDraw; | |
218 | } | |
219 | ||
220 | @Override | |
221 | void paint(Rectangle rect, PaintEvent e) { | |
222 | ||
f1fae91f | 223 | if (fIsInUpdate || null == fTimeProvider) { |
8e1f81f1 PT |
224 | return; |
225 | } | |
226 | ||
227 | GC gc = e.gc; | |
228 | gc.fillRectangle(rect); | |
229 | ||
f1fae91f PT |
230 | long time0 = fTimeProvider.getTime0(); |
231 | long time1 = fTimeProvider.getTime1(); | |
232 | long selectedTime = fTimeProvider.getSelectedTime(); | |
233 | int leftSpace = fTimeProvider.getNameSpace(); | |
234 | int timeSpace = fTimeProvider.getTimeSpace(); | |
8e1f81f1 | 235 | |
f1fae91f PT |
236 | gc.setBackground(getColorScheme().getColor(TimeGraphColorScheme.TOOL_BACKGROUND)); |
237 | gc.setForeground(getColorScheme().getColor(TimeGraphColorScheme.TOOL_FOREGROUND)); | |
238 | Rectangle rect0 = new Rectangle(0, 0, 0, 0); | |
239 | Utils.init(rect0, rect); | |
8e1f81f1 PT |
240 | |
241 | // draw top left area | |
f1fae91f PT |
242 | rect0.width = leftSpace; |
243 | rect0.x += 4; | |
244 | rect0.width -= 4; | |
245 | Rectangle absHeaderRect = new Rectangle(rect0.x, rect0.y, rect0.width, rect0.height); | |
246 | rect0.x -= 4; | |
247 | rect0.width += 4; | |
8e1f81f1 PT |
248 | |
249 | // prepare and draw right rect of the timescale | |
f1fae91f PT |
250 | rect0.x += leftSpace; |
251 | rect0.width = rect.width - leftSpace; | |
8e1f81f1 PT |
252 | |
253 | // draw bottom border and erase all other area | |
254 | gc.drawLine(rect.x, rect.y + rect.height - 1, rect.x + rect.width - 1, | |
255 | rect.y + rect.height - 1); | |
f1fae91f PT |
256 | rect0.height--; |
257 | gc.fillRectangle(rect0); | |
8e1f81f1 | 258 | |
f1fae91f | 259 | if (RIGHT_BUTTON == fDragState && null != fTimeProvider) { |
5b2b9bd7 | 260 | // draw selected zoom region background |
f1fae91f PT |
261 | gc.setBackground(getColorScheme().getBkColor(true, false, true)); |
262 | if (fDragX0 < fDragX) { | |
263 | gc.fillRectangle(new Rectangle(leftSpace + fDragX0, rect0.y, fDragX - fDragX0, rect0.height)); | |
264 | } else if (fDragX0 > fDragX) { | |
265 | gc.fillRectangle(new Rectangle(leftSpace + fDragX, rect0.y, fDragX0 - fDragX, rect0.height)); | |
5b2b9bd7 PT |
266 | } |
267 | } | |
268 | ||
8e1f81f1 PT |
269 | if (time1 <= time0 || timeSpace < 2) { |
270 | return; | |
271 | } | |
272 | ||
273 | int numDigits = calculateDigits(time0, time1); | |
274 | ||
275 | int labelWidth = gc.getCharWidth('0') * numDigits; | |
276 | double pixelsPerNanoSec = (timeSpace <= RIGHT_MARGIN) ? 0 : | |
277 | (double) (timeSpace - RIGHT_MARGIN) / (time1 - time0); | |
f1fae91f | 278 | long timeDelta = calcTimeDelta(labelWidth, pixelsPerNanoSec); |
8e1f81f1 | 279 | |
f1fae91f | 280 | TimeDraw timeDraw = getTimeDraw(timeDelta); |
8e1f81f1 | 281 | |
5b2b9bd7 | 282 | // draw selected zoom region lines |
f1fae91f PT |
283 | if (RIGHT_BUTTON == fDragState && null != fTimeProvider) { |
284 | gc.drawLine(leftSpace + fDragX0, rect.y, leftSpace + fDragX0, rect.y + rect.height); | |
285 | gc.drawLine(leftSpace + fDragX, rect.y, leftSpace + fDragX, rect.y + rect.height); | |
8e1f81f1 PT |
286 | } |
287 | ||
f1fae91f | 288 | if (rect0.isEmpty()) { |
8e1f81f1 PT |
289 | return; |
290 | } | |
291 | ||
292 | // draw selected time | |
f1fae91f PT |
293 | int x = rect0.x + (int) ((selectedTime - time0) * pixelsPerNanoSec); |
294 | if (x >= rect0.x && x < rect0.x + rect0.width) { | |
295 | gc.setForeground(getColorScheme().getColor(TimeGraphColorScheme.SELECTED_TIME)); | |
296 | gc.drawLine(x, rect0.y + rect0.height - 6, x, rect0.y | |
297 | + rect0.height); | |
298 | gc.setForeground(getColorScheme().getColor(TimeGraphColorScheme.TOOL_FOREGROUND)); | |
8e1f81f1 PT |
299 | } |
300 | ||
301 | // draw time scale ticks | |
f1fae91f PT |
302 | rect0.y = rect.y; |
303 | rect0.height = rect.height - 4; | |
304 | rect0.width = labelWidth; | |
8e1f81f1 PT |
305 | |
306 | long time; | |
f1fae91f PT |
307 | if (fTimeProvider != null && fTimeProvider.getTimeFormat().equals(TimeFormat.CALENDAR)) { |
308 | time = floorToCalendar(time0, timeDelta); | |
8e1f81f1 | 309 | } else { |
f1fae91f | 310 | time = (time0 / timeDelta) * timeDelta; |
8e1f81f1 | 311 | if (time != time0) { |
f1fae91f | 312 | time += timeDelta; |
8e1f81f1 PT |
313 | } |
314 | } | |
315 | ||
f1fae91f | 316 | int y = rect0.y + rect0.height; |
8e1f81f1 | 317 | |
f1fae91f | 318 | if (fTimeProvider != null && fTimeProvider.getTimeFormat().equals(TimeFormat.CALENDAR)) { |
8e1f81f1 PT |
319 | timeDraw.drawAbsHeader(gc, time, absHeaderRect); |
320 | } | |
321 | ||
322 | while (true) { | |
323 | x = rect.x + leftSpace + (int) (Math.floor((time - time0) * pixelsPerNanoSec)); | |
f1fae91f | 324 | if (x >= rect.x + leftSpace + rect.width - rect0.width) { |
8e1f81f1 PT |
325 | break; |
326 | } | |
327 | if (x >= rect.x + leftSpace) { | |
328 | gc.drawLine(x, y, x, y + 4); | |
f1fae91f PT |
329 | rect0.x = x; |
330 | if (x + rect0.width <= rect.x + rect.width) { | |
331 | timeDraw.draw(gc, time, rect0); | |
8e1f81f1 PT |
332 | } |
333 | } | |
f1fae91f | 334 | if (pixelsPerNanoSec == 0 || time > Long.MAX_VALUE - timeDelta || timeDelta == 0) { |
8e1f81f1 PT |
335 | break; |
336 | } | |
f1fae91f PT |
337 | if (fTimeProvider != null && fTimeProvider.getTimeFormat().equals(TimeFormat.CALENDAR)) { |
338 | if (timeDelta >= YEAR_IN_NS) { | |
339 | long millis = time / MILLISEC_IN_NS; | |
8e1f81f1 | 340 | GREGORIAN_CALENDAR.setTime(new Date(millis)); |
f1fae91f | 341 | GREGORIAN_CALENDAR.add(Calendar.YEAR, (int) (timeDelta / YEAR_IN_NS)); |
8e1f81f1 | 342 | millis = GREGORIAN_CALENDAR.getTimeInMillis(); |
f1fae91f PT |
343 | time = millis * MILLISEC_IN_NS; |
344 | } else if (timeDelta >= MONTH_IN_NS) { | |
345 | long millis = time / MILLISEC_IN_NS; | |
8e1f81f1 | 346 | GREGORIAN_CALENDAR.setTime(new Date(millis)); |
f1fae91f | 347 | GREGORIAN_CALENDAR.add(Calendar.MONTH, (int) (timeDelta / MONTH_IN_NS)); |
8e1f81f1 | 348 | millis = GREGORIAN_CALENDAR.getTimeInMillis(); |
f1fae91f PT |
349 | time = millis * MILLISEC_IN_NS; |
350 | } else if (timeDelta >= DAY_IN_NS) { | |
351 | long millis = time / MILLISEC_IN_NS; | |
8e1f81f1 | 352 | GREGORIAN_CALENDAR.setTime(new Date(millis)); |
f1fae91f | 353 | GREGORIAN_CALENDAR.add(Calendar.DAY_OF_MONTH, (int) (timeDelta / DAY_IN_NS)); |
8e1f81f1 | 354 | millis = GREGORIAN_CALENDAR.getTimeInMillis(); |
f1fae91f | 355 | time = millis * MILLISEC_IN_NS; |
8e1f81f1 | 356 | } else { |
f1fae91f | 357 | time += timeDelta; |
8e1f81f1 PT |
358 | } |
359 | } else { | |
f1fae91f | 360 | time += timeDelta; |
8e1f81f1 PT |
361 | } |
362 | } | |
363 | } | |
364 | ||
f1fae91f | 365 | private static long floorToCalendar(long time, long timeDelta) { |
41b5c37f AM |
366 | long ret = time; |
367 | ||
f1fae91f PT |
368 | if (timeDelta >= YEAR_IN_NS) { |
369 | GREGORIAN_CALENDAR.setTime(new Date(ret / MILLISEC_IN_NS)); | |
8e1f81f1 PT |
370 | int year = GREGORIAN_CALENDAR.get(Calendar.YEAR); |
371 | int yearDelta = (int) (timeDelta / YEAR_IN_NS); | |
372 | year = (year / yearDelta) * yearDelta; | |
373 | GREGORIAN_CALENDAR.set(Calendar.YEAR, year); | |
374 | GREGORIAN_CALENDAR.set(Calendar.MONTH, 0); // January 1st of year | |
375 | GREGORIAN_CALENDAR.set(Calendar.DAY_OF_MONTH, 1); | |
376 | GREGORIAN_CALENDAR.set(Calendar.HOUR_OF_DAY, 0); | |
377 | GREGORIAN_CALENDAR.set(Calendar.MINUTE, 0); | |
378 | GREGORIAN_CALENDAR.set(Calendar.SECOND, 0); | |
379 | GREGORIAN_CALENDAR.set(Calendar.MILLISECOND, 0); | |
f1fae91f PT |
380 | ret = GREGORIAN_CALENDAR.getTimeInMillis() * MILLISEC_IN_NS; |
381 | } else if (timeDelta >= MONTH_IN_NS) { | |
382 | GREGORIAN_CALENDAR.setTime(new Date(ret / MILLISEC_IN_NS)); | |
8e1f81f1 PT |
383 | int month = GREGORIAN_CALENDAR.get(Calendar.MONTH); |
384 | int monthDelta = (int) (timeDelta / MONTH_IN_NS); | |
385 | month = (month / monthDelta) * monthDelta; | |
386 | GREGORIAN_CALENDAR.set(Calendar.MONTH, month); | |
387 | GREGORIAN_CALENDAR.set(Calendar.DAY_OF_MONTH, 1); // 1st of month | |
388 | GREGORIAN_CALENDAR.set(Calendar.HOUR_OF_DAY, 0); | |
389 | GREGORIAN_CALENDAR.set(Calendar.MINUTE, 0); | |
390 | GREGORIAN_CALENDAR.set(Calendar.SECOND, 0); | |
391 | GREGORIAN_CALENDAR.set(Calendar.MILLISECOND, 0); | |
f1fae91f | 392 | ret = GREGORIAN_CALENDAR.getTimeInMillis() * MILLISEC_IN_NS; |
8e1f81f1 | 393 | } else { |
f1fae91f | 394 | long offset = GREGORIAN_CALENDAR.getTimeZone().getOffset(ret / MILLISEC_IN_NS) * MILLISEC_IN_NS; |
41b5c37f AM |
395 | ret += offset; |
396 | ret = (ret / timeDelta) * timeDelta; | |
397 | ret -= offset; | |
8e1f81f1 | 398 | } |
41b5c37f | 399 | return ret; |
8e1f81f1 PT |
400 | } |
401 | ||
402 | private int calculateDigits(long time0, long time1) { | |
403 | int numDigits = 5; | |
404 | long timeRange = time1 - time0; | |
405 | ||
f1fae91f | 406 | if (fTimeProvider.getTimeFormat().equals(TimeFormat.CALENDAR)) { |
8e1f81f1 PT |
407 | // Calculate the number of digits to represent the minutes provided |
408 | // 11:222 | |
409 | // HH:mm:ss | |
410 | numDigits += 8; | |
411 | if (timeRange < 10000) { | |
412 | // HH:11:222:333:444__ | |
413 | numDigits += 10; | |
414 | } else if (timeRange < 10000000) { | |
415 | // HH:11:222:333__ | |
416 | numDigits += 6; | |
417 | } | |
418 | } else { | |
f1fae91f | 419 | long sec = time1 / SEC_IN_NS; |
8e1f81f1 PT |
420 | numDigits = Long.toString(sec).length(); |
421 | int thousandGroups = (numDigits - 1) / 3; | |
422 | numDigits += thousandGroups; | |
423 | numDigits += 12; // .000 000 000 | |
424 | } | |
425 | ||
426 | return numDigits; | |
427 | } | |
428 | ||
429 | @Override | |
430 | public void mouseDown(MouseEvent e) { | |
431 | getParent().setFocus(); | |
f1fae91f PT |
432 | if (fDragState == NO_BUTTON && null != fTimeProvider) { |
433 | int x = e.x - fTimeProvider.getNameSpace(); | |
434 | if (LEFT_BUTTON == e.button && x > 0) { | |
8e1f81f1 | 435 | setCapture(true); |
f1fae91f PT |
436 | fDragState = LEFT_BUTTON; |
437 | } else if (RIGHT_BUTTON == e.button) { | |
438 | fDragState = RIGHT_BUTTON; | |
8e1f81f1 PT |
439 | } |
440 | if (x < 0) { | |
441 | x = 0; | |
f1fae91f PT |
442 | } else if (x > getSize().x - fTimeProvider.getNameSpace()) { |
443 | x = getSize().x - fTimeProvider.getNameSpace(); | |
8e1f81f1 | 444 | } |
f1fae91f PT |
445 | fDragX = x; |
446 | fDragX0 = x; | |
447 | fTime0bak = fTimeProvider.getTime0(); | |
448 | fTime1bak = fTimeProvider.getTime1(); | |
8e1f81f1 PT |
449 | } |
450 | } | |
451 | ||
452 | @Override | |
453 | public void mouseUp(MouseEvent e) { | |
f1fae91f | 454 | if (e.button == LEFT_BUTTON && fDragState == LEFT_BUTTON) { |
8e1f81f1 | 455 | setCapture(false); |
f1fae91f | 456 | fDragState = NO_BUTTON; |
8e1f81f1 PT |
457 | |
458 | // Notify time provider to check the need for listener notification | |
f1fae91f PT |
459 | if (fDragX != fDragX0 && fTimeProvider.getTime0() != fTimeProvider.getTime1()) { |
460 | fTimeProvider.setStartFinishTimeNotify(fTimeProvider.getTime0(), fTimeProvider.getTime1()); | |
8e1f81f1 | 461 | } |
f1fae91f PT |
462 | } else if (e.button == RIGHT_BUTTON && fDragState == RIGHT_BUTTON && null != fTimeProvider) { |
463 | fDragState = NO_BUTTON; | |
464 | if (fDragX0 == fDragX || fTimeProvider.getTime0() == fTimeProvider.getTime1()) { | |
5b2b9bd7 | 465 | redraw(); |
8e1f81f1 PT |
466 | return; |
467 | } | |
f1fae91f PT |
468 | int timeSpace = fTimeProvider.getTimeSpace(); |
469 | int leftSpace = fTimeProvider.getNameSpace(); | |
8e1f81f1 PT |
470 | int x = Math.max(0, e.x - leftSpace); |
471 | if (timeSpace > 0) { | |
f1fae91f PT |
472 | fDragX = x; |
473 | if (fDragX0 > fDragX) { // drag right to left | |
474 | fDragX = fDragX0; | |
475 | fDragX0 = x; | |
8e1f81f1 | 476 | } |
f1fae91f PT |
477 | long time0 = fTime0bak + (long) ((fTime1bak - fTime0bak) * ((double) fDragX0 / timeSpace)); |
478 | long time1 = fTime0bak + (long) ((fTime1bak - fTime0bak) * ((double) fDragX / timeSpace)); | |
8e1f81f1 | 479 | |
f1fae91f PT |
480 | fTimeProvider.setStartFinishTimeNotify(time0, time1); |
481 | fTime0bak = fTimeProvider.getTime0(); | |
482 | fTime1bak = fTimeProvider.getTime1(); | |
8e1f81f1 PT |
483 | } |
484 | } | |
485 | } | |
486 | ||
487 | @Override | |
488 | public void mouseMove(MouseEvent e) { | |
f1fae91f | 489 | if (fDragX0 < 0 || fDragState == NO_BUTTON || fTimeProvider == null) { |
8e1f81f1 PT |
490 | return; |
491 | } | |
492 | Point size = getSize(); | |
f1fae91f PT |
493 | int leftSpace = fTimeProvider.getNameSpace(); |
494 | int timeSpace = fTimeProvider.getTimeSpace(); | |
8e1f81f1 | 495 | int x = e.x - leftSpace; |
f1fae91f PT |
496 | if (LEFT_BUTTON == fDragState) { |
497 | if (x > 0 && size.x > leftSpace && fDragX != x) { | |
498 | fDragX = x; | |
499 | if (fTimeProvider.getTime0() == fTimeProvider.getTime1()) { | |
8e1f81f1 PT |
500 | return; |
501 | } | |
f1fae91f | 502 | long interval = (long) ((fTime1bak - fTime0bak) * ((double) fDragX0 / fDragX)); |
8e1f81f1 | 503 | if (interval == Long.MAX_VALUE) { |
f1fae91f | 504 | fTimeProvider.setStartFinishTime(fTime0bak, Long.MAX_VALUE); |
8e1f81f1 | 505 | } else { |
f1fae91f PT |
506 | long time1 = fTime0bak + (long) ((fTime1bak - fTime0bak) * ((double) fDragX0 / fDragX)); |
507 | fTimeProvider.setStartFinishTime(fTime0bak, time1); | |
8e1f81f1 PT |
508 | } |
509 | } | |
f1fae91f | 510 | } else if (RIGHT_BUTTON == fDragState) { |
8e1f81f1 | 511 | if (x < 0) { |
f1fae91f | 512 | fDragX = 0; |
8e1f81f1 | 513 | } else if (x > timeSpace) { |
f1fae91f | 514 | fDragX = timeSpace; |
8e1f81f1 | 515 | } else { |
f1fae91f | 516 | fDragX = x; |
8e1f81f1 PT |
517 | } |
518 | redraw(); | |
519 | } | |
520 | } | |
521 | ||
522 | @Override | |
523 | public void mouseDoubleClick(MouseEvent e) { | |
f1fae91f PT |
524 | if (e.button == 1 && null != fTimeProvider && fTimeProvider.getTime0() != fTimeProvider.getTime1() && (e.stateMask & SWT.BUTTON_MASK) == 0) { |
525 | fTimeProvider.resetStartFinishTime(); | |
526 | fTimeProvider.notifyStartFinishTime(); | |
527 | fTime0bak = fTimeProvider.getTime0(); | |
528 | fTime1bak = fTimeProvider.getTime1(); | |
8e1f81f1 PT |
529 | } |
530 | } | |
531 | } | |
532 | ||
533 | abstract class TimeDraw { | |
f1fae91f PT |
534 | protected static final long MICROSEC_IN_NS = 1000; |
535 | protected static final long MILLISEC_IN_NS = 1000000; | |
536 | protected static final long MILLISEC_IN_US = 1000; | |
537 | protected static final long SEC_IN_NS = 1000000000; | |
538 | protected static final long SEC_IN_MS = 1000; | |
539 | private static final String S = "" ; //$NON-NLS-1$ | |
540 | private static final String S0 = "0" ; //$NON-NLS-1$ | |
541 | private static final String S00 = "00"; //$NON-NLS-1$ | |
542 | protected static final long PAD_1000 = 1000; | |
543 | protected static final SimpleDateFormat SEC_FORMAT_HEADER = new SimpleDateFormat("yyyy MMM dd"); //$NON-NLS-1$ | |
544 | protected static final SimpleDateFormat SEC_FORMAT = new SimpleDateFormat("HH:mm:ss"); //$NON-NLS-1$ | |
545 | protected static final SimpleDateFormat MIN_FORMAT_HEADER = new SimpleDateFormat("yyyy MMM dd"); //$NON-NLS-1$ | |
546 | protected static final SimpleDateFormat MIN_FORMAT = new SimpleDateFormat("HH:mm"); //$NON-NLS-1$ | |
547 | protected static final SimpleDateFormat HOURS_FORMAT_HEADER = new SimpleDateFormat("yyyy"); //$NON-NLS-1$ | |
548 | protected static final SimpleDateFormat HOURS_FORMAT = new SimpleDateFormat("MMM dd HH:mm"); //$NON-NLS-1$ | |
549 | protected static final SimpleDateFormat DAY_FORMAT_HEADER = new SimpleDateFormat("yyyy"); //$NON-NLS-1$ | |
550 | protected static final SimpleDateFormat DAY_FORMAT = new SimpleDateFormat("MMM dd"); //$NON-NLS-1$ | |
551 | protected static final SimpleDateFormat MONTH_FORMAT = new SimpleDateFormat("yyyy MMM"); //$NON-NLS-1$ | |
552 | protected static final SimpleDateFormat YEAR_FORMAT = new SimpleDateFormat("yyyy"); //$NON-NLS-1$ | |
8e1f81f1 PT |
553 | |
554 | static String sep(long n) { | |
555 | StringBuilder retVal = new StringBuilder(); | |
556 | String s = Long.toString(n); | |
557 | for (int i = 0; i < s.length(); i++) { | |
558 | int pos = s.length() - i - 1; | |
559 | retVal.append(s.charAt(i)); | |
560 | if (pos % 3 == 0 && pos != 0) { | |
561 | retVal.append(' '); | |
562 | } | |
563 | } | |
564 | return retVal.toString(); | |
565 | } | |
566 | ||
567 | static String pad(long n) { | |
568 | String s; | |
569 | if (n < 10) { | |
570 | s = S00; | |
571 | } else if (n < 100) { | |
572 | s = S0; | |
573 | } else { | |
574 | s = S; | |
575 | } | |
576 | return s + n; | |
577 | } | |
578 | ||
579 | public abstract void draw(GC gc, long time, Rectangle rect); | |
580 | ||
a0a88f65 | 581 | /** |
f1fae91f | 582 | * Override to draw absolute time header. This is for the time information |
a0a88f65 AM |
583 | * not shown in the draw of each tick |
584 | * | |
585 | * @param gc | |
586 | * Graphics context | |
f1fae91f PT |
587 | * @param nanosec |
588 | * time in nanosec | |
a0a88f65 AM |
589 | * @param absHeaderRect |
590 | * Header rectangle | |
591 | */ | |
f1fae91f | 592 | public void drawAbsHeader(GC gc, long nanosec, Rectangle absHeaderRect) { |
8e1f81f1 | 593 | } |
8e1f81f1 PT |
594 | } |
595 | ||
596 | class TimeDrawSec extends TimeDraw { | |
8e1f81f1 | 597 | @Override |
f1fae91f PT |
598 | public void draw(GC gc, long nanosec, Rectangle rect) { |
599 | long sec = nanosec / SEC_IN_NS; | |
600 | Utils.drawText(gc, sep(sec), rect, true); | |
8e1f81f1 PT |
601 | } |
602 | } | |
603 | ||
604 | class TimeDrawMillisec extends TimeDraw { | |
8e1f81f1 | 605 | @Override |
f1fae91f PT |
606 | public void draw(GC gc, long nanosec, Rectangle rect) { |
607 | long millisec = nanosec / MILLISEC_IN_NS; | |
608 | long ms = millisec % PAD_1000; | |
609 | long sec = millisec / SEC_IN_MS; | |
610 | Utils.drawText(gc, sep(sec) + "." + pad(ms), rect, true); //$NON-NLS-1$ | |
8e1f81f1 PT |
611 | } |
612 | } | |
613 | ||
614 | class TimeDrawMicrosec extends TimeDraw { | |
8e1f81f1 | 615 | @Override |
f1fae91f PT |
616 | public void draw(GC gc, long nanosec, Rectangle rect) { |
617 | long microsec = nanosec / MICROSEC_IN_NS; | |
618 | long us = microsec % PAD_1000; | |
619 | long millisec = microsec / MILLISEC_IN_US; | |
620 | long ms = millisec % PAD_1000; | |
621 | long sec = millisec / SEC_IN_MS; | |
622 | Utils.drawText(gc, sep(sec) + "." + pad(ms) + " " + pad(us), rect, true); //$NON-NLS-1$ //$NON-NLS-2$ | |
8e1f81f1 PT |
623 | } |
624 | } | |
625 | ||
626 | class TimeDrawNanosec extends TimeDraw { | |
8e1f81f1 | 627 | @Override |
f1fae91f PT |
628 | public void draw(GC gc, long nanosec, Rectangle rect) { |
629 | long ns = nanosec % PAD_1000; | |
630 | long microsec = nanosec / MICROSEC_IN_NS; | |
631 | long us = microsec % PAD_1000; | |
632 | long millisec = microsec / MILLISEC_IN_US; | |
633 | long ms = millisec % PAD_1000; | |
634 | long sec = millisec / SEC_IN_MS; | |
635 | Utils.drawText(gc, sep(sec) + "." + pad(ms) + " " + pad(us) + " " + pad(ns), rect, true); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ | |
8e1f81f1 PT |
636 | } |
637 | } | |
638 | ||
639 | class TimeDrawAbsYear extends TimeDraw { | |
8e1f81f1 | 640 | @Override |
f1fae91f PT |
641 | public void draw(GC gc, long nanosec, Rectangle rect) { |
642 | String stime = YEAR_FORMAT.format(new Date(nanosec / MILLISEC_IN_NS)); | |
8e1f81f1 PT |
643 | Utils.drawText(gc, stime, rect, true); |
644 | } | |
8e1f81f1 PT |
645 | } |
646 | ||
647 | class TimeDrawAbsMonth extends TimeDraw { | |
8e1f81f1 | 648 | @Override |
f1fae91f PT |
649 | public void draw(GC gc, long nanosec, Rectangle rect) { |
650 | String stime = MONTH_FORMAT.format(new Date(nanosec / MILLISEC_IN_NS)); | |
8e1f81f1 PT |
651 | Utils.drawText(gc, stime, rect, true); |
652 | } | |
8e1f81f1 PT |
653 | } |
654 | ||
655 | class TimeDrawAbsDay extends TimeDraw { | |
8e1f81f1 | 656 | @Override |
f1fae91f PT |
657 | public void draw(GC gc, long nanosec, Rectangle rect) { |
658 | String stime = DAY_FORMAT.format(new Date(nanosec / MILLISEC_IN_NS)); | |
8e1f81f1 PT |
659 | Utils.drawText(gc, stime, rect, true); |
660 | } | |
661 | ||
662 | @Override | |
f1fae91f PT |
663 | public void drawAbsHeader(GC gc, long nanosec, Rectangle rect) { |
664 | String header = DAY_FORMAT_HEADER.format(new Date(nanosec / MILLISEC_IN_NS)); | |
8e1f81f1 PT |
665 | int headerwidth = gc.stringExtent(header).x + 4; |
666 | if (headerwidth <= rect.width) { | |
667 | rect.x += (rect.width - headerwidth); | |
668 | Utils.drawText(gc, header, rect, true); | |
669 | } | |
670 | } | |
8e1f81f1 PT |
671 | } |
672 | ||
673 | class TimeDrawAbsHrs extends TimeDraw { | |
8e1f81f1 | 674 | @Override |
f1fae91f PT |
675 | public void draw(GC gc, long nanosec, Rectangle rect) { |
676 | String stime = HOURS_FORMAT.format(new Date(nanosec / MILLISEC_IN_NS)); | |
8e1f81f1 PT |
677 | Utils.drawText(gc, stime, rect, true); |
678 | } | |
679 | ||
680 | @Override | |
f1fae91f PT |
681 | public void drawAbsHeader(GC gc, long nanosec, Rectangle rect) { |
682 | String header = HOURS_FORMAT_HEADER.format(new Date(nanosec / MILLISEC_IN_NS)); | |
8e1f81f1 PT |
683 | int headerwidth = gc.stringExtent(header).x + 4; |
684 | if (headerwidth <= rect.width) { | |
685 | rect.x += (rect.width - headerwidth); | |
686 | Utils.drawText(gc, header, rect, true); | |
687 | } | |
688 | } | |
8e1f81f1 PT |
689 | } |
690 | ||
691 | class TimeDrawAbsMin extends TimeDraw { | |
8e1f81f1 | 692 | @Override |
f1fae91f PT |
693 | public void draw(GC gc, long nanosec, Rectangle rect) { |
694 | String stime = MIN_FORMAT.format(new Date(nanosec / MILLISEC_IN_NS)); | |
8e1f81f1 PT |
695 | Utils.drawText(gc, stime, rect, true); |
696 | } | |
697 | ||
698 | @Override | |
f1fae91f PT |
699 | public void drawAbsHeader(GC gc, long nanosec, Rectangle rect) { |
700 | String header = MIN_FORMAT_HEADER.format(new Date(nanosec / MILLISEC_IN_NS)); | |
8e1f81f1 PT |
701 | int headerwidth = gc.stringExtent(header).x + 4; |
702 | if (headerwidth <= rect.width) { | |
703 | rect.x += (rect.width - headerwidth); | |
704 | Utils.drawText(gc, header, rect, true); | |
705 | } | |
706 | } | |
8e1f81f1 PT |
707 | } |
708 | ||
709 | class TimeDrawAbsSec extends TimeDraw { | |
8e1f81f1 | 710 | @Override |
f1fae91f PT |
711 | public void draw(GC gc, long nanosec, Rectangle rect) { |
712 | String stime = SEC_FORMAT.format(new Date(nanosec / MILLISEC_IN_NS)); | |
8e1f81f1 PT |
713 | Utils.drawText(gc, stime, rect, true); |
714 | } | |
715 | ||
716 | @Override | |
f1fae91f PT |
717 | public void drawAbsHeader(GC gc, long nanosec, Rectangle rect) { |
718 | String header = SEC_FORMAT_HEADER.format(new Date(nanosec / MILLISEC_IN_NS)); | |
8e1f81f1 PT |
719 | int headerwidth = gc.stringExtent(header).x + 4; |
720 | if (headerwidth <= rect.width) { | |
721 | rect.x += (rect.width - headerwidth); | |
722 | Utils.drawText(gc, header, rect, true); | |
723 | } | |
724 | } | |
8e1f81f1 PT |
725 | } |
726 | ||
727 | class TimeDrawAbsMillisec extends TimeDraw { | |
8e1f81f1 | 728 | @Override |
f1fae91f PT |
729 | public void draw(GC gc, long nanosec, Rectangle rect) { |
730 | String stime = SEC_FORMAT.format(new Date(nanosec / MILLISEC_IN_NS)); | |
731 | String ns = Utils.formatNs(nanosec, Resolution.MILLISEC); | |
8e1f81f1 PT |
732 | |
733 | Utils.drawText(gc, stime + "." + ns, rect, true); //$NON-NLS-1$ | |
734 | } | |
735 | ||
736 | @Override | |
f1fae91f PT |
737 | public void drawAbsHeader(GC gc, long nanosec, Rectangle rect) { |
738 | String header = SEC_FORMAT_HEADER.format(new Date(nanosec / MILLISEC_IN_NS)); | |
8e1f81f1 PT |
739 | int headerwidth = gc.stringExtent(header).x + 4; |
740 | if (headerwidth <= rect.width) { | |
741 | rect.x += (rect.width - headerwidth); | |
742 | Utils.drawText(gc, header, rect, true); | |
743 | } | |
744 | } | |
8e1f81f1 PT |
745 | } |
746 | ||
747 | class TimeDrawAbsMicroSec extends TimeDraw { | |
8e1f81f1 | 748 | @Override |
f1fae91f PT |
749 | public void draw(GC gc, long nanosec, Rectangle rect) { |
750 | String stime = SEC_FORMAT.format(new Date(nanosec / MILLISEC_IN_NS)); | |
751 | String micr = Utils.formatNs(nanosec, Resolution.MICROSEC); | |
8e1f81f1 PT |
752 | Utils.drawText(gc, stime + "." + micr, rect, true); //$NON-NLS-1$ |
753 | } | |
754 | ||
755 | @Override | |
f1fae91f PT |
756 | public void drawAbsHeader(GC gc, long nanosec, Rectangle rect) { |
757 | String header = SEC_FORMAT_HEADER.format(new Date(nanosec / MILLISEC_IN_NS)); | |
8e1f81f1 PT |
758 | int headerwidth = gc.stringExtent(header).x + 4; |
759 | if (headerwidth <= rect.width) { | |
760 | rect.x += (rect.width - headerwidth); | |
761 | Utils.drawText(gc, header, rect, true); | |
762 | } | |
763 | } | |
8e1f81f1 PT |
764 | } |
765 | ||
766 | class TimeDrawAbsNanoSec extends TimeDraw { | |
8e1f81f1 | 767 | @Override |
f1fae91f PT |
768 | public void draw(GC gc, long nanosec, Rectangle rect) { |
769 | String stime = SEC_FORMAT.format(new Date(nanosec / MILLISEC_IN_NS)); | |
770 | String ns = Utils.formatNs(nanosec, Resolution.NANOSEC); | |
8e1f81f1 PT |
771 | Utils.drawText(gc, stime + "." + ns, rect, true); //$NON-NLS-1$ |
772 | } | |
773 | ||
774 | @Override | |
f1fae91f PT |
775 | public void drawAbsHeader(GC gc, long nanosec, Rectangle rect) { |
776 | String header = SEC_FORMAT_HEADER.format(new Date(nanosec / MILLISEC_IN_NS)); | |
8e1f81f1 PT |
777 | int headerwidth = gc.stringExtent(header).x + 4; |
778 | if (headerwidth <= rect.width) { | |
779 | rect.x += (rect.width - headerwidth); | |
780 | Utils.drawText(gc, header, rect, true); | |
781 | } | |
782 | } | |
8e1f81f1 | 783 | } |
026664b7 | 784 | |
026664b7 | 785 | class TimeDrawNumber extends TimeDraw { |
026664b7 XR |
786 | @Override |
787 | public void draw(GC gc, long time, Rectangle rect) { | |
788 | String stime = NumberFormat.getInstance().format(time); | |
789 | Utils.drawText(gc, stime, rect, true); | |
790 | } | |
026664b7 | 791 | } |