/******************************************************************************* * Copyright (c) 2008, 2017 Innoopract Informationssysteme GmbH and others. * 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: * Innoopract Informationssysteme GmbH - initial API and implementation * EclipseSource - ongoing development ******************************************************************************/ package org.eclipse.swt.widgets; import java.text.DateFormat; import java.text.DateFormatSymbols; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.StringTokenizer; import org.eclipse.rap.rwt.RWT; import org.eclipse.rap.rwt.internal.lifecycle.WidgetLCA; import org.eclipse.rap.rwt.internal.textsize.TextSizeUtil; import org.eclipse.rap.rwt.internal.theme.ThemeAdapter; import org.eclipse.rap.rwt.theme.BoxDimensions; import org.eclipse.swt.SWT; import org.eclipse.swt.SWTException; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.internal.widgets.IDateTimeAdapter; import org.eclipse.swt.internal.widgets.datetimekit.DateTimeLCA; import org.eclipse.swt.internal.widgets.datetimekit.DateTimeThemeAdapter; /** * Instances of this class are selectable user interface objects that allow the * user to enter and modify date or time values. * <p> * Note that although this class is a subclass of <code>Composite</code>, it * does not make sense to add children to it, or set a layout on it. * </p> * <dl> * <dt><b>Styles:</b></dt> * <dd>DATE, TIME, CALENDAR, SHORT, MEDIUM, LONG, DROP_DOWN</dd> * <dt><b>Events:</b></dt> * <dd>Selection</dd> * </dl> * <p> * Note: Only one of the styles DATE, TIME, or CALENDAR may be specified, and * only one of the styles SHORT, MEDIUM, or LONG may be specified. * The DROP_DOWN style is a <em>HINT</em>, and it is only valid with the DATE style. * </p> * <p> * IMPORTANT: This class is <em>not</em> intended to be subclassed. * </p> * * @since 1.1.1 */ public class DateTime extends Composite { private final class DateTimeAdapter implements IDateTimeAdapter { @Override public Rectangle getBounds( int widget ) { Rectangle result = new Rectangle( 0, 0, 0, 0 ); switch( widget ) { case WEEKDAY_TEXTFIELD: result = weekdayTextFieldBounds; break; case DAY_TEXTFIELD: result = dayTextFieldBounds; break; case MONTH_TEXTFIELD: result = monthTextFieldBounds; break; case YEAR_TEXTFIELD: result = yearTextFieldBounds; break; case WEEKDAY_MONTH_SEPARATOR: result = separator0Bounds; break; case MONTH_DAY_SEPARATOR: result = separator1Bounds; break; case DAY_YEAR_SEPARATOR: result = separator2Bounds; break; case SPINNER: result = spinnerBounds; break; case HOURS_TEXTFIELD: result = hoursTextFieldBounds; break; case MINUTES_TEXTFIELD: result = minutesTextFieldBounds; break; case SECONDS_TEXTFIELD: result = secondsTextFieldBounds; break; case HOURS_MINUTES_SEPARATOR: result = separator3Bounds; break; case MINUTES_SECONDS_SEPARATOR: result = separator4Bounds; break; case DROP_DOWN_BUTTON: result = dropDownButtonBounds; break; } return result; } @Override public Point getCellSize() { return new Point( cellSize.x, cellSize.y ); } @Override public String[] getMonthNames() { return monthNames; } @Override public String[] getWeekdayNames() { return weekdayNames; } @Override public String[] getWeekdayShortNames() { return weekdayShortNames; } @Override public String getDateSeparator() { return dateSeparator; } @Override public String getDatePattern() { return datePattern; } } private static final int V_PADDING = 1; private static final int H_PADDING = 6; private static final int CALENDAR_HEADER_HEIGHT = 24; private static final int MIN_CELL_WIDTH = 24; private static final int MIN_CELL_HEIGHT = 16; private static final int CELL_PADDING = 2; private final String[] monthNames; private final String[] weekdayNames; private final String[] weekdayShortNames; private final String dateSeparator; private final String datePattern; private final Point cellSize; private transient IDateTimeAdapter dateTimeAdapter; private final Calendar rightNow; private Date minimum; private Date maximum; private boolean ignoreLimits; // Date fields private Rectangle weekdayTextFieldBounds; private Rectangle dayTextFieldBounds; private Rectangle monthTextFieldBounds; private Rectangle yearTextFieldBounds; private Rectangle separator0Bounds; private Rectangle separator1Bounds; private Rectangle separator2Bounds; private Rectangle spinnerBounds; // Time fields private Rectangle hoursTextFieldBounds; private Rectangle minutesTextFieldBounds; private Rectangle secondsTextFieldBounds; private Rectangle separator3Bounds; private Rectangle separator4Bounds; // Date drop down button private Rectangle dropDownButtonBounds; /** * Constructs a new instance of this class given its parent and a style value * describing its behavior and appearance. * <p> * The style value is either one of the style constants defined in class * <code>SWT</code> which is applicable to instances of this class, or must * be built by <em>bitwise OR</em>'ing together (that is, using the * <code>int</code> "|" operator) two or more of those <code>SWT</code> * style constants. The class description lists the style constants that are * applicable to the class. Style bits are also inherited from superclasses. * </p> * * @param parent a composite control which will be the parent of the new * instance (cannot be null) * @param style the style of control to construct * @exception IllegalArgumentException * <ul> * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> * </ul> * @exception SWTException * <ul> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the * thread that created the parent</li> * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed * subclass</li> * </ul> * @see SWT#DATE * @see SWT#TIME * @see SWT#CALENDAR * @see Widget#checkSubclass * @see Widget#getStyle */ public DateTime( Composite parent, int style ) { super( parent, checkStyle( style ) ); rightNow = Calendar.getInstance(); DateFormatSymbols symbols = new DateFormatSymbols( RWT.getLocale() ); monthNames = symbols.getMonths(); weekdayNames = symbols.getWeekdays(); weekdayShortNames = symbols.getShortWeekdays(); dateSeparator = getDateSeparator(); datePattern = getDatePattern( dateSeparator ); cellSize = computeCellSize(); computeSubWidgetsBounds(); } @Override void initState() { removeState( /* CANVAS | */THEME_BACKGROUND ); } /** * Adds the listener to the collection of listeners who will be notified when * the control is selected by the user, by sending it one of the messages * defined in the <code>SelectionListener</code> interface. * <p> * <code>widgetSelected</code> is called when the user changes the control's * value. <code>widgetDefaultSelected</code> is not called. * </p> * * @param listener the listener which should be notified * @exception IllegalArgumentException * <ul> * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> * </ul> * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the * thread that created the receiver</li> * </ul> * @see SelectionListener * @see #removeSelectionListener * @see SelectionEvent */ 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 ); } /** * Removes the listener from the collection of listeners who will be notified * when the control is selected by the user. * * @param listener the listener which should no longer be notified * @exception IllegalArgumentException * <ul> * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> * </ul> * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the * thread that created the receiver</li> * </ul> * @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 ); } /** * Returns the receiver's hours. * <p> * Hours is an integer between 0 and 23. * </p> * * @return an integer between 0 and 23 * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the * thread that created the receiver</li> * </ul> */ public int getHours() { checkWidget(); return rightNow.get( Calendar.HOUR_OF_DAY ); } /** * Returns the receiver's minutes. * <p> * Minutes is an integer between 0 and 59. * </p> * * @return an integer between 0 and 59 * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the * thread that created the receiver</li> * </ul> */ public int getMinutes() { checkWidget(); return rightNow.get( Calendar.MINUTE ); } /** * Returns the receiver's seconds. * <p> * Seconds is an integer between 0 and 59. * </p> * * @return an integer between 0 and 59 * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the * thread that created the receiver</li> * </ul> */ public int getSeconds() { checkWidget(); return rightNow.get( Calendar.SECOND ); } /** * Returns the receiver's date, or day of the month. * <p> * The first day of the month is 1, and the last day depends on the month and * year. * </p> * * @return a positive integer beginning with 1 * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the * thread that created the receiver</li> * </ul> */ public int getDay() { checkWidget(); return rightNow.get( Calendar.DATE ); } /** * Returns the receiver's month. * <p> * The first month of the year is 0, and the last month is 11. * </p> * * @return an integer between 0 and 11 * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the * thread that created the receiver</li> * </ul> */ public int getMonth() { checkWidget(); return rightNow.get( Calendar.MONTH ); } /** * Returns the receiver's year. * <p> * The first year is 1752 and the last year is 9999. * </p> * * @return an integer between 1752 and 9999 * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the * thread that created the receiver</li> * </ul> */ public int getYear() { checkWidget(); return rightNow.get( Calendar.YEAR ); } /** * Returns the minimum value which the receiver will allow or null if no minimum limit is applied. * * @return the minimum value * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the * receiver</li> * </ul> * @since 3.2 */ public Date getMinimum() { checkWidget(); return minimum == null ? null : ( Date )minimum.clone(); } /** * Returns the maximum value which the receiver will allow or null if no maximum limit is applied. * * @return the maximum value * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the * receiver</li> * </ul> * @since 3.2 */ public Date getMaximum() { checkWidget(); return maximum == null ? null : ( Date )maximum.clone(); } /** * Sets the receiver's hours. * <p> * Hours is an integer between 0 and 23. * </p> * * @param hours an integer between 0 and 23 * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the * thread that created the receiver</li> * </ul> */ public void setHours( int hours ) { checkWidget(); if( checkTime( hours, getMinutes(), getSeconds() ) ) { rightNow.set( Calendar.HOUR_OF_DAY, hours ); applyLimits(); } } /** * Sets the receiver's minutes. * <p> * Minutes is an integer between 0 and 59. * </p> * * @param minutes an integer between 0 and 59 * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the * thread that created the receiver</li> * </ul> */ public void setMinutes( int minutes ) { checkWidget(); if( checkTime( getHours(), minutes, getSeconds() ) ) { rightNow.set( Calendar.MINUTE, minutes ); applyLimits(); } } /** * Sets the receiver's seconds. * <p> * Seconds is an integer between 0 and 59. * </p> * * @param seconds an integer between 0 and 59 * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the * thread that created the receiver</li> * </ul> */ public void setSeconds( int seconds ) { checkWidget(); if( checkTime( getHours(), getMinutes(), seconds ) ) { rightNow.set( Calendar.SECOND, seconds ); applyLimits(); } } /** * Sets the receiver's date, or day of the month, to the specified day. * <p> * The first day of the month is 1, and the last day depends on the month and * year. * </p> * * @param day a positive integer beginning with 1 * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the * thread that created the receiver</li> * </ul> */ public void setDay( int day ) { checkWidget(); int month = rightNow.get( Calendar.MONTH ); int year = rightNow.get( Calendar.YEAR ); if( checkDate( year, month, day ) ) { rightNow.set( Calendar.DATE, day ); applyLimits(); } } /** * Sets the receiver's month. * <p> * The first month of the year is 0, and the last month is 11. * </p> * * @param month an integer between 0 and 11 * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the * thread that created the receiver</li> * </ul> */ public void setMonth( int month ) { checkWidget(); int day = rightNow.get( Calendar.DATE ); int year = rightNow.get( Calendar.YEAR ); if( checkDate( year, month, day ) ) { rightNow.set( Calendar.MONTH, month ); applyLimits(); } } /** * Sets the receiver's year. * <p> * The first year is 1752 and the last year is 9999. * </p> * * @param year an integer between 1752 and 9999 * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the * thread that created the receiver</li> * </ul> */ public void setYear( int year ) { checkWidget(); int day = rightNow.get( Calendar.DATE ); int month = rightNow.get( Calendar.MONTH ); if( checkDate( year, month, day ) ) { rightNow.set( Calendar.YEAR, year ); applyLimits(); } } /** * Sets the minimum value. If this value is greater than or equal to the maximum, the value is * ignored. * * @param date the new minimum * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the * receiver</li> * </ul> * @since 3.2 */ public void setMinimum( Date date ) { checkWidget(); if( date == null ) { minimum = null; applyLimits(); } else if( maximum == null || date.getTime() < maximum.getTime() ) { minimum = ( Date )date.clone(); applyLimits(); } } /** * Sets the maximum value. If this value is lower than or equal to the minimum, the value is * ignored. * * @param date the new maximum * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the * receiver</li> * </ul> * @since 3.2 */ public void setMaximum( Date date ) { checkWidget(); if( date == null ) { maximum = null; applyLimits(); } else if( minimum == null || date.getTime() > minimum.getTime() ) { maximum = ( Date )date.clone(); applyLimits(); } } /** * Sets the receiver's year, month, and day in a single operation. * <p> * This is the recommended way to set the date, because setting the year, * month, and day separately may result in invalid intermediate dates. * </p> * * @param year an integer between 1752 and 9999 * @param month an integer between 0 and 11 * @param day a positive integer beginning with 1 * * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that * created the receiver</li> * </ul> * * @since 1.2 */ public void setDate( int year, int month, int day ) { checkWidget(); if( checkDate( year, month, day ) ) { // reset ignoreLimits = true; setYear( 9996 ); setMonth( 0 ); setDay( 1 ); // set setYear( year ); setMonth( month ); setDay( day ); ignoreLimits = false; applyLimits(); } } /** * Sets the receiver's hours, minutes, and seconds in a single operation. * * @param hours an integer between 0 and 23 * @param minutes an integer between 0 and 59 * @param seconds an integer between 0 and 59 * * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that * created the receiver</li> * </ul> * * @since 1.2 */ public void setTime( int hours, int minutes, int seconds ) { checkWidget(); if( checkTime( hours, minutes, seconds ) ) { setHours( hours ); setMinutes( minutes ); setSeconds( seconds ); } } @Override public void setFont( Font font ) { if( font != getFont() ) { super.setFont( font ); } computeSubWidgetsBounds(); } @Override @SuppressWarnings("unchecked") public <T> T getAdapter( Class<T> adapter ) { if( adapter == IDateTimeAdapter.class ) { if( dateTimeAdapter == null ) { dateTimeAdapter = new DateTimeAdapter(); } return ( T )dateTimeAdapter; } if( adapter == WidgetLCA.class ) { return ( T )DateTimeLCA.INSTANCE; } return super.getAdapter( adapter ); } @Override public Point computeSize( int wHint, int hHint, boolean changed ) { checkWidget(); int width = 0, height = 0; if( wHint == SWT.DEFAULT || hHint == SWT.DEFAULT ) { Point size = computeSubWidgetsBounds(); width = size.x; height = size.y; } if( width == 0 ) { width = DEFAULT_WIDTH; } if( height == 0 ) { height = DEFAULT_HEIGHT; } if( wHint != SWT.DEFAULT ) { width = wHint; } if( hHint != SWT.DEFAULT ) { height = hHint; } return new Point( width, height ); } @Override public void setBounds( Rectangle bounds ) { super.setBounds( bounds ); // [if] Recalculate the sub widgets bounds important for using it in FillLayout where the // DateTime#computeSize() is not call. computeSubWidgetsBounds(); } private Point computeCellSize() { int width = MIN_CELL_WIDTH; int height = MIN_CELL_HEIGHT; for( int i = 0; i < weekdayShortNames.length; i++ ) { Point nameSize = TextSizeUtil.stringExtent( getFont(), weekdayShortNames[ i ] ); width = Math.max( width, nameSize.x + CELL_PADDING ); height = Math.max( height, nameSize.y + CELL_PADDING ); } return new Point( width, height ); } private Point computeSubWidgetsBounds() { Font font = getFont(); int width = 0, height = 0; BoxDimensions padding = getFieldPadding(); BoxDimensions border = getBorder(); if( ( style & SWT.CALENDAR ) != 0 ) { width = cellSize.x * 8 + border.left + border.right; height = cellSize.y * 7 + CALENDAR_HEADER_HEIGHT + border.top + border.bottom; } else if( ( style & SWT.DATE ) != 0 ) { Point prefSize = new Point( 0, 0 ); if( datePattern.equals( "MDY" ) ) { prefSize = computeMDYBounds(); } else if( datePattern.equals( "DMY" ) ) { prefSize = computeDMYBounds(); } else { if( ( style & SWT.MEDIUM ) != 0 ) { prefSize = computeYMDBounds(); } else { prefSize = computeMDYBounds(); } } // Overall default widget size width = prefSize.x + border.left + border.right; height = prefSize.y + border.top + border.bottom; } else if( ( style & SWT.TIME ) != 0 ) { // Hours text field hoursTextFieldBounds = new Rectangle( padding.left, padding.top, 0, 0 ); hoursTextFieldBounds.width = TextSizeUtil.stringExtent( font, "88" ).x + H_PADDING; hoursTextFieldBounds.height = TextSizeUtil.stringExtent( font, "88" ).y + V_PADDING; // Hours minutes separator separator3Bounds = new Rectangle( 0, padding.top, 0, 0 ); separator3Bounds.x = hoursTextFieldBounds.x + hoursTextFieldBounds.width; separator3Bounds.width = TextSizeUtil.stringExtent( font, ":" ).x; separator3Bounds.height = hoursTextFieldBounds.height; // Minutes text field minutesTextFieldBounds = new Rectangle( 0, padding.top, 0, 0 ); minutesTextFieldBounds.x = separator3Bounds.x + separator3Bounds.width; minutesTextFieldBounds.width = hoursTextFieldBounds.width; minutesTextFieldBounds.height = hoursTextFieldBounds.height; // Minutes seconds separator separator4Bounds = new Rectangle( 0, padding.top, 0, 0 ); separator4Bounds.x = minutesTextFieldBounds.x + minutesTextFieldBounds.width; separator4Bounds.width = separator3Bounds.width; separator4Bounds.height = hoursTextFieldBounds.height; // Seconds text field secondsTextFieldBounds = new Rectangle( 0, padding.top, 0, 0 ); secondsTextFieldBounds.x = separator4Bounds.x + separator4Bounds.width; secondsTextFieldBounds.width = hoursTextFieldBounds.width; secondsTextFieldBounds.height = hoursTextFieldBounds.height; // The spinner bounds spinnerBounds = new Rectangle( 0, 0, 0, 0 ); spinnerBounds.x = minutesTextFieldBounds.x + minutesTextFieldBounds.width + padding.left; if( ( style & SWT.MEDIUM ) != 0 || ( style & SWT.LONG) != 0 ) { spinnerBounds.x = secondsTextFieldBounds.x + secondsTextFieldBounds.width + padding.left; } spinnerBounds.width = getSpinnerButtonWidth(); spinnerBounds.height = hoursTextFieldBounds.height + padding.top + padding.bottom; // Overall default widget size width = spinnerBounds.x + spinnerBounds.width + border.left + border.right; height = spinnerBounds.height + border.top + border.bottom; } adjustButtonsBounds(); return new Point( width, height ); } private Point computeMDYBounds() { Font font = getFont(); BoxDimensions padding = getFieldPadding(); // The weekday text field bounds weekdayTextFieldBounds = new Rectangle( padding.left, padding.top, 0, 0 ); if( ( style & SWT.LONG ) != 0 ) { weekdayTextFieldBounds.width = getMaxWidth( weekdayNames ) + H_PADDING + 2; } weekdayTextFieldBounds.height = TextSizeUtil.stringExtent( font, weekdayNames[1] ).y + V_PADDING; // The weekday month separator bounds separator0Bounds = new Rectangle( 0, padding.top, 0, 0 ); separator0Bounds.x = weekdayTextFieldBounds.x + weekdayTextFieldBounds.width; if( ( style & SWT.LONG ) != 0 ) { separator0Bounds.width = TextSizeUtil.stringExtent( font, "," ).x; } separator0Bounds.height = weekdayTextFieldBounds.height; // The month text field bounds monthTextFieldBounds = new Rectangle( 0, padding.top, 0, 0 ); monthTextFieldBounds.x = separator0Bounds.x + separator0Bounds.width; if( ( style & SWT.MEDIUM ) != 0 ) { monthTextFieldBounds.width = TextSizeUtil.stringExtent( font, "88" ).x + H_PADDING; } else { monthTextFieldBounds.width = getMaxWidth( monthNames ) + H_PADDING + 2; } monthTextFieldBounds.height = weekdayTextFieldBounds.height; // The month date separator bounds separator1Bounds = new Rectangle( 0, padding.top, 0, 0 ); separator1Bounds.x = monthTextFieldBounds.x + monthTextFieldBounds.width; if( ( style & SWT.MEDIUM ) != 0 ) { separator1Bounds.width = TextSizeUtil.stringExtent( font, dateSeparator ).x; } separator1Bounds.height = weekdayTextFieldBounds.height; // The date text field bounds dayTextFieldBounds = new Rectangle( 0, padding.top, 0, 0 ); dayTextFieldBounds.x = separator1Bounds.x + separator1Bounds.width; if( ( style & SWT.SHORT ) == 0 ) { dayTextFieldBounds.width = TextSizeUtil.stringExtent( font, "88" ).x + H_PADDING; } dayTextFieldBounds.height = weekdayTextFieldBounds.height; // The date year separator bounds separator2Bounds = new Rectangle( 0, padding.top, 0, 0 ); separator2Bounds.x = dayTextFieldBounds.x + dayTextFieldBounds.width; if( ( style & SWT.MEDIUM ) != 0 ) { separator2Bounds.width = TextSizeUtil.stringExtent( font, dateSeparator ).x; } else { separator2Bounds.width = TextSizeUtil.stringExtent( font, "," ).x; } separator2Bounds.height = weekdayTextFieldBounds.height; // The year text field bounds yearTextFieldBounds = new Rectangle( 0, padding.top, 0, 0 ); yearTextFieldBounds.x = separator2Bounds.x + separator2Bounds.width; yearTextFieldBounds.width = TextSizeUtil.stringExtent( font, "8888" ).x + H_PADDING; yearTextFieldBounds.height = weekdayTextFieldBounds.height; // The spinner bounds spinnerBounds = new Rectangle( 0, 0, 0, 0 ); spinnerBounds.x = yearTextFieldBounds.x + yearTextFieldBounds.width + padding.left; spinnerBounds.width = getSpinnerButtonWidth(); spinnerBounds.height = weekdayTextFieldBounds.height + padding.top + padding.bottom; // The drop-down button bounds dropDownButtonBounds = new Rectangle( spinnerBounds.x, spinnerBounds.y, getDropDownButtonWidth(), spinnerBounds.height ); // Overall default widget size int width = spinnerBounds.x; int height = spinnerBounds.height; if( ( style & SWT.DROP_DOWN ) == 0 ) { width += spinnerBounds.width; } else { width += dropDownButtonBounds.width; } return new Point( width, height ); } private Point computeDMYBounds() { Font font = getFont(); BoxDimensions padding = getFieldPadding(); // The weekday text field bounds weekdayTextFieldBounds = new Rectangle( padding.left, padding.top, 0, 0 ); if( ( style & SWT.LONG ) != 0 ) { weekdayTextFieldBounds.width = getMaxWidth( weekdayNames ) + H_PADDING + 2; } weekdayTextFieldBounds.height = TextSizeUtil.stringExtent( font, weekdayNames[1] ).y + V_PADDING; // The weekday day separator bounds separator0Bounds = new Rectangle( 0, padding.top, 0, 0 ); separator0Bounds.x = weekdayTextFieldBounds.x + weekdayTextFieldBounds.width; if( ( style & SWT.LONG ) != 0 ) { separator0Bounds.width = TextSizeUtil.stringExtent( font, "," ).x; } separator0Bounds.height = weekdayTextFieldBounds.height; // The day text field bounds dayTextFieldBounds = new Rectangle( 0, padding.top, 0, 0 ); dayTextFieldBounds.x = separator0Bounds.x + separator0Bounds.width; if( ( style & SWT.SHORT ) == 0 ) { dayTextFieldBounds.width = TextSizeUtil.stringExtent( font, "88" ).x + H_PADDING; } dayTextFieldBounds.height = weekdayTextFieldBounds.height; // The day month separator bounds separator1Bounds = new Rectangle( 0, padding.top, 0, 0 ); separator1Bounds.x = dayTextFieldBounds.x + dayTextFieldBounds.width; if( ( style & SWT.MEDIUM ) != 0 ) { separator1Bounds.width = TextSizeUtil.stringExtent( font, dateSeparator ).x; } separator1Bounds.height = weekdayTextFieldBounds.height; // The month text field bounds monthTextFieldBounds = new Rectangle( 0, padding.top, 0, 0 ); monthTextFieldBounds.x = separator1Bounds.x + separator1Bounds.width; if( ( style & SWT.MEDIUM ) != 0 ) { monthTextFieldBounds.width = TextSizeUtil.stringExtent( font, "88" ).x + H_PADDING; } else { monthTextFieldBounds.width = getMaxWidth( monthNames ) + H_PADDING + 2; } monthTextFieldBounds.height = weekdayTextFieldBounds.height; // The month year separator bounds separator2Bounds = new Rectangle( 0, padding.top, 0, 0 ); separator2Bounds.x = monthTextFieldBounds.x + monthTextFieldBounds.width; if( ( style & SWT.MEDIUM ) != 0 ) { separator2Bounds.width = TextSizeUtil.stringExtent( font, dateSeparator ).x; } else { separator2Bounds.width = TextSizeUtil.stringExtent( font, "," ).x; } separator2Bounds.height = weekdayTextFieldBounds.height; // The year text field bounds yearTextFieldBounds = new Rectangle( 0, padding.top, 0, 0 ); yearTextFieldBounds.x = separator2Bounds.x + separator2Bounds.width; yearTextFieldBounds.width = TextSizeUtil.stringExtent( font, "8888" ).x + H_PADDING; yearTextFieldBounds.height = weekdayTextFieldBounds.height; // The spinner bounds spinnerBounds = new Rectangle( 0, 0, 0, 0 ); spinnerBounds.x = yearTextFieldBounds.x + yearTextFieldBounds.width + padding.left; spinnerBounds.width = getSpinnerButtonWidth(); spinnerBounds.height = weekdayTextFieldBounds.height + padding.top + padding.bottom; // The drop-down button bounds dropDownButtonBounds = new Rectangle( spinnerBounds.x, spinnerBounds.y, getDropDownButtonWidth(), spinnerBounds.height ); // Overall default widget size int width = spinnerBounds.x; int height = spinnerBounds.height; if( ( style & SWT.DROP_DOWN ) == 0 ) { width += spinnerBounds.width; } else { width += dropDownButtonBounds.width; } return new Point( width, height ); } private Point computeYMDBounds() { Font font = getFont(); BoxDimensions padding = getFieldPadding(); // The weekday text field bounds weekdayTextFieldBounds = new Rectangle( padding.left, padding.top, 0, 0 ); if( ( style & SWT.LONG ) != 0 ) { weekdayTextFieldBounds.width = getMaxWidth( weekdayNames ) + H_PADDING + 2; } weekdayTextFieldBounds.height = TextSizeUtil.stringExtent( font, weekdayNames[1] ).y + V_PADDING; // The weekday day separator bounds separator0Bounds = new Rectangle( 0, padding.top, 0, 0 ); separator0Bounds.x = weekdayTextFieldBounds.x + weekdayTextFieldBounds.width; if( ( style & SWT.LONG ) != 0 ) { separator0Bounds.width = TextSizeUtil.stringExtent( font, "," ).x; } separator0Bounds.height = weekdayTextFieldBounds.height; // The year text field bounds yearTextFieldBounds = new Rectangle( 0, padding.top, 0, 0 ); yearTextFieldBounds.x = separator0Bounds.x + separator0Bounds.width; yearTextFieldBounds.width = TextSizeUtil.stringExtent( font, "8888" ).x + H_PADDING; yearTextFieldBounds.height = weekdayTextFieldBounds.height; // The year month separator bounds separator1Bounds = new Rectangle( 0, padding.top, 0, 0 ); separator1Bounds.x = yearTextFieldBounds.x + yearTextFieldBounds.width; if( ( style & SWT.MEDIUM ) != 0 ) { separator1Bounds.width = TextSizeUtil.stringExtent( font, dateSeparator ).x; } // The month text field bounds monthTextFieldBounds = new Rectangle( 0, padding.top, 0, 0 ); monthTextFieldBounds.x = separator1Bounds.x + separator1Bounds.width; if( ( style & SWT.MEDIUM ) != 0 ) { monthTextFieldBounds.width = TextSizeUtil.stringExtent( font, "88" ).x + H_PADDING; } else { monthTextFieldBounds.width = getMaxWidth( monthNames ) + H_PADDING + 2; } monthTextFieldBounds.height = weekdayTextFieldBounds.height; // The month day separator bounds separator2Bounds = new Rectangle( 0, padding.top, 0, 0 ); separator2Bounds.x = monthTextFieldBounds.x + monthTextFieldBounds.width; if( ( style & SWT.MEDIUM ) != 0 ) { separator2Bounds.width = TextSizeUtil.stringExtent( font, dateSeparator ).x; } else { separator2Bounds.width = TextSizeUtil.stringExtent( font, "," ).x; } separator2Bounds.height = weekdayTextFieldBounds.height; // The day text field bounds dayTextFieldBounds = new Rectangle( 0, padding.top, 0, 0 ); dayTextFieldBounds.x = separator2Bounds.x + separator2Bounds.width; if( ( style & SWT.SHORT ) == 0 ) { dayTextFieldBounds.width = TextSizeUtil.stringExtent( font, "88" ).x + H_PADDING; } dayTextFieldBounds.height = weekdayTextFieldBounds.height; separator1Bounds.height = weekdayTextFieldBounds.height; // The spinner bounds spinnerBounds = new Rectangle( 0, 0, 0, 0 ); spinnerBounds.x = dayTextFieldBounds.x + dayTextFieldBounds.width + padding.left; spinnerBounds.width = getSpinnerButtonWidth(); spinnerBounds.height = weekdayTextFieldBounds.height + padding.top + padding.bottom; // The drop-down button bounds dropDownButtonBounds = new Rectangle( spinnerBounds.x, spinnerBounds.y, getDropDownButtonWidth(), spinnerBounds.height ); // Overall default widget size int width = spinnerBounds.x; int height = spinnerBounds.height; if( ( style & SWT.DROP_DOWN ) == 0 ) { width += spinnerBounds.width; } else { width += dropDownButtonBounds.width; } return new Point( width, height ); } private void adjustButtonsBounds() { Point size = getSize(); BoxDimensions border = getBorder(); if( ( style & SWT.DROP_DOWN ) != 0 ) { dropDownButtonBounds.x = size.x - ( border.left + border.right ) - dropDownButtonBounds.width; dropDownButtonBounds.height = size.y - ( border.top + border.bottom ); } else if( ( style & SWT.DATE ) != 0 || ( style & SWT.TIME ) != 0 ) { spinnerBounds.x = size.x - ( border.left + border.right ) - spinnerBounds.width; spinnerBounds.height = size.y - ( border.top + border.bottom ); } } private int getSpinnerButtonWidth() { return getDateTimeThemeAdapter().getSpinnerButtonWidth( this ); } private DateTimeThemeAdapter getDateTimeThemeAdapter() { return ( DateTimeThemeAdapter )getAdapter( ThemeAdapter.class ); } private int getDropDownButtonWidth() { return getDateTimeThemeAdapter().getDropDownButtonWidth( this ); } private BoxDimensions getFieldPadding() { return getDateTimeThemeAdapter().getFieldPadding( this ); } @Override String getNameText() { return "DateTime"; } private static int getDaysInMonth( int month, int year ) { GregorianCalendar cal = new GregorianCalendar( year, month, 1 ); return cal.getActualMaximum( Calendar.DAY_OF_MONTH ); } private int getMaxWidth( String[] strings ) { Font font = getFont(); int result = 0; for( int i = 0; i < strings.length; i++ ) { int width = TextSizeUtil.stringExtent( font, strings[ i ] ).x; result = Math.max( result, width ); } return result; } private static String getDateSeparator() { DateFormat dateFormat = DateFormat.getDateInstance( DateFormat.SHORT, RWT.getLocale() ); String datePattern = ( ( SimpleDateFormat )dateFormat ).toPattern(); String result = ""; int index = 0; while( Character.isLetter( datePattern.charAt( index ) ) ) { index++; } result = Character.toString( datePattern.charAt( index ) ); return result; } private static String getDatePattern( String dateSeparator ) { DateFormat format = DateFormat.getDateInstance( DateFormat.SHORT, RWT.getLocale() ); String datePattern = ( ( SimpleDateFormat )format ).toPattern(); String result = ""; StringTokenizer tokenizer = new StringTokenizer( datePattern, dateSeparator ); while ( tokenizer.hasMoreTokens() ) { String token = tokenizer.nextToken(); result += Character.toString( token.charAt( 0 ) ); } return result.toUpperCase(); } private static boolean checkDate( int year, int month, int day ) { int daysInMonth = getDaysInMonth( month, year ); boolean validYear = ( year >= 1752 && year <= 9999 && day <= daysInMonth ); boolean validMonth = ( month >= 0 && month <= 11 && day <= daysInMonth ); boolean validDay = ( day >= 1 && day <= daysInMonth ); return validYear && validMonth && validDay; } private static boolean checkTime( int hours, int minutes, int seconds ) { boolean validHours = ( hours >= 0 && hours <= 23 ); boolean validMinutes = ( minutes >= 0 && minutes <= 59 ); boolean validSeconds = ( seconds >= 0 && seconds <= 59 ); return validHours && validMinutes && validSeconds; } static int checkStyle( int value ) { /* * Even though it is legal to create this widget with scroll bars, they * serve no useful purpose because they do not automatically scroll the * widget's client area. The fix is to clear the SWT style. */ int style = value; style &= ~( SWT.H_SCROLL | SWT.V_SCROLL ); style = checkBits( style, SWT.DATE, SWT.TIME, SWT.CALENDAR, 0, 0, 0 ); style = checkBits( style, SWT.MEDIUM, SWT.SHORT, SWT.LONG, 0, 0, 0 ); if( ( style & SWT.DATE ) == 0 ) { style &= ~SWT.DROP_DOWN; } return style; } private void applyLimits() { if( !ignoreLimits ) { if( maximum != null && rightNow.getTimeInMillis() > maximum.getTime() ) { rightNow.setTime( maximum ); } if( minimum != null && rightNow.getTimeInMillis() < minimum.getTime() ) { rightNow.setTime( minimum ); } } } }