[
Date Prev][
Date Next][
Thread Prev][
Thread Next][
Date Index][
Thread Index]
[
List Home]
[nebula-dev] Modification request to CalendarCombo project
|
Hi,
My name is Steeve. I work for Logiciels INFO-DATA company. For our
application, we improved some classes of CalendarCombo project. Knowing
liscencing, we want to share our improvement with community. I join our
code in this mail hoping it will be added to CalendarCombo project. If
our code does not exactly fit, please let us know what we should change
so it fit.
In details,
we fixed what seemed a bug in
org.eclipse.nebula.widgets.calendarcombo.DateHelper.class parse method.
Parse method didnt parse with additionalDateFormats parameter. Now it is.
we added org.eclipse.nebula.widgets.calendarcombo.IDateFactory.cass
interface that is used by CalendarComposit2
we added org.eclipse.nebula.widgets.calendarcombo.CalendarFactory.cass
an IDateFactory implementation with Calendar
we added
org.eclipse.nebula.widgets.calendarcombo.CalendarComposite2.class which
is copy of CalendarComposite with less feature and that is really
independant, as CalendarComposite is too much linked with CalendarCombo.
CalendarComposite2 allow user to use its own date class via IDateFactory
interface.
Thanks for this great products
Steeve
Logiciels INFO-DATA
Index: src/org/eclipse/nebula/widgets/calendarcombo/DateHelper.java
===================================================================
RCS file: /cvsroot/technology/org.eclipse.swt.nebula/org.eclipse.nebula.widgets.calendarcombo/src/org/eclipse/nebula/widgets/calendarcombo/DateHelper.java,v
retrieving revision 1.14
diff -u -r1.14 DateHelper.java
--- src/org/eclipse/nebula/widgets/calendarcombo/DateHelper.java 31 Aug 2009 10:40:38 -0000 1.14
+++ src/org/eclipse/nebula/widgets/calendarcombo/DateHelper.java 11 Nov 2010 18:32:56 -0000
@@ -149,15 +149,57 @@
public static Calendar parse(final String comboText, final Locale locale, final String dateFormat, final char[] acceptedSeparatorChars, final List additionalDateFormats)
throws CalendarDateParseException, Exception {
+
+ Calendar parsed = null;
+ Exception ex = null;
+
boolean isNumeric = comboText.replaceAll("[^0-9]", "").length() == comboText.length();
if (isNumeric) {
- return numericParse(comboText, locale, false);
+ try {
+ parsed = numericParse(comboText, locale, false);
+ } catch (Exception e) {
+ //maybe dont care
+ ex = ex==null?e:ex;
+ }
}
- else {
- return slashParse(comboText, dateFormat, acceptedSeparatorChars, locale);
+
+ if (parsed == null){
+ try {
+ parsed = slashParse(comboText, dateFormat, acceptedSeparatorChars, locale);
+ } catch (Exception e) {
+ //maybe dont care
+ ex = ex==null?e:ex;
+ }
+ }
+
+ if (parsed == null){
+ if (additionalDateFormats != null) {
+// final Calendar today = Calendar.getInstance(locale);
+ for (int i = 0; i < additionalDateFormats.size(); i++) {
+ try {
+ String format = (String) additionalDateFormats.get(i);
+ Date date = DateHelper.getDate(comboText, format);
+ if (date != null){
+ parsed = calendarize(date, locale);
+ break;
+ }
+ }
+ catch (Exception e) {
+ //maybe dont care
+ ex = ex==null?e:ex;
+ }
+ }
+ }
+ }
+
+ if (parsed == null){
+ //no more try
+ throw ex;
}
+ return parsed;
+
//return null;
//}
Index: src/org/eclipse/nebula/widgets/calendarcombo/IDateFactory.java
===================================================================
RCS file: src/org/eclipse/nebula/widgets/calendarcombo/IDateFactory.java
diff -N src/org/eclipse/nebula/widgets/calendarcombo/IDateFactory.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/nebula/widgets/calendarcombo/IDateFactory.java 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,161 @@
+/*******************************************************************************
+ * Copyright (c) Steeve St-Laurent - www.infodata.ca - sstlaurent@xxxxxxxxxxx
+ * 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:
+ * sstlaurent@xxxxxxxxxxx - initial API
+ *******************************************************************************/
+
+package org.eclipse.nebula.widgets.calendarcombo;
+
+import java.util.Locale;
+
+/**
+ * a factory used to create "date implementation independant" components
+ */
+public interface IDateFactory {
+
+ /**
+ * @return never null
+ */
+ Locale getLocale();
+
+ /**
+ * create a new date instance
+ * @param day start from 1 to month end
+ * @param month start from 1 to 12
+ * @param year
+ * @return never null
+ * @throws NullPointerException if date is invalid
+ */
+ Object newDate(int day, int month, int year);
+ /**
+ * clear date instance
+ * @param date cant be null
+ * @return cleared date, never null, may or may not return same instance as param date
+ * @throws NullPointerException if date is null
+ */
+ Object clear(Object date);
+ /**
+ * create new today date instance
+ * @return today date, never null
+ */
+ Object today();
+ /**
+ * add x day to date instance
+ * @param date cant be null
+ * @param day
+ * @return modifed date, never null, may or may not return same instance as param date
+ * @throws NullPointerException if date is null
+ */
+ Object plusDay(Object date, int day);
+ /**
+ * add x month to date instance
+ * @param date cant be null
+ * @param month
+ * @return modifed date, never null, may or may not return same instance as param date
+ * @throws NullPointerException if date is null
+ */
+ Object plusMonth(Object date, int month);
+ /**
+ * add x year to date instance
+ * @param date cant be null
+ * @param year
+ * @return modifed date, never null, may or may not return same instance as param date
+ * @throws NullPointerException if date is null
+ */
+ Object plusYear(Object date, int year);
+ /**
+ * set date instance day to x
+ * @param date cant be null
+ * @param day, start from 1 to monthEnd
+ * @return modifed date, never null, may or may not return same instance as param date
+ * @throws NullPointerException if date is null or day is invalid
+ */
+ Object withDay(Object date, int day);
+ /**
+ * set date instance year to x
+ * @param date cant be null
+ * @param year
+ * @return modifed date, never null, may or may not return same instance as param date
+ * @throws NullPointerException if date is null
+ */
+ Object withYear(Object date, int year);
+ /**
+ * set date instance month to x
+ * @param date cant be null
+ * @param month, start from 1 to 12
+ * @return modifed date, never null, may or may not return same instance as param date
+ * @throws NullPointerException if date is null or month is invalid
+ */
+ Object withMonth(Object date, int month);
+
+ /**
+ * set date instance dayOfWeek to x
+ * @param date cant be null
+ * @param dayOfWeek, start from 1 to 7, sunday to saturday
+ * @return modifed date, never null, may or may not return same instance as param date
+ * @throws NullPointerException if date is null or month is invalid
+ */
+ Object withDayOfWeek(Object date, int dayOfWeek);
+
+ /**
+ * @param date cant be null
+ * @return date day, start from 1 to 31
+ * @throws NullPointerException if date is null
+ */
+ int getDay(Object date);
+ /**
+ * @param date cant be null
+ * @return date month, start from 1 to 12
+ * @throws NullPointerException if date is null
+ */
+ int getMonth(Object date);
+ /**
+ * @param date cant be null
+ * @return date year
+ * @throws NullPointerException if date is null
+ */
+ int getYear(Object date);
+ /**
+ * @param date cant be null
+ * @return date day of week, start from 1 to 7, sunday to saturday
+ * @throws NullPointerException if date is null
+ */
+ int getDayOfWeek(Object date);
+
+ /**
+ * @param date1 can be null
+ * @param date2 can be null
+ * @return true if date1 equals date2
+ */
+ boolean equals(Object date1, Object date2);
+
+ /**
+ * @param date cant be null
+ * @return a clone of date
+ * @throws NullPointerException if date is null
+ */
+ Object clone(Object date);
+
+ /**
+ * clone date1 instance to date2 instance
+ * @param date1 cant be null
+ * @param date2 cant be null
+ * @return date2
+ * @throws NullPointerException if date is null
+ */
+ Object clone(Object date1, Object date2);
+
+ /**
+ * Gets what the first day of the week is; e.g., <code>SUNDAY</code> in the U.S.,
+ * <code>MONDAY</code> in France.
+ *
+ * @return the first day of the week. 1 to 7, sunday to saturday
+ */
+ int getFirstDayOfWeek();
+
+}
Index: src/org/eclipse/nebula/widgets/calendarcombo/CalendarFactory.java
===================================================================
RCS file: src/org/eclipse/nebula/widgets/calendarcombo/CalendarFactory.java
diff -N src/org/eclipse/nebula/widgets/calendarcombo/CalendarFactory.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/nebula/widgets/calendarcombo/CalendarFactory.java 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,163 @@
+/*******************************************************************************
+ * Copyright (c) Steeve St-Laurent - www.infodata.ca - sstlaurent@xxxxxxxxxxx
+ * 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:
+ * sstlaurent@xxxxxxxxxxx - initial implementations
+ *******************************************************************************/
+
+package org.eclipse.nebula.widgets.calendarcombo;
+
+import java.util.Calendar;
+import java.util.Locale;
+
+/**
+ * a factory that work on calendar
+ */
+public class CalendarFactory implements IDateFactory {
+
+ private final Locale locale;
+ private final int firstDayOfWeek;
+
+ /**
+ * @param new factory with locale set to {@link Locale#getDefault()}
+ */
+ public CalendarFactory() {
+ this(null);
+ }
+
+ /**
+ * @param locale can be null, default to {@link Locale#getDefault()}
+ */
+ public CalendarFactory(Locale locale) {
+ if (locale == null) {
+ locale = Locale.getDefault();
+ }
+ this.locale = locale;
+ this.firstDayOfWeek = ((Calendar)today()).getFirstDayOfWeek();
+ }
+
+ public Object plusDay(Object date, int day) {
+ Calendar c = ((Calendar)date);
+ c.add(Calendar.DATE, day);
+ return c;
+ }
+
+ public Object plusMonth(Object date, int month) {
+ Calendar c = ((Calendar)date);
+ c.add(Calendar.MONTH, month);
+ return c;
+ }
+
+ public Object plusYear(Object date, int year) {
+ Calendar c = ((Calendar)date);
+ c.add(Calendar.YEAR, year);
+ return c;
+ }
+
+ public Object clear(Object date) {
+ Calendar c = ((Calendar)date);
+ c.clear();
+ return c;
+ }
+
+ public boolean equals(Object date1, Object date2) {
+ if (date1 == date2) {
+ return true;
+ }
+ if (date1 == null || date2 == null) {
+ return false;
+ }
+ return DateHelper.sameDate((Calendar)date1, (Calendar)date2);
+ }
+
+ public int getDay(Object date) {
+ Calendar c = ((Calendar)date);
+ return c.get(Calendar.DATE);
+ }
+
+ public int getDayOfWeek(Object date) {
+ Calendar c = ((Calendar)date);
+ return c.get(Calendar.DAY_OF_WEEK);
+ }
+
+ public int getMonth(Object date) {
+ Calendar c = ((Calendar)date);
+ return c.get(Calendar.MONTH) + 1;
+ }
+
+ public int getYear(Object date) {
+ Calendar c = ((Calendar)date);
+ return c.get(Calendar.YEAR);
+ }
+
+ public Object newDate(int day, int month, int year) {
+ Calendar c = Calendar.getInstance(this.locale);
+ c.set(Calendar.YEAR, year);
+ c.set(Calendar.MONTH, month-1);
+ c.set(Calendar.DATE, day);
+ return toMidnight(c);
+ }
+
+ private static Calendar toMidnight(Calendar c){
+ c.set(Calendar.HOUR, 0);
+ c.set(Calendar.HOUR_OF_DAY, 0);
+ c.set(Calendar.MINUTE, 0);
+ c.set(Calendar.SECOND, 0);
+ c.set(Calendar.MILLISECOND, 0);
+ return c;
+ }
+
+ public Object today() {
+ return toMidnight(Calendar.getInstance(this.locale));
+ }
+
+ public Object withDay(Object date, int day) {
+ Calendar c = ((Calendar)date);
+ c.set(Calendar.DAY_OF_MONTH, day);
+ return c;
+ }
+
+ public Object withMonth(Object date, int month) {
+ Calendar c = ((Calendar)date);
+ c.set(Calendar.MONTH, month-1);
+ return c;
+ }
+
+ public Object withYear(Object date, int year) {
+ Calendar c = ((Calendar)date);
+ c.set(Calendar.YEAR, year);
+ return c;
+ }
+
+ public Object withDayOfWeek(Object date, int dayOfWeek) {
+ Calendar c = ((Calendar)date);
+ c.set(Calendar.DAY_OF_WEEK, dayOfWeek);
+ return c;
+ }
+
+ public Object clone(Object date) {
+ Calendar c = ((Calendar)date);
+ return c.clone();
+ }
+
+ public int getFirstDayOfWeek() {
+ return this.firstDayOfWeek;
+ }
+
+ public Object clone(Object date1, Object date2) {
+ Calendar c1 = ((Calendar)date1);
+ Calendar c2 = ((Calendar)date2);
+ c2.set(Calendar.YEAR, c1.get(Calendar.YEAR));
+ c2.set(Calendar.MONTH, c1.get(Calendar.MONTH));
+ c2.set(Calendar.DATE, c1.get(Calendar.DATE));
+ return toMidnight(c2);
+ }
+
+ public Locale getLocale() {
+ return this.locale;
+ }
+}
Index: src/org/eclipse/nebula/widgets/calendarcombo/CalendarComposite2.java
===================================================================
RCS file: src/org/eclipse/nebula/widgets/calendarcombo/CalendarComposite2.java
diff -N src/org/eclipse/nebula/widgets/calendarcombo/CalendarComposite2.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/nebula/widgets/calendarcombo/CalendarComposite2.java 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,829 @@
+/*******************************************************************************
+ * Copyright (c) Emil Crumhorn - Hexapixel.com - emil.crumhorn@xxxxxxxxx
+ * Copyright (c) Steeve St-Laurent - www.infodata.ca - sstlaurent@xxxxxxxxxxx
+ * 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:
+ * emil.crumhorn@xxxxxxxxx - initial API and implementation of CalendarComposite
+ * sstlaurent@xxxxxxxxxxx - copy and epuration of CalendarComposite
+ *******************************************************************************/
+
+package org.eclipse.nebula.widgets.calendarcombo;
+
+
+import java.text.DateFormatSymbols;
+import java.util.ArrayList;
+import java.util.Locale;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+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.events.PaintListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Canvas;
+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.Layout;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * this class does not support {@link ISettings#showMonthPickerOnMonthNameMousePress()} because if does not support month picker<br>
+ * this class does not support {@link IColorManager#getDisabledDayForegroundColor()} because it does not support disallowed dates<br>
+ * this class is really independant from {@link CalendarCombo}<br>
+ * this class allow user to use its own date class via {@link IDateFactory} interface
+ */
+public class CalendarComposite2 extends Canvas implements MouseListener, MouseMoveListener {
+
+ public static interface IDateListener {
+ /**
+ * @see IDateFactory for date type
+ * @param date
+ */
+ void dateChanged(Object date);
+ }
+
+ private static final boolean OS_CARBON = "carbon".equals(SWT.getPlatform());
+ private static final boolean OS_GTK = "gtk".equals(SWT.getPlatform());
+ private static final boolean OS_WINDOWS = "win32".equals(SWT.getPlatform());
+
+ private final int ARROW_LEFT = 1;
+
+ private final int ARROW_RIGHT = 2;
+
+ private Button mButtonToday;
+
+ private Button mButtonNone;
+
+ private Rectangle mLeftArrowBounds;
+
+ private Rectangle mRightArrowBounds;
+
+ private Object mCalendar;
+
+ private Object mToday;
+
+ private int mDatesTopY = 0;
+
+ private int mDayXs[] = new int[7];
+
+ private CalDay[] mDays = new CalDay[7 * 6];
+
+ private Object mSelectedDay;
+
+ private static DateFormatSymbols mDFS;
+
+ private String mMonths[];
+
+ private static String[] mDayTitles = null;
+
+ private ArrayList mListeners;
+
+ private boolean mNoDayClicked;
+
+ // this thread deals with holding down the mouse over the arrows to jump
+ // months quickly
+
+ // milliseconds
+ private static final int ARROW_SLOW_TIME = 300;
+
+ private static final int ARROW_FAST_TIME = 120;
+
+ // after how many iterations do we switch speed? (after how many months
+ // flipped-by)
+ private static final int ARROW_SPEED_SWITCH_COUNT = 10;
+
+ // default speed
+ private int mArrowSleepTime = ARROW_SLOW_TIME;
+
+ // various variables used to keep track
+ private int mArrowIterations = 0;
+
+ private Thread mArrowThread;
+
+ private boolean mArrowRun;
+
+ private boolean mArrowPause;
+
+ private int mArrowThreadDirection = 0;
+
+ private final IColorManager mColorManager;
+
+ private final ISettings mSettings;
+
+ private final IDateFactory mDateFactory;
+
+ public CalendarComposite2(Composite parent, IColorManager colorManager, ISettings settings, IDateFactory dateFactory) {
+ super(parent, SWT.NO_BACKGROUND | SWT.DOUBLE_BUFFERED);
+ this.mColorManager = colorManager;
+ this.mSettings = settings;
+ this.mDateFactory = dateFactory == null ? new CalendarFactory(this.mSettings == null ? null : this.mSettings.getLocale()) : dateFactory;
+
+ init();
+
+ build();
+ }
+
+ private void init() {
+ mDFS = new DateFormatSymbols(this.mDateFactory.getLocale());
+ this.mMonths = mDFS.getMonths();
+
+ if (this.mCalendar == null) {
+ this.mCalendar = this.mDateFactory.today();
+ }
+ if (this.mToday == null) {
+ this.mToday = this.mDateFactory.today();
+ }
+
+ String[] weekdays = mDFS.getWeekdays();
+ mDayTitles = new String[weekdays.length];
+ for (int i = 0; i < weekdays.length; i++) {
+ String weekday = weekdays[i];
+ if (weekday.length() > 0) {
+ mDayTitles[i] = weekday.substring(0, 1).toUpperCase();
+ }
+ }
+
+ //will be reused in draw days methods
+ for (int i = 0; i < 42; i++) {
+ this.mDays[i] = new CalDay(i, this.mDateFactory.today(), new Rectangle(0,0,0,0));
+ }
+
+ }
+
+ private void build() {
+ // button height & width and spacers
+ int bheight = this.mSettings.getButtonHeight();
+ int bwidth = this.mSettings.getButtonWidth();
+ int buttonStyle = SWT.PUSH;
+ this.mListeners = new ArrayList();
+
+ // Mac buttons need different flag to look normal
+ if (OS_CARBON) {
+ bheight = this.mSettings.getCarbonButtonHeight();
+ bwidth = this.mSettings.getButtonWidthCarbon();
+ buttonStyle = SWT.FLAT;
+ }
+
+ setLayout(new ButtonSectionLayout());
+
+ addPaintListener(new PaintListener() {
+ public void paintControl(PaintEvent event) {
+ paint(event);
+ }
+ });
+
+ addMouseListener(this);
+ addMouseMoveListener(this);
+ addKeyListener(new KeyListener() {
+ public void keyReleased(KeyEvent e) {
+ }
+ public void keyPressed(KeyEvent e) {
+ CalendarComposite2.this.keyPressed(e.keyCode, e.stateMask);
+ }
+ });
+
+ this.mButtonToday = new Button(this, buttonStyle | SWT.NO_FOCUS);
+ this.mButtonToday.setText(this.mSettings.getTodayText());
+ this.mButtonToday.setLayoutData(new GridData(bwidth, bheight));
+ this.mButtonToday.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event event) {
+ clickedTodayButton();
+ }
+ });
+
+ this.mButtonNone = new Button(this, buttonStyle | SWT.NO_FOCUS);
+ this.mButtonNone.setText(this.mSettings.getNoneText());
+ this.mButtonNone.setLayoutData(new GridData(bwidth, bheight));
+ this.mButtonNone.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event event) {
+ clickedNoneButton();
+ }
+ });
+ }
+
+ private void clickedTodayButton() {
+ setReferenceDate(this.mDateFactory.today());
+ this.mSelectedDay = this.mDateFactory.today();
+ notifyListeners();
+ }
+
+ private void clickedNoneButton() {
+ selectDate(null);
+ notifyListeners();
+ }
+
+ private void paint(PaintEvent event) {
+ GC gc = event.gc;
+ drawChartOntoGC(gc);
+ }
+
+ private void drawChartOntoGC(GC gc) {
+ Rectangle bounds = super.getBounds();
+ gc.setBackground(this.mColorManager.getCalendarBackgroundColor());
+ gc.fillRectangle(bounds);
+
+ Font used = null;
+ if (OS_CARBON) {
+ used = this.mSettings.getCarbonDrawFont();
+ if (used != null) {
+ gc.setFont(used);
+ }
+ }
+
+ // header
+ drawHeader(gc);
+ // day titles
+ drawTitleDays(gc);
+ // days
+ drawDays(gc);
+ // 1 pixel border
+ drawBorder(gc);
+
+ gc.dispose();
+ if (used != null) {
+ used.dispose();
+ }
+ }
+
+ public void setReferenceDate(Object date) {
+ this.mCalendar = date == null ? this.mDateFactory.today() : this.mDateFactory.clone(date);
+ redraw();
+ }
+
+ public void selectDate(Object date){
+ this.mSelectedDay = date == null ? null : this.mDateFactory.clone(date);
+ redraw();
+ }
+
+ public void nextMonth() {
+ this.mSelectedDay = null;
+ this.mCalendar = this.mDateFactory.plusMonth(this.mCalendar, 1);
+ this.mCalendar = this.mDateFactory.withDay(this.mCalendar, 1);
+ redraw();
+ }
+
+ public void prevMonth() {
+ this.mSelectedDay = null;
+ this.mCalendar = this.mDateFactory.plusMonth(this.mCalendar, -1);
+ this.mCalendar = this.mDateFactory.withDay(this.mCalendar, 1);
+ redraw();
+ }
+
+ public void goToToday() {
+ this.mSelectedDay = null;
+ this.mCalendar = this.mDateFactory.today();
+ redraw();
+ }
+
+ // draws 1 pixel border around entire calendar
+ private void drawBorder(GC gc) {
+ Rectangle bounds = super.getBounds();
+ gc.setForeground(this.mColorManager.getCalendarBorderColor());
+ gc.drawRectangle(bounds.x, bounds.y, bounds.width - 1, bounds.height - 1);
+ }
+
+ private void drawHeader(GC gc) {
+ Rectangle bounds = super.getBounds();
+ Rectangle bgRect = new Rectangle(bounds.x + this.mSettings.getHeaderLeftMargin(), bounds.y + this.mSettings.getHeaderTopMargin(), this.mSettings.getCalendarWidth() - 13, bounds.y
+ + this.mSettings.getHeaderHeight());
+ gc.setBackground(this.mColorManager.getCalendarHeaderColor());
+ gc.fillRectangle(bgRect);
+ drawArrow(gc, bounds.x + this.mSettings.getHeaderLeftMargin() + this.mSettings.getArrowLeftSpacing() + 1, bounds.y + this.mSettings.getHeaderTopMargin() + this.mSettings.getArrowTopSpacing()
+ + 4, this.ARROW_LEFT);
+ drawArrow(gc, bounds.x + this.mSettings.getCalendarWidth() - 13 - this.mSettings.getHeaderRightMargin(), bounds.y + this.mSettings.getHeaderTopMargin() + this.mSettings.getArrowTopSpacing()
+ + 4, this.ARROW_RIGHT);
+
+ String toDraw = this.mMonths[this.mDateFactory.getMonth(this.mCalendar)-1] + " " + this.mDateFactory.getYear(this.mCalendar);
+ Point strWidth = gc.stringExtent(toDraw);
+
+ int avail = this.mSettings.getCalendarWidth() - 13 - strWidth.x;
+ avail /= 2;
+
+ gc.drawString(toDraw, bounds.x + this.mSettings.getHeaderLeftMargin() + avail, bounds.y + this.mSettings.getHeaderTopMargin() + 1, true);
+ }
+
+ private void drawTitleDays(GC gc) {
+ // fetch the first day of the week, and draw starting on that day
+ int fdow = this.mDateFactory.getFirstDayOfWeek();
+
+ Rectangle bounds = super.getBounds();
+ int xStart = this.mSettings.getDatesLeftMargin() + 5;
+ int yStart = bounds.y + this.mSettings.getHeaderTopMargin() + this.mSettings.getHeaderHeight() + 1;
+
+ int spacer = 0;
+ int letterHeight = 0;
+
+ for (int i = 0; i < 7; i++) {
+ Point strWidth = gc.stringExtent(mDayTitles[fdow]);
+
+ int x = xStart + this.mSettings.getOneDateBoxSize() + spacer - strWidth.x;
+ // don't add the string width, as our string width later when
+ // drawing days will differ
+ this.mDayXs[i] = xStart + this.mSettings.getOneDateBoxSize() + spacer;
+
+ gc.drawString(mDayTitles[fdow], x, yStart, true);
+
+ letterHeight = strWidth.y;
+ spacer += this.mSettings.getOneDateBoxSize() + this.mSettings.getBoxSpacer();
+
+ fdow++;
+ if (fdow > 7) {
+ fdow = 1;
+ }
+ }
+
+ int lineStart = yStart + 1 + letterHeight;
+ gc.setForeground(this.mColorManager.getLineColor());
+ gc.drawLine(this.mSettings.getDatesLeftMargin() + 1, lineStart, bounds.width - this.mSettings.getDatesRightMargin() - 3, lineStart);
+
+ this.mDatesTopY = lineStart + 3;
+ }
+
+ private void drawDays(GC gc) {
+ gc.setBackground(this.mColorManager.getCalendarBackgroundColor());
+ gc.setForeground(this.mColorManager.getTextColor());
+
+ Rectangle bounds = super.getBounds();
+ int spacer = 0;
+
+// debug("mCalendar", this.mCalendar);
+ Object start = this.mDateFactory.clone(this.mCalendar);
+// debug("start", start);
+ start = this.mDateFactory.withDay(start, 1);
+// debug("start", start);
+ start = this.mDateFactory.withDayOfWeek(start, 1);
+// debug("start", start);
+
+ Object date = start;
+
+ final int monthToShow = this.mDateFactory.getMonth(this.mCalendar);
+
+ int col = 0;
+ int colCount = 0;
+
+ String typicalLetter = "8";
+ int strHeight = gc.stringExtent(typicalLetter).y;
+
+ gc.setForeground(this.mColorManager.getLineColor());
+
+ int lastY = 0;
+
+ for (int y = 0; y < 42; y++) {
+
+// debug(date);
+
+ // new row
+ if (y % 7 == 0 && y != 0) {
+ spacer += strHeight + 2;
+ }
+
+ if (colCount > 6) {
+ colCount = 0;
+ col = 0;
+ }
+
+ if (this.mDateFactory.getMonth(date) == monthToShow) {
+ gc.setForeground(this.mColorManager.getTextColor());
+ } else {
+ gc.setForeground(this.mColorManager.getPreviousAndNextMonthForegroundColor());
+ }
+
+ String dateStr = String.valueOf(this.mDateFactory.getDay(date));
+ Point width = gc.stringExtent(dateStr);
+
+ if (this.mDateFactory.equals(this.mSelectedDay, date)) {
+ gc.setBackground(this.mColorManager.getSelectedDayColor());
+ gc.fillRectangle(this.mDayXs[col] - this.mSettings.getOneDateBoxSize() - 4, this.mDatesTopY + spacer - 1, this.mSettings.getOneDateBoxSize() + 5, 14);
+ gc.setBackground(this.mColorManager.getCalendarBackgroundColor());
+ }
+
+ this.mDays[y].bounds.x = this.mDayXs[col] - this.mSettings.getOneDateBoxSize() - 4;
+ this.mDays[y].bounds.y = this.mDatesTopY + spacer - 1;
+ this.mDays[y].bounds.width = this.mSettings.getOneDateBoxSize() + 5;
+ this.mDays[y].bounds.height = 14;
+ this.mDays[y].number = y;
+ this.mDays[y].date = this.mDateFactory.clone(date, this.mDays[y].date);
+
+ gc.drawString(dateStr, this.mDayXs[col] - width.x, this.mDatesTopY + spacer, true);
+
+ if (this.mDateFactory.equals(this.mToday, date)) {
+ Color old = gc.getForeground();
+ gc.setForeground(this.mColorManager.getSelectedDayBorderColor());
+ gc.drawRectangle(this.mDayXs[col] - this.mSettings.getOneDateBoxSize() - 4, this.mDatesTopY + spacer - 1, this.mSettings.getOneDateBoxSize() + 5, 14);
+ gc.setForeground(old);
+ }
+
+ date = this.mDateFactory.plusDay(date, 1);
+ col++;
+ colCount++;
+ lastY = this.mDatesTopY + spacer;
+ }
+
+ lastY += strHeight + 1;
+
+ gc.setForeground(this.mColorManager.getLineColor());
+ gc.drawLine(this.mSettings.getDatesLeftMargin() + 1, lastY, bounds.width - this.mSettings.getDatesRightMargin() - 3, lastY);
+ }
+
+ private void debug(Object date) {
+ System.out.println(this.mDateFactory.getDay(date) + "-"+this.mDateFactory.getMonth(date) +"-"+this.mDateFactory.getYear(date));
+ }
+ private void debug(String text, Object date) {
+ System.out.println(text + " " + this.mDateFactory.getDay(date) + "-"+this.mDateFactory.getMonth(date) +"-"+this.mDateFactory.getYear(date));
+ }
+
+ private void drawArrow(GC gc, int x, int y, int style) {
+ gc.setForeground(this.mColorManager.getArrowColor());
+ switch (style) {
+ case ARROW_RIGHT:
+ gc.drawLine(x, y - 4, x, y + 4);
+ gc.drawLine(x + 1, y - 3, x + 1, y + 3);
+ gc.drawLine(x + 2, y - 2, x + 2, y + 2);
+ gc.drawLine(x + 3, y - 1, x + 3, y + 1);
+ gc.drawLine(x + 4, y, x + 4, y);
+ this.mRightArrowBounds = new Rectangle(x - 4, y - 4, x + 8, y);
+ break;
+ case ARROW_LEFT:
+ gc.drawLine(x - 1, y, x - 1, y);
+ gc.drawLine(x, y - 1, x, y + 1);
+ gc.drawLine(x + 1, y - 2, x + 1, y + 2);
+ gc.drawLine(x + 2, y - 3, x + 2, y + 3);
+ gc.drawLine(x + 3, y - 4, x + 3, y + 4);
+ this.mLeftArrowBounds = new Rectangle(x - 4, y - 4, x + 8, y);
+ break;
+ }
+ }
+
+ private static boolean isInside(int x, int y, Rectangle rect) {
+ if (rect == null) {
+ return false;
+ }
+
+ if (x >= rect.x && y >= rect.y && x <= (rect.x + rect.width) && y <= (rect.y + rect.height)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ public void mouseMove(MouseEvent e) {
+
+ // "dragging" the day, just paint it
+ if (e.stateMask != 0) {
+ doDaySelection(e.x, e.y);
+ }
+
+ if (this.mArrowRun && this.mArrowThread != null) {
+ if (!isInside(e.x, e.y, this.mLeftArrowBounds) && !isInside(e.x, e.y, this.mRightArrowBounds)) {
+ this.mArrowPause = true;
+ // also pause the speed
+ this.mArrowIterations = 0;
+ this.mArrowSleepTime = ARROW_SLOW_TIME;
+ } else {
+ if (isInside(e.x, e.y, this.mLeftArrowBounds)) {
+ this.mArrowThreadDirection = this.ARROW_LEFT;
+ } else {
+ this.mArrowThreadDirection = this.ARROW_RIGHT;
+ }
+
+ this.mArrowPause = false;
+ }
+ }
+ }
+
+ public void mouseDoubleClick(MouseEvent event) {
+ }
+
+ // draw the date selection on mouse down to give that flicker of "response"
+ // to the user
+ public void mouseDown(MouseEvent event) {
+
+ if (isInside(event.x, event.y, this.mLeftArrowBounds)) {
+ prevMonth();
+
+ runArrowThread(this.ARROW_LEFT);
+
+ return;
+ }
+
+ if (isInside(event.x, event.y, this.mRightArrowBounds)) {
+ nextMonth();
+
+ runArrowThread(this.ARROW_RIGHT);
+
+ return;
+ }
+
+ doDaySelection(event.x, event.y);
+
+ }
+
+ private void killArrowThread() {
+ if (this.mArrowThread != null) {
+ this.mArrowPause = true;
+ this.mArrowThreadDirection = 0;
+ this.mArrowRun = false;
+ this.mArrowThread = null;
+ }
+ }
+
+ private void runArrowThread(int direction) {
+ this.mArrowThreadDirection = direction;
+ this.mArrowIterations = 0;
+ this.mArrowSleepTime = ARROW_SLOW_TIME;
+ this.mArrowRun = false;
+ this.mArrowPause = false;
+ this.mArrowThread = new Thread() {
+ public void run() {
+ while (CalendarComposite2.this.mArrowRun) {
+ try {
+ sleep(CalendarComposite2.this.mArrowSleepTime);
+
+ if (!CalendarComposite2.this.mArrowPause) {
+ CalendarComposite2.this.mArrowIterations++;
+ }
+
+ if (CalendarComposite2.this.mArrowIterations > ARROW_SPEED_SWITCH_COUNT && CalendarComposite2.this.mArrowSleepTime != ARROW_FAST_TIME) {
+ CalendarComposite2.this.mArrowSleepTime = ARROW_FAST_TIME;
+ }
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ if (!CalendarComposite2.this.mArrowPause) {
+ Display.getDefault().syncExec(new Runnable() {
+ public void run() {
+ if (isDisposed()) {
+ CalendarComposite2.this.mArrowRun = false;
+ CalendarComposite2.this.mArrowThread = null;
+ } else {
+ if (CalendarComposite2.this.mArrowThreadDirection == CalendarComposite2.this.ARROW_LEFT) {
+ prevMonth();
+ } else if (CalendarComposite2.this.mArrowThreadDirection == CalendarComposite2.this.ARROW_RIGHT) {
+ nextMonth();
+ }
+ }
+ }
+ });
+ }
+ }
+ }
+ };
+ this.mArrowPause = false;
+ this.mArrowRun = true;
+ this.mArrowThread.start();
+ }
+
+ // selects a day at x, y if there is a day there to select
+ private void doDaySelection(int x, int y) {
+ int curYear = this.mDateFactory.getYear(this.mCalendar);
+ int curMonth = this.mDateFactory.getMonth(this.mCalendar);
+ this.mNoDayClicked = false;
+
+ for (int i = 0; i < this.mDays.length; i++) {
+ if (isInside(x, y, this.mDays[i].bounds)) {
+
+ int year = this.mDateFactory.getYear(this.mDays[i].date);
+ int month = this.mDateFactory.getMonth(this.mDays[i].date);
+
+ if (year == curYear) {
+ if (month < curMonth) {
+ prevMonth();
+ return;
+ } else if (month > curMonth) {
+ nextMonth();
+ return;
+ }
+ } else {
+ if (year < curYear) {
+ prevMonth();
+ return;
+ } else if (year > curYear) {
+ nextMonth();
+ return;
+ }
+ }
+
+ if (this.mSelectedDay == null) {
+ this.mSelectedDay = this.mDateFactory.clone(this.mDays[i].date);
+ } else {
+ this.mSelectedDay = this.mDateFactory.clone(this.mDays[i].date, this.mSelectedDay);
+ }
+
+ redraw();
+ return;
+ }
+ }
+
+ this.mNoDayClicked = true;
+ }
+
+ public void mouseUp(MouseEvent event) {
+ killArrowThread();
+
+ if (this.mNoDayClicked) {
+ this.mNoDayClicked = false;
+ return;
+ }
+
+ if (this.mSelectedDay != null) {
+ notifyListeners();
+ }
+ }
+
+ private void notifyListeners() {
+ final Object date = this.mSelectedDay == null ? null : this.mDateFactory.clone(this.mSelectedDay);
+ for (int i = 0; i < this.mListeners.size(); i++) {
+ IDateListener listener = (IDateListener) this.mListeners.get(i);
+ listener.dateChanged(date);
+ }
+ }
+
+ public void addDateListener(IDateListener listener) {
+ if (!this.mListeners.contains(listener)) {
+ this.mListeners.add(listener);
+ }
+ }
+
+ public void removeCalendarListener(IDateListener listener) {
+ this.mListeners.remove(listener);
+ }
+
+ private void keyPressed(int keyCode, int stateMask) {
+ if (this.mSelectedDay == null) {
+ this.mSelectedDay = this.mDateFactory.clone(this.mCalendar);
+ this.mSelectedDay = this.mDateFactory.withDay(this.mSelectedDay, 1);
+ redraw();
+ } else {
+ if (keyCode == SWT.ARROW_RIGHT) {
+ int monthNow = this.mDateFactory.getMonth(this.mSelectedDay);
+ this.mSelectedDay = this.mDateFactory.plusDay(this.mSelectedDay, 1);
+ int monthAfter = this.mDateFactory.getMonth(this.mSelectedDay);
+ if (monthAfter != monthNow) {
+ this.mCalendar = this.mDateFactory.plusMonth(this.mCalendar, 1);
+ }
+ redraw();
+ } else if (keyCode == SWT.ARROW_LEFT) {
+ int monthNow = this.mDateFactory.getMonth(this.mSelectedDay);
+ this.mSelectedDay = this.mDateFactory.plusDay(this.mSelectedDay, -1);
+ int monthAfter = this.mDateFactory.getMonth(this.mSelectedDay);
+ if (monthAfter != monthNow) {
+ this.mCalendar = this.mDateFactory.plusMonth(this.mCalendar, -1);
+ }
+ redraw();
+ } else if (keyCode == SWT.ARROW_UP) {
+ int monthNow = this.mDateFactory.getMonth(this.mSelectedDay);
+ this.mSelectedDay = this.mDateFactory.plusDay(this.mSelectedDay, -7);
+ int monthAfter = this.mDateFactory.getMonth(this.mSelectedDay);
+ if (monthAfter != monthNow) {
+ this.mCalendar = this.mDateFactory.plusMonth(this.mCalendar, -1);
+ }
+ redraw();
+ } else if (keyCode == SWT.ARROW_DOWN) {
+ int monthNow = this.mDateFactory.getMonth(this.mSelectedDay);
+ this.mSelectedDay = this.mDateFactory.plusDay(this.mSelectedDay, 7);
+ int monthAfter = this.mDateFactory.getMonth(this.mSelectedDay);
+ if (monthAfter != monthNow) {
+ this.mCalendar = this.mDateFactory.plusMonth(this.mCalendar, 1);
+ }
+ redraw();
+ } else if (keyCode == SWT.CR || keyCode == SWT.LF) {
+ notifyListeners();
+ return;
+ }
+ }
+ }
+
+ private class ButtonSectionLayout extends Layout {
+
+ protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) {
+ return new Point(153, 160);
+ }
+
+ protected void layout(Composite composite, boolean flushCache) {
+ int bheight = CalendarComposite2.this.mSettings.getButtonHeight();
+ int bwidth = CalendarComposite2.this.mSettings.getButtonWidth();
+ int vspacer = CalendarComposite2.this.mSettings.getButtonVerticalSpace();
+ int bspacer = CalendarComposite2.this.mSettings.getButtonsHorizontalSpace();
+ if (OS_CARBON) {
+ bwidth = CalendarComposite2.this.mSettings.getButtonWidthCarbon();
+ bheight = CalendarComposite2.this.mSettings.getCarbonButtonHeight();
+ vspacer = CalendarComposite2.this.mSettings.getCarbonButtonVerticalSpace();
+ bspacer = CalendarComposite2.this.mSettings.getCarbonButtonsHorizontalSpace();
+ }
+
+ // see how much space we put on the left and right sides of the
+ // buttons
+ int width = CalendarComposite2.this.mSettings.getCalendarWidth() - (bwidth * 2) - bspacer;
+ width /= 2;
+
+ int button1Left = width;
+ int button2Left = CalendarComposite2.this.mSettings.getCalendarWidth() - width - bwidth;
+
+ Control[] children = composite.getChildren();
+ for (int i = 0; i < children.length; i++) {
+ switch (i) {
+ case 0:
+ children[i].setBounds(button1Left, vspacer, bwidth, bheight);
+
+ break;
+ case 1:
+ children[i].setBounds(button2Left, vspacer, bwidth, bheight);
+ break;
+ }
+
+ }
+ }
+
+ }
+
+ private final class CalDay {
+ public Object date;
+ public int number;
+ public Rectangle bounds;
+ public CalDay(int number, Object date, Rectangle bounds) {
+ this.date = date;
+ this.bounds = bounds;
+ this.number = number;
+ }
+ }
+
+ public static void main(String[] args) {
+
+ Display display = new Display();
+ Shell shell = new Shell(display);
+ shell.setLayout(new FillLayout());
+
+ CalendarComposite2 calendar = new CalendarComposite2(shell, new MyColorManager(), new MySettings(), null);
+ calendar.addDateListener(new IDateListener(){
+ public void dateChanged(Object date) {
+ System.out.println(date);
+ }
+ });
+
+ shell.pack();
+ shell.open();
+ while (!shell.isDisposed()) {
+ if (!display.readAndDispatch()) {
+ display.sleep();
+ }
+ }
+ display.dispose();
+
+ }
+
+ private static class MyColorManager extends AbstractColorManager {
+ private MyColorManager() {
+ super(SKIN_BLUE);
+ }
+ }
+
+ private static class MySettings extends AbstractSettings {
+
+ private Font macFont;
+
+ MySettings() {
+ super();
+ }
+
+ public Locale getLocale() {
+ return Locale.getDefault();
+ }
+
+ public String getNoneText() {
+ return "Aucun";
+ }
+
+ public String getTodayText() {
+ return "Auj.";
+ }
+
+ public boolean showCalendarInRightCorner() {
+ return true;
+ }
+
+ public Font getCarbonDrawFont() {
+ if (OS_CARBON && (this.macFont == null || this.macFont.isDisposed())) {
+ this.macFont = new Font(Display.getDefault(), "Lucida Grande", 11, SWT.NORMAL);
+ }
+ return this.macFont;
+ }
+
+ }
+}
/*******************************************************************************
* Copyright (c) Steeve St-Laurent - www.infodata.ca - sstlaurent@xxxxxxxxxxx
* 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:
* sstlaurent@xxxxxxxxxxx - initial API
*******************************************************************************/
package org.eclipse.nebula.widgets.calendarcombo;
import java.util.Locale;
/**
* a factory used to create "date implementation independant" components
*/
public interface IDateFactory {
/**
* @return never null
*/
Locale getLocale();
/**
* create a new date instance
* @param day start from 1 to month end
* @param month start from 1 to 12
* @param year
* @return never null
* @throws NullPointerException if date is invalid
*/
Object newDate(int day, int month, int year);
/**
* clear date instance
* @param date cant be null
* @return cleared date, never null, may or may not return same instance as param date
* @throws NullPointerException if date is null
*/
Object clear(Object date);
/**
* create new today date instance
* @return today date, never null
*/
Object today();
/**
* add x day to date instance
* @param date cant be null
* @param day
* @return modifed date, never null, may or may not return same instance as param date
* @throws NullPointerException if date is null
*/
Object plusDay(Object date, int day);
/**
* add x month to date instance
* @param date cant be null
* @param month
* @return modifed date, never null, may or may not return same instance as param date
* @throws NullPointerException if date is null
*/
Object plusMonth(Object date, int month);
/**
* add x year to date instance
* @param date cant be null
* @param year
* @return modifed date, never null, may or may not return same instance as param date
* @throws NullPointerException if date is null
*/
Object plusYear(Object date, int year);
/**
* set date instance day to x
* @param date cant be null
* @param day, start from 1 to monthEnd
* @return modifed date, never null, may or may not return same instance as param date
* @throws NullPointerException if date is null or day is invalid
*/
Object withDay(Object date, int day);
/**
* set date instance year to x
* @param date cant be null
* @param year
* @return modifed date, never null, may or may not return same instance as param date
* @throws NullPointerException if date is null
*/
Object withYear(Object date, int year);
/**
* set date instance month to x
* @param date cant be null
* @param month, start from 1 to 12
* @return modifed date, never null, may or may not return same instance as param date
* @throws NullPointerException if date is null or month is invalid
*/
Object withMonth(Object date, int month);
/**
* set date instance dayOfWeek to x
* @param date cant be null
* @param dayOfWeek, start from 1 to 7, sunday to saturday
* @return modifed date, never null, may or may not return same instance as param date
* @throws NullPointerException if date is null or month is invalid
*/
Object withDayOfWeek(Object date, int dayOfWeek);
/**
* @param date cant be null
* @return date day, start from 1 to 31
* @throws NullPointerException if date is null
*/
int getDay(Object date);
/**
* @param date cant be null
* @return date month, start from 1 to 12
* @throws NullPointerException if date is null
*/
int getMonth(Object date);
/**
* @param date cant be null
* @return date year
* @throws NullPointerException if date is null
*/
int getYear(Object date);
/**
* @param date cant be null
* @return date day of week, start from 1 to 7, sunday to saturday
* @throws NullPointerException if date is null
*/
int getDayOfWeek(Object date);
/**
* @param date1 can be null
* @param date2 can be null
* @return true if date1 equals date2
*/
boolean equals(Object date1, Object date2);
/**
* @param date cant be null
* @return a clone of date
* @throws NullPointerException if date is null
*/
Object clone(Object date);
/**
* clone date1 instance to date2 instance
* @param date1 cant be null
* @param date2 cant be null
* @return date2
* @throws NullPointerException if date is null
*/
Object clone(Object date1, Object date2);
/**
* Gets what the first day of the week is; e.g., <code>SUNDAY</code> in the U.S.,
* <code>MONDAY</code> in France.
*
* @return the first day of the week. 1 to 7, sunday to saturday
*/
int getFirstDayOfWeek();
}
/*******************************************************************************
* Copyright (c) Emil Crumhorn - Hexapixel.com - emil.crumhorn@xxxxxxxxx
* Copyright (c) Steeve St-Laurent - www.infodata.ca - sstlaurent@xxxxxxxxxxx
* 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:
* emil.crumhorn@xxxxxxxxx - initial API and implementation of CalendarComposite
* sstlaurent@xxxxxxxxxxx - copy and epuration of CalendarComposite
*******************************************************************************/
package org.eclipse.nebula.widgets.calendarcombo;
import java.text.DateFormatSymbols;
import java.util.ArrayList;
import java.util.Locale;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
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.events.PaintListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Canvas;
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.Layout;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
/**
* this class does not support {@link ISettings#showMonthPickerOnMonthNameMousePress()} because if does not support month picker<br>
* this class does not support {@link IColorManager#getDisabledDayForegroundColor()} because it does not support disallowed dates<br>
* this class is really independant from {@link CalendarCombo}<br>
* this class allow user to use its own date class via {@link IDateFactory} interface
*/
public class CalendarComposite2 extends Canvas implements MouseListener, MouseMoveListener {
public static interface IDateListener {
/**
* @see IDateFactory for date type
* @param date
*/
void dateChanged(Object date);
}
private static final boolean OS_CARBON = "carbon".equals(SWT.getPlatform());
private static final boolean OS_GTK = "gtk".equals(SWT.getPlatform());
private static final boolean OS_WINDOWS = "win32".equals(SWT.getPlatform());
private final int ARROW_LEFT = 1;
private final int ARROW_RIGHT = 2;
private Button mButtonToday;
private Button mButtonNone;
private Rectangle mLeftArrowBounds;
private Rectangle mRightArrowBounds;
private Object mCalendar;
private Object mToday;
private int mDatesTopY = 0;
private int mDayXs[] = new int[7];
private CalDay[] mDays = new CalDay[7 * 6];
private Object mSelectedDay;
private static DateFormatSymbols mDFS;
private String mMonths[];
private static String[] mDayTitles = null;
private ArrayList mListeners;
private boolean mNoDayClicked;
// this thread deals with holding down the mouse over the arrows to jump
// months quickly
// milliseconds
private static final int ARROW_SLOW_TIME = 300;
private static final int ARROW_FAST_TIME = 120;
// after how many iterations do we switch speed? (after how many months
// flipped-by)
private static final int ARROW_SPEED_SWITCH_COUNT = 10;
// default speed
private int mArrowSleepTime = ARROW_SLOW_TIME;
// various variables used to keep track
private int mArrowIterations = 0;
private Thread mArrowThread;
private boolean mArrowRun;
private boolean mArrowPause;
private int mArrowThreadDirection = 0;
private final IColorManager mColorManager;
private final ISettings mSettings;
private final IDateFactory mDateFactory;
public CalendarComposite2(Composite parent, IColorManager colorManager, ISettings settings, IDateFactory dateFactory) {
super(parent, SWT.NO_BACKGROUND | SWT.DOUBLE_BUFFERED);
this.mColorManager = colorManager;
this.mSettings = settings;
this.mDateFactory = dateFactory == null ? new CalendarFactory(this.mSettings == null ? null : this.mSettings.getLocale()) : dateFactory;
init();
build();
}
private void init() {
mDFS = new DateFormatSymbols(this.mDateFactory.getLocale());
this.mMonths = mDFS.getMonths();
if (this.mCalendar == null) {
this.mCalendar = this.mDateFactory.today();
}
if (this.mToday == null) {
this.mToday = this.mDateFactory.today();
}
String[] weekdays = mDFS.getWeekdays();
mDayTitles = new String[weekdays.length];
for (int i = 0; i < weekdays.length; i++) {
String weekday = weekdays[i];
if (weekday.length() > 0) {
mDayTitles[i] = weekday.substring(0, 1).toUpperCase();
}
}
//will be reused in draw days methods
for (int i = 0; i < 42; i++) {
this.mDays[i] = new CalDay(i, this.mDateFactory.today(), new Rectangle(0,0,0,0));
}
}
private void build() {
// button height & width and spacers
int bheight = this.mSettings.getButtonHeight();
int bwidth = this.mSettings.getButtonWidth();
int buttonStyle = SWT.PUSH;
this.mListeners = new ArrayList();
// Mac buttons need different flag to look normal
if (OS_CARBON) {
bheight = this.mSettings.getCarbonButtonHeight();
bwidth = this.mSettings.getButtonWidthCarbon();
buttonStyle = SWT.FLAT;
}
setLayout(new ButtonSectionLayout());
addPaintListener(new PaintListener() {
public void paintControl(PaintEvent event) {
paint(event);
}
});
addMouseListener(this);
addMouseMoveListener(this);
addKeyListener(new KeyListener() {
public void keyReleased(KeyEvent e) {
}
public void keyPressed(KeyEvent e) {
CalendarComposite2.this.keyPressed(e.keyCode, e.stateMask);
}
});
this.mButtonToday = new Button(this, buttonStyle | SWT.NO_FOCUS);
this.mButtonToday.setText(this.mSettings.getTodayText());
this.mButtonToday.setLayoutData(new GridData(bwidth, bheight));
this.mButtonToday.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event event) {
clickedTodayButton();
}
});
this.mButtonNone = new Button(this, buttonStyle | SWT.NO_FOCUS);
this.mButtonNone.setText(this.mSettings.getNoneText());
this.mButtonNone.setLayoutData(new GridData(bwidth, bheight));
this.mButtonNone.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event event) {
clickedNoneButton();
}
});
}
private void clickedTodayButton() {
setReferenceDate(this.mDateFactory.today());
this.mSelectedDay = this.mDateFactory.today();
notifyListeners();
}
private void clickedNoneButton() {
selectDate(null);
notifyListeners();
}
private void paint(PaintEvent event) {
GC gc = event.gc;
drawChartOntoGC(gc);
}
private void drawChartOntoGC(GC gc) {
Rectangle bounds = super.getBounds();
gc.setBackground(this.mColorManager.getCalendarBackgroundColor());
gc.fillRectangle(bounds);
Font used = null;
if (OS_CARBON) {
used = this.mSettings.getCarbonDrawFont();
if (used != null) {
gc.setFont(used);
}
}
// header
drawHeader(gc);
// day titles
drawTitleDays(gc);
// days
drawDays(gc);
// 1 pixel border
drawBorder(gc);
gc.dispose();
if (used != null) {
used.dispose();
}
}
public void setReferenceDate(Object date) {
this.mCalendar = date == null ? this.mDateFactory.today() : this.mDateFactory.clone(date);
redraw();
}
public void selectDate(Object date){
this.mSelectedDay = date == null ? null : this.mDateFactory.clone(date);
redraw();
}
public void nextMonth() {
this.mSelectedDay = null;
this.mCalendar = this.mDateFactory.plusMonth(this.mCalendar, 1);
this.mCalendar = this.mDateFactory.withDay(this.mCalendar, 1);
redraw();
}
public void prevMonth() {
this.mSelectedDay = null;
this.mCalendar = this.mDateFactory.plusMonth(this.mCalendar, -1);
this.mCalendar = this.mDateFactory.withDay(this.mCalendar, 1);
redraw();
}
public void goToToday() {
this.mSelectedDay = null;
this.mCalendar = this.mDateFactory.today();
redraw();
}
// draws 1 pixel border around entire calendar
private void drawBorder(GC gc) {
Rectangle bounds = super.getBounds();
gc.setForeground(this.mColorManager.getCalendarBorderColor());
gc.drawRectangle(bounds.x, bounds.y, bounds.width - 1, bounds.height - 1);
}
private void drawHeader(GC gc) {
Rectangle bounds = super.getBounds();
Rectangle bgRect = new Rectangle(bounds.x + this.mSettings.getHeaderLeftMargin(), bounds.y + this.mSettings.getHeaderTopMargin(), this.mSettings.getCalendarWidth() - 13, bounds.y
+ this.mSettings.getHeaderHeight());
gc.setBackground(this.mColorManager.getCalendarHeaderColor());
gc.fillRectangle(bgRect);
drawArrow(gc, bounds.x + this.mSettings.getHeaderLeftMargin() + this.mSettings.getArrowLeftSpacing() + 1, bounds.y + this.mSettings.getHeaderTopMargin() + this.mSettings.getArrowTopSpacing()
+ 4, this.ARROW_LEFT);
drawArrow(gc, bounds.x + this.mSettings.getCalendarWidth() - 13 - this.mSettings.getHeaderRightMargin(), bounds.y + this.mSettings.getHeaderTopMargin() + this.mSettings.getArrowTopSpacing()
+ 4, this.ARROW_RIGHT);
String toDraw = this.mMonths[this.mDateFactory.getMonth(this.mCalendar)-1] + " " + this.mDateFactory.getYear(this.mCalendar);
Point strWidth = gc.stringExtent(toDraw);
int avail = this.mSettings.getCalendarWidth() - 13 - strWidth.x;
avail /= 2;
gc.drawString(toDraw, bounds.x + this.mSettings.getHeaderLeftMargin() + avail, bounds.y + this.mSettings.getHeaderTopMargin() + 1, true);
}
private void drawTitleDays(GC gc) {
// fetch the first day of the week, and draw starting on that day
int fdow = this.mDateFactory.getFirstDayOfWeek();
Rectangle bounds = super.getBounds();
int xStart = this.mSettings.getDatesLeftMargin() + 5;
int yStart = bounds.y + this.mSettings.getHeaderTopMargin() + this.mSettings.getHeaderHeight() + 1;
int spacer = 0;
int letterHeight = 0;
for (int i = 0; i < 7; i++) {
Point strWidth = gc.stringExtent(mDayTitles[fdow]);
int x = xStart + this.mSettings.getOneDateBoxSize() + spacer - strWidth.x;
// don't add the string width, as our string width later when
// drawing days will differ
this.mDayXs[i] = xStart + this.mSettings.getOneDateBoxSize() + spacer;
gc.drawString(mDayTitles[fdow], x, yStart, true);
letterHeight = strWidth.y;
spacer += this.mSettings.getOneDateBoxSize() + this.mSettings.getBoxSpacer();
fdow++;
if (fdow > 7) {
fdow = 1;
}
}
int lineStart = yStart + 1 + letterHeight;
gc.setForeground(this.mColorManager.getLineColor());
gc.drawLine(this.mSettings.getDatesLeftMargin() + 1, lineStart, bounds.width - this.mSettings.getDatesRightMargin() - 3, lineStart);
this.mDatesTopY = lineStart + 3;
}
private void drawDays(GC gc) {
gc.setBackground(this.mColorManager.getCalendarBackgroundColor());
gc.setForeground(this.mColorManager.getTextColor());
Rectangle bounds = super.getBounds();
int spacer = 0;
// debug("mCalendar", this.mCalendar);
Object start = this.mDateFactory.clone(this.mCalendar);
// debug("start", start);
start = this.mDateFactory.withDay(start, 1);
// debug("start", start);
start = this.mDateFactory.withDayOfWeek(start, 1);
// debug("start", start);
Object date = start;
final int monthToShow = this.mDateFactory.getMonth(this.mCalendar);
int col = 0;
int colCount = 0;
String typicalLetter = "8";
int strHeight = gc.stringExtent(typicalLetter).y;
gc.setForeground(this.mColorManager.getLineColor());
int lastY = 0;
for (int y = 0; y < 42; y++) {
// debug(date);
// new row
if (y % 7 == 0 && y != 0) {
spacer += strHeight + 2;
}
if (colCount > 6) {
colCount = 0;
col = 0;
}
if (this.mDateFactory.getMonth(date) == monthToShow) {
gc.setForeground(this.mColorManager.getTextColor());
} else {
gc.setForeground(this.mColorManager.getPreviousAndNextMonthForegroundColor());
}
String dateStr = String.valueOf(this.mDateFactory.getDay(date));
Point width = gc.stringExtent(dateStr);
if (this.mDateFactory.equals(this.mSelectedDay, date)) {
gc.setBackground(this.mColorManager.getSelectedDayColor());
gc.fillRectangle(this.mDayXs[col] - this.mSettings.getOneDateBoxSize() - 4, this.mDatesTopY + spacer - 1, this.mSettings.getOneDateBoxSize() + 5, 14);
gc.setBackground(this.mColorManager.getCalendarBackgroundColor());
}
this.mDays[y].bounds.x = this.mDayXs[col] - this.mSettings.getOneDateBoxSize() - 4;
this.mDays[y].bounds.y = this.mDatesTopY + spacer - 1;
this.mDays[y].bounds.width = this.mSettings.getOneDateBoxSize() + 5;
this.mDays[y].bounds.height = 14;
this.mDays[y].number = y;
this.mDays[y].date = this.mDateFactory.clone(date, this.mDays[y].date);
gc.drawString(dateStr, this.mDayXs[col] - width.x, this.mDatesTopY + spacer, true);
if (this.mDateFactory.equals(this.mToday, date)) {
Color old = gc.getForeground();
gc.setForeground(this.mColorManager.getSelectedDayBorderColor());
gc.drawRectangle(this.mDayXs[col] - this.mSettings.getOneDateBoxSize() - 4, this.mDatesTopY + spacer - 1, this.mSettings.getOneDateBoxSize() + 5, 14);
gc.setForeground(old);
}
date = this.mDateFactory.plusDay(date, 1);
col++;
colCount++;
lastY = this.mDatesTopY + spacer;
}
lastY += strHeight + 1;
gc.setForeground(this.mColorManager.getLineColor());
gc.drawLine(this.mSettings.getDatesLeftMargin() + 1, lastY, bounds.width - this.mSettings.getDatesRightMargin() - 3, lastY);
}
private void debug(Object date) {
System.out.println(this.mDateFactory.getDay(date) + "-"+this.mDateFactory.getMonth(date) +"-"+this.mDateFactory.getYear(date));
}
private void debug(String text, Object date) {
System.out.println(text + " " + this.mDateFactory.getDay(date) + "-"+this.mDateFactory.getMonth(date) +"-"+this.mDateFactory.getYear(date));
}
private void drawArrow(GC gc, int x, int y, int style) {
gc.setForeground(this.mColorManager.getArrowColor());
switch (style) {
case ARROW_RIGHT:
gc.drawLine(x, y - 4, x, y + 4);
gc.drawLine(x + 1, y - 3, x + 1, y + 3);
gc.drawLine(x + 2, y - 2, x + 2, y + 2);
gc.drawLine(x + 3, y - 1, x + 3, y + 1);
gc.drawLine(x + 4, y, x + 4, y);
this.mRightArrowBounds = new Rectangle(x - 4, y - 4, x + 8, y);
break;
case ARROW_LEFT:
gc.drawLine(x - 1, y, x - 1, y);
gc.drawLine(x, y - 1, x, y + 1);
gc.drawLine(x + 1, y - 2, x + 1, y + 2);
gc.drawLine(x + 2, y - 3, x + 2, y + 3);
gc.drawLine(x + 3, y - 4, x + 3, y + 4);
this.mLeftArrowBounds = new Rectangle(x - 4, y - 4, x + 8, y);
break;
}
}
private static boolean isInside(int x, int y, Rectangle rect) {
if (rect == null) {
return false;
}
if (x >= rect.x && y >= rect.y && x <= (rect.x + rect.width) && y <= (rect.y + rect.height)) {
return true;
}
return false;
}
public void mouseMove(MouseEvent e) {
// "dragging" the day, just paint it
if (e.stateMask != 0) {
doDaySelection(e.x, e.y);
}
if (this.mArrowRun && this.mArrowThread != null) {
if (!isInside(e.x, e.y, this.mLeftArrowBounds) && !isInside(e.x, e.y, this.mRightArrowBounds)) {
this.mArrowPause = true;
// also pause the speed
this.mArrowIterations = 0;
this.mArrowSleepTime = ARROW_SLOW_TIME;
} else {
if (isInside(e.x, e.y, this.mLeftArrowBounds)) {
this.mArrowThreadDirection = this.ARROW_LEFT;
} else {
this.mArrowThreadDirection = this.ARROW_RIGHT;
}
this.mArrowPause = false;
}
}
}
public void mouseDoubleClick(MouseEvent event) {
}
// draw the date selection on mouse down to give that flicker of "response"
// to the user
public void mouseDown(MouseEvent event) {
if (isInside(event.x, event.y, this.mLeftArrowBounds)) {
prevMonth();
runArrowThread(this.ARROW_LEFT);
return;
}
if (isInside(event.x, event.y, this.mRightArrowBounds)) {
nextMonth();
runArrowThread(this.ARROW_RIGHT);
return;
}
doDaySelection(event.x, event.y);
}
private void killArrowThread() {
if (this.mArrowThread != null) {
this.mArrowPause = true;
this.mArrowThreadDirection = 0;
this.mArrowRun = false;
this.mArrowThread = null;
}
}
private void runArrowThread(int direction) {
this.mArrowThreadDirection = direction;
this.mArrowIterations = 0;
this.mArrowSleepTime = ARROW_SLOW_TIME;
this.mArrowRun = false;
this.mArrowPause = false;
this.mArrowThread = new Thread() {
public void run() {
while (CalendarComposite2.this.mArrowRun) {
try {
sleep(CalendarComposite2.this.mArrowSleepTime);
if (!CalendarComposite2.this.mArrowPause) {
CalendarComposite2.this.mArrowIterations++;
}
if (CalendarComposite2.this.mArrowIterations > ARROW_SPEED_SWITCH_COUNT && CalendarComposite2.this.mArrowSleepTime != ARROW_FAST_TIME) {
CalendarComposite2.this.mArrowSleepTime = ARROW_FAST_TIME;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
if (!CalendarComposite2.this.mArrowPause) {
Display.getDefault().syncExec(new Runnable() {
public void run() {
if (isDisposed()) {
CalendarComposite2.this.mArrowRun = false;
CalendarComposite2.this.mArrowThread = null;
} else {
if (CalendarComposite2.this.mArrowThreadDirection == CalendarComposite2.this.ARROW_LEFT) {
prevMonth();
} else if (CalendarComposite2.this.mArrowThreadDirection == CalendarComposite2.this.ARROW_RIGHT) {
nextMonth();
}
}
}
});
}
}
}
};
this.mArrowPause = false;
this.mArrowRun = true;
this.mArrowThread.start();
}
// selects a day at x, y if there is a day there to select
private void doDaySelection(int x, int y) {
int curYear = this.mDateFactory.getYear(this.mCalendar);
int curMonth = this.mDateFactory.getMonth(this.mCalendar);
this.mNoDayClicked = false;
for (int i = 0; i < this.mDays.length; i++) {
if (isInside(x, y, this.mDays[i].bounds)) {
int year = this.mDateFactory.getYear(this.mDays[i].date);
int month = this.mDateFactory.getMonth(this.mDays[i].date);
if (year == curYear) {
if (month < curMonth) {
prevMonth();
return;
} else if (month > curMonth) {
nextMonth();
return;
}
} else {
if (year < curYear) {
prevMonth();
return;
} else if (year > curYear) {
nextMonth();
return;
}
}
if (this.mSelectedDay == null) {
this.mSelectedDay = this.mDateFactory.clone(this.mDays[i].date);
} else {
this.mSelectedDay = this.mDateFactory.clone(this.mDays[i].date, this.mSelectedDay);
}
redraw();
return;
}
}
this.mNoDayClicked = true;
}
public void mouseUp(MouseEvent event) {
killArrowThread();
if (this.mNoDayClicked) {
this.mNoDayClicked = false;
return;
}
if (this.mSelectedDay != null) {
notifyListeners();
}
}
private void notifyListeners() {
final Object date = this.mSelectedDay == null ? null : this.mDateFactory.clone(this.mSelectedDay);
for (int i = 0; i < this.mListeners.size(); i++) {
IDateListener listener = (IDateListener) this.mListeners.get(i);
listener.dateChanged(date);
}
}
public void addDateListener(IDateListener listener) {
if (!this.mListeners.contains(listener)) {
this.mListeners.add(listener);
}
}
public void removeCalendarListener(IDateListener listener) {
this.mListeners.remove(listener);
}
private void keyPressed(int keyCode, int stateMask) {
if (this.mSelectedDay == null) {
this.mSelectedDay = this.mDateFactory.clone(this.mCalendar);
this.mSelectedDay = this.mDateFactory.withDay(this.mSelectedDay, 1);
redraw();
} else {
if (keyCode == SWT.ARROW_RIGHT) {
int monthNow = this.mDateFactory.getMonth(this.mSelectedDay);
this.mSelectedDay = this.mDateFactory.plusDay(this.mSelectedDay, 1);
int monthAfter = this.mDateFactory.getMonth(this.mSelectedDay);
if (monthAfter != monthNow) {
this.mCalendar = this.mDateFactory.plusMonth(this.mCalendar, 1);
}
redraw();
} else if (keyCode == SWT.ARROW_LEFT) {
int monthNow = this.mDateFactory.getMonth(this.mSelectedDay);
this.mSelectedDay = this.mDateFactory.plusDay(this.mSelectedDay, -1);
int monthAfter = this.mDateFactory.getMonth(this.mSelectedDay);
if (monthAfter != monthNow) {
this.mCalendar = this.mDateFactory.plusMonth(this.mCalendar, -1);
}
redraw();
} else if (keyCode == SWT.ARROW_UP) {
int monthNow = this.mDateFactory.getMonth(this.mSelectedDay);
this.mSelectedDay = this.mDateFactory.plusDay(this.mSelectedDay, -7);
int monthAfter = this.mDateFactory.getMonth(this.mSelectedDay);
if (monthAfter != monthNow) {
this.mCalendar = this.mDateFactory.plusMonth(this.mCalendar, -1);
}
redraw();
} else if (keyCode == SWT.ARROW_DOWN) {
int monthNow = this.mDateFactory.getMonth(this.mSelectedDay);
this.mSelectedDay = this.mDateFactory.plusDay(this.mSelectedDay, 7);
int monthAfter = this.mDateFactory.getMonth(this.mSelectedDay);
if (monthAfter != monthNow) {
this.mCalendar = this.mDateFactory.plusMonth(this.mCalendar, 1);
}
redraw();
} else if (keyCode == SWT.CR || keyCode == SWT.LF) {
notifyListeners();
return;
}
}
}
private class ButtonSectionLayout extends Layout {
protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) {
return new Point(153, 160);
}
protected void layout(Composite composite, boolean flushCache) {
int bheight = CalendarComposite2.this.mSettings.getButtonHeight();
int bwidth = CalendarComposite2.this.mSettings.getButtonWidth();
int vspacer = CalendarComposite2.this.mSettings.getButtonVerticalSpace();
int bspacer = CalendarComposite2.this.mSettings.getButtonsHorizontalSpace();
if (OS_CARBON) {
bwidth = CalendarComposite2.this.mSettings.getButtonWidthCarbon();
bheight = CalendarComposite2.this.mSettings.getCarbonButtonHeight();
vspacer = CalendarComposite2.this.mSettings.getCarbonButtonVerticalSpace();
bspacer = CalendarComposite2.this.mSettings.getCarbonButtonsHorizontalSpace();
}
// see how much space we put on the left and right sides of the
// buttons
int width = CalendarComposite2.this.mSettings.getCalendarWidth() - (bwidth * 2) - bspacer;
width /= 2;
int button1Left = width;
int button2Left = CalendarComposite2.this.mSettings.getCalendarWidth() - width - bwidth;
Control[] children = composite.getChildren();
for (int i = 0; i < children.length; i++) {
switch (i) {
case 0:
children[i].setBounds(button1Left, vspacer, bwidth, bheight);
break;
case 1:
children[i].setBounds(button2Left, vspacer, bwidth, bheight);
break;
}
}
}
}
private final class CalDay {
public Object date;
public int number;
public Rectangle bounds;
public CalDay(int number, Object date, Rectangle bounds) {
this.date = date;
this.bounds = bounds;
this.number = number;
}
}
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setLayout(new FillLayout());
CalendarComposite2 calendar = new CalendarComposite2(shell, new MyColorManager(), new MySettings(), null);
calendar.addDateListener(new IDateListener(){
public void dateChanged(Object date) {
System.out.println(date);
}
});
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}
private static class MyColorManager extends AbstractColorManager {
private MyColorManager() {
super(SKIN_BLUE);
}
}
private static class MySettings extends AbstractSettings {
private Font macFont;
MySettings() {
super();
}
public Locale getLocale() {
return Locale.getDefault();
}
public String getNoneText() {
return "Aucun";
}
public String getTodayText() {
return "Auj.";
}
public boolean showCalendarInRightCorner() {
return true;
}
public Font getCarbonDrawFont() {
if (OS_CARBON && (this.macFont == null || this.macFont.isDisposed())) {
this.macFont = new Font(Display.getDefault(), "Lucida Grande", 11, SWT.NORMAL);
}
return this.macFont;
}
}
}
/*******************************************************************************
* Copyright (c) Steeve St-Laurent - www.infodata.ca - sstlaurent@xxxxxxxxxxx
* 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:
* sstlaurent@xxxxxxxxxxx - initial implementations
*******************************************************************************/
package org.eclipse.nebula.widgets.calendarcombo;
import java.util.Calendar;
import java.util.Locale;
/**
* a factory that work on calendar
*/
public class CalendarFactory implements IDateFactory {
private final Locale locale;
private final int firstDayOfWeek;
/**
* @param new factory with locale set to {@link Locale#getDefault()}
*/
public CalendarFactory() {
this(null);
}
/**
* @param locale can be null, default to {@link Locale#getDefault()}
*/
public CalendarFactory(Locale locale) {
if (locale == null) {
locale = Locale.getDefault();
}
this.locale = locale;
this.firstDayOfWeek = ((Calendar)today()).getFirstDayOfWeek();
}
public Object plusDay(Object date, int day) {
Calendar c = ((Calendar)date);
c.add(Calendar.DATE, day);
return c;
}
public Object plusMonth(Object date, int month) {
Calendar c = ((Calendar)date);
c.add(Calendar.MONTH, month);
return c;
}
public Object plusYear(Object date, int year) {
Calendar c = ((Calendar)date);
c.add(Calendar.YEAR, year);
return c;
}
public Object clear(Object date) {
Calendar c = ((Calendar)date);
c.clear();
return c;
}
public boolean equals(Object date1, Object date2) {
if (date1 == date2) {
return true;
}
if (date1 == null || date2 == null) {
return false;
}
return DateHelper.sameDate((Calendar)date1, (Calendar)date2);
}
public int getDay(Object date) {
Calendar c = ((Calendar)date);
return c.get(Calendar.DATE);
}
public int getDayOfWeek(Object date) {
Calendar c = ((Calendar)date);
return c.get(Calendar.DAY_OF_WEEK);
}
public int getMonth(Object date) {
Calendar c = ((Calendar)date);
return c.get(Calendar.MONTH) + 1;
}
public int getYear(Object date) {
Calendar c = ((Calendar)date);
return c.get(Calendar.YEAR);
}
public Object newDate(int day, int month, int year) {
Calendar c = Calendar.getInstance(this.locale);
c.set(Calendar.YEAR, year);
c.set(Calendar.MONTH, month-1);
c.set(Calendar.DATE, day);
return toMidnight(c);
}
private static Calendar toMidnight(Calendar c){
c.set(Calendar.HOUR, 0);
c.set(Calendar.HOUR_OF_DAY, 0);
c.set(Calendar.MINUTE, 0);
c.set(Calendar.SECOND, 0);
c.set(Calendar.MILLISECOND, 0);
return c;
}
public Object today() {
return toMidnight(Calendar.getInstance(this.locale));
}
public Object withDay(Object date, int day) {
Calendar c = ((Calendar)date);
c.set(Calendar.DAY_OF_MONTH, day);
return c;
}
public Object withMonth(Object date, int month) {
Calendar c = ((Calendar)date);
c.set(Calendar.MONTH, month-1);
return c;
}
public Object withYear(Object date, int year) {
Calendar c = ((Calendar)date);
c.set(Calendar.YEAR, year);
return c;
}
public Object withDayOfWeek(Object date, int dayOfWeek) {
Calendar c = ((Calendar)date);
c.set(Calendar.DAY_OF_WEEK, dayOfWeek);
return c;
}
public Object clone(Object date) {
Calendar c = ((Calendar)date);
return c.clone();
}
public int getFirstDayOfWeek() {
return this.firstDayOfWeek;
}
public Object clone(Object date1, Object date2) {
Calendar c1 = ((Calendar)date1);
Calendar c2 = ((Calendar)date2);
c2.set(Calendar.YEAR, c1.get(Calendar.YEAR));
c2.set(Calendar.MONTH, c1.get(Calendar.MONTH));
c2.set(Calendar.DATE, c1.get(Calendar.DATE));
return toMidnight(c2);
}
public Locale getLocale() {
return this.locale;
}
}
/*******************************************************************************
* Copyright (c) Emil Crumhorn - Hexapixel.com - emil.crumhorn@xxxxxxxxx
* 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:
* emil.crumhorn@xxxxxxxxx - initial API and implementation
*******************************************************************************/
package org.eclipse.nebula.widgets.calendarcombo;
import java.text.DateFormat;
import java.text.DateFormatSymbols;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Locale;
import java.util.StringTokenizer;
public class DateHelper {
private static final long MILLISECONDS_IN_DAY = 24 * 60 * 60 * 1000;
public static long daysBetween(Calendar start, Calendar end, Locale locale) {
// create copies
GregorianCalendar startDate = new GregorianCalendar(locale);
GregorianCalendar endDate = new GregorianCalendar(locale);
// switch calendars to pure Julian mode for correct day-between
// calculation, from the Java API:
// - To obtain a pure Julian calendar, set the change date to
// Date(Long.MAX_VALUE).
startDate.setGregorianChange(new Date(Long.MAX_VALUE));
endDate.setGregorianChange(new Date(Long.MAX_VALUE));
// set them
startDate.setTime(start.getTime());
endDate.setTime(end.getTime());
// force times to be exactly the same
startDate.set(Calendar.HOUR_OF_DAY, 12);
endDate.set(Calendar.HOUR_OF_DAY, 12);
startDate.set(Calendar.MINUTE, 0);
endDate.set(Calendar.MINUTE, 0);
startDate.set(Calendar.SECOND, 0);
endDate.set(Calendar.SECOND, 0);
startDate.set(Calendar.MILLISECOND, 0);
endDate.set(Calendar.MILLISECOND, 0);
// now we should be able to do a "safe" millisecond/day caluclation to
// get the number of days
long endMilli = endDate.getTimeInMillis();
long startMilli = startDate.getTimeInMillis();
// calculate # of days, finally
long diff = (endMilli - startMilli) / MILLISECONDS_IN_DAY;
return diff;
}
public static long daysBetween(Date start, Date end, Locale locale) {
Calendar dEnd = Calendar.getInstance(locale);
Calendar dStart = Calendar.getInstance(locale);
dEnd.setTime(end);
dStart.setTime(start);
return daysBetween(dStart, dEnd, locale);
}
public static boolean isToday(Date date, Locale locale) {
Calendar cal = Calendar.getInstance(locale);
cal.setTime(date);
return isToday(cal, locale);
}
public static boolean isToday(Calendar cal, Locale locale) {
Calendar today = Calendar.getInstance(locale);
if (today.get(Calendar.YEAR) == cal.get(Calendar.YEAR)) {
if (today.get(Calendar.DAY_OF_YEAR) == cal.get(Calendar.DAY_OF_YEAR)) {
return true;
}
}
return false;
}
public static String getDate(Calendar cal, String dateFormat) {
Calendar toUse = (Calendar) cal.clone();
toUse.add(Calendar.MONTH, -1);
SimpleDateFormat df = new SimpleDateFormat(dateFormat);
df.setLenient(true);
return df.format(cal.getTime());
}
public static boolean sameDate(Date date1, Date date2, Locale locale) {
Calendar cal1 = Calendar.getInstance(locale);
Calendar cal2 = Calendar.getInstance(locale);
cal1.setTime(date1);
cal2.setTime(date2);
return sameDate(cal1, cal2);
}
public static boolean sameDate(Calendar cal1, Calendar cal2) {
if (cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR)) {
if (cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR)) {
return true;
}
}
return false;
}
public static Date getDate(String str, String dateFormat) throws Exception {
SimpleDateFormat df = new SimpleDateFormat(dateFormat);
df.setLenient(false);
return df.parse(str);
}
public static Calendar getDate(String str, String dateFormat, Locale locale) throws Exception {
SimpleDateFormat df = new SimpleDateFormat(dateFormat, locale);
Date d = df.parse(str);
Calendar cal = Calendar.getInstance(locale);
cal.setTime(d);
return cal;
}
public static Calendar parseDate(String str, Locale locale) throws Exception {
Date foo = DateFormat.getDateInstance(DateFormat.SHORT, locale).parse(str);
Calendar cal = Calendar.getInstance(locale);
cal.setTime(foo);
return cal;
}
private static Calendar calendarize(Date date, Locale locale) {
Calendar cal = Calendar.getInstance(locale);
cal.setTime(date);
return cal;
}
public static Calendar parse(final String comboText, final Locale locale, final String dateFormat, final char[] acceptedSeparatorChars, final List additionalDateFormats)
throws CalendarDateParseException, Exception {
Calendar parsed = null;
Exception ex = null;
boolean isNumeric = comboText.replaceAll("[^0-9]", "").length() == comboText.length();
if (isNumeric) {
try {
parsed = numericParse(comboText, locale, false);
} catch (Exception e) {
//maybe dont care
ex = ex==null?e:ex;
}
}
if (parsed == null){
try {
parsed = slashParse(comboText, dateFormat, acceptedSeparatorChars, locale);
} catch (Exception e) {
//maybe dont care
ex = ex==null?e:ex;
}
}
if (parsed == null){
if (additionalDateFormats != null) {
// final Calendar today = Calendar.getInstance(locale);
for (int i = 0; i < additionalDateFormats.size(); i++) {
try {
String format = (String) additionalDateFormats.get(i);
Date date = DateHelper.getDate(comboText, format);
if (date != null){
parsed = calendarize(date, locale);
break;
}
}
catch (Exception e) {
//maybe dont care
ex = ex==null?e:ex;
}
}
}
}
if (parsed == null){
//no more try
throw ex;
}
return parsed;
//return null;
//}
/*if (comboText.length() == 0) {
return null;
}
try {
// start with a hard parse as date format parses can return
// false positives on various locales.
// false positives may sound good, but they're bad, as they can
// cause a year to end up 2000 years off...
try {
mStartDate = DateHelper.parseDateHard(comboText, locale);
return mStartDate;
}
catch (Exception err) {
}
// try true date format parse
mStartDate = DateHelper.getDate(comboText, dateFormat, locale);
return mStartDate;
// System.err.println("Got here 2 - Settings parse " +
// mStartDate.getTime());
}
catch (Exception err) {
// try the locale (this is error prone due to how java parses
// dates)
try {
mStartDate = DateHelper.parseDate(comboText, locale);
return mStartDate;
// System.err.println("Got here 3 - Locale parse " +
// mStartDate.getTime());
}
catch (Exception deeper) {
try {
mStartDate = DateHelper.slashParse(comboText, dateFormat, acceptedSeparatorChars, locale);
return mStartDate;
}
catch (Exception ohwell) {
// System.err.println("Failed parse, trying additional formats");
if (additionalDateFormats != null) {
try {
for (int i = 0; i < additionalDateFormats.size(); i++) {
try {
String format = (String) additionalDateFormats.get(i);
Date date = DateHelper.getDate(comboText, format);
return mStartDate;
}
catch (Exception failed) {
// keep trying
}
}
}
catch (Exception err2) {
// don't care
}
}
}
return mStartDate;
}
}
}
catch (Exception err) {
err.printStackTrace();
}*/
//return null;
}
/**
* This method will try its best to parse a date based on the current
* Locale.
*
* @param str
* String to parse
* @param locale
* Current Locale
* @return Calendar or null on failure
* @throws CalendarDateParseException
* If date could not be parsed
* @throws Exception
* on any unforseen issues or bad parse errors
*/
public static Calendar parseDateHard(final String str, final Locale locale) throws CalendarDateParseException, Exception {
try {
DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
String actualLocalePattern = ((SimpleDateFormat) df).toPattern();
try {
Calendar foo = slashParse(str, actualLocalePattern, new char[] {
'/', '-', '.'
}, locale);
return foo;
}
catch (Exception err) {
}
try {
Date foo = df.parse(str);
return calendarize(foo, locale);
}
catch (Exception err) {
// some locales already have 4 y's
if (actualLocalePattern.indexOf("yyyy") == -1)
actualLocalePattern = actualLocalePattern.replaceAll("yy", "yyyy");
try {
Date foo = df.parse(str);
return calendarize(foo, locale);
}
catch (Exception err2) {
// fall through
}
}
}
catch (Exception err) {
// fall through
}
try {
Date foo = DateFormat.getDateInstance().parse(str);
return calendarize(foo, locale);
}
catch (Exception err) {
try {
Integer.parseInt(str);
try {
DateFormat df = DateFormat.getDateInstance();
df.setLenient(false);
Date foo = df.parse(str);
return calendarize(foo, locale);
}
catch (Exception err2) {
return numericParse(str, locale, true);
}
}
catch (Exception failedInt) {
// clear the bad chars and try again
StringBuffer buf = new StringBuffer();
for (int i = 0; i < str.length(); i++) {
if (str.charAt(i) >= '0' && str.charAt(i) <= '9')
buf.append(str.charAt(i));
}
String fixed = buf.toString();
try {
Integer.parseInt(fixed);
return numericParse(fixed, locale, true);
}
catch (Exception forgetit) {
throw new CalendarDateParseException(forgetit, CalendarDateParseException.TYPE_EXCEPTION);
}
}
}
}
// date formats with a single M d y etc are highly problematic (US dates),
// so replace them with their proper format so that we can parse
// or else we'll be parsing years like "03" as "0003"
public static String dateFormatFix(String str) {
if (str.indexOf("M") != -1 && str.indexOf("MM") == -1 && str.indexOf("MMM") == -1) {
str = str.replaceAll("M", "MM");
}
if (str.indexOf("d") != -1 && str.indexOf("dd") == -1 && str.indexOf("ddd") == -1) {
str = str.replaceAll("d", "dd");
}
if (str.indexOf("y") != -1 && str.indexOf("yy") == -1 && str.indexOf("yyy") == -1) {
str = str.replaceAll("y", "yy");
}
return str;
}
public static Calendar numericParse(String str, Locale locale, boolean doUsEuParse) throws Exception {
// we always start with the locale and try to parse that numerically, if
// that fails we'll try another few possibilities before we give up
DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
String actualLocalePattern = ((SimpleDateFormat) df).toPattern();
// remove all non letters which will leave us with a clean date pattern
actualLocalePattern = dateFormatFix(actualLocalePattern.replaceAll("[^a-zA-Z]", ""));
actualLocalePattern = actualLocalePattern.replaceAll("G", "");
// parse it into long / short versions where the year is 4 or 2 digits
String actualLocaleLong = "";
String actualLocaleShort = "";
if (actualLocalePattern.indexOf("yyyy") == -1) {
actualLocaleShort = actualLocalePattern;
actualLocaleLong = actualLocalePattern.replaceAll("yy", "yyyy");
}
else {
actualLocaleLong = actualLocalePattern;
actualLocaleShort = actualLocalePattern.replaceAll("yyyy", "yy");
}
Date parsed = null;
// now parse it according to locale if we can
try {
if (str.length() == 6) {
SimpleDateFormat sdf = new SimpleDateFormat(actualLocaleShort);
parsed = sdf.parse(str);
}
else if (str.length() == 5) {
// if a user enters a 5-digit date we assume they were clever enough to get the day and month in 2 digit formats, and the last being the year in a 1 digit format.
// so we need to 2-digitize the year and re-parse. As any 1900-year would be 2 digit except for early 1900's (we don't care) we assume it's 2000+. As I said, if we
// get here the user is really pushing their luck on parsing anyway and we're doing them a favor to begin with
StringBuffer buf = new StringBuffer();
buf.append(str.substring(0, 4));
buf.append("0");
buf.append(str.substring(4, 5));
return numericParse(buf.toString(), locale, doUsEuParse);
}
else {
SimpleDateFormat sdf = new SimpleDateFormat(actualLocaleLong);
parsed = sdf.parse(str);
}
if (parsed != null)
return calendarize(parsed, locale);
}
catch (ParseException pe) {
// ignore, try more
}
if (doUsEuParse) {
// try a couple of pre-defined formats, it's highly likely it's
// either
// US or European
String usFormat6 = "MMddyy";
String usFormat8 = "MMddyyyy";
String euFormat6 = "ddMMyy";
String euFormat8 = "ddMMyyyy";
if (locale.equals(Locale.US)) {
if (str.length() == 6) {
SimpleDateFormat sdf = new SimpleDateFormat(usFormat6);
parsed = sdf.parse(str);
}
else {
SimpleDateFormat sdf = new SimpleDateFormat(usFormat8);
parsed = sdf.parse(str);
}
}
else {
if (str.length() == 6) {
SimpleDateFormat sdf = new SimpleDateFormat(euFormat6);
parsed = sdf.parse(str);
}
else {
SimpleDateFormat sdf = new SimpleDateFormat(euFormat8);
parsed = sdf.parse(str);
}
}
}
if (parsed != null) {
return calendarize(parsed, locale);
}
return null;
}
public static int getCalendarTypeForString(String oneChar) {
int calType = -1;
switch (oneChar.charAt(0)) {
case 'G':
calType = Calendar.ERA;
break;
case 'y':
calType = Calendar.YEAR;
break;
case 'M':
calType = Calendar.MONTH;
break;
case 'd':
calType = Calendar.DAY_OF_MONTH;
break;
case 'E':
calType = Calendar.DAY_OF_WEEK;
break;
case 'D':
calType = Calendar.DAY_OF_YEAR;
break;
case 'F':
calType = Calendar.DATE;
break;
case 'h':
calType = Calendar.HOUR;
break;
case 'm':
calType = Calendar.MINUTE;
break;
case 's':
calType = Calendar.SECOND;
break;
case 'S':
calType = Calendar.MILLISECOND;
break;
case 'w':
calType = Calendar.WEEK_OF_YEAR;
break;
case 'W':
calType = Calendar.WEEK_OF_MONTH;
break;
case 'a':
calType = Calendar.AM_PM;
break;
case 'k':
calType = Calendar.HOUR_OF_DAY;
break;
case 'K':
// ?
break;
case 'z':
calType = Calendar.ZONE_OFFSET;
break;
}
return calType;
}
/**
* This method assumes the dateFormat has a separator char in it, and that
* we can use that to determine what the user entered by using that
* separator to split up the user entered date, and then do some logic on
* it. This is by no means a foolproof method and should not be relied upon
* returning 100% correct dates all the time.
*
* @param str
* String to parse
* @param dateFormat
* DateFormat to use
* @param separators
* Separator chars that can be encountered
* @param locale
* Locale
* @return Calendar
* @throws CalendarDateParseException
* If date could not be parsed
* @throws Exception
* If any step of the parsing failed
*/
public static Calendar slashParse(final String str, final String dateFormat, final char[] separators, final Locale locale) throws CalendarDateParseException, Exception {
int start = -1;
String splitter = null;
String dateFormatToUse = dateFormat;
for (int i = 0; i < separators.length; i++) {
start = str.indexOf(separators[i]);
if (start != -1) {
splitter = String.valueOf(separators[i]);
break;
}
}
if (start == -1)
throw new CalendarDateParseException("Failed to find splitter char", CalendarDateParseException.TYPE_NO_SLPITTER_CHAR);
// replace dateFormat until we have same splitter
for (int i = 0; i < separators.length; i++) {
if (String.valueOf(separators[i]).equals(splitter))
continue;
dateFormatToUse = dateFormatToUse.replaceAll("\\" + String.valueOf(separators[i]), splitter);
}
Calendar toReturn = Calendar.getInstance(locale);
StringTokenizer st = new StringTokenizer(str, splitter);
StringTokenizer st2 = new StringTokenizer(dateFormatToUse, splitter);
if (st.countTokens() != st2.countTokens())
throw new CalendarDateParseException("Date format does not match date string in terms of splitter character numbers", CalendarDateParseException.TYPE_INSUFFICIENT_SPLITTERS);
// variables we'll be extracting
int monthToSet = -1;
int dayToSet = -1;
int yearToSet = -1;
// reset, skipping month this time
st = new StringTokenizer(str, splitter);
st2 = new StringTokenizer(dateFormatToUse, splitter);
while (st.hasMoreTokens()) {
String dateValue = st.nextToken();
String dateType = st2.nextToken();
dateValue = dateValue.replaceAll(" ", "");
dateType = dateType.replaceAll(" ", "");
int calType = getCalendarTypeForString(dateType);
// we already did month
if (calType == Calendar.MONTH) {
monthToSet = Integer.parseInt(dateValue);
continue;
}
if (calType == Calendar.YEAR) {
yearToSet = Integer.parseInt(dateValue);
continue;
}
if (calType == Calendar.DATE) {
dayToSet = Integer.parseInt(dateValue);
continue;
}
toReturn.set(calType, Integer.parseInt(dateValue));
}
// set all date parameters at the same time, or else we'll get month-skipping due to setting a value later (such as a date that is too high
// for the current month). (-1 for month as Calendar class is month-zero-based).
if (monthToSet != -1 && dayToSet != -1 && yearToSet != -1) {
toReturn.set(yearToSet, monthToSet-1, dayToSet);
}
else {
// set what we know
if (yearToSet != -1) {
toReturn.set(Calendar.YEAR, yearToSet);
}
if (monthToSet != -1) {
toReturn.set(Calendar.MONTH, monthToSet-1);
}
if (dayToSet != -1) {
toReturn.set(Calendar.DATE, dayToSet);
}
}
if (toReturn.get(Calendar.YEAR) < 100)
toReturn.set(Calendar.YEAR, toReturn.get(Calendar.YEAR) + 2000);
toReturn.set(Calendar.HOUR_OF_DAY, 0);
toReturn.set(Calendar.MINUTE, 0);
toReturn.set(Calendar.SECOND, 0);
toReturn.set(Calendar.MILLISECOND, 0);
return toReturn;
}
/**
* Parses a string (representing a month) and returns it's corresponding
* value as a Calendar month. This is used to parse MMM month dates
*
* @param monthStr
* String to parse
* @param locale
* Locale to use
* @return Month value or -1 if not found
*/
private static int getMonthForString(String monthStr, Locale locale) {
DateFormatSymbols dfs = new DateFormatSymbols(locale);
String[] months = dfs.getMonths();
for (int i = 0; i < months.length; i++) {
if (months[i].toLowerCase(locale).startsWith(monthStr.toLowerCase(locale))) {
return i + 1;
}
}
return -1;
}
/*public Calendar smartParse(String dateStr, Locale locale) {
// Samples:
// 080101 20080101
// 08/01/01 2008/01/01
return null;
}*/
}