-/*****************************************************************************\r
- * Copyright (c) 2007, 2008 Intel Corporation, 2010, 2012 Ericsson.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- * Intel Corporation - Initial API and implementation\r
- * Ruslan A. Scherbakov, Intel - Initial API and implementation\r
- * Alvaro Sanchez-Leon - Updated for TMF\r
- * Patrick Tasse - Refactoring\r
- *\r
- *****************************************************************************/\r
-\r
-package org.eclipse.linuxtools.tmf.ui.widgets.timegraph.widgets;\r
-\r
-import java.text.SimpleDateFormat;\r
-import java.util.Calendar;\r
-import java.util.Date;\r
-\r
-import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.widgets.Utils.Resolution;\r
-import org.eclipse.swt.SWT;\r
-import org.eclipse.swt.events.MouseEvent;\r
-import org.eclipse.swt.events.MouseListener;\r
-import org.eclipse.swt.events.MouseMoveListener;\r
-import org.eclipse.swt.events.PaintEvent;\r
-import org.eclipse.swt.graphics.GC;\r
-import org.eclipse.swt.graphics.Point;\r
-import org.eclipse.swt.graphics.Rectangle;\r
-import org.eclipse.swt.widgets.Composite;\r
-\r
-/**\r
- * Implementation of the scale for the time graph view.\r
- *\r
- * This goes above the "gantt chart" area.\r
- *\r
- * @version 1.0\r
- * @author Alvaro Sanchez-Leon\r
- * @author Patrick Tasse\r
- */\r
-public class TimeGraphScale extends TimeGraphBaseControl implements\r
- MouseListener, MouseMoveListener {\r
-\r
- /**\r
- * Standard constructor\r
- *\r
- * @param parent\r
- * The parent composite object\r
- * @param colors\r
- * The color scheme to use\r
- */\r
- public TimeGraphScale(Composite parent, TimeGraphColorScheme colors) {\r
- super(parent, colors, SWT.NO_BACKGROUND | SWT.NO_FOCUS | SWT.DOUBLE_BUFFERED);\r
- addMouseListener(this);\r
- addMouseMoveListener(this);\r
- }\r
-\r
- private static final long SEC_IN_NS = 1000000000;\r
- private static final long MIN_IN_NS = 60 * SEC_IN_NS;\r
- private static final long HOUR_IN_NS = 60 * MIN_IN_NS;\r
- private static final long DAY_IN_NS = 24 * HOUR_IN_NS;\r
- private static final long MONTH_IN_NS = 31 * DAY_IN_NS; // upper limit\r
- private static final long YEAR_IN_NS = 366 * DAY_IN_NS; // upper limit\r
-\r
- private static final double LOG10_1 = Math.log10(1);\r
- private static final double LOG10_2 = Math.log10(2);\r
- private static final double LOG10_3 = Math.log10(3);\r
- private static final double LOG10_5 = Math.log10(5);\r
-\r
- private static final Calendar GREGORIAN_CALENDAR = Calendar.getInstance();\r
-\r
- private ITimeDataProvider _timeProvider;\r
- private int _dragState = 0;\r
- private int _dragX0 = 0;\r
- private int _dragX = 0;\r
- private long _time0bak;\r
- private long _time1bak;\r
- private boolean _isInUpdate;\r
- private final Rectangle _rect0 = new Rectangle(0, 0, 0, 0);\r
- private int _height;\r
-\r
- /**\r
- * Assign the time provider for this scale\r
- *\r
- * @param timeProvider\r
- * The provider to use\r
- */\r
- public void setTimeProvider(ITimeDataProvider timeProvider) {\r
- _timeProvider = timeProvider;\r
- }\r
-\r
- private long _timeDelta;\r
-\r
- @Override\r
- public Point computeSize(int wHint, int hHint, boolean changed) {\r
- return super.computeSize(wHint, _height, changed);\r
- }\r
-\r
- /**\r
- * Set the height of the scale\r
- *\r
- * @param height\r
- * The height to use\r
- */\r
- public void setHeight(int height) {\r
- this._height = height;\r
- }\r
-\r
- private void calcTimeDelta(int width, double pixelsPerNanoSec) {\r
- double minDelta = (pixelsPerNanoSec == 0) ? YEAR_IN_NS : width / pixelsPerNanoSec;\r
- long unit = 1;\r
- if (_timeProvider != null && _timeProvider.isCalendarFormat()) {\r
- if (minDelta > 6 * MONTH_IN_NS) {\r
- unit = YEAR_IN_NS;\r
- } else if (minDelta > 3 * MONTH_IN_NS) {\r
- unit = 6 * MONTH_IN_NS;\r
- } else if (minDelta > 10 * DAY_IN_NS) {\r
- unit = MONTH_IN_NS;\r
- } else if (minDelta > 12 * HOUR_IN_NS) {\r
- unit = DAY_IN_NS;\r
- } else if (minDelta > 3 * HOUR_IN_NS) {\r
- unit = 6 * HOUR_IN_NS;\r
- } else if (minDelta > 30 * MIN_IN_NS) {\r
- unit = HOUR_IN_NS;\r
- } else if (minDelta > 10 * MIN_IN_NS) {\r
- unit = 15 * MIN_IN_NS;\r
- } else if (minDelta > 30 * SEC_IN_NS) {\r
- unit = MIN_IN_NS;\r
- } else if (minDelta > 20 * SEC_IN_NS) {\r
- unit = 30 * SEC_IN_NS;\r
- } else if (minDelta <= 1) {\r
- _timeDelta = 1;\r
- return;\r
- }\r
- }\r
- double log = Math.log10(minDelta / unit);\r
- long pow10 = (long) log;\r
- double remainder = log - pow10;\r
- if (remainder < LOG10_1) {\r
- _timeDelta = (long) Math.pow(10, pow10) * unit;\r
- } else if (remainder < LOG10_2) {\r
- _timeDelta = 2 * (long) Math.pow(10, pow10) * unit;\r
- } else if (remainder < LOG10_3 && unit >= HOUR_IN_NS && unit < YEAR_IN_NS) {\r
- _timeDelta = 3 * (long) Math.pow(10, pow10) * unit;\r
- } else if (remainder < LOG10_5) {\r
- _timeDelta = 5 * (long) Math.pow(10, pow10) * unit;\r
- } else {\r
- _timeDelta = 10 * (long) Math.pow(10, pow10) * unit;\r
- }\r
- }\r
-\r
- private static TimeDraw TIMEDRAW_NANOSEC = new TimeDrawNanosec();\r
- private static TimeDraw TIMEDRAW_MICROSEC = new TimeDrawMicrosec();\r
- private static TimeDraw TIMEDRAW_MILLISEC = new TimeDrawMillisec();\r
- private static TimeDraw TIMEDRAW_SEC = new TimeDrawSec();\r
- private static TimeDraw TIMEDRAW_ABS_NANOSEC = new TimeDrawAbsNanoSec();\r
- private static TimeDraw TIMEDRAW_ABS_MICROSEC = new TimeDrawAbsMicroSec();\r
- private static TimeDraw TIMEDRAW_ABS_MILLISEC = new TimeDrawAbsMillisec();\r
- private static TimeDraw TIMEDRAW_ABS_SEC = new TimeDrawAbsSec();\r
- private static TimeDraw TIMEDRAW_ABS_MIN = new TimeDrawAbsMin();\r
- private static TimeDraw TIMEDRAW_ABS_HRS = new TimeDrawAbsHrs();\r
- private static TimeDraw TIMEDRAW_ABS_DAY = new TimeDrawAbsDay();\r
- private static TimeDraw TIMEDRAW_ABS_MONTH = new TimeDrawAbsMonth();\r
- private static TimeDraw TIMEDRAW_ABS_YEAR = new TimeDrawAbsYear();\r
-\r
- TimeDraw getTimeDraw(long timeDelta) {\r
- TimeDraw timeDraw;\r
- if (_timeProvider != null && _timeProvider.isCalendarFormat()) {\r
- if (timeDelta >= YEAR_IN_NS) {\r
- timeDraw = TIMEDRAW_ABS_YEAR;\r
- } else if (timeDelta >= MONTH_IN_NS) {\r
- timeDraw = TIMEDRAW_ABS_MONTH;\r
- } else if (timeDelta >= DAY_IN_NS) {\r
- timeDraw = TIMEDRAW_ABS_DAY;\r
- } else if (timeDelta >= HOUR_IN_NS) {\r
- timeDraw = TIMEDRAW_ABS_HRS;\r
- } else if (timeDelta >= MIN_IN_NS) {\r
- timeDraw = TIMEDRAW_ABS_MIN;\r
- } else if (timeDelta >= SEC_IN_NS) {\r
- timeDraw = TIMEDRAW_ABS_SEC;\r
- } else if (timeDelta >= 1000000) {\r
- timeDraw = TIMEDRAW_ABS_MILLISEC;\r
- } else if (timeDelta >= 1000) {\r
- timeDraw = TIMEDRAW_ABS_MICROSEC;\r
- } else {\r
- timeDraw = TIMEDRAW_ABS_NANOSEC;\r
- }\r
- return timeDraw;\r
- }\r
- if (timeDelta >= 1000000000) {\r
- timeDraw = TIMEDRAW_SEC;\r
- } else if (timeDelta >= 1000000) {\r
- timeDraw = TIMEDRAW_MILLISEC;\r
- } else if (timeDelta >= 1000) {\r
- timeDraw = TIMEDRAW_MICROSEC;\r
- } else {\r
- timeDraw = TIMEDRAW_NANOSEC;\r
- }\r
- return timeDraw;\r
- }\r
-\r
- @Override\r
- void paint(Rectangle rect, PaintEvent e) {\r
-\r
- if (_isInUpdate || null == _timeProvider) {\r
- return;\r
- }\r
-\r
- GC gc = e.gc;\r
- gc.fillRectangle(rect);\r
-\r
- long time0 = _timeProvider.getTime0();\r
- long time1 = _timeProvider.getTime1();\r
- long selectedTime = _timeProvider.getSelectedTime();\r
- int leftSpace = _timeProvider.getNameSpace();\r
- int timeSpace = _timeProvider.getTimeSpace();\r
-\r
- gc.setBackground(_colors.getColor(TimeGraphColorScheme.TOOL_BACKGROUND));\r
- gc.setForeground(_colors.getColor(TimeGraphColorScheme.TOOL_FOREGROUND));\r
- Utils.init(_rect0, rect);\r
-\r
- // draw top left area\r
- _rect0.width = leftSpace;\r
- _rect0.x += 4;\r
- _rect0.width -= 4;\r
- Rectangle absHeaderRect = new Rectangle(_rect0.x, _rect0.y, _rect0.width, _rect0.height);\r
- _rect0.x -= 4;\r
- _rect0.width += 4;\r
-\r
- // prepare and draw right rect of the timescale\r
- _rect0.x += leftSpace;\r
- _rect0.width = rect.width - leftSpace;\r
-\r
- // draw bottom border and erase all other area\r
- gc.drawLine(rect.x, rect.y + rect.height - 1, rect.x + rect.width - 1,\r
- rect.y + rect.height - 1);\r
- _rect0.height--;\r
- gc.fillRectangle(_rect0);\r
-\r
- if (time1 <= time0 || timeSpace < 2) {\r
- return;\r
- }\r
-\r
- int numDigits = calculateDigits(time0, time1);\r
-\r
- int labelWidth = gc.getCharWidth('0') * numDigits;\r
- double pixelsPerNanoSec = (timeSpace <= RIGHT_MARGIN) ? 0 :\r
- (double) (timeSpace - RIGHT_MARGIN) / (time1 - time0);\r
- calcTimeDelta(labelWidth, pixelsPerNanoSec);\r
-\r
- TimeDraw timeDraw = getTimeDraw(_timeDelta);\r
-\r
- // draw zoom rectangle\r
- if (3 == _dragState && null != _timeProvider) {\r
- if (_dragX0 < _dragX) {\r
- gc.drawRectangle(leftSpace + _dragX0, rect.y, _dragX - _dragX0 - 1, rect.height - 8);\r
- } else if (_dragX0 > _dragX) {\r
- gc.drawRectangle(leftSpace + _dragX, rect.y, _dragX0 - _dragX - 1, rect.height - 8);\r
- }\r
- }\r
-\r
- if (_rect0.isEmpty()) {\r
- return;\r
- }\r
-\r
- // draw selected time\r
- int x = _rect0.x + (int) ((selectedTime - time0) * pixelsPerNanoSec);\r
- if (x >= _rect0.x && x < _rect0.x + _rect0.width) {\r
- gc.setForeground(_colors.getColor(TimeGraphColorScheme.SELECTED_TIME));\r
- gc.drawLine(x, _rect0.y + _rect0.height - 6, x, _rect0.y\r
- + _rect0.height);\r
- gc.setForeground(_colors.getColor(TimeGraphColorScheme.TOOL_FOREGROUND));\r
- }\r
-\r
- // draw time scale ticks\r
- _rect0.y = rect.y;\r
- _rect0.height = rect.height - 4;\r
- _rect0.width = labelWidth;\r
-\r
- long time;\r
- if (_timeProvider != null && _timeProvider.isCalendarFormat()) {\r
- time = floorToCalendar(time0, _timeDelta);\r
- } else {\r
- time = (long) (Math.ceil((double) time0 / _timeDelta) * _timeDelta);\r
- }\r
-\r
- int y = _rect0.y + _rect0.height;\r
-\r
- if (_timeProvider != null && _timeProvider.isCalendarFormat()) {\r
- timeDraw.drawAbsHeader(gc, time, absHeaderRect);\r
- }\r
-\r
- while (true) {\r
- x = rect.x + leftSpace + (int) (Math.floor((time - time0) * pixelsPerNanoSec));\r
- if (x >= rect.x + leftSpace + rect.width - _rect0.width) {\r
- break;\r
- }\r
- if (x >= rect.x + leftSpace) {\r
- gc.drawLine(x, y, x, y + 4);\r
- _rect0.x = x;\r
- if (x + _rect0.width <= rect.x + rect.width) {\r
- timeDraw.draw(gc, time, _rect0);\r
- }\r
- }\r
- if (pixelsPerNanoSec == 0 || time > Long.MAX_VALUE - _timeDelta || _timeDelta == 0) {\r
- break;\r
- }\r
- if (_timeProvider != null && _timeProvider.isCalendarFormat()) {\r
- if (_timeDelta >= YEAR_IN_NS) {\r
- long millis = time / 1000000L;\r
- GREGORIAN_CALENDAR.setTime(new Date(millis));\r
- GREGORIAN_CALENDAR.add(Calendar.YEAR, (int) (_timeDelta / YEAR_IN_NS));\r
- millis = GREGORIAN_CALENDAR.getTimeInMillis();\r
- time = millis * 1000000L;\r
- } else if (_timeDelta >= MONTH_IN_NS) {\r
- long millis = time / 1000000L;\r
- GREGORIAN_CALENDAR.setTime(new Date(millis));\r
- GREGORIAN_CALENDAR.add(Calendar.MONTH, (int) (_timeDelta / MONTH_IN_NS));\r
- millis = GREGORIAN_CALENDAR.getTimeInMillis();\r
- time = millis * 1000000L;\r
- } else if (_timeDelta >= DAY_IN_NS) {\r
- long millis = time / 1000000L;\r
- GREGORIAN_CALENDAR.setTime(new Date(millis));\r
- GREGORIAN_CALENDAR.add(Calendar.DAY_OF_MONTH, (int) (_timeDelta / DAY_IN_NS));\r
- millis = GREGORIAN_CALENDAR.getTimeInMillis();\r
- time = millis * 1000000L;\r
- } else {\r
- time += _timeDelta;\r
- }\r
- } else {\r
- time += _timeDelta;\r
- }\r
- }\r
- }\r
-\r
- private long floorToCalendar(long time, long timeDelta) {\r
- if (_timeDelta >= YEAR_IN_NS) {\r
- GREGORIAN_CALENDAR.setTime(new Date(time / 1000000));\r
- int year = GREGORIAN_CALENDAR.get(Calendar.YEAR);\r
- int yearDelta = (int) (timeDelta / YEAR_IN_NS);\r
- year = (year / yearDelta) * yearDelta;\r
- GREGORIAN_CALENDAR.set(Calendar.YEAR, year);\r
- GREGORIAN_CALENDAR.set(Calendar.MONTH, 0); // January 1st of year\r
- GREGORIAN_CALENDAR.set(Calendar.DAY_OF_MONTH, 1);\r
- GREGORIAN_CALENDAR.set(Calendar.HOUR_OF_DAY, 0);\r
- GREGORIAN_CALENDAR.set(Calendar.MINUTE, 0);\r
- GREGORIAN_CALENDAR.set(Calendar.SECOND, 0);\r
- GREGORIAN_CALENDAR.set(Calendar.MILLISECOND, 0);\r
- time = GREGORIAN_CALENDAR.getTimeInMillis() * 1000000;\r
- } else if (_timeDelta >= MONTH_IN_NS) {\r
- GREGORIAN_CALENDAR.setTime(new Date(time / 1000000));\r
- int month = GREGORIAN_CALENDAR.get(Calendar.MONTH);\r
- int monthDelta = (int) (timeDelta / MONTH_IN_NS);\r
- month = (month / monthDelta) * monthDelta;\r
- GREGORIAN_CALENDAR.set(Calendar.MONTH, month);\r
- GREGORIAN_CALENDAR.set(Calendar.DAY_OF_MONTH, 1); // 1st of month\r
- GREGORIAN_CALENDAR.set(Calendar.HOUR_OF_DAY, 0);\r
- GREGORIAN_CALENDAR.set(Calendar.MINUTE, 0);\r
- GREGORIAN_CALENDAR.set(Calendar.SECOND, 0);\r
- GREGORIAN_CALENDAR.set(Calendar.MILLISECOND, 0);\r
- time = GREGORIAN_CALENDAR.getTimeInMillis() * 1000000;\r
- } else {\r
- long offset = GREGORIAN_CALENDAR.getTimeZone().getOffset(time / 1000000L) * 1000000L;\r
- time += offset;\r
- time = (time / timeDelta) * timeDelta;\r
- time -= offset;\r
- }\r
- return time;\r
- }\r
-\r
- private int calculateDigits(long time0, long time1) {\r
- int numDigits = 5;\r
- long timeRange = time1 - time0;\r
-\r
- if (_timeProvider.isCalendarFormat()) {\r
- // Calculate the number of digits to represent the minutes provided\r
- // 11:222\r
- // HH:mm:ss\r
- numDigits += 8;\r
- if (timeRange < 10000) {\r
- // HH:11:222:333:444__\r
- numDigits += 10;\r
- } else if (timeRange < 10000000) {\r
- // HH:11:222:333__\r
- numDigits += 6;\r
- }\r
- } else {\r
- // Calculate the number of digits to represent the minutes provided\r
- long min = (long) ((time1 * 1E-9) / 60); // to sec then to minutes\r
- String strMinutes = String.valueOf(min);\r
- // 11:222\r
- if (strMinutes != null) {\r
- numDigits += strMinutes.length();\r
- } else {\r
- numDigits += 2;\r
- }\r
- if (timeRange < 10000) {\r
- // 11:222:333:444__\r
- numDigits += 8;\r
- } else if (timeRange < 10000000) {\r
- // 11:222:333__\r
- numDigits += 4;\r
- }\r
- }\r
-\r
- return numDigits;\r
- }\r
-\r
- @Override\r
- public void mouseDown(MouseEvent e) {\r
- getParent().setFocus();\r
- if (_dragState == 0 && null != _timeProvider) {\r
- int x = e.x - _timeProvider.getNameSpace();\r
- if (1 == e.button && x > 0) {\r
- setCapture(true);\r
- _dragState = 1;\r
- } else if (3 == e.button) {\r
- _dragState = 3;\r
- }\r
- if (x < 0) {\r
- x = 0;\r
- } else if (x > getSize().x - _timeProvider.getNameSpace()) {\r
- x = getSize().x - _timeProvider.getNameSpace();\r
- }\r
- _dragX = _dragX0 = x;\r
- _time0bak = _timeProvider.getTime0();\r
- _time1bak = _timeProvider.getTime1();\r
- }\r
- }\r
-\r
- @Override\r
- public void mouseUp(MouseEvent e) {\r
- if (e.button == 1 && _dragState == 1) {\r
- setCapture(false);\r
- _dragState = 0;\r
-\r
- // Notify time provider to check the need for listener notification\r
- if (_dragX != _dragX0 && _timeProvider.getTime0() != _timeProvider.getTime1()) {\r
- _timeProvider.setStartFinishTimeNotify(_timeProvider.getTime0(), _timeProvider.getTime1());\r
- }\r
- } else if (e.button == 3 && _dragState == 3 && null != _timeProvider) {\r
- _dragState = 0;\r
- if (_dragX0 == _dragX || _timeProvider.getTime0() == _timeProvider.getTime1()) {\r
- return;\r
- }\r
- int timeSpace = _timeProvider.getTimeSpace();\r
- int leftSpace = _timeProvider.getNameSpace();\r
- int x = Math.max(0, e.x - leftSpace);\r
- if (timeSpace > 0) {\r
- _dragX = x;\r
- if (_dragX0 > _dragX) { // drag right to left\r
- _dragX = _dragX0;\r
- _dragX0 = x;\r
- }\r
- long time0 = _time0bak + (long) ((_time1bak - _time0bak) * ((double) _dragX0 / timeSpace));\r
- long time1 = _time0bak + (long) ((_time1bak - _time0bak) * ((double) _dragX / timeSpace));\r
-\r
- _timeProvider.setStartFinishTimeNotify(time0, time1);\r
- _time0bak = _timeProvider.getTime0();\r
- _time1bak = _timeProvider.getTime1();\r
- }\r
- }\r
- }\r
-\r
- @Override\r
- public void mouseMove(MouseEvent e) {\r
- if (_dragX0 < 0 || _dragState == 0 || _timeProvider == null) {\r
- return;\r
- }\r
- Point size = getSize();\r
- int leftSpace = _timeProvider.getNameSpace();\r
- int timeSpace = _timeProvider.getTimeSpace();\r
- int x = e.x - leftSpace;\r
- if (1 == _dragState) {\r
- if (x > 0 && size.x > leftSpace && _dragX != x) {\r
- _dragX = x;\r
- if (_timeProvider.getTime0() == _timeProvider.getTime1()) {\r
- return;\r
- }\r
- long interval = (long) ((_time1bak - _time0bak) * ((double) _dragX0 / _dragX));\r
- if (interval == Long.MAX_VALUE) {\r
- _timeProvider.setStartFinishTime(_time0bak, Long.MAX_VALUE);\r
- } else {\r
- long time1 = _time0bak + (long) ((_time1bak - _time0bak) * ((double) _dragX0 / _dragX));\r
- _timeProvider.setStartFinishTime(_time0bak, time1);\r
- }\r
- }\r
- } else if (3 == _dragState) {\r
- if (x < 0) {\r
- _dragX = 0;\r
- } else if (x > timeSpace) {\r
- _dragX = timeSpace;\r
- } else {\r
- _dragX = x;\r
- }\r
- redraw();\r
- }\r
- }\r
-\r
- @Override\r
- public void mouseDoubleClick(MouseEvent e) {\r
- if (null != _timeProvider && _timeProvider.getTime0() != _timeProvider.getTime1()) {\r
- _timeProvider.resetStartFinishTime();\r
- _time0bak = _timeProvider.getTime0();\r
- _time1bak = _timeProvider.getTime1();\r
- }\r
- }\r
-}\r
-\r
-abstract class TimeDraw {\r
- static String S = ":" ; //$NON-NLS-1$\r
- static String S0 = ":0" ; //$NON-NLS-1$\r
- static String S00 = ":00"; //$NON-NLS-1$\r
- protected static final SimpleDateFormat stimeformat = new SimpleDateFormat("HH:mm:ss"); //$NON-NLS-1$\r
- protected static final SimpleDateFormat stimeformatheader = new SimpleDateFormat("yyyy MMM dd"); //$NON-NLS-1$\r
- protected static final SimpleDateFormat sminformat = new SimpleDateFormat("HH:mm"); //$NON-NLS-1$\r
- protected static final SimpleDateFormat sminformatheader = new SimpleDateFormat("yyyy MMM dd"); //$NON-NLS-1$\r
- protected static final SimpleDateFormat shrsformat = new SimpleDateFormat("MMM dd HH:mm"); //$NON-NLS-1$\r
- protected static final SimpleDateFormat shrsformatheader = new SimpleDateFormat("yyyy"); //$NON-NLS-1$\r
- protected static final SimpleDateFormat sdayformat = new SimpleDateFormat("MMM dd"); //$NON-NLS-1$\r
- protected static final SimpleDateFormat sdayformatheader = new SimpleDateFormat("yyyy"); //$NON-NLS-1$\r
- protected static final SimpleDateFormat smonthformat = new SimpleDateFormat("yyyy MMM"); //$NON-NLS-1$\r
- protected static final SimpleDateFormat syearformat = new SimpleDateFormat("yyyy"); //$NON-NLS-1$\r
-\r
- static String pad(long n) {\r
- String s = S;\r
- if (n < 10) {\r
- s = S00;\r
- } else if (n < 100) {\r
- s = S0;\r
- }\r
- return s + n;\r
- }\r
-\r
- public abstract void draw(GC gc, long time, Rectangle rect);\r
-\r
- public void drawAbsHeader(GC gc, long time, Rectangle absHeaderRect) {\r
- // Override to draw absolute time header\r
- // This is for the time information not shown in the draw of each tick\r
- }\r
-\r
- public abstract String hint();\r
-}\r
-\r
-class TimeDrawSec extends TimeDraw {\r
- static String _hint = "sec"; //$NON-NLS-1$\r
-\r
- @Override\r
- public void draw(GC gc, long time, Rectangle rect) {\r
- time /= 1000000000;\r
- Utils.drawText(gc, time + "", rect, true); //$NON-NLS-1$\r
- }\r
-\r
- @Override\r
- public String hint() {\r
- return _hint;\r
- }\r
-}\r
-\r
-class TimeDrawMillisec extends TimeDraw {\r
- static String _hint = "s:ms"; //$NON-NLS-1$\r
-\r
- @Override\r
- public void draw(GC gc, long time, Rectangle rect) {\r
- time /= 1000000;\r
- long ms = time % 1000;\r
- time /= 1000;\r
- Utils.drawText(gc, time + pad(ms), rect, true);\r
- }\r
-\r
- @Override\r
- public String hint() {\r
- return _hint;\r
- }\r
-}\r
-\r
-class TimeDrawMicrosec extends TimeDraw {\r
- static String _hint = "s:ms:mcs"; //$NON-NLS-1$\r
-\r
- @Override\r
- public void draw(GC gc, long time, Rectangle rect) {\r
- time /= 1000;\r
- long mcs = time % 1000;\r
- time /= 1000;\r
- long ms = time % 1000;\r
- time /= 1000;\r
- Utils.drawText(gc, time + pad(ms) + pad(mcs), rect, true);\r
- }\r
-\r
- @Override\r
- public String hint() {\r
- return _hint;\r
- }\r
-}\r
-\r
-class TimeDrawNanosec extends TimeDraw {\r
- static String _hint = "s:ms:mcs:ns"; //$NON-NLS-1$\r
-\r
- @Override\r
- public void draw(GC gc, long time, Rectangle rect) {\r
- long ns = time % 1000;\r
- time /= 1000;\r
- long mcs = time % 1000;\r
- time /= 1000;\r
- long ms = time % 1000;\r
- time /= 1000;\r
- Utils.drawText(gc, time + pad(ms) + pad(mcs) + pad(ns), rect, true);\r
- }\r
-\r
- @Override\r
- public String hint() {\r
- return _hint;\r
- }\r
-}\r
-\r
-class TimeDrawAbsYear extends TimeDraw {\r
- static String _hint = "YYYY"; //$NON-NLS-1$\r
-\r
- @Override\r
- public void draw(GC gc, long time, Rectangle rect) {\r
- String stime = syearformat.format(new Date(time / 1000000));\r
- Utils.drawText(gc, stime, rect, true);\r
- }\r
-\r
- @Override\r
- public String hint() {\r
- return _hint;\r
- }\r
-}\r
-\r
-class TimeDrawAbsMonth extends TimeDraw {\r
- static String _hint = "YYYY Mmm"; //$NON-NLS-1$\r
-\r
- @Override\r
- public void draw(GC gc, long time, Rectangle rect) {\r
- String stime = smonthformat.format(new Date(time / 1000000));\r
- Utils.drawText(gc, stime, rect, true);\r
- }\r
-\r
- @Override\r
- public String hint() {\r
- return _hint;\r
- }\r
-}\r
-\r
-class TimeDrawAbsDay extends TimeDraw {\r
- static String _hint = "Mmm dd"; //$NON-NLS-1$\r
-\r
- @Override\r
- public void draw(GC gc, long time, Rectangle rect) {\r
- String stime = sdayformat.format(new Date(time / 1000000));\r
- Utils.drawText(gc, stime, rect, true);\r
- }\r
-\r
- @Override\r
- public void drawAbsHeader(GC gc, long time, Rectangle rect) {\r
- String header = sdayformatheader.format(new Date(time / 1000000));\r
- int headerwidth = gc.stringExtent(header).x + 4;\r
- if (headerwidth <= rect.width) {\r
- rect.x += (rect.width - headerwidth);\r
- Utils.drawText(gc, header, rect, true);\r
- }\r
- }\r
-\r
- @Override\r
- public String hint() {\r
- return _hint;\r
- }\r
-}\r
-\r
-class TimeDrawAbsHrs extends TimeDraw {\r
- static String _hint = "Mmm dd HH:mm"; //$NON-NLS-1$\r
-\r
- @Override\r
- public void draw(GC gc, long time, Rectangle rect) {\r
- String stime = shrsformat.format(new Date(time / 1000000));\r
- Utils.drawText(gc, stime, rect, true);\r
- }\r
-\r
- @Override\r
- public void drawAbsHeader(GC gc, long time, Rectangle rect) {\r
- String header = shrsformatheader.format(new Date(time / 1000000));\r
- int headerwidth = gc.stringExtent(header).x + 4;\r
- if (headerwidth <= rect.width) {\r
- rect.x += (rect.width - headerwidth);\r
- Utils.drawText(gc, header, rect, true);\r
- }\r
- }\r
-\r
- @Override\r
- public String hint() {\r
- return _hint;\r
- }\r
-}\r
-\r
-class TimeDrawAbsMin extends TimeDraw {\r
- static String _hint = "HH:mm"; //$NON-NLS-1$\r
-\r
- @Override\r
- public void draw(GC gc, long time, Rectangle rect) {\r
- String stime = sminformat.format(new Date(time / 1000000));\r
- Utils.drawText(gc, stime, rect, true);\r
- }\r
-\r
- @Override\r
- public void drawAbsHeader(GC gc, long time, Rectangle rect) {\r
- String header = sminformatheader.format(new Date(time / 1000000));\r
- int headerwidth = gc.stringExtent(header).x + 4;\r
- if (headerwidth <= rect.width) {\r
- rect.x += (rect.width - headerwidth);\r
- Utils.drawText(gc, header, rect, true);\r
- }\r
- }\r
-\r
-\r
- @Override\r
- public String hint() {\r
- return _hint;\r
- }\r
-}\r
-\r
-class TimeDrawAbsSec extends TimeDraw {\r
- static String _hint = "HH:mm:ss"; //$NON-NLS-1$\r
-\r
- @Override\r
- public void draw(GC gc, long time, Rectangle rect) {\r
- String stime = stimeformat.format(new Date(time / 1000000));\r
- Utils.drawText(gc, stime, rect, true);\r
- }\r
-\r
- @Override\r
- public void drawAbsHeader(GC gc, long time, Rectangle rect) {\r
- String header = stimeformatheader.format(new Date(time / 1000000));\r
- int headerwidth = gc.stringExtent(header).x + 4;\r
- if (headerwidth <= rect.width) {\r
- rect.x += (rect.width - headerwidth);\r
- Utils.drawText(gc, header, rect, true);\r
- }\r
- }\r
-\r
- @Override\r
- public String hint() {\r
- return _hint;\r
- }\r
-}\r
-\r
-class TimeDrawAbsMillisec extends TimeDraw {\r
- static String _hint = "HH:ss:ms"; //$NON-NLS-1$\r
-\r
- @Override\r
- public void draw(GC gc, long time, Rectangle rect) {\r
- String stime = stimeformat.format(new Date(time / 1000000));\r
- String ns = Utils.formatNs(time, Resolution.MILLISEC);\r
-\r
- Utils.drawText(gc, stime + "." + ns, rect, true); //$NON-NLS-1$\r
- }\r
-\r
- @Override\r
- public void drawAbsHeader(GC gc, long time, Rectangle rect) {\r
- String header = stimeformatheader.format(new Date(time / 1000000));\r
- int headerwidth = gc.stringExtent(header).x + 4;\r
- if (headerwidth <= rect.width) {\r
- rect.x += (rect.width - headerwidth);\r
- Utils.drawText(gc, header, rect, true);\r
- }\r
- }\r
-\r
- @Override\r
- public String hint() {\r
- return _hint;\r
- }\r
-}\r
-\r
-class TimeDrawAbsMicroSec extends TimeDraw {\r
- static String _hint = "HH:ss:ms:mcs"; //$NON-NLS-1$\r
-\r
- @Override\r
- public void draw(GC gc, long time, Rectangle rect) {\r
- String stime = stimeformat.format(new Date(time / 1000000));\r
- String micr = Utils.formatNs(time, Resolution.MICROSEC);\r
- Utils.drawText(gc, stime + "." + micr, rect, true); //$NON-NLS-1$\r
- }\r
-\r
- @Override\r
- public void drawAbsHeader(GC gc, long time, Rectangle rect) {\r
- String header = stimeformatheader.format(new Date(time / 1000000));\r
- int headerwidth = gc.stringExtent(header).x + 4;\r
- if (headerwidth <= rect.width) {\r
- rect.x += (rect.width - headerwidth);\r
- Utils.drawText(gc, header, rect, true);\r
- }\r
- }\r
-\r
- @Override\r
- public String hint() {\r
- return _hint;\r
- }\r
-}\r
-\r
-class TimeDrawAbsNanoSec extends TimeDraw {\r
- static String _hint = "HH:ss:ms:mcs:ns"; //$NON-NLS-1$\r
-\r
- @Override\r
- public void draw(GC gc, long time, Rectangle rect) {\r
- String stime = stimeformat.format(new Date(time / 1000000));\r
- String ns = Utils.formatNs(time, Resolution.NANOSEC);\r
- Utils.drawText(gc, stime + "." + ns, rect, true); //$NON-NLS-1$\r
- }\r
-\r
- @Override\r
- public void drawAbsHeader(GC gc, long time, Rectangle rect) {\r
- String header = stimeformatheader.format(new Date(time / 1000000));\r
- int headerwidth = gc.stringExtent(header).x + 4;\r
- if (headerwidth <= rect.width) {\r
- rect.x += (rect.width - headerwidth);\r
- Utils.drawText(gc, header, rect, true);\r
- }\r
- }\r
-\r
- @Override\r
- public String hint() {\r
- return _hint;\r
- }\r
-}\r
+/*****************************************************************************
+ * Copyright (c) 2007, 2008 Intel Corporation, 2010, 2012 Ericsson.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Intel Corporation - Initial API and implementation
+ * Ruslan A. Scherbakov, Intel - Initial API and implementation
+ * Alvaro Sanchez-Leon - Updated for TMF
+ * Patrick Tasse - Refactoring
+ *
+ *****************************************************************************/
+
+package org.eclipse.linuxtools.tmf.ui.widgets.timegraph.widgets;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+
+import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.widgets.Utils.Resolution;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Composite;
+
+/**
+ * Implementation of the scale for the time graph view.
+ *
+ * This goes above the "gantt chart" area.
+ *
+ * @version 1.0
+ * @author Alvaro Sanchez-Leon
+ * @author Patrick Tasse
+ */
+public class TimeGraphScale extends TimeGraphBaseControl implements
+ MouseListener, MouseMoveListener {
+
+ /**
+ * Standard constructor
+ *
+ * @param parent
+ * The parent composite object
+ * @param colors
+ * The color scheme to use
+ */
+ public TimeGraphScale(Composite parent, TimeGraphColorScheme colors) {
+ super(parent, colors, SWT.NO_BACKGROUND | SWT.NO_FOCUS | SWT.DOUBLE_BUFFERED);
+ addMouseListener(this);
+ addMouseMoveListener(this);
+ }
+
+ private static final long SEC_IN_NS = 1000000000;
+ private static final long MIN_IN_NS = 60 * SEC_IN_NS;
+ private static final long HOUR_IN_NS = 60 * MIN_IN_NS;
+ private static final long DAY_IN_NS = 24 * HOUR_IN_NS;
+ private static final long MONTH_IN_NS = 31 * DAY_IN_NS; // upper limit
+ private static final long YEAR_IN_NS = 366 * DAY_IN_NS; // upper limit
+
+ private static final double LOG10_1 = Math.log10(1);
+ private static final double LOG10_2 = Math.log10(2);
+ private static final double LOG10_3 = Math.log10(3);
+ private static final double LOG10_5 = Math.log10(5);
+
+ private static final Calendar GREGORIAN_CALENDAR = Calendar.getInstance();
+
+ private ITimeDataProvider _timeProvider;
+ private int _dragState = 0;
+ private int _dragX0 = 0;
+ private int _dragX = 0;
+ private long _time0bak;
+ private long _time1bak;
+ private boolean _isInUpdate;
+ private final Rectangle _rect0 = new Rectangle(0, 0, 0, 0);
+ private int _height;
+
+ /**
+ * Assign the time provider for this scale
+ *
+ * @param timeProvider
+ * The provider to use
+ */
+ public void setTimeProvider(ITimeDataProvider timeProvider) {
+ _timeProvider = timeProvider;
+ }
+
+ private long _timeDelta;
+
+ @Override
+ public Point computeSize(int wHint, int hHint, boolean changed) {
+ return super.computeSize(wHint, _height, changed);
+ }
+
+ /**
+ * Set the height of the scale
+ *
+ * @param height
+ * The height to use
+ */
+ public void setHeight(int height) {
+ this._height = height;
+ }
+
+ private void calcTimeDelta(int width, double pixelsPerNanoSec) {
+ double minDelta = (pixelsPerNanoSec == 0) ? YEAR_IN_NS : width / pixelsPerNanoSec;
+ long unit = 1;
+ if (_timeProvider != null && _timeProvider.isCalendarFormat()) {
+ if (minDelta > 6 * MONTH_IN_NS) {
+ unit = YEAR_IN_NS;
+ } else if (minDelta > 3 * MONTH_IN_NS) {
+ unit = 6 * MONTH_IN_NS;
+ } else if (minDelta > 10 * DAY_IN_NS) {
+ unit = MONTH_IN_NS;
+ } else if (minDelta > 12 * HOUR_IN_NS) {
+ unit = DAY_IN_NS;
+ } else if (minDelta > 3 * HOUR_IN_NS) {
+ unit = 6 * HOUR_IN_NS;
+ } else if (minDelta > 30 * MIN_IN_NS) {
+ unit = HOUR_IN_NS;
+ } else if (minDelta > 10 * MIN_IN_NS) {
+ unit = 15 * MIN_IN_NS;
+ } else if (minDelta > 30 * SEC_IN_NS) {
+ unit = MIN_IN_NS;
+ } else if (minDelta > 20 * SEC_IN_NS) {
+ unit = 30 * SEC_IN_NS;
+ } else if (minDelta <= 1) {
+ _timeDelta = 1;
+ return;
+ }
+ }
+ double log = Math.log10(minDelta / unit);
+ long pow10 = (long) log;
+ double remainder = log - pow10;
+ if (remainder < LOG10_1) {
+ _timeDelta = (long) Math.pow(10, pow10) * unit;
+ } else if (remainder < LOG10_2) {
+ _timeDelta = 2 * (long) Math.pow(10, pow10) * unit;
+ } else if (remainder < LOG10_3 && unit >= HOUR_IN_NS && unit < YEAR_IN_NS) {
+ _timeDelta = 3 * (long) Math.pow(10, pow10) * unit;
+ } else if (remainder < LOG10_5) {
+ _timeDelta = 5 * (long) Math.pow(10, pow10) * unit;
+ } else {
+ _timeDelta = 10 * (long) Math.pow(10, pow10) * unit;
+ }
+ }
+
+ private static TimeDraw TIMEDRAW_NANOSEC = new TimeDrawNanosec();
+ private static TimeDraw TIMEDRAW_MICROSEC = new TimeDrawMicrosec();
+ private static TimeDraw TIMEDRAW_MILLISEC = new TimeDrawMillisec();
+ private static TimeDraw TIMEDRAW_SEC = new TimeDrawSec();
+ private static TimeDraw TIMEDRAW_ABS_NANOSEC = new TimeDrawAbsNanoSec();
+ private static TimeDraw TIMEDRAW_ABS_MICROSEC = new TimeDrawAbsMicroSec();
+ private static TimeDraw TIMEDRAW_ABS_MILLISEC = new TimeDrawAbsMillisec();
+ private static TimeDraw TIMEDRAW_ABS_SEC = new TimeDrawAbsSec();
+ private static TimeDraw TIMEDRAW_ABS_MIN = new TimeDrawAbsMin();
+ private static TimeDraw TIMEDRAW_ABS_HRS = new TimeDrawAbsHrs();
+ private static TimeDraw TIMEDRAW_ABS_DAY = new TimeDrawAbsDay();
+ private static TimeDraw TIMEDRAW_ABS_MONTH = new TimeDrawAbsMonth();
+ private static TimeDraw TIMEDRAW_ABS_YEAR = new TimeDrawAbsYear();
+
+ TimeDraw getTimeDraw(long timeDelta) {
+ TimeDraw timeDraw;
+ if (_timeProvider != null && _timeProvider.isCalendarFormat()) {
+ if (timeDelta >= YEAR_IN_NS) {
+ timeDraw = TIMEDRAW_ABS_YEAR;
+ } else if (timeDelta >= MONTH_IN_NS) {
+ timeDraw = TIMEDRAW_ABS_MONTH;
+ } else if (timeDelta >= DAY_IN_NS) {
+ timeDraw = TIMEDRAW_ABS_DAY;
+ } else if (timeDelta >= HOUR_IN_NS) {
+ timeDraw = TIMEDRAW_ABS_HRS;
+ } else if (timeDelta >= MIN_IN_NS) {
+ timeDraw = TIMEDRAW_ABS_MIN;
+ } else if (timeDelta >= SEC_IN_NS) {
+ timeDraw = TIMEDRAW_ABS_SEC;
+ } else if (timeDelta >= 1000000) {
+ timeDraw = TIMEDRAW_ABS_MILLISEC;
+ } else if (timeDelta >= 1000) {
+ timeDraw = TIMEDRAW_ABS_MICROSEC;
+ } else {
+ timeDraw = TIMEDRAW_ABS_NANOSEC;
+ }
+ return timeDraw;
+ }
+ if (timeDelta >= 1000000000) {
+ timeDraw = TIMEDRAW_SEC;
+ } else if (timeDelta >= 1000000) {
+ timeDraw = TIMEDRAW_MILLISEC;
+ } else if (timeDelta >= 1000) {
+ timeDraw = TIMEDRAW_MICROSEC;
+ } else {
+ timeDraw = TIMEDRAW_NANOSEC;
+ }
+ return timeDraw;
+ }
+
+ @Override
+ void paint(Rectangle rect, PaintEvent e) {
+
+ if (_isInUpdate || null == _timeProvider) {
+ return;
+ }
+
+ GC gc = e.gc;
+ gc.fillRectangle(rect);
+
+ long time0 = _timeProvider.getTime0();
+ long time1 = _timeProvider.getTime1();
+ long selectedTime = _timeProvider.getSelectedTime();
+ int leftSpace = _timeProvider.getNameSpace();
+ int timeSpace = _timeProvider.getTimeSpace();
+
+ gc.setBackground(_colors.getColor(TimeGraphColorScheme.TOOL_BACKGROUND));
+ gc.setForeground(_colors.getColor(TimeGraphColorScheme.TOOL_FOREGROUND));
+ Utils.init(_rect0, rect);
+
+ // draw top left area
+ _rect0.width = leftSpace;
+ _rect0.x += 4;
+ _rect0.width -= 4;
+ Rectangle absHeaderRect = new Rectangle(_rect0.x, _rect0.y, _rect0.width, _rect0.height);
+ _rect0.x -= 4;
+ _rect0.width += 4;
+
+ // prepare and draw right rect of the timescale
+ _rect0.x += leftSpace;
+ _rect0.width = rect.width - leftSpace;
+
+ // draw bottom border and erase all other area
+ gc.drawLine(rect.x, rect.y + rect.height - 1, rect.x + rect.width - 1,
+ rect.y + rect.height - 1);
+ _rect0.height--;
+ gc.fillRectangle(_rect0);
+
+ if (time1 <= time0 || timeSpace < 2) {
+ return;
+ }
+
+ int numDigits = calculateDigits(time0, time1);
+
+ int labelWidth = gc.getCharWidth('0') * numDigits;
+ double pixelsPerNanoSec = (timeSpace <= RIGHT_MARGIN) ? 0 :
+ (double) (timeSpace - RIGHT_MARGIN) / (time1 - time0);
+ calcTimeDelta(labelWidth, pixelsPerNanoSec);
+
+ TimeDraw timeDraw = getTimeDraw(_timeDelta);
+
+ // draw zoom rectangle
+ if (3 == _dragState && null != _timeProvider) {
+ if (_dragX0 < _dragX) {
+ gc.drawRectangle(leftSpace + _dragX0, rect.y, _dragX - _dragX0 - 1, rect.height - 8);
+ } else if (_dragX0 > _dragX) {
+ gc.drawRectangle(leftSpace + _dragX, rect.y, _dragX0 - _dragX - 1, rect.height - 8);
+ }
+ }
+
+ if (_rect0.isEmpty()) {
+ return;
+ }
+
+ // draw selected time
+ int x = _rect0.x + (int) ((selectedTime - time0) * pixelsPerNanoSec);
+ if (x >= _rect0.x && x < _rect0.x + _rect0.width) {
+ gc.setForeground(_colors.getColor(TimeGraphColorScheme.SELECTED_TIME));
+ gc.drawLine(x, _rect0.y + _rect0.height - 6, x, _rect0.y
+ + _rect0.height);
+ gc.setForeground(_colors.getColor(TimeGraphColorScheme.TOOL_FOREGROUND));
+ }
+
+ // draw time scale ticks
+ _rect0.y = rect.y;
+ _rect0.height = rect.height - 4;
+ _rect0.width = labelWidth;
+
+ long time;
+ if (_timeProvider != null && _timeProvider.isCalendarFormat()) {
+ time = floorToCalendar(time0, _timeDelta);
+ } else {
+ time = (time0 / _timeDelta) * _timeDelta;
+ if (time != time0) {
+ time += _timeDelta;
+ }
+ }
+
+ int y = _rect0.y + _rect0.height;
+
+ if (_timeProvider != null && _timeProvider.isCalendarFormat()) {
+ timeDraw.drawAbsHeader(gc, time, absHeaderRect);
+ }
+
+ while (true) {
+ x = rect.x + leftSpace + (int) (Math.floor((time - time0) * pixelsPerNanoSec));
+ if (x >= rect.x + leftSpace + rect.width - _rect0.width) {
+ break;
+ }
+ if (x >= rect.x + leftSpace) {
+ gc.drawLine(x, y, x, y + 4);
+ _rect0.x = x;
+ if (x + _rect0.width <= rect.x + rect.width) {
+ timeDraw.draw(gc, time, _rect0);
+ }
+ }
+ if (pixelsPerNanoSec == 0 || time > Long.MAX_VALUE - _timeDelta || _timeDelta == 0) {
+ break;
+ }
+ if (_timeProvider != null && _timeProvider.isCalendarFormat()) {
+ if (_timeDelta >= YEAR_IN_NS) {
+ long millis = time / 1000000L;
+ GREGORIAN_CALENDAR.setTime(new Date(millis));
+ GREGORIAN_CALENDAR.add(Calendar.YEAR, (int) (_timeDelta / YEAR_IN_NS));
+ millis = GREGORIAN_CALENDAR.getTimeInMillis();
+ time = millis * 1000000L;
+ } else if (_timeDelta >= MONTH_IN_NS) {
+ long millis = time / 1000000L;
+ GREGORIAN_CALENDAR.setTime(new Date(millis));
+ GREGORIAN_CALENDAR.add(Calendar.MONTH, (int) (_timeDelta / MONTH_IN_NS));
+ millis = GREGORIAN_CALENDAR.getTimeInMillis();
+ time = millis * 1000000L;
+ } else if (_timeDelta >= DAY_IN_NS) {
+ long millis = time / 1000000L;
+ GREGORIAN_CALENDAR.setTime(new Date(millis));
+ GREGORIAN_CALENDAR.add(Calendar.DAY_OF_MONTH, (int) (_timeDelta / DAY_IN_NS));
+ millis = GREGORIAN_CALENDAR.getTimeInMillis();
+ time = millis * 1000000L;
+ } else {
+ time += _timeDelta;
+ }
+ } else {
+ time += _timeDelta;
+ }
+ }
+ }
+
+ private long floorToCalendar(long time, long timeDelta) {
+ if (_timeDelta >= YEAR_IN_NS) {
+ GREGORIAN_CALENDAR.setTime(new Date(time / 1000000));
+ int year = GREGORIAN_CALENDAR.get(Calendar.YEAR);
+ int yearDelta = (int) (timeDelta / YEAR_IN_NS);
+ year = (year / yearDelta) * yearDelta;
+ GREGORIAN_CALENDAR.set(Calendar.YEAR, year);
+ GREGORIAN_CALENDAR.set(Calendar.MONTH, 0); // January 1st of year
+ GREGORIAN_CALENDAR.set(Calendar.DAY_OF_MONTH, 1);
+ GREGORIAN_CALENDAR.set(Calendar.HOUR_OF_DAY, 0);
+ GREGORIAN_CALENDAR.set(Calendar.MINUTE, 0);
+ GREGORIAN_CALENDAR.set(Calendar.SECOND, 0);
+ GREGORIAN_CALENDAR.set(Calendar.MILLISECOND, 0);
+ time = GREGORIAN_CALENDAR.getTimeInMillis() * 1000000;
+ } else if (_timeDelta >= MONTH_IN_NS) {
+ GREGORIAN_CALENDAR.setTime(new Date(time / 1000000));
+ int month = GREGORIAN_CALENDAR.get(Calendar.MONTH);
+ int monthDelta = (int) (timeDelta / MONTH_IN_NS);
+ month = (month / monthDelta) * monthDelta;
+ GREGORIAN_CALENDAR.set(Calendar.MONTH, month);
+ GREGORIAN_CALENDAR.set(Calendar.DAY_OF_MONTH, 1); // 1st of month
+ GREGORIAN_CALENDAR.set(Calendar.HOUR_OF_DAY, 0);
+ GREGORIAN_CALENDAR.set(Calendar.MINUTE, 0);
+ GREGORIAN_CALENDAR.set(Calendar.SECOND, 0);
+ GREGORIAN_CALENDAR.set(Calendar.MILLISECOND, 0);
+ time = GREGORIAN_CALENDAR.getTimeInMillis() * 1000000;
+ } else {
+ long offset = GREGORIAN_CALENDAR.getTimeZone().getOffset(time / 1000000L) * 1000000L;
+ time += offset;
+ time = (time / timeDelta) * timeDelta;
+ time -= offset;
+ }
+ return time;
+ }
+
+ private int calculateDigits(long time0, long time1) {
+ int numDigits = 5;
+ long timeRange = time1 - time0;
+
+ if (_timeProvider.isCalendarFormat()) {
+ // Calculate the number of digits to represent the minutes provided
+ // 11:222
+ // HH:mm:ss
+ numDigits += 8;
+ if (timeRange < 10000) {
+ // HH:11:222:333:444__
+ numDigits += 10;
+ } else if (timeRange < 10000000) {
+ // HH:11:222:333__
+ numDigits += 6;
+ }
+ } else {
+ long sec = time1 / 1000000000;
+ numDigits = Long.toString(sec).length();
+ int thousandGroups = (numDigits - 1) / 3;
+ numDigits += thousandGroups;
+ numDigits += 12; // .000 000 000
+ }
+
+ return numDigits;
+ }
+
+ @Override
+ public void mouseDown(MouseEvent e) {
+ getParent().setFocus();
+ if (_dragState == 0 && null != _timeProvider) {
+ int x = e.x - _timeProvider.getNameSpace();
+ if (1 == e.button && x > 0) {
+ setCapture(true);
+ _dragState = 1;
+ } else if (3 == e.button) {
+ _dragState = 3;
+ }
+ if (x < 0) {
+ x = 0;
+ } else if (x > getSize().x - _timeProvider.getNameSpace()) {
+ x = getSize().x - _timeProvider.getNameSpace();
+ }
+ _dragX = _dragX0 = x;
+ _time0bak = _timeProvider.getTime0();
+ _time1bak = _timeProvider.getTime1();
+ }
+ }
+
+ @Override
+ public void mouseUp(MouseEvent e) {
+ if (e.button == 1 && _dragState == 1) {
+ setCapture(false);
+ _dragState = 0;
+
+ // Notify time provider to check the need for listener notification
+ if (_dragX != _dragX0 && _timeProvider.getTime0() != _timeProvider.getTime1()) {
+ _timeProvider.setStartFinishTimeNotify(_timeProvider.getTime0(), _timeProvider.getTime1());
+ }
+ } else if (e.button == 3 && _dragState == 3 && null != _timeProvider) {
+ _dragState = 0;
+ if (_dragX0 == _dragX || _timeProvider.getTime0() == _timeProvider.getTime1()) {
+ return;
+ }
+ int timeSpace = _timeProvider.getTimeSpace();
+ int leftSpace = _timeProvider.getNameSpace();
+ int x = Math.max(0, e.x - leftSpace);
+ if (timeSpace > 0) {
+ _dragX = x;
+ if (_dragX0 > _dragX) { // drag right to left
+ _dragX = _dragX0;
+ _dragX0 = x;
+ }
+ long time0 = _time0bak + (long) ((_time1bak - _time0bak) * ((double) _dragX0 / timeSpace));
+ long time1 = _time0bak + (long) ((_time1bak - _time0bak) * ((double) _dragX / timeSpace));
+
+ _timeProvider.setStartFinishTimeNotify(time0, time1);
+ _time0bak = _timeProvider.getTime0();
+ _time1bak = _timeProvider.getTime1();
+ }
+ }
+ }
+
+ @Override
+ public void mouseMove(MouseEvent e) {
+ if (_dragX0 < 0 || _dragState == 0 || _timeProvider == null) {
+ return;
+ }
+ Point size = getSize();
+ int leftSpace = _timeProvider.getNameSpace();
+ int timeSpace = _timeProvider.getTimeSpace();
+ int x = e.x - leftSpace;
+ if (1 == _dragState) {
+ if (x > 0 && size.x > leftSpace && _dragX != x) {
+ _dragX = x;
+ if (_timeProvider.getTime0() == _timeProvider.getTime1()) {
+ return;
+ }
+ long interval = (long) ((_time1bak - _time0bak) * ((double) _dragX0 / _dragX));
+ if (interval == Long.MAX_VALUE) {
+ _timeProvider.setStartFinishTime(_time0bak, Long.MAX_VALUE);
+ } else {
+ long time1 = _time0bak + (long) ((_time1bak - _time0bak) * ((double) _dragX0 / _dragX));
+ _timeProvider.setStartFinishTime(_time0bak, time1);
+ }
+ }
+ } else if (3 == _dragState) {
+ if (x < 0) {
+ _dragX = 0;
+ } else if (x > timeSpace) {
+ _dragX = timeSpace;
+ } else {
+ _dragX = x;
+ }
+ redraw();
+ }
+ }
+
+ @Override
+ public void mouseDoubleClick(MouseEvent e) {
+ if (null != _timeProvider && _timeProvider.getTime0() != _timeProvider.getTime1()) {
+ _timeProvider.resetStartFinishTime();
+ _time0bak = _timeProvider.getTime0();
+ _time1bak = _timeProvider.getTime1();
+ }
+ }
+}
+
+abstract class TimeDraw {
+ static String S = "" ; //$NON-NLS-1$
+ static String S0 = "0" ; //$NON-NLS-1$
+ static String S00 = "00"; //$NON-NLS-1$
+ protected static final SimpleDateFormat stimeformat = new SimpleDateFormat("HH:mm:ss"); //$NON-NLS-1$
+ protected static final SimpleDateFormat stimeformatheader = new SimpleDateFormat("yyyy MMM dd"); //$NON-NLS-1$
+ protected static final SimpleDateFormat sminformat = new SimpleDateFormat("HH:mm"); //$NON-NLS-1$
+ protected static final SimpleDateFormat sminformatheader = new SimpleDateFormat("yyyy MMM dd"); //$NON-NLS-1$
+ protected static final SimpleDateFormat shrsformat = new SimpleDateFormat("MMM dd HH:mm"); //$NON-NLS-1$
+ protected static final SimpleDateFormat shrsformatheader = new SimpleDateFormat("yyyy"); //$NON-NLS-1$
+ protected static final SimpleDateFormat sdayformat = new SimpleDateFormat("MMM dd"); //$NON-NLS-1$
+ protected static final SimpleDateFormat sdayformatheader = new SimpleDateFormat("yyyy"); //$NON-NLS-1$
+ protected static final SimpleDateFormat smonthformat = new SimpleDateFormat("yyyy MMM"); //$NON-NLS-1$
+ protected static final SimpleDateFormat syearformat = new SimpleDateFormat("yyyy"); //$NON-NLS-1$
+
+ static String sep(long n) {
+ StringBuilder retVal = new StringBuilder();
+ String s = Long.toString(n);
+ for (int i = 0; i < s.length(); i++) {
+ int pos = s.length() - i - 1;
+ retVal.append(s.charAt(i));
+ if (pos % 3 == 0 && pos != 0) {
+ retVal.append(' ');
+ }
+ }
+ return retVal.toString();
+ }
+
+ static String pad(long n) {
+ String s;
+ if (n < 10) {
+ s = S00;
+ } else if (n < 100) {
+ s = S0;
+ } else {
+ s = S;
+ }
+ return s + n;
+ }
+
+ public abstract void draw(GC gc, long time, Rectangle rect);
+
+ public void drawAbsHeader(GC gc, long time, Rectangle absHeaderRect) {
+ // Override to draw absolute time header
+ // This is for the time information not shown in the draw of each tick
+ }
+
+ public abstract String hint();
+}
+
+class TimeDrawSec extends TimeDraw {
+ static String _hint = "sec"; //$NON-NLS-1$
+
+ @Override
+ public void draw(GC gc, long time, Rectangle rect) {
+ time /= 1000000000;
+ Utils.drawText(gc, sep(time), rect, true); //$NON-NLS-1$
+ }
+
+ @Override
+ public String hint() {
+ return _hint;
+ }
+}
+
+class TimeDrawMillisec extends TimeDraw {
+ static String _hint = "0.000"; //$NON-NLS-1$
+
+ @Override
+ public void draw(GC gc, long time, Rectangle rect) {
+ time /= 1000000;
+ long ms = time % 1000;
+ time /= 1000;
+ Utils.drawText(gc, sep(time) + "." + pad(ms), rect, true); //$NON-NLS-1$
+ }
+
+ @Override
+ public String hint() {
+ return _hint;
+ }
+}
+
+class TimeDrawMicrosec extends TimeDraw {
+ static String _hint = "0.000 000"; //$NON-NLS-1$
+
+ @Override
+ public void draw(GC gc, long time, Rectangle rect) {
+ time /= 1000;
+ long mcs = time % 1000;
+ time /= 1000;
+ long ms = time % 1000;
+ time /= 1000;
+ Utils.drawText(gc, sep(time) + "." + pad(ms) + " " + pad(mcs), rect, true); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ @Override
+ public String hint() {
+ return _hint;
+ }
+}
+
+class TimeDrawNanosec extends TimeDraw {
+ static String _hint = "0.000 000 000"; //$NON-NLS-1$
+
+ @Override
+ public void draw(GC gc, long time, Rectangle rect) {
+ long ns = time % 1000;
+ time /= 1000;
+ long mcs = time % 1000;
+ time /= 1000;
+ long ms = time % 1000;
+ time /= 1000;
+ Utils.drawText(gc, sep(time) + "." + pad(ms) + " " + pad(mcs) + " " + pad(ns), rect, true); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ @Override
+ public String hint() {
+ return _hint;
+ }
+}
+
+class TimeDrawAbsYear extends TimeDraw {
+ static String _hint = "YYYY"; //$NON-NLS-1$
+
+ @Override
+ public void draw(GC gc, long time, Rectangle rect) {
+ String stime = syearformat.format(new Date(time / 1000000));
+ Utils.drawText(gc, stime, rect, true);
+ }
+
+ @Override
+ public String hint() {
+ return _hint;
+ }
+}
+
+class TimeDrawAbsMonth extends TimeDraw {
+ static String _hint = "YYYY Mmm"; //$NON-NLS-1$
+
+ @Override
+ public void draw(GC gc, long time, Rectangle rect) {
+ String stime = smonthformat.format(new Date(time / 1000000));
+ Utils.drawText(gc, stime, rect, true);
+ }
+
+ @Override
+ public String hint() {
+ return _hint;
+ }
+}
+
+class TimeDrawAbsDay extends TimeDraw {
+ static String _hint = "Mmm dd"; //$NON-NLS-1$
+
+ @Override
+ public void draw(GC gc, long time, Rectangle rect) {
+ String stime = sdayformat.format(new Date(time / 1000000));
+ Utils.drawText(gc, stime, rect, true);
+ }
+
+ @Override
+ public void drawAbsHeader(GC gc, long time, Rectangle rect) {
+ String header = sdayformatheader.format(new Date(time / 1000000));
+ int headerwidth = gc.stringExtent(header).x + 4;
+ if (headerwidth <= rect.width) {
+ rect.x += (rect.width - headerwidth);
+ Utils.drawText(gc, header, rect, true);
+ }
+ }
+
+ @Override
+ public String hint() {
+ return _hint;
+ }
+}
+
+class TimeDrawAbsHrs extends TimeDraw {
+ static String _hint = "Mmm dd HH:mm"; //$NON-NLS-1$
+
+ @Override
+ public void draw(GC gc, long time, Rectangle rect) {
+ String stime = shrsformat.format(new Date(time / 1000000));
+ Utils.drawText(gc, stime, rect, true);
+ }
+
+ @Override
+ public void drawAbsHeader(GC gc, long time, Rectangle rect) {
+ String header = shrsformatheader.format(new Date(time / 1000000));
+ int headerwidth = gc.stringExtent(header).x + 4;
+ if (headerwidth <= rect.width) {
+ rect.x += (rect.width - headerwidth);
+ Utils.drawText(gc, header, rect, true);
+ }
+ }
+
+ @Override
+ public String hint() {
+ return _hint;
+ }
+}
+
+class TimeDrawAbsMin extends TimeDraw {
+ static String _hint = "HH:mm"; //$NON-NLS-1$
+
+ @Override
+ public void draw(GC gc, long time, Rectangle rect) {
+ String stime = sminformat.format(new Date(time / 1000000));
+ Utils.drawText(gc, stime, rect, true);
+ }
+
+ @Override
+ public void drawAbsHeader(GC gc, long time, Rectangle rect) {
+ String header = sminformatheader.format(new Date(time / 1000000));
+ int headerwidth = gc.stringExtent(header).x + 4;
+ if (headerwidth <= rect.width) {
+ rect.x += (rect.width - headerwidth);
+ Utils.drawText(gc, header, rect, true);
+ }
+ }
+
+
+ @Override
+ public String hint() {
+ return _hint;
+ }
+}
+
+class TimeDrawAbsSec extends TimeDraw {
+ static String _hint = "HH:mm:ss"; //$NON-NLS-1$
+
+ @Override
+ public void draw(GC gc, long time, Rectangle rect) {
+ String stime = stimeformat.format(new Date(time / 1000000));
+ Utils.drawText(gc, stime, rect, true);
+ }
+
+ @Override
+ public void drawAbsHeader(GC gc, long time, Rectangle rect) {
+ String header = stimeformatheader.format(new Date(time / 1000000));
+ int headerwidth = gc.stringExtent(header).x + 4;
+ if (headerwidth <= rect.width) {
+ rect.x += (rect.width - headerwidth);
+ Utils.drawText(gc, header, rect, true);
+ }
+ }
+
+ @Override
+ public String hint() {
+ return _hint;
+ }
+}
+
+class TimeDrawAbsMillisec extends TimeDraw {
+ static String _hint = "HH:ss:ms"; //$NON-NLS-1$
+
+ @Override
+ public void draw(GC gc, long time, Rectangle rect) {
+ String stime = stimeformat.format(new Date(time / 1000000));
+ String ns = Utils.formatNs(time, Resolution.MILLISEC);
+
+ Utils.drawText(gc, stime + "." + ns, rect, true); //$NON-NLS-1$
+ }
+
+ @Override
+ public void drawAbsHeader(GC gc, long time, Rectangle rect) {
+ String header = stimeformatheader.format(new Date(time / 1000000));
+ int headerwidth = gc.stringExtent(header).x + 4;
+ if (headerwidth <= rect.width) {
+ rect.x += (rect.width - headerwidth);
+ Utils.drawText(gc, header, rect, true);
+ }
+ }
+
+ @Override
+ public String hint() {
+ return _hint;
+ }
+}
+
+class TimeDrawAbsMicroSec extends TimeDraw {
+ static String _hint = "HH:ss:ms:mcs"; //$NON-NLS-1$
+
+ @Override
+ public void draw(GC gc, long time, Rectangle rect) {
+ String stime = stimeformat.format(new Date(time / 1000000));
+ String micr = Utils.formatNs(time, Resolution.MICROSEC);
+ Utils.drawText(gc, stime + "." + micr, rect, true); //$NON-NLS-1$
+ }
+
+ @Override
+ public void drawAbsHeader(GC gc, long time, Rectangle rect) {
+ String header = stimeformatheader.format(new Date(time / 1000000));
+ int headerwidth = gc.stringExtent(header).x + 4;
+ if (headerwidth <= rect.width) {
+ rect.x += (rect.width - headerwidth);
+ Utils.drawText(gc, header, rect, true);
+ }
+ }
+
+ @Override
+ public String hint() {
+ return _hint;
+ }
+}
+
+class TimeDrawAbsNanoSec extends TimeDraw {
+ static String _hint = "HH:ss:ms:mcs:ns"; //$NON-NLS-1$
+
+ @Override
+ public void draw(GC gc, long time, Rectangle rect) {
+ String stime = stimeformat.format(new Date(time / 1000000));
+ String ns = Utils.formatNs(time, Resolution.NANOSEC);
+ Utils.drawText(gc, stime + "." + ns, rect, true); //$NON-NLS-1$
+ }
+
+ @Override
+ public void drawAbsHeader(GC gc, long time, Rectangle rect) {
+ String header = stimeformatheader.format(new Date(time / 1000000));
+ int headerwidth = gc.stringExtent(header).x + 4;
+ if (headerwidth <= rect.width) {
+ rect.x += (rect.width - headerwidth);
+ Utils.drawText(gc, header, rect, true);
+ }
+ }
+
+ @Override
+ public String hint() {
+ return _hint;
+ }
+}