/*******************************************************************************
* Copyright (c) 2005, 2009 Eric Wuillai.
* 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:
* Eric Wuillai (eric@wdev91.com) - initial API and implementation
*******************************************************************************/
package org.eclipse.nebula.widgets.datechooser;
import java.util.Date;
import java.util.Locale;
import org.eclipse.nebula.widgets.formattedtext.DateFormatter;
import org.eclipse.nebula.widgets.formattedtext.DefaultFormatterFactory;
import org.eclipse.nebula.widgets.formattedtext.FormattedText;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Text;
/**
* DateChooserCombo widget. This class represents a date field editor that combines
* a text field and a calendar. Implementation is based on <code>FormattedText</code>
* and <code>DateChooser</code>.<p>
*
* Issues notification when the text content is modified or when a date is
* selected in the calendar.<p>
*
* <dl>
* <dt><b>Styles:</b>
* <dd>BORDER, READ_ONLY, FLAT</dd>
* <dt><b>Events:</b>
* <dd>Modify</dd>
* <dd>Selection</dd>
* </dl>
*/
public class DateChooserCombo extends AbstractCombo {
/** Default image filename */
protected static final String IMAGE = "/org/eclipse/nebula/widgets/datechooser/DateChooserCombo.png";
/** Default image for the button */
protected static Image buttonImage;
/** FormattedText widget for edition of the date */
protected FormattedText formattedText;
/** Flag to set footer visible or not in the popup */
protected boolean footerVisible = false;
/** Flag to set grid visible or not in the popup */
protected int gridVisible = DateChooser.GRID_FULL;
/** Flag to set weeks numbers visible or not */
protected boolean weeksVisible = false;
/** Calendar theme */
protected DateChooserTheme theme;
/** Locale used for localized names and formats */
protected Locale locale;
static {
buttonImage = new Image(Display.getCurrent(),
DateChooserCombo.class.getResourceAsStream(IMAGE));
}
/**
* Constructs a new instance of this class given its parent and a style value
* describing its behavior and appearance.<p>
*
* The widget is initialized with a default image for the button, and a
* default <code>DateFormatter</code>.
*
* @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
*/
public DateChooserCombo(Composite parent, int style) {
super(parent, style);
setTheme(DateChooserTheme.getDefaultTheme());
setImage(buttonImage);
setCreateOnDrop(true);
pack();
}
/**
* Adds the listener to the collection of listeners who will be notified when
* keys are pressed and released on the system keyboard, by sending it one of
* the messages defined in the KeyListener interface.<b>
* The listener is set on the Text widget, as there is no sense to have it
* on the Composite.
*
* @param listener the listener which should be notified
*/
public void addKeyListener(KeyListener listener) {
checkWidget();
text.addKeyListener(listener);
}
/**
* Called just before the popup is dropped. The selected date of the
* calendar is set to the current date present in the formatted text.
*
* @see org.eclipse.nebula.widgets.datechooser.AbstractCombo#beforeDrop()
*/
protected void beforeDrop() {
Date d = (Date) formattedText.getValue();
DateChooser cal = (DateChooser) popupContent;
if ( d != null ) {
cal.setSelectedDate(d);
cal.setFocusOnDate(d);
} else {
cal.clearSelection();
cal.setFocusOnToday(false);
}
}
/**
* Returns the preferred size of the receiver.<br>
* If wHint == SWT.DEFAULT, the preferred size is computed to adjust the width
* to display a date in the MM/dd/yyyy format. If a DateFormatter with more
* larger edit or display patterns is used, the width of the combo must be
* set programmatically.
*
* @param wHint the width hint (can be SWT.DEFAULT)
* @param hHint the height hint (can be SWT.DEFAULT)
* @param changed <code>true</code> if the control's contents have changed, and <code>false</code> otherwise
* @return the preferred size of the control.
*/
public Point computeSize(int wHint, int hHint, boolean changed) {
checkWidget();
Point size = new Point(wHint, hHint);
Point textSize = text.computeSize(SWT.DEFAULT, SWT.DEFAULT, changed);
Point buttonSize = button.computeSize(SWT.DEFAULT, SWT.DEFAULT, changed);
int borderWidth = getBorderWidth();
if ( wHint == SWT.DEFAULT ) {
GC gc = new GC(formattedText.getControl());
int width = gc.textExtent("01/01/2000 ").x;
gc.dispose();
size.x = width + buttonSize.x + 2 * borderWidth;
}
if ( hHint == SWT.DEFAULT ) {
if ( WIN32 ) {
buttonSize.y = ((GridData) button.getLayoutData()).heightHint;
}
size.y = Math.max(textSize.y, buttonSize.y) + 2 * borderWidth;
}
return size;
}
/**
* Creates the button widget. The default appearance with an arrow is
* replaced by a button with an image.
*
* @param style button style
* @return the created Button control
* @see org.eclipse.nebula.widgets.datechooser.AbstractCombo#createButtonControl(int)
*/
protected Button createButtonControl(int style) {
style &= ~(SWT.ARROW | SWT.DOWN);
return new Button(this, style | SWT.PUSH);
}
/**
* Creates the popup content. The content is a <code>DateChooser</code>.
*
* @param parent The parent Composite that will contain the control
* @return The created Control for the popup content
*/
protected Control createPopupContent(Composite parent) {
DateChooser cal = new DateChooser(parent, SWT.NONE);
cal.setTheme(theme);
if ( locale != null ) {
cal.setLocale(locale);
}
cal.setGridVisible(gridVisible);
cal.setFooterVisible(footerVisible);
cal.setWeeksVisible(weeksVisible);
cal.setAutoSelectOnFooter(true);
cal.pack();
return cal;
}
/**
* Creates the text widget. Overrides the default implementation to create a
* <code>FormattedText</code> with the default formatter for <code>Date</code>
* values.
* The formatter is provided by <code>DefaultFormatterFactory</code>. By default
* a <code>DateFormatter</code> is returned. This can be changed by
* registering a new formatter for Date class.
*
* @param style text style
* @return the created Text control
* @see org.eclipse.nebula.widgets.datechooser.AbstractCombo#createTextControl(int)
*/
protected Text createTextControl(int style) {
formattedText = new FormattedText(this, SWT.NONE);
formattedText.setFormatter(DefaultFormatterFactory.createFormatter(Date.class));
return formattedText.getControl();
}
/**
* This method is called when a SWT.Selection is notify in the popup content,
* allowing to update the Text widget content.
*
* @return true if the SWT.Selection event must be propagated, else false
*/
protected boolean doSelection() {
formattedText.setValue(((DateChooser) popupContent).getSelectedDate());
return true;
}
/*
* (non-Javadoc)
* @see org.eclipse.nebula.widgets.datechooser.AbstractCombo#dropDown(boolean)
*/
protected void dropDown(boolean drop) {
super.dropDown(drop);
if ( drop && GTK ) {
/*
* Bug GTK. When the popup is displayed, the calendar does not gain the
* focus until the user click into it with the mouse. Then the keyboard
* is unusable.
*/
popupContent.traverse(SWT.TRAVERSE_TAB_NEXT);
}
}
/**
* Returns the grid visibility status.
*
* @return Returns the grid visible status.
*/
public int getGridVisible() {
checkWidget();
return gridVisible;
}
/**
* Returns the current <code>Date</code> value of the widget.<p>
*
* @return Current value
*/
public Date getValue() {
checkWidget();
return (Date) formattedText.getValue();
}
/**
* Returns true if footer is visible in the popup calendar.
*
* @return <code>true</code> if footer visible, else <code>false</code>
*/
public boolean isFooterVisible() {
checkWidget();
return footerVisible;
}
/**
* Returns true if grid is visible in the calendar popup.
*
* @return Returns the grid visible status.
* @deprecated
*/
public boolean isGridVisible() {
checkWidget();
return gridVisible == DateChooser.GRID_FULL;
}
/**
* Returns true if weeks numbers are visible.
*
* @return Returns the weeks numbers visible status.
*/
public boolean isWeeksVisible() {
checkWidget();
return weeksVisible;
}
/**
* Removes the listener from the collection of listeners who will
* be notified when keys are pressed and released on the system keyboard.
*
* @param listener the listener which should no longer be notified
*/
public void removeKeyListener(KeyListener listener) {
checkWidget();
formattedText.getControl().removeKeyListener(listener);
}
/**
* Sets the footer of popup calendar visible or not. The footer displays the
* today date. It is not visible by default.
*
* @param footerVisible <code>true</code> to set footer visible, else <code>false</code>
*/
public void setFooterVisible(boolean footerVisible) {
checkWidget();
this.footerVisible = footerVisible;
}
/**
* Associates a new <code>DateFormatter</code> to the text widget, replacing
* the default one.
*
* @param formatter date formatter
*/
public void setFormatter(DateFormatter formatter) {
checkWidget();
formattedText.setFormatter(formatter);
this.locale = formatter.getLocale();
}
/**
* Sets the grid visible or not in the calendar popup. By default, the grid
* is visible.
*
* @param gridVisible <code>true</code> to set grid visible, else <code>false</code>
* @deprecated
*/
public void setGridVisible(boolean gridVisible) {
setGridVisible(gridVisible ? DateChooser.GRID_FULL : DateChooser.GRID_NONE);
}
/**
* Sets the grid visible or not. By default, the grid is visible. The
* possible values are GRID_FULL, GRID_LINES and GRID_NONE.
*
* @param gridVisible grid visibility flag
*/
public void setGridVisible(int gridVisible) {
this.gridVisible = gridVisible;
}
/**
* Sets a new image to display on the button, replacing the default one.
*
* @param image new image
*/
public void setImage(Image image) {
checkWidget();
if ( image == null ) SWT.error(SWT.ERROR_NULL_ARGUMENT);
GridData buttonLayout = (GridData) button.getLayoutData();
if ( WIN32 ) {
ImageData id = image.getImageData();
buttonLayout.widthHint = id.width + 4;
buttonLayout.heightHint = id.height + 6;
}
button.setImage(image);
pack();
}
/**
* Sets the locale used both by the input mask and the calendar.
*
* @param locale locale
*/
public void setLocale(Locale locale) {
checkWidget();
this.locale = locale;
((DateFormatter) formattedText.getFormatter()).setLocale(locale);
}
/**
* Sets the theme to apply to the calendar popup.
*
* @param theme new theme (must not be null)
*/
public void setTheme(DateChooserTheme theme) {
checkWidget();
if ( theme == null ) SWT.error(SWT.ERROR_NULL_ARGUMENT);
this.theme = theme;
this.gridVisible = theme.gridVisible;
}
/**
* Sets a new <code>Date</code> value.
*
* @param value new date value
*/
public void setValue(Date value) {
checkWidget();
formattedText.setValue(value);
}
/**
* Sets the weeks numbers visible or not. By default, the weeks are NOT
* visible.
*
* @param weeksVisible <code>true</code> to set weeks visible, else <code>false</code>
*/
public void setWeeksVisible(boolean weeksVisible) {
checkWidget();
this.weeksVisible = weeksVisible;
}
}