/*******************************************************************************
* Portions created by Andrey Onistchuk are copyright (c) 2005 Andrey Onistchuk.
*
* 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:
* Andrey Onistchuk - initial implementation.
* Sebastian Thomschke - small bug fixes.
*******************************************************************************/
package com.tiff.common.ui.datepicker;
import java.text.DateFormatSymbols;
import java.util.Calendar;
import java.util.Date;
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTError;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.TypedListener;
/**
* The date picker panel
*
* changes by sebthom ~ renamed private method doubleClick to onMouseDoubleClick ~ renamed private
* method dateSelected to onDateSelected ~ removed StringBuffer usage in getCurrentMonthName method
* - corrected firstDayOfTheWeek bug (26.06.2004) ~ setDate will throw the Selection event + you can
* use setDate(null) to clear the selection, the calendar will display the current date, but getDate
* will return null until the user explicitely selects a date from the control
*
* changes by Daniel Lutz <danlutz@watz.ch> ~ DatePanel.onPaint(): draw currently selected day/date
* in respect to selectedDate instead of interally selected day of month
*
* @author <a href="mailto:andy@tiff.ru">Andrey Onistchuk</a>
* @version $Revision: 1.2 $
*/
public class DatePicker extends Composite {
// ~ Inner Classes ----------------------------------------------------------
private class DatePanel extends Canvas {
// ~ Instance fields ----------------------------------------------------
private int colSize;
private Display display = Display.getCurrent();
private int rowSize;
private Calendar temp = Calendar.getInstance();
// ~ Constructors -------------------------------------------------------
public DatePanel(Composite parent, int style){
super(parent, style | SWT.NO_BACKGROUND | SWT.NO_REDRAW_RESIZE);
GC gc = new GC(this);
Point p = gc.stringExtent("Q");
gc.dispose();
colSize = p.x * 3;
rowSize = (int) (p.y * 1.4);
addPaintListener(new PaintListener() {
public void paintControl(PaintEvent event){
onPaint(event);
}
});
addControlListener(new ControlAdapter() {
public void controlResized(ControlEvent e){
redraw();
}
});
addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e){
onKeyDown(e);
}
});
addMouseListener(new MouseAdapter() {
public void mouseDoubleClick(MouseEvent e){
onMouseDoubleClick();
}
public void mouseDown(MouseEvent e){
onMouseDown(e);
}
});
addMouseMoveListener(new MouseMoveListener() {
public void mouseMove(MouseEvent e){
onMouseMove(e);
}
});
}
// ~ Methods ------------------------------------------------------------
private int computeOffset(int day){
switch (day) {
case Calendar.MONDAY:
return 1;
case Calendar.TUESDAY:
return 2;
case Calendar.WEDNESDAY:
return 3;
case Calendar.THURSDAY:
return 4;
case Calendar.FRIDAY:
return 5;
case Calendar.SATURDAY:
return 6;
case Calendar.SUNDAY:
return 7;
}
return -1;
}
public Point computeSize(int wHint, int hHint, boolean changed){ // Höhe vergrössert (gerry)
return new Point(colSize * 7, rowSize * 8);
}
/**
* Method drawTextImage.
*
* @param gc
* @param string
* @param x
* @param y
* @param colSize
* @param rowSize
*/
private void drawTextImage(GC gc, String string, int x, int y, int colSize, int rowSize){
gc.fillRectangle(x, y, colSize, rowSize);
gc.drawString(string, x, y, true);
}
private int getDayFromPoint(int x, int y){
int result = -1;
for (int i = 1; i <= 31; i++) {
Point p = getDayPoint(i);
Rectangle r = new Rectangle(p.x, p.y, colSize, rowSize);
if (r.contains(x, y)) {
result = i;
break;
}
}
return result;
}
private String getDayName(int day){
return dateSymbols.getShortWeekdays()[day];
}
private Point getDayPoint(int day){
syncTime();
temp.set(Calendar.DAY_OF_MONTH, 1);
int firstDayOffset = computeOffset(temp.get(Calendar.DAY_OF_WEEK)) - 1;
temp.set(Calendar.DAY_OF_MONTH, day);
int dayOffset = computeOffset(temp.get(Calendar.DAY_OF_WEEK));
int x = (dayOffset - 1) * colSize;
int y = (1 + (((firstDayOffset + day) - 1) / 7)) * rowSize;
return new Point(x, y);
}
private int getMaxDay(){
syncTime();
int day = 28;
for (int i = 0; i < 10; i++) {
temp.set(Calendar.DAY_OF_MONTH, day);
if (temp.get(Calendar.MONTH) != cal.get(Calendar.MONTH)) {
return day - 1;
}
day++;
}
return -1;
}
private void onKeyDown(KeyEvent e){
if (e.character == SWT.ESC) {
onDateSelected(false);
return;
}
if ((e.character == ' ') || (e.character == '\r')) {
onDateSelected(true);
return;
}
int day = cal.get(Calendar.DAY_OF_MONTH);
int month = cal.get(Calendar.MONTH);
int oldDay = day;
int oldMonth = month;
if (e.keyCode == SWT.ARROW_LEFT) {
day--;
}
if (e.keyCode == SWT.ARROW_RIGHT) {
day++;
}
if (e.keyCode == SWT.ARROW_UP) {
day = ((day - 7) < 1 ? oldDay : day - 7);
}
if (e.keyCode == SWT.ARROW_DOWN) {
day = ((day + 7) > getMaxDay() ? oldDay : day + 7);
}
if (e.keyCode == SWT.PAGE_UP) {
month--;
}
if (e.keyCode == SWT.PAGE_DOWN) {
month++;
}
cal.set(Calendar.MONTH, month);
cal.set(Calendar.DAY_OF_MONTH, day);
if ((day != oldDay) || (month != oldMonth)) {
redraw();
if (month != oldMonth) {
updateMonthLabel();
}
}
}
private void onMouseDown(MouseEvent e){
int day = getDayFromPoint(e.x, e.y);
if (day > 0) {
cal.set(Calendar.DAY_OF_MONTH, day);
onDateSelected(true);
updateDate();
}
}
private void onMouseMove(MouseEvent e){
int day = getDayFromPoint(e.x, e.y);
selection = day;
updateDate();
}
private boolean isSelectedDate(Calendar cal){
if (selectedDate == null) {
return false;
}
final Calendar temp = Calendar.getInstance();
temp.setTime(selectedDate);
if (temp.get(Calendar.YEAR) == cal.get(Calendar.YEAR)
&& temp.get(Calendar.MONTH) == cal.get(Calendar.MONTH)
&& temp.get(Calendar.DAY_OF_MONTH) == cal.get(Calendar.DAY_OF_MONTH)) {
return true;
} else {
return false;
}
}
private void onPaint(PaintEvent event){
Rectangle rect = getClientArea();
GC gc0 = event.gc;
Image image = new Image(display, rect.width, rect.height);
GC gc = new GC(image);
gc.setBackground(display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND));
gc.fillRectangle(rect);
int x = 0;
int y = 0;
// draw the weekdays
int firstDayOfWeek = Calendar.getInstance().getFirstDayOfWeek();
for (int i = 0; i < 7; i++) {
if (i == 6) {
gc.setForeground(display.getSystemColor(SWT.COLOR_RED));
}
int dayIndex = firstDayOfWeek + i > 7 ? firstDayOfWeek + i - 7 : firstDayOfWeek + i;
drawTextImage(gc, getDayName(dayIndex), x, 0, colSize, rowSize);
x += colSize;
}
gc.setForeground(display.getSystemColor(SWT.COLOR_BLACK));
y += rowSize;
gc.drawLine(0, 0, rect.width, 0);
gc.drawLine(0, y - 1, rect.width, y - 1);
syncTime();
int day = 1;
while (true) {
temp.set(Calendar.DAY_OF_MONTH, day);
if (temp.get(Calendar.MONTH) != cal.get(Calendar.MONTH)) {
break;
}
int dayOffset = computeOffset(temp.get(Calendar.DAY_OF_WEEK));
Point p = getDayPoint(day);
// if (day == cal.get(Calendar.DAY_OF_MONTH))
if (isSelectedDate(temp)) {
gc.setForeground(display.getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT));
gc.setBackground(display.getSystemColor(SWT.COLOR_LIST_SELECTION));
} else if (day == selection) {
gc.setForeground(display.getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT));
gc.setBackground(display.getSystemColor(SWT.COLOR_BLACK));
} else {
gc.setBackground(display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND));
gc.setForeground(display.getSystemColor(dayOffset == 7 ? SWT.COLOR_RED
: SWT.COLOR_BLACK));
}
drawTextImage(gc, "" + day, p.x, p.y, colSize, rowSize);
day++;
}
gc0.drawImage(image, 0, 0);
gc.dispose();
image.dispose();
}
private void syncTime(){
temp.setTimeInMillis(cal.getTimeInMillis());
}
}
// ~ Instance fields --------------------------------------------------------
private Calendar cal = Calendar.getInstance();
private DatePanel datePanel;
private DateFormatSymbols dateSymbols = new DateFormatSymbols();
private Label monthLabel;
private Date selectedDate;
private int selection = -1;
// ~ Constructors -----------------------------------------------------------
public DatePicker(Composite parent, int style){
super(parent, style);
GridLayout gridLayout = new GridLayout();
gridLayout.numColumns = 5;
gridLayout.verticalSpacing = gridLayout.horizontalSpacing = 0;
gridLayout.marginHeight = gridLayout.marginWidth = 0;
setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_DARK_CYAN));
setLayout(gridLayout);
GridData gridData;
// previous year
Button prevYear = new Button(this, SWT.FLAT);
gridData = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
gridData.heightHint = gridData.widthHint = 20;
prevYear.setLayoutData(gridData);
prevYear.setText("<<");
prevYear.setSelection(false);
prevYear.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e){
cal.roll(Calendar.YEAR, -1);
updateDate();
}
});
// previous month
Button prevMonth = new Button(this, SWT.FLAT);
gridData = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
gridData.heightHint = gridData.widthHint = 20;
prevMonth.setLayoutData(gridData);
prevMonth.setText("<");
prevMonth.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e){
cal.add(Calendar.MONTH, -1);
updateDate();
}
});
// current month
monthLabel = new Label(this, SWT.CENTER);
gridData = new GridData(GridData.FILL_HORIZONTAL | GridData.CENTER);
gridData.heightHint = prevYear.computeSize(20, 20).y;
gridData.grabExcessHorizontalSpace = true;
monthLabel.setLayoutData(gridData);
// next month
Button nextMonth = new Button(this, SWT.FLAT);
gridData = new GridData(GridData.HORIZONTAL_ALIGN_END);
gridData.heightHint = gridData.widthHint = 20;
nextMonth.setLayoutData(gridData);
nextMonth.setText(">");
nextMonth.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e){
cal.add(Calendar.MONTH, 1);
updateDate();
}
});
// next year
Button nextYear = new Button(this, SWT.FLAT);
gridData = new GridData(GridData.HORIZONTAL_ALIGN_END);
gridData.heightHint = gridData.widthHint = 20;
nextYear.setLayoutData(gridData);
nextYear.setText(">>");
nextYear.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e){
cal.roll(Calendar.YEAR, 1);
updateDate();
}
});
// a panel
datePanel = new DatePanel(this, SWT.NONE);
gridData = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
gridData.horizontalSpan = 5;
datePanel.setLayoutData(gridData);
updateDate();
}
// ~ Methods ----------------------------------------------------------------
/**
* Adds the listener to receive events.
*
* @param listener
* the listener
*
* @exception SWTError
* (ERROR_THREAD_INVALID_ACCESS) when called from the wrong thread
* @exception SWTError
* (ERROR_WIDGET_DISPOSED) when the widget has been disposed
* @exception SWTError
* (ERROR_NULL_ARGUMENT) when listener is null
*
* @see SelectionListener
* @see #removeSelectionListener
*/
public void addSelectionListener(SelectionListener listener){
checkWidget();
if (listener == null) {
SWT.error(SWT.ERROR_NULL_ARGUMENT);
}
TypedListener typedListener = new TypedListener(listener);
addListener(SWT.Selection, typedListener);
addListener(SWT.DefaultSelection, typedListener);
}
public Point computeSize(int wHint, int hHint, boolean changed){
Point pSize = datePanel.computeSize(wHint, hHint, changed);
Point labelSize = monthLabel.computeSize(wHint, hHint, changed);
int x = (pSize.x < (labelSize.x + 80) ? labelSize.x + 80 : pSize.x);
int y = (pSize.y < (labelSize.y + 20) ? labelSize.y + 20 : pSize.y);
return new Point(x, y);
}
/**
* gets the name of the current month
*/
private String getCurrentMonthName(){
return getMonthName(cal.get(Calendar.MONTH)) + ", " + cal.get(Calendar.YEAR);
}
/**
* gets the currently selected date
*/
public Date getDate(){
return selectedDate == null ? null : (Date) selectedDate.clone();
}
/**
* gets the name of the month
*
* @param month
*/
private String getMonthName(int month){
return dateSymbols.getMonths()[month];
}
private void onDateSelected(boolean good){
if (good)
selectedDate = cal.getTime();
Event event = new Event();
event.doit = good;
notifyListeners(SWT.Selection, event);
}
private void onMouseDoubleClick(){
Event event = new Event();
event.doit = true;
notifyListeners(SWT.MouseDoubleClick, event);
}
/**
* Removes the listener.
*
* @param listener
* the listener
*
* @exception SWTError
* (ERROR_THREAD_INVALID_ACCESS) when called from the wrong thread
* @exception SWTError
* (ERROR_WIDGET_DISPOSED) when the widget has been disposed
* @exception SWTError
* (ERROR_NULL_ARGUMENT) when listener is null
*
* @see SelectionListener
* @see #addSelectionListener
*/
public void removeSelectionListener(SelectionListener listener){
checkWidget();
if (listener == null) {
SWT.error(SWT.ERROR_NULL_ARGUMENT);
}
removeListener(SWT.Selection, listener);
removeListener(SWT.DefaultSelection, listener);
}
/**
* resets the date picker's date to today
*/
public void reset(){
cal = Calendar.getInstance();
updateDate();
}
/**
* sets the date
*
* @param date
*/
public void setDate(Date date){
if (date == null)
selectedDate = null;
else
selectedDate = (Date) date.clone();
cal.setTime(selectedDate == null ? new Date() : date);
// updateMonthLabel();
// redraw();
updateDate();
}
private void updateDate(){
datePanel.redraw();
updateMonthLabel();
}
private void updateMonthLabel(){
monthLabel.setText(getCurrentMonthName());
}
}