/**************************************************************************** * Copyright (c) 2007-2008 Jeremy Dowdall * 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: * Jeremy Dowdall <jeremyd@aspencloud.com> - initial API and implementation *****************************************************************************/ package org.eclipse.nebula.widgets.cdatetime; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import org.eclipse.nebula.cwt.v.VButton; import org.eclipse.nebula.cwt.v.VLayout; import org.eclipse.nebula.cwt.v.VPanel; import org.eclipse.nebula.cwt.v.VTracker; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; class AnalogTimePicker extends VPanel { class BaseLayout extends VLayout { @Override protected Point computeSize(VPanel panel, int wHint, int hHint, boolean flushCache) { Point size = dialPanel.computeSize(wHint, hHint, flushCache); if(digitalClock != null) { size.y += digitalClock.computeSize(wHint, hHint, flushCache).y; } return size; } @Override protected void layout(VPanel panel, boolean flushCache) { Rectangle clientArea = panel.getClientArea(); Point dclockSize = (digitalClock != null) ? digitalClock.computeSize(SWT.DEFAULT, SWT.DEFAULT, flushCache) : new Point(0, 0); int dwidth = clientArea.width; int dheight = Math.min(dwidth, clientArea.height - dclockSize.y); if(dheight < dwidth) dwidth = dheight; int dx = clientArea.x + (clientArea.width - dwidth) / 2; int dy = clientArea.y + (clientArea.height - dheight - dclockSize.y) / 2; dialPanel.setBounds(dx, dy, dwidth, dheight); if(digitalClock != null) { digitalClock.setBounds(clientArea.x + (clientArea.width - dclockSize.x) / 2, dy + dheight, dclockSize.x, dclockSize.y); } } } class DialLayout extends VLayout { @Override protected Point computeSize(VPanel panel, int wHint, int hHint, boolean flushCache) { return new Point(200, 200); } @Override protected void layout(VPanel panel, boolean flushCache) { Rectangle r = panel.getClientArea(); dialRadius = (Math.min(r.width, r.height) - 10) / 2; dialCenter.x = r.x + r.width / 2; dialCenter.y = r.y + r.height / 2; if(timeNow != null) { timeNow.setBounds(dialCenter.x - 11, dialCenter.y - 11, 22, 22); } if(timeAmPm != null) { Point size = timeAmPm.computeSize(-1, -1); timeAmPm.setBounds(dialCenter.x - (size.x / 2), dialCenter.y + (dialRadius / 3) - (size.y / 4), size.x, size.y); } } } VPanel dialPanel; VButton timeNow; VButton timeAmPm; CDateTime digitalClock; int dialRadius; Point dialCenter = new Point(0, 0); boolean setH = false; boolean setM = false; boolean setS = false; boolean overHour = false; boolean overMin = false; boolean overSec = false; boolean is24Hour; boolean hourHand; boolean minHand; boolean secHand; boolean am_pm; boolean compact; private int[] snap = { 1, 1 }; long increment = 300000; // 5 minutes private CDateTime cdt; String pattern; private Listener tapl; public AnalogTimePicker(CDateTime parent) { super(parent.pickerPanel, parent.style); cdt = parent; compact = (cdt.style & CDT.COMPACT) != 0; createContents(); } public AnalogTimePicker(CDateTime cdt, DatePicker parent) { super(parent, 0); this.cdt = cdt; compact = (cdt.style & CDT.COMPACT) != 0; createContents(); } protected void createContents() { setLayout(new BaseLayout()); dialPanel = new VPanel(this, SWT.NONE); dialPanel.setLayout(new DialLayout()); timeAmPm = new VButton(dialPanel, SWT.NO_FOCUS); timeAmPm.setText("PM"); //$NON-NLS-1$ timeAmPm.setForeground(getDisplay().getSystemColor(SWT.COLOR_DARK_BLUE)); timeAmPm.setMargins(4, 4); timeAmPm.setEnabled(!dialPanel.hasStyle(CDT.READ_ONLY)); tapl = new Listener() { public void handleEvent(Event event) { if(event.widget == null) { Calendar tmpcal = cdt.getCalendarInstance(); tmpcal.set(Calendar.AM_PM, (tmpcal.get(Calendar.AM_PM) == 0) ? 1 : 0); setSelection(tmpcal.getTime()); cdt.fireSelectionChanged(Calendar.AM_PM); } } }; timeAmPm.addListener(SWT.Selection, tapl); timeAmPm.addListener(SWT.MouseWheel, tapl); Listener listener = new Listener() { public void handleEvent(Event event) { if(cdt.getEditable()) { switch(event.type) { case SWT.Deactivate: if(VTracker.isMouseDown()) { handleMouseUp(); overHour = overMin = overSec = false; redraw(); } break; case SWT.MouseDown: handleMouseDown(); break; case SWT.MouseMove: handleMouseMove(event.x, event.y); break; case SWT.MouseUp: handleMouseUp(); break; case SWT.MouseWheel: handleMouseWheel(event.count); break; } } } }; dialPanel.addListener(SWT.Deactivate, listener); dialPanel.addListener(SWT.MouseDown, listener); dialPanel.addListener(SWT.MouseMove, listener); dialPanel.addListener(SWT.MouseUp, listener); dialPanel.addListener(SWT.MouseWheel, listener); dialPanel.setPainter(new AnalogClockPainter(cdt, this)); } public int[] getFields() { return new int[] { Calendar.HOUR_OF_DAY, Calendar.HOUR, Calendar.MINUTE, Calendar.SECOND, Calendar.AM_PM }; } long getIncrement() { return increment; } /** * Get the snap intervals used when setting the minutes and seconds. * * @return an int[2] where int[0] is the minutes snap, and int[1] is the * seconds snap * @see #setTimeSnap(int, int) */ int[] getSnap() { return snap; } private void handleMouseDown() { if(overHour) { setH = true; } else if(overMin) { setM = true; } else if(overSec) { setS = true; } if(setH || setM || setS) { dialPanel.getComposite().setCursor(getDisplay().getSystemCursor(SWT.CURSOR_SIZEALL)); if(timeAmPm != null) { timeAmPm.setEnabled(false); } } } private void handleMouseMove(int x, int y) { Calendar tmpcal = cdt.getCalendarInstance(); int dx = x - dialCenter.x; int dy = y - dialCenter.y; double val; if(dx == 0) { if(dy > 0) { val = 30; } else { val = 0; } } else if(dy == 0) { if(dx > 0) { val = 15; } else { val = 45; } } else { val = (30 * Math.atan((double) dy / (double) dx) / Math.PI) + 15; if(dx < 0) { val += 30; } } if(setH) { val = is24Hour ? val / 2.5 : val / 5; int v = (int) ((val) - (double) tmpcal.get(Calendar.MINUTE) / 60 + .5); if(is24Hour && v > 23) v = 23; if(!is24Hour && v > 11) v = 11; int field = is24Hour ? Calendar.HOUR_OF_DAY : Calendar.HOUR; tmpcal.set(field, v); setSelection(tmpcal.getTime()); } else if(setM) { int v = (int) (val + 0.5); if(v > 59) v = 59; tmpcal.set(Calendar.MINUTE, v); setSelection(tmpcal.getTime()); } else if(setS) { int v = (int) (val + 0.5); if(v > 59) v = 59; tmpcal.set(Calendar.SECOND, v); setSelection(tmpcal.getTime()); } else { boolean rd = false; if(overHour || overMin || overSec) { rd = true; } overHour = false; overMin = false; overSec = false; if(Math.sqrt(dx * dx + dy * dy) < dialRadius) { double h = (tmpcal.get(is24Hour ? Calendar.HOUR_OF_DAY : Calendar.HOUR) + (double) tmpcal.get(Calendar.MINUTE) / 60) * (is24Hour ? 2.5 : 5); int m = tmpcal.get(Calendar.MINUTE); int s = tmpcal.get(Calendar.SECOND); if(hourHand && val - 1 < h && h <= val + 1) { overHour = true; rd = true; } else if(minHand && val - 1 < m && m <= val + 1) { overMin = true; rd = true; } else if(secHand && val - 1 < s && s <= val + 1) { overSec = true; rd = true; } } if(rd) { dialPanel.redraw(); } } } private void handleMouseUp() { dialPanel.getComposite().setCursor(getDisplay().getSystemCursor(SWT.CURSOR_ARROW)); if(timeAmPm != null) { timeAmPm.setEnabled(true); } if(setH) { int field = is24Hour ? Calendar.HOUR_OF_DAY : Calendar.HOUR; cdt.fireSelectionChanged(field); } else if(setM) { cdt.fireSelectionChanged(Calendar.MINUTE); } else if(setS) { cdt.fireSelectionChanged(Calendar.SECOND); } setH = setM = setS = false; } private void handleMouseWheel(int count) { long time = cdt.getCalendarTimeInMillis(); time += (count > 0) ? increment : -increment; setSelection(snap(new Date(time), true)); cdt.fireSelectionChanged(); } public void setFields(int[] calendarFields) { is24Hour = false; hourHand = false; minHand = false; secHand = false; am_pm = false; for(int field : calendarFields) { if(field == Calendar.HOUR_OF_DAY) { is24Hour = true; } else if(field == Calendar.HOUR) { hourHand = true; } else if(field == Calendar.MINUTE) { minHand = true; } else if(field == Calendar.SECOND) { secHand = true; } else if(field == Calendar.AM_PM) { am_pm = true; } } if((cdt.style & CDT.CLOCK_12_HOUR) != 0) { is24Hour = false; hourHand = true; am_pm = true; } else if((cdt.style & CDT.CLOCK_24_HOUR) != 0) { is24Hour = true; } if(is24Hour) { hourHand = true; am_pm = false; } timeAmPm.setVisible(am_pm); boolean sepOK = false; pattern = ""; //$NON-NLS-1$ String cdtPattern = cdt.getPattern(); for(int i = 0; i < cdtPattern.length(); i++) { char c = cdtPattern.charAt(i); if("Hhmsa".indexOf(c) > -1) { //$NON-NLS-1$ pattern += c; sepOK = true; } else { if(sepOK && ":., ".indexOf(c) > -1) { //$NON-NLS-1$ pattern += c; } sepOK = false; } } if(digitalClock != null) { digitalClock.setPattern(pattern); } updateLabels(); } public boolean setFocus() { if(timeNow != null) { return timeNow.setFocus(); } else { return getComposite().forceFocus(); } } void setIncrement(long millis) { increment = millis; } private void setSelection(Date date) { cdt.setSelection(snap(date)); } /** * Set the snap for the minutes and seconds. If the value given for either * parameter is less than or equal to zero then its corresponding snap will * be set to its default of one (1). * * @param min * the snap interval for the minutes * @param sec * the snap interval for the seconds * @see #getSnap() */ void setSnap(int min, int sec) { snap[0] = (min < 0) ? 1 : min; snap[1] = (sec < 0) ? 1 : sec; } @Override public boolean setStyle(int style, boolean set) { if((style & SWT.READ_ONLY) != 0) { if(timeAmPm != null && !timeAmPm.isDisposed()) { timeAmPm.setEnabled(!set); } if(timeNow != null && !timeNow.isDisposed()) { timeNow.setEnabled(!set); } } return super.setStyle(style, set); } /** * perform the snap and return a new "snapped" Date object */ private Date snap(Date date) { return snap(date, false); } /** * perform the snap and return a new "snapped" Date object */ private Date snap(Date date, boolean toIncrement) { Calendar tmpcal = cdt.getCalendarInstance(); tmpcal.setTime(date); int msnap = toIncrement ? (int) (increment / 60000) : snap[0]; int v = tmpcal.get(Calendar.MINUTE); int m = v % msnap; if(m != 0) { v += (m > msnap / 2) ? (msnap - m) : -m; if(v > 59) v = 0; tmpcal.set(Calendar.MINUTE, v); } if(!toIncrement) { v = tmpcal.get(Calendar.SECOND); m = v % snap[1]; if(m != 0) { v += (m > snap[1] / 2) ? (snap[1] - m) : -m; if(v > 59) v = 0; tmpcal.set(Calendar.SECOND, v); } } return tmpcal.getTime(); } void updateLabels() { if(timeNow != null) { timeNow.setToolTipText(Resources.getString("nav_current_time", cdt.getLocale())); //$NON-NLS-1$ } } void updateView() { if(digitalClock != null) { digitalClock.setSelection(new Date(cdt.getCalendarTimeInMillis())); } if(timeAmPm != null) { SimpleDateFormat sdf = new SimpleDateFormat("a"); //$NON-NLS-1$ timeAmPm.setText(sdf.format(cdt.getCalendarTime())); } dialPanel.redraw(); dialPanel.update(); } }