package org.xmind.ui.datepicker; import static java.util.Calendar.DATE; import static java.util.Calendar.DAY_OF_WEEK; import static java.util.Calendar.MONTH; import static java.util.Calendar.SATURDAY; import static java.util.Calendar.SUNDAY; import static java.util.Calendar.YEAR; import static org.eclipse.jface.resource.JFaceResources.DEFAULT_FONT; import java.util.Calendar; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.draw2d.FigureCanvas; import org.eclipse.draw2d.GridData; import org.eclipse.draw2d.GridLayout; import org.eclipse.draw2d.IFigure; import org.eclipse.draw2d.Layer; import org.eclipse.draw2d.MouseEvent; import org.eclipse.draw2d.MouseListener; import org.eclipse.draw2d.MouseMotionListener; import org.eclipse.draw2d.Viewport; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IMenuListener2; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.Viewer; import org.eclipse.swt.SWT; import org.eclipse.swt.events.FocusEvent; import org.eclipse.swt.events.FocusListener; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; import org.xmind.ui.dialogs.PopupDialog; import org.xmind.ui.resources.ColorUtils; import org.xmind.ui.resources.FontUtils; import org.xmind.ui.viewers.SWTUtils; /** * A viewer to pick a date on the calendar. */ public class DatePicker2 extends Viewer { private static final String[] MONTHS = new String[] { Messages.January, Messages.Feburary, Messages.March, Messages.April, Messages.May, Messages.June, Messages.July, Messages.August, Messages.September, Messages.October, Messages.November, Messages.December }; private static final String[] WEEK_SYMBOLS = new String[] { Messages.Sunday, Messages.Monday, Messages.Tuesday, Messages.Wednesday, Messages.Thursday, Messages.Friday, Messages.Saturday }; private static final int FUTURE_YEARS = 7; private static final int PASSED_YEARS = 3; private static final String COLOR_TEXT = "#000000"; //$NON-NLS-1$ private static final String COLOR_WEEKEND = "#EE0000"; //$NON-NLS-1$ private static final String COLOR_SEPARATOR = "#C0C0C0"; //$NON-NLS-1$ private static final String COLOR_TODAY = "#ff9900"; //$NON-NLS-1$ private static final String COLOR_WEEK_SYMBOL = "#808080"; //$NON-NLS-1$ private static final String COLOR_ARROW_BORDER = "#808080"; //$NON-NLS-1$ private static final String COLOR_ARROW_FILL = "#C0C0C0"; //$NON-NLS-1$ private static final String COLOR_CANCEL = "#D80000"; //$NON-NLS-1$ private static final int NORMAL_ALPHA = 0xff; private static final int SIBLING_MONTH_ALPHA = 0x20; private static final int DURATION = 200; private static final int TOTAL_DAYS = 42; private static Color validColor = ColorUtils.getColor("#ffffff"); //$NON-NLS-1$ private static Color invalidColor = ColorUtils.getColor("#f0f0f0"); //$NON-NLS-1$ private class EventHandler implements MouseListener, MouseMotionListener, Listener { private boolean dayPressed = false; private BaseFigure target = null; public void attach(IFigure figure) { figure.addMouseListener(this); figure.addMouseMotionListener(this); } public void detach(IFigure figure) { figure.removeMouseListener(this); figure.removeMouseMotionListener(this); } public void install(Control control) { control.addListener(SWT.MouseUp, this); control.addListener(SWT.MouseWheel, this); control.addListener(SWT.KeyDown, this); } public void uninstall(Control control) { control.removeListener(SWT.MouseUp, this); control.removeListener(SWT.MouseWheel, this); control.removeListener(SWT.KeyDown, this); } public void handleEvent(Event event) { if (event.type == SWT.MouseUp) { dayPressed = false; if (target != null) { final BaseFigure eventTarget = target; target.setPressed(false); target = null; selected(eventTarget); } } else if (event.type == SWT.MouseWheel) { if (event.count == 0) return; // wheel upwards, count > 0, month should decrease // wheel downwards, count < 0, month should increase rollMonth(event.count > 0 ? -1 : 1); } else if (event.type == SWT.KeyDown) { handleKeyPress(event.keyCode, event.stateMask); } } private void handleKeyPress(int key, int mask) { if (SWTUtils.matchKey(mask, key, 0, SWT.ARROW_UP)) { lastMonthSelected(true); } else if (SWTUtils.matchKey(mask, key, 0, SWT.ARROW_DOWN)) { nextMonthSelected(true); } else if (SWTUtils.matchKey(mask, key, 0, SWT.ARROW_LEFT)) { lastYearSelected(true); } else if (SWTUtils.matchKey(mask, key, 0, SWT.ARROW_RIGHT)) { nextYearSelected(true); } } public void mouseDoubleClicked(MouseEvent me) { // do nothing } public void mouseDragged(MouseEvent me) { // do nothing } public void mouseEntered(MouseEvent me) { if (target == null) { BaseFigure source = (BaseFigure) me.getSource(); if (source instanceof DayFigure && dayPressed) { source.setPressed(true); source.setPreselected(false); } else { if (source == monthFigure || source == lastMonth || source == nextMonth) { monthFigure.setPreselected(true); lastMonth.getContent().setVisible(true); nextMonth.getContent().setVisible(true); } else if (source == yearFigure || source == lastYear || source == nextYear) { yearFigure.setPreselected(true); lastYear.getContent().setVisible(true); nextYear.getContent().setVisible(true); } source.setPreselected(true); } } } public void mouseExited(MouseEvent me) { if (target == null) { BaseFigure source = (BaseFigure) me.getSource(); if (source instanceof DayFigure) { source.setPreselected(false); if (dayPressed) { source.setPressed(false); } } else { if (source == monthFigure || source == lastMonth || source == nextMonth) { monthFigure.setPreselected(false); lastMonth.getContent().setVisible(false); nextMonth.getContent().setVisible(false); } else if (source == yearFigure || source == lastYear || source == nextYear) { yearFigure.setPreselected(false); lastYear.getContent().setVisible(false); nextYear.getContent().setVisible(false); } source.setPreselected(false); } } } public void mouseHover(MouseEvent me) { // do nothing } public void mouseMoved(MouseEvent me) { // do nothing } public void mousePressed(MouseEvent me) { BaseFigure source = (BaseFigure) me.getSource(); source.setPressed(true); source.setPreselected(false); if (source instanceof DayFigure) { dayPressed = true; } else { target = source; } } public void mouseReleased(MouseEvent me) { BaseFigure source = (BaseFigure) me.getSource(); source.setPressed(false); if (source instanceof DayFigure) { if (dayPressed) { daySelected((DayFigure) me.getSource()); } source.setPreselected(true); } else { if (!source.isSelected()) { source.setPreselected(true); } } } } private class DropdownDatePicker extends PopupDialog { public DropdownDatePicker(Shell parent) { super(parent, SWT.NO_TRIM, true, false, false, false, false, null, null); } @Override protected Control createDialogArea(Composite parent) { Composite composite = (Composite) super.createDialogArea(parent); createDatePicker(composite); datePicker.setLayoutData(new org.eclipse.swt.layout.GridData( SWT.FILL, SWT.FILL, true, true)); initDatePicker(); update(); return composite; } @Override public boolean close() { boolean closed = super.close(); if (closed) { datePicker = null; } return closed; } @Override protected Point getInitialLocation(Point initialSize) { Control c = DatePicker2.this.getControl(); org.eclipse.swt.graphics.Rectangle r = c.getBounds(); return c.toDisplay(-2, r.height - 1); } } private class MonthAction extends Action { private int month; public MonthAction(int month) { super(MONTHS[month]); this.month = month; } public void run() { monthSelected(month); } } private class YearAction extends Action { private int year; public void setYear(int year) { this.year = year; setText("" + year); //$NON-NLS-1$ } public int getYear() { return year; } public void run() { yearSelected(year); } } private class AnimationAdvisor implements IAnimationAdvisor { private int monthsToRoll = 0; private int yearsToRoll = 0; private int duration = -1; private int oldYear; private int oldMonth; private int newYear; private int newMonth; public void addMonthsToRoll(int count) { monthsToRoll += count; duration = -1; } public void addYearsToRoll(int count) { yearsToRoll += count; duration = -1; } public int getDuration() { if (duration < 0) { int steps = Math.abs(monthsToRoll) + Math.abs(yearsToRoll); duration = steps == 0 ? 0 : DURATION / steps; } return duration; } public IFigure getLayer() { return DatePicker2.this.dateLayer; } public int getMonthsToRoll() { return monthsToRoll; } public int getNewMonth() { return newMonth; } public int getNewYear() { return newYear; } public int getOldMonth() { return oldMonth; } public int getOldYear() { return oldYear; } public IFigure getPanel() { return DatePicker2.this.datePanel; } public int getYearsToRoll() { return yearsToRoll; } public void initNewDay(DayFigure figure) { updateDayFigure(figure, oldYear, oldMonth); } public void initOldDay(DayFigure figure) { figure.setPreselected(false); figure.setSelected(isSameDay(figure.getDate(), getSelectedDate())); eventHandler.detach(figure); } public boolean isDone() { return monthsToRoll == 0 && yearsToRoll == 0; } public void setEndMonth(int newYear, int newMonth) { this.newYear = newYear; this.newMonth = newMonth; } public void setStartMonth(int oldYear, int oldMonth) { this.oldYear = oldYear; this.oldMonth = oldMonth; } public void updateNewDay(DayFigure figure) { figure.setPreselected(false); figure.setSelected(isSameDay(figure.getDate(), getSelectedDate())); eventHandler.attach(figure); updateDayFigure(figure, newYear, newMonth); } } private int style; private Control control; private FigureCanvas datePicker; private Composite placeholder; private Text yearText; private Text monthText; private Text dayText; private Button showDatePickerButton; private Label slash1; private Label slash2; private ILabelProvider dateLabelProvider; private DropdownDatePicker dropdownDatePicker; private Calendar today; private int currentMonth; private int currentYear; private Calendar selection; private IFigure dateLayer; private IFigure datePanel; private MonthFigure monthFigure; private YearFigure yearFigure; private BaseFigure todayFigure; private BaseFigure cancelFigure; private EventHandler eventHandler; private MenuManager monthMenu; private MonthAction[] monthActions; private MenuManager yearMenu; private YearAction[] yearActions; private ArrowFigure lastYear; private ArrowFigure nextYear; private ArrowFigure lastMonth; private ArrowFigure nextMonth; private boolean firingSelectionChange = false; private boolean animating = false; private IAnimationAdvisor animationAdvisor = new AnimationAdvisor(); private String oldValue = ""; //$NON-NLS-1$ private Calendar minTime; private Calendar maxTime; /** * Constructs a new instance of this class given its parent and a style * value describing its behavior and appearance. * * @param parent * a composite control which will be the parent of the new * instance (cannot be null) * @param style * the style of control to construct * @see SWT#SIMPLE * @see SWT#DROP_DOWN * @see SWT#CANCEL */ public DatePicker2(Composite parent, int style) { this(parent, style, Calendar.getInstance()); } public DatePicker2(Composite parent, int style, Calendar today) { this.today = today; this.currentMonth = today.get(MONTH); this.currentYear = today.get(YEAR); this.style = style; if ((style & SWT.DROP_DOWN) != 0) { createPlaceholder(parent); this.control = placeholder; initPlaceholder(); } else { createDatePicker(parent); this.control = datePicker; initDatePicker(); } update(); } private void createPlaceholder(Composite parent) { Composite composite = new Composite(parent, SWT.BORDER); org.eclipse.swt.layout.GridLayout layout = new org.eclipse.swt.layout.GridLayout( 6, false); layout.marginHeight = 0; layout.marginWidth = 0; layout.horizontalSpacing = 0; composite.setLayout(layout); composite.setBackground(validColor); dayText = new Text(composite, SWT.SINGLE | SWT.NONE); org.eclipse.swt.layout.GridData dateData = new org.eclipse.swt.layout.GridData( SWT.RIGHT, SWT.CENTER, false, false); // dateData.widthHint = 14; dayText.setLayoutData(dateData); dayText.setTextLimit(2); dayText.setBackground(dayText.getParent().getBackground()); slash1 = new Label(composite, SWT.NONE); slash1.setLayoutData(new org.eclipse.swt.layout.GridData(SWT.CENTER, SWT.CENTER, false, false)); slash1.setBackground(slash1.getParent().getBackground()); slash1.setText("/"); //$NON-NLS-1$ monthText = new Text(composite, SWT.SINGLE | SWT.NONE); org.eclipse.swt.layout.GridData monthData = new org.eclipse.swt.layout.GridData( SWT.CENTER, SWT.CENTER, false, false); // monthData.widthHint = 14; monthText.setLayoutData(monthData); monthText.setTextLimit(2); monthText.setBackground(monthText.getParent().getBackground()); slash2 = new Label(composite, SWT.NONE); slash2.setLayoutData(new org.eclipse.swt.layout.GridData(SWT.CENTER, SWT.CENTER, false, false)); slash2.setBackground(slash2.getParent().getBackground()); slash2.setText("/"); //$NON-NLS-1$ yearText = new Text(composite, SWT.SINGLE | SWT.NONE); org.eclipse.swt.layout.GridData yearData = new org.eclipse.swt.layout.GridData( SWT.LEFT, SWT.CENTER, false, false); // yearData.widthHint = 28; yearText.setLayoutData(yearData); yearText.setTextLimit(4); yearText.setBackground(yearText.getParent().getBackground()); showDatePickerButton = new Button(composite, SWT.ARROW | SWT.DOWN); showDatePickerButton.setLayoutData(new org.eclipse.swt.layout.GridData( SWT.LEFT, SWT.CENTER, false, false)); filterInput(); this.placeholder = composite; } private void filterInput() { checkInput(dayText); checkInput(monthText); checkInput(yearText); } private void checkInput(final Text field) { KeyListener keyListener = new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { switch (e.character) { case SWT.ESC: field.setText(oldValue); field.getParent().forceFocus(); break; case SWT.CR: field.getParent().forceFocus(); break; } } }; FocusListener focusListener = new FocusListener() { public void focusLost(FocusEvent e) { editField(field, oldValue); oldValue = ""; //$NON-NLS-1$ } public void focusGained(FocusEvent e) { oldValue = field.getText(); field.selectAll(); } }; field.addKeyListener(keyListener); field.addFocusListener(focusListener); } private void editField(Text field, String oldValue) { String text = field.getText(); if (text.equals(oldValue)) { return; } if ("".equals(text)) { //$NON-NLS-1$ field.setText(oldValue); return; } int value = 0; text = text.trim(); Pattern re = Pattern.compile("^\\d+$"); //$NON-NLS-1$ Matcher m = re.matcher(text); if (!m.matches()) { field.setText(oldValue); return; } value = parseInt(m.group()); int calendarField = (field == yearText ? Calendar.YEAR : (field == monthText ? Calendar.MONTH : Calendar.DAY_OF_MONTH)); if (calendarField == Calendar.MONTH) { value -= 1; } Calendar newTime = Calendar.getInstance(); newTime.setTimeInMillis(getSelectedDate().getTimeInMillis()); newTime.set(calendarField, value); newTime = checkTime(newTime); changeDate(newTime); refresh(); } private int parseInt(String s) { if (s != null && !"".equals(s)) {//$NON-NLS-1$ try { return Integer.parseInt(s, 10); } catch (NumberFormatException e) { } } return 0; } private Calendar checkTime(Calendar newTime) { final Calendar calendar = Calendar.getInstance(); calendar.setTimeInMillis(newTime.getTimeInMillis()); if (calendar.before(minTime)) { MessageDialog.openInformation(Display.getDefault().getActiveShell(), Messages.TimeCheckInvalid_label, Messages.TimeCheckInvalidSmall_message); calendar.setTimeInMillis(minTime.getTimeInMillis()); } else if (calendar.after(maxTime)) { MessageDialog.openConfirm(Display.getDefault().getActiveShell(), Messages.TimeCheckInvalid_label, Messages.TimeCheckInvalidBig_message); calendar.setTimeInMillis(maxTime.getTimeInMillis()); } return calendar; } private void createDatePicker(Composite parent) { this.datePicker = new FigureCanvas(parent); } public void setLabelProvider(ILabelProvider labelProvider) { this.dateLabelProvider = labelProvider; update(); } public ILabelProvider getLabelProvider() { if (this.dateLabelProvider == null) { this.dateLabelProvider = new DateLabelProvider(); } return this.dateLabelProvider; } public Control getControl() { return control; } public FigureCanvas getDatePicker() { return datePicker; } public void setBackground(Color color) { if (control != null && !control.isDisposed()) { control.setBackground(color); } } public void setEnabled(boolean enabled) { if (placeholder != null) { placeholder.setEnabled(enabled); placeholder.setBackground(enabled ? validColor : invalidColor); yearText.setEnabled(enabled); yearText.setBackground(enabled ? validColor : invalidColor); monthText.setEnabled(enabled); monthText.setBackground(enabled ? validColor : invalidColor); dayText.setEnabled(enabled); dayText.setBackground(enabled ? validColor : invalidColor); slash1.setEnabled(enabled); slash1.setBackground(enabled ? validColor : invalidColor); slash2.setEnabled(enabled); slash2.setBackground(enabled ? validColor : invalidColor); showDatePickerButton.setEnabled(enabled); } } public Composite getPlaceholder() { return placeholder; } public ISelection getSelection() { return new DateSelection(selection); } @Override public void setSelection(ISelection selection) { setSelection(selection, true); } @Override public void setSelection(ISelection selection, boolean reveal) { if (selection instanceof DateSelection) { setDateSelection(((DateSelection) selection).getDate(), reveal); } else if (selection instanceof IStructuredSelection) { Object sel = ((IStructuredSelection) selection).getFirstElement(); if (sel instanceof Calendar) { setDateSelection((Calendar) sel, reveal); } } } public void setDateSelection(Calendar date, boolean reveal) { changeDate(date); if (reveal && date != null) { changeCalendar(date.get(YEAR), date.get(MONTH)); } update(); } private void initPlaceholder() { showDatePickerButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { if (dropdownDatePicker == null || dropdownDatePicker.getShell() == null || dropdownDatePicker.getShell().isDisposed() || !dropdownDatePicker.isClosing()) showDropdown(); } }); } private void showDropdown() { placeholder.forceFocus(); createDropdownDatePicker(); dropdownDatePicker.open(); Shell shell = dropdownDatePicker.getShell(); if (shell != null && !shell.isDisposed()) { shell.addListener(SWT.Dispose, new Listener() { public void handleEvent(Event event) { if (placeholder != null && !placeholder.isDisposed()) { placeholder.forceFocus(); } } }); } } /** * Shows the drop-down menu if this date picker is created with * <code>SWT.DROP_DOWN</code> style bit. */ public void open() { showDropdown(); } private void createDropdownDatePicker() { if (dropdownDatePicker != null) return; dropdownDatePicker = new DropdownDatePicker(control.getShell()); } private void initDatePicker() { datePicker.setScrollBarVisibility(FigureCanvas.NEVER); eventHandler = new EventHandler(); eventHandler.install(datePicker); datePicker.addListener(SWT.Dispose, new Listener() { public void handleEvent(Event event) { eventHandler.uninstall(datePicker); } }); Viewport viewport = new Viewport(true); viewport.setContentsTracksHeight(true); viewport.setContentsTracksWidth(true); datePicker.setViewport(viewport); IFigure container = new Layer(); datePicker.setContents(container); GridLayout containerLayout = new GridLayout(1, true); containerLayout.horizontalSpacing = 3; containerLayout.verticalSpacing = 3; containerLayout.marginHeight = 3; containerLayout.marginWidth = 3; container.setLayoutManager(containerLayout); createTopPanel(container); createSeparator(container); createWeekPanel(container); createDaysPanel(container); createSeparator(container); createBottomPanel(container); } private void createTopPanel(IFigure parent) { IFigure panel = new Layer(); GridData panelConstraint = new GridData(SWT.FILL, SWT.FILL, true, false); parent.add(panel, panelConstraint); GridLayout panelLayout = new GridLayout(12, true); panelLayout.horizontalSpacing = 0; panelLayout.verticalSpacing = 0; panelLayout.marginHeight = 0; panelLayout.marginWidth = 0; panel.setLayoutManager(panelLayout); lastMonth = createArrowFigure(panel, ArrowFigure.UP); monthFigure = createMonthFigure(panel); nextMonth = createArrowFigure(panel, ArrowFigure.DOWN); lastYear = createArrowFigure(panel, ArrowFigure.LEFT); yearFigure = createYearFigure(panel); nextYear = createArrowFigure(panel, ArrowFigure.RIGHT); } private MonthFigure createMonthFigure(IFigure parent) { MonthFigure figure = new MonthFigure(); figure.setTextCandidates(MONTHS); GridData constraint = new GridData(SWT.FILL, SWT.FILL, true, true); constraint.horizontalSpan = 5; parent.add(figure, constraint); eventHandler.attach(figure); return figure; } private YearFigure createYearFigure(IFigure parent) { YearFigure figure = new YearFigure(); figure.setTextCandidates(new String[] { "0000" }); //$NON-NLS-1$ GridData constraint = new GridData(SWT.FILL, SWT.FILL, true, true); constraint.horizontalSpan = 3; parent.add(figure, constraint); eventHandler.attach(figure); return figure; } private ArrowFigure createArrowFigure(IFigure parent, int orientation) { ArrowFigure arrow = new ArrowFigure(); arrow.setOrientation(orientation); arrow.setForegroundColor(ColorUtils.getColor(COLOR_ARROW_BORDER)); arrow.setBackgroundColor(ColorUtils.getColor(COLOR_ARROW_FILL)); arrow.getContent().setVisible(false); GridData constraint = new GridData(SWT.FILL, SWT.FILL, true, true); parent.add(arrow, constraint); eventHandler.attach(arrow); return arrow; } private void createSeparator(IFigure parent) { HorizontalLine line = new HorizontalLine(); line.setMargin(3); line.setForegroundColor(ColorUtils.getColor(COLOR_SEPARATOR)); GridData constraint = new GridData(SWT.FILL, SWT.FILL, true, false); constraint.heightHint = 3; parent.add(line, constraint); } private void createWeekPanel(IFigure parent) { IFigure panel = new Layer(); GridData panelConstraint = new GridData(SWT.FILL, SWT.FILL, true, false); parent.add(panel, panelConstraint); GridLayout panelLayout = new GridLayout(7, true); panelLayout.horizontalSpacing = 0; panelLayout.verticalSpacing = 0; panelLayout.marginHeight = 0; panelLayout.marginWidth = 0; panel.setLayoutManager(panelLayout); Font symbolFont = FontUtils.getRelativeHeight(DEFAULT_FONT, -2); for (int i = 0; i < 7; i++) { TextLayer symbol = new TextLayer(); symbol.setFont(symbolFont); symbol.setText(WEEK_SYMBOLS[i]); if (i == 0 || i == 6) { symbol.setForegroundColor(ColorUtils.getColor(COLOR_WEEKEND)); } else { symbol.setForegroundColor( ColorUtils.getColor(COLOR_WEEK_SYMBOL)); } GridData symbolConstraint = new GridData(SWT.FILL, SWT.FILL, true, true); panel.add(symbol, symbolConstraint); } } private void createDaysPanel(IFigure parent) { dateLayer = new Layer(); GridData layerConstraint = new GridData(SWT.FILL, SWT.FILL, true, true); parent.add(dateLayer, layerConstraint); dateLayer.setLayoutManager(new ConstraintStackLayout()); datePanel = new Layer(); dateLayer.add(datePanel, null); datePanel.setLayoutManager(new DatePanelLayout()); for (int i = 0; i < TOTAL_DAYS; i++) { DayFigure dayFigure = new DayFigure(); eventHandler.attach(dayFigure); datePanel.add(dayFigure); } } private void createBottomPanel(IFigure parent) { boolean hasCancel = (style & SWT.CANCEL) != 0; IFigure panel = new Layer(); GridData panelConstraint = new GridData(SWT.FILL, SWT.FILL, true, false); parent.add(panel, panelConstraint); GridLayout panelLayout = new GridLayout(hasCancel ? 2 : 1, false); panelLayout.horizontalSpacing = 0; panelLayout.verticalSpacing = 0; panelLayout.marginHeight = 0; panelLayout.marginWidth = 0; panel.setLayoutManager(panelLayout); todayFigure = createTodayFigure(panel); if (hasCancel) { cancelFigure = createCancelFigure(panel); } } private BaseFigure createTodayFigure(IFigure parent) { BaseFigure figure = new BaseFigure(); figure.setFont(FontUtils.getRelativeHeight(DEFAULT_FONT, -2)); figure.setForegroundColor(ColorUtils.getColor(COLOR_TODAY)); GridData constraint = new GridData(SWT.FILL, SWT.FILL, true, true); parent.add(figure, constraint); eventHandler.attach(figure); return figure; } private BaseFigure createCancelFigure(IFigure parent) { BaseFigure figure = new BaseFigure(); figure.setText(" X "); //$NON-NLS-1$ figure.setFont(FontUtils.getRelativeHeight(DEFAULT_FONT, -2)); figure.setForegroundColor(ColorUtils.getColor(COLOR_CANCEL)); GridData constraint = new GridData(SWT.FILL, SWT.FILL, false, true); parent.add(figure, constraint); eventHandler.attach(figure); return figure; } private Calendar getSelectedDate() { return selection; } private void changeCalendar(int newYear, int newMonth) { changeCalendar(newYear, newMonth, false); } private void changeCalendar(int newYear, int newMonth, boolean smooth) { boolean calendarChanged = newMonth != currentMonth || newYear != currentYear; if (!calendarChanged) return; if (smooth) { if (datePicker != null) { int months = (newYear - currentYear) * 12 + newMonth - currentMonth; animationAdvisor.addYearsToRoll(months / 12); animationAdvisor.addMonthsToRoll(months % 12); performRollCalendarAnimation(currentYear, currentMonth); } currentYear = newYear; currentMonth = newMonth; } else { currentYear = newYear; currentMonth = newMonth; if (datePicker != null) { updateCalendar(); } updateSelection(); } } private void updateCalendar() { today = Calendar.getInstance(); updateDayFigures(datePanel.getChildren(), currentYear, currentMonth); monthFigure.setMonth(currentMonth); yearFigure.setYear(currentYear); todayFigure.setText(Messages.TodayPattern); // todayFigure.setText(NLS.bind(Messages.TodayPattern, // String.format("%1$tb %1$te, %1$tY", today))); //$NON-NLS-1$ } private void updateDayFigures(List dayFigures, int year, int month) { Calendar date = getCalendarStart(today, year, month); for (int i = 0; i < dayFigures.size(); i++) { DayFigure dayFigure = (DayFigure) dayFigures.get(i); date = (Calendar) date.clone(); if (i > 0) date.add(DATE, 1); dayFigure.setDate(date); updateDayFigure(dayFigure, year, month); } } private void updateDayFigure(DayFigure figure, int year, int month) { figure.setFont(FontUtils.getBold(DEFAULT_FONT)); Calendar date = figure.getDate(); if (isSameDay(date, today)) { figure.setForegroundColor(ColorUtils.getColor(COLOR_TODAY)); } else if (isWeekend(date)) { figure.setForegroundColor(ColorUtils.getColor(COLOR_WEEKEND)); } else { figure.setForegroundColor(ColorUtils.getColor(COLOR_TEXT)); } if (date.get(MONTH) == month && date.get(YEAR) == year) { figure.setTextAlpha(NORMAL_ALPHA); } else { figure.setTextAlpha(SIBLING_MONTH_ALPHA); } } private void updateSelection() { if (datePicker != null) { for (Object figure : datePanel.getChildren()) { DayFigure dayFigure = (DayFigure) figure; dayFigure.setSelected( isSameDay(dayFigure.getDate(), getSelectedDate())); } } if (placeholder != null && selection != null) { String day = String.format("%1$td", selection); //$NON-NLS-1$ String month = String.format("%1$tm", selection); //$NON-NLS-1$ String year = String.format("%1$tY", selection); //$NON-NLS-1$ dayText.setText(day); monthText.setText(month); yearText.setText(year); } placeholder.layout(); } private void update() { if (datePicker != null) { updateCalendar(); } updateSelection(); } private void performRollCalendarAnimation(int oldYear, int oldMonth) { if (animating) return; animating = true; final int newYear, newMonth; final CalendarAnimation animation; if (animationAdvisor.getYearsToRoll() != 0) { if (animationAdvisor.getYearsToRoll() < 0) { newYear = oldYear - 1; newMonth = oldMonth; animation = new LastYearAnimation(animationAdvisor); } else { newYear = oldYear + 1; newMonth = oldMonth; animation = new NextYearAnimation(animationAdvisor); } } else if (animationAdvisor.getMonthsToRoll() != 0) { if (animationAdvisor.getMonthsToRoll() < 0) { newYear = oldMonth <= 0 ? oldYear - 1 : oldYear; newMonth = oldMonth <= 0 ? 11 : oldMonth - 1; animation = new LastMonthAnimation(animationAdvisor); } else { newYear = oldMonth >= 11 ? oldYear + 1 : oldYear; newMonth = oldMonth >= 11 ? 0 : oldMonth + 1; animation = new NextMonthAnimation(animationAdvisor); } } else { newYear = oldYear; newMonth = oldMonth; animation = null; } monthFigure.setMonth(newMonth); yearFigure.setYear(newYear); if (animation != null) { animationAdvisor.setStartMonth(oldYear, oldMonth); animationAdvisor.setEndMonth(newYear, newMonth); animation.callback(new Runnable() { public void run() { datePanel = animation.getNewPanel(); if (animationAdvisor.isDone()) { animating = false; } else { Display.getCurrent().asyncExec(new Runnable() { public void run() { animating = false; performRollCalendarAnimation(newYear, newMonth); } }); } } }).start(); } } private void selected(final BaseFigure target) { if (target instanceof MonthFigure) { target.setSelected(true); showMonthPopup(); } else if (target instanceof YearFigure) { target.setSelected(true); showYearPopup(); } else if (target == todayFigure) { todaySelected(); } else if (target == lastMonth) { lastMonthSelected(true); } else if (target == nextMonth) { nextMonthSelected(true); } else if (target == lastYear) { lastYearSelected(true); } else if (target == nextYear) { nextYearSelected(true); } else if (target == cancelFigure) { cancelSelected(); } } private void showMonthPopup() { createMonthMenu(); for (int month = 0; month < monthActions.length; month++) { MonthAction action = monthActions[month]; action.setChecked(month == currentMonth); } Rectangle b = monthFigure.getBounds(); Point loc = control.toDisplay(b.x, b.y + b.height); final Menu menu = monthMenu.createContextMenu(control); menu.setLocation(loc.x + 10, loc.y + 1); menu.setVisible(true); } private void createMonthMenu() { if (monthMenu != null) return; monthMenu = new MenuManager(); monthMenu.addMenuListener(new IMenuListener2() { public void menuAboutToShow(IMenuManager manager) { // do nothing } public void menuAboutToHide(IMenuManager manager) { monthFigure.setSelected(false); } }); monthActions = new MonthAction[12]; for (int month = 0; month < 12; month++) { MonthAction action = new MonthAction(month); monthMenu.add(action); monthActions[month] = action; } } private void showYearPopup() { createYearMenu(); int start = currentYear - PASSED_YEARS; for (int year = 0; year < yearActions.length; year++) { YearAction action = yearActions[year]; action.setYear(start + year); action.setChecked(action.getYear() == currentYear); } Rectangle b = yearFigure.getBounds(); Point loc = control.toDisplay(b.x, b.y + b.height); Menu menu = yearMenu.createContextMenu(control); menu.setLocation(loc.x, loc.y + 1); menu.setVisible(true); } private void createYearMenu() { if (yearMenu != null) return; yearMenu = new MenuManager(); yearMenu.addMenuListener(new IMenuListener2() { public void menuAboutToShow(IMenuManager manager) { // do nothing } public void menuAboutToHide(IMenuManager manager) { yearFigure.setSelected(false); } }); yearActions = new YearAction[FUTURE_YEARS + PASSED_YEARS + 1]; for (int year = 0; year < yearActions.length; year++) { YearAction action = new YearAction(); yearMenu.add(action); yearActions[year] = action; } } private void monthSelected(int month) { changeCalendar(currentYear, month); } private void yearSelected(int year) { changeCalendar(year, currentMonth); } private void daySelected(DayFigure day) { Calendar date = day.getDate(); changeDate(date); if (date != null && date.get(MONTH) != currentMonth) { changeCalendar(date.get(YEAR), date.get(MONTH), true); } } private void todaySelected() { changeDate(today); changeCalendar(today.get(YEAR), today.get(MONTH)); } private void lastMonthSelected(boolean smooth) { if (currentMonth <= 0) { changeCalendar(currentYear - 1, 11, smooth); } else { changeCalendar(currentYear, currentMonth - 1, smooth); } } private void nextMonthSelected(boolean smooth) { if (currentMonth >= 11) { changeCalendar(currentYear + 1, 0, smooth); } else { changeCalendar(currentYear, currentMonth + 1, smooth); } } private void lastYearSelected(boolean smooth) { changeCalendar(currentYear - 1, currentMonth, smooth); } private void nextYearSelected(boolean smooth) { changeCalendar(currentYear + 1, currentMonth, smooth); } private void cancelSelected() { changeDate(null); } private void rollMonth(int count) { Calendar temp = (Calendar) today.clone(); temp.set(YEAR, currentYear); temp.set(MONTH, currentMonth); temp.add(MONTH, count); changeCalendar(temp.get(YEAR), temp.get(MONTH), true); } private void changeDate(Calendar date) { if (date != null) { date.set(Calendar.MILLISECOND, 0); } this.selection = date; updateSelection(); if (firingSelectionChange) return; firingSelectionChange = true; if (dropdownDatePicker != null) { control.getDisplay().asyncExec(new Runnable() { public void run() { dropdownDatePicker.close(); } }); } fireSelectionChanged( new SelectionChangedEvent(DatePicker2.this, getSelection())); firingSelectionChange = false; } @Override public Object getInput() { return today; } @Override public void refresh() { update(); } @Override public void setInput(Object input) { if (input instanceof Calendar) { Calendar oldInput = this.today; this.today = (Calendar) input; inputChanged(input, oldInput); } } @Override protected void inputChanged(Object input, Object oldInput) { update(); } public void setMinTime(Calendar minTime) { if (minTime != null) { minTime.set(Calendar.MILLISECOND, 0); this.minTime = minTime; } } public Calendar getMinTime() { return minTime; } public void setMaxTime(Calendar maxTime) { if (maxTime != null) { maxTime.set(Calendar.MILLISECOND, 0); this.maxTime = maxTime; } } public Calendar getMaxTime() { return maxTime; } private static boolean isWeekend(Calendar date) { int dow = date.get(DAY_OF_WEEK); return dow == SUNDAY || dow == SATURDAY; } private static Calendar getCalendarStart(Calendar date, int year, int month) { date = (Calendar) date.clone(); date.set(year, month, 1); while (date.get(DAY_OF_WEEK) != SUNDAY) { date.add(DATE, -1); } return date; } private static boolean isSameDay(Calendar date1, Calendar date2) { if (date1 == null) return date2 == null; if (date2 == null) return false; return date1.get(DATE) == date2.get(DATE) && date1.get(MONTH) == date2.get(MONTH) && date1.get(YEAR) == date2.get(YEAR); } }