001: /*
002: *
003: * JMoney - A Personal Finance Manager
004: * Copyright (c) 2004 Nigel Westbury <westbury@users.sourceforge.net>
005: *
006: *
007: * This program is free software; you can redistribute it and/or modify
008: * it under the terms of the GNU General Public License as published by
009: * the Free Software Foundation; either version 2 of the License, or
010: * (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
020: *
021: */
022:
023: package net.sf.jmoney.fields;
024:
025: import java.util.Calendar;
026: import java.util.Date;
027:
028: import net.sf.jmoney.JMoneyPlugin;
029: import net.sf.jmoney.VerySimpleDateFormat;
030:
031: import org.eclipse.jface.resource.ImageDescriptor;
032: import org.eclipse.swt.SWT;
033: import org.eclipse.swt.events.KeyAdapter;
034: import org.eclipse.swt.events.KeyEvent;
035: import org.eclipse.swt.events.ModifyListener;
036: import org.eclipse.swt.events.SelectionAdapter;
037: import org.eclipse.swt.events.SelectionEvent;
038: import org.eclipse.swt.events.SelectionListener;
039: import org.eclipse.swt.events.ShellAdapter;
040: import org.eclipse.swt.events.ShellEvent;
041: import org.eclipse.swt.graphics.Image;
042: import org.eclipse.swt.graphics.Point;
043: import org.eclipse.swt.graphics.Rectangle;
044: import org.eclipse.swt.layout.RowLayout;
045: import org.eclipse.swt.widgets.Button;
046: import org.eclipse.swt.widgets.Composite;
047: import org.eclipse.swt.widgets.DateTime;
048: import org.eclipse.swt.widgets.Display;
049: import org.eclipse.swt.widgets.Layout;
050: import org.eclipse.swt.widgets.Shell;
051: import org.eclipse.swt.widgets.Text;
052:
053: /**
054: * A control for entering dates. This control contains both a
055: * text box and a button to the right that pops up a calendar.
056: * <P>
057: * This control uses the current date format set in the JMoney
058: * preferences.
059: *
060: * @author Nigel Westbury
061: */
062: public class DateControl extends DateComposite {
063:
064: // TODO Listen to date format changes.
065: private VerySimpleDateFormat fDateFormat = new VerySimpleDateFormat(
066: JMoneyPlugin.getDefault().getDateFormat());
067:
068: /**
069: * The text box containing the date
070: */
071: private Text textControl;
072:
073: /**
074: * The small button to the right of the date that brings up the date picker
075: */
076: private Button button;
077:
078: static private Image threeDotsImage = null;
079:
080: private DateTime swtcal = null;
081:
082: /**
083: * @param parent
084: * @param style
085: */
086: public DateControl(final Composite parent) {
087: super (parent, SWT.NULL);
088:
089: setBackgroundMode(SWT.INHERIT_FORCE);
090:
091: setLayout(new DialogCellLayout());
092:
093: textControl = new Text(this , SWT.LEFT);
094:
095: textControl.addKeyListener(new KeyAdapter() {
096: @Override
097: public void keyPressed(KeyEvent e) {
098: // CTRL + and CTRL - increment and decrement the date respectively.
099: // It would be even easier for the user the CTRL did not have to be
100: // pressed, but then it would not be possible to have a date format
101: // that contains '-'.
102: if (e.stateMask == SWT.CONTROL
103: && (e.character == '+' || e.character == '-')) {
104:
105: Calendar calendar = Calendar.getInstance();
106: calendar.setTime(fDateFormat.parse(textControl
107: .getText()));
108:
109: if (e.character == '+') {
110: calendar.add(Calendar.DAY_OF_MONTH, 1);
111: } else {
112: calendar.add(Calendar.DAY_OF_MONTH, -1);
113: }
114:
115: textControl.setText(fDateFormat.format(calendar
116: .getTime()));
117:
118: e.doit = false;
119: }
120: }
121: });
122:
123: button = new Button(this , SWT.DOWN);
124: if (threeDotsImage == null) {
125: ImageDescriptor descriptor = JMoneyPlugin
126: .createImageDescriptor("icons/dots_button.gif");
127: threeDotsImage = descriptor.createImage();
128: }
129: button.setImage(threeDotsImage);
130:
131: button.addSelectionListener(new SelectionAdapter() {
132: @Override
133: public void widgetSelected(SelectionEvent event) {
134: final Shell shell = new Shell(parent.getShell(),
135: SWT.ON_TOP);
136: shell.setLayout(new RowLayout());
137: swtcal = new DateTime(shell, SWT.CALENDAR);
138:
139: // Set the currently set date into the calendar control
140: // (If the parse method returned null then the text control did not
141: // contain a valid date. In this case no date is set into the
142: // date picker).
143: try {
144: String t = textControl.getText();
145: Date date = fDateFormat.parse(t);
146: Calendar calendar = Calendar.getInstance();
147: calendar.setTime(date);
148: swtcal.setYear(calendar.get(Calendar.YEAR));
149: swtcal.setMonth(calendar.get(Calendar.MONTH));
150: swtcal.setDay(calendar.get(Calendar.DAY_OF_MONTH));
151: } catch (IllegalArgumentException e) {
152: // The date format was invalid.
153: // Ignore this error (the calendar control
154: // does not require a date to be set).
155: }
156:
157: swtcal.addSelectionListener(new SelectionListener() {
158: public void widgetDefaultSelected(SelectionEvent e) {
159: // TODO Auto-generated method stub
160:
161: }
162:
163: public void widgetSelected(SelectionEvent e) {
164: Calendar cal = Calendar.getInstance();
165:
166: /*
167: * First reset all fields. Otherwise differences in the time
168: * part, even if the difference is only milliseconds, will cause
169: * the date comparisons to fail.
170: *
171: * Note also it is critical that whatever is done here is exactly the
172: * same as that done in VerySimpleDateFormat, otherwise dates will not
173: * match. For example, if you replace clear() here with setTimeInMillis(0)
174: * then we get a different object (because data other than the date and time
175: * such as time zone information will be different).
176: */
177: cal.clear();
178:
179: cal.set(swtcal.getYear(), swtcal.getMonth(),
180: swtcal.getDay());
181: Date date = cal.getTime();
182: textControl.setText(fDateFormat.format(date));
183: }
184: });
185:
186: shell.pack();
187:
188: // Position the calendar shell below the date control,
189: // unless the date control is so near the bottom of the display that
190: // the calendar control would go off the bottom of the display,
191: // in which case position the calendar shell above the date control.
192: Display display = getDisplay();
193: Rectangle rect = display.map(parent, null, getBounds());
194: int calendarShellHeight = shell.getSize().y;
195: if (rect.y + rect.height + calendarShellHeight <= display
196: .getBounds().height) {
197: shell.setLocation(rect.x, rect.y + rect.height);
198: } else {
199: shell.setLocation(rect.x, rect.y
200: - calendarShellHeight);
201: }
202:
203: shell.open();
204:
205: shell.addShellListener(new ShellAdapter() {
206: @Override
207: public void shellDeactivated(ShellEvent e) {
208: shell.close();
209: swtcal = null;
210: }
211: });
212: }
213: });
214: }
215:
216: /**
217: * Internal class for laying out the dialog.
218: */
219: private class DialogCellLayout extends Layout {
220: @Override
221: public void layout(Composite editor, boolean force) {
222: Rectangle bounds = editor.getClientArea();
223: Point size = textControl.computeSize(SWT.DEFAULT,
224: SWT.DEFAULT, force);
225: textControl.setBounds(0, 0, bounds.width - size.y,
226: bounds.height);
227: button.setBounds(bounds.width - size.y, 0, size.y,
228: bounds.height);
229: }
230:
231: @Override
232: public Point computeSize(Composite editor, int wHint,
233: int hHint, boolean force) {
234: if (JMoneyPlugin.DEBUG)
235: System.out.println("wHint =" + wHint + ", " + hHint);
236: if (wHint != SWT.DEFAULT && hHint != SWT.DEFAULT)
237: return new Point(wHint, hHint);
238: Point contentsSize = textControl.computeSize(SWT.DEFAULT,
239: SWT.DEFAULT, force);
240: Point buttonSize = button.computeSize(SWT.DEFAULT,
241: SWT.DEFAULT, force);
242: if (JMoneyPlugin.DEBUG)
243: System.out.println("contents =" + contentsSize.x + ", "
244: + contentsSize.y);
245: if (JMoneyPlugin.DEBUG)
246: System.out.println("contents =" + buttonSize.x + ", "
247: + buttonSize.y);
248: // Just return the button width to ensure the button is not clipped
249: // if the label is long. Date text needs 60
250: // The label will just use whatever extra width there is
251: Point result = new Point(60 + buttonSize.x,
252: // Math.max(contentsSize.y, buttonSize.y));
253: contentsSize.y);
254: return result;
255: }
256: }
257:
258: /**
259: * @param object
260: */
261: @Override
262: public void setDate(Date date) {
263: if (date == null) {
264: textControl.setText("");
265: } else {
266: this .textControl.setText(fDateFormat.format(date));
267: }
268: }
269:
270: /**
271: * @return the date, or null if a valid date is not set in
272: * the control
273: */
274: @Override
275: public Date getDate() {
276: String text = textControl.getText();
277: try {
278: return fDateFormat.parse(text);
279: } catch (IllegalArgumentException e) {
280: return null;
281: }
282: }
283:
284: /**
285: * @param listener
286: */
287: public void addModifyListener(ModifyListener listener) {
288: textControl.addModifyListener(listener);
289: }
290:
291: /**
292: * @param listener
293: */
294: public void removeModifyListener(ModifyListener listener) {
295: textControl.removeModifyListener(listener);
296: }
297: }
|