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.reconciliation.reconcilePage;
024:
025: import java.util.Calendar;
026: import java.util.Date;
027: import java.util.regex.Matcher;
028: import java.util.regex.Pattern;
029:
030: import net.sf.jmoney.AlternativeContentLayout;
031: import net.sf.jmoney.JMoneyPlugin;
032: import net.sf.jmoney.fields.DateControl;
033: import net.sf.jmoney.reconciliation.BankStatement;
034:
035: import org.eclipse.jface.dialogs.Dialog;
036: import org.eclipse.jface.dialogs.IDialogConstants;
037: import org.eclipse.swt.SWT;
038: import org.eclipse.swt.events.ModifyEvent;
039: import org.eclipse.swt.events.ModifyListener;
040: import org.eclipse.swt.events.SelectionAdapter;
041: import org.eclipse.swt.events.SelectionEvent;
042: import org.eclipse.swt.graphics.Rectangle;
043: import org.eclipse.swt.layout.GridData;
044: import org.eclipse.swt.layout.GridLayout;
045: import org.eclipse.swt.widgets.Button;
046: import org.eclipse.swt.widgets.Composite;
047: import org.eclipse.swt.widgets.Control;
048: import org.eclipse.swt.widgets.Label;
049: import org.eclipse.swt.widgets.Shell;
050: import org.eclipse.swt.widgets.Text;
051:
052: /**
053: * An input dialog for soliciting a statement number or statement date from the user.
054: *
055: * @author Nigel Westbury
056: */
057: class NewStatementDialog extends Dialog {
058: /**
059: * The last statement to exist before this request to create a new statement. This statement is
060: * used to determine a default value for the new statement and also to determine the method by which
061: * statements in this account are identified (by number or by date).
062: */
063: private BankStatement lastStatement;
064:
065: /**
066: * The input value; the empty string by default.
067: */
068: private BankStatement value = null;
069:
070: /**
071: * Ok button widget.
072: */
073: private Button okButton;
074:
075: /**
076: * Error message label widget.
077: */
078: private Text errorMessageText;
079:
080: private int statementIdType;
081: private static final int NOT_YET_DETERMINED = 0;
082: private static final int BY_NUMBER = 1;
083: private static final int BY_DATE = 2;
084: private Composite byDateComposite;
085: private Composite byNumberComposite;
086:
087: private AlternativeContentLayout alternativeContentLayout;
088:
089: /**
090: * Input widget for statement number.
091: */
092: Text text;
093:
094: /**
095: * Input widget for statement date.
096: */
097: DateControl dateControl;
098:
099: /**
100: * Creates an input dialog with OK and Cancel buttons. Note that the dialog
101: * will have no visual representation (no widgets) until it is told to open.
102: * <p>
103: * Note that the <code>open</code> method blocks for input dialogs.
104: * </p>
105: *
106: * @param parentShell
107: * the parent shell
108: * @param lastStatement
109: */
110: public NewStatementDialog(Shell parentShell,
111: BankStatement lastStatement) {
112: super (parentShell);
113: this .lastStatement = lastStatement;
114: }
115:
116: @Override
117: protected void buttonPressed(int buttonId) {
118: if (buttonId == IDialogConstants.OK_ID) {
119: switch (statementIdType) {
120: case BY_NUMBER:
121: value = new BankStatement(text.getText());
122: break;
123: case BY_DATE:
124: Date date = dateControl.getDate();
125: value = new BankStatement(date);
126: break;
127: default:
128: throw new RuntimeException("bad case");
129: }
130: } else {
131: value = null;
132: }
133: super .buttonPressed(buttonId);
134: }
135:
136: @Override
137: protected void configureShell(Shell shell) {
138: super .configureShell(shell);
139: shell.setText("New Statement");
140: }
141:
142: @Override
143: protected void createButtonsForButtonBar(Composite parent) {
144: // create OK and Cancel buttons by default
145: okButton = createButton(parent, IDialogConstants.OK_ID,
146: IDialogConstants.OK_LABEL, true);
147: createButton(parent, IDialogConstants.CANCEL_ID,
148: IDialogConstants.CANCEL_LABEL, false);
149: }
150:
151: @Override
152: protected Control createDialogArea(Composite parent) {
153: Composite composite = (Composite) super
154: .createDialogArea(parent);
155:
156: if (lastStatement == null) {
157: statementIdType = NOT_YET_DETERMINED;
158:
159: Label label = new Label(composite, SWT.WRAP);
160: label
161: .setText("You are creating the first statement for this account. "
162: + "Some banks number their statements (common in United Kingdom) while some bank do not (common in United States). "
163: + "If the statements are not numbered then you must identify each statement by date.");
164:
165: GridData messageData = new GridData();
166: Rectangle rect = getShell().getMonitor().getClientArea();
167: messageData.widthHint = rect.width / 2;
168: label.setLayoutData(messageData);
169:
170: Button byNumberButton = new Button(composite, SWT.RADIO);
171: byNumberButton
172: .setText("Identify Bank Statements by Sequence Number");
173: byNumberButton.addSelectionListener(new SelectionAdapter() {
174: @Override
175: public void widgetSelected(SelectionEvent e) {
176: promptForNumber();
177: }
178: });
179:
180: Button byDateButton = new Button(composite, SWT.RADIO);
181: byDateButton.setText("Identify Bank Statements by Date");
182: byDateButton.addSelectionListener(new SelectionAdapter() {
183: @Override
184: public void widgetSelected(SelectionEvent e) {
185: promptForDate();
186: }
187: });
188: }
189:
190: Composite alternativeContentContainer = new Composite(
191: composite, 0);
192: alternativeContentLayout = new AlternativeContentLayout();
193: alternativeContentContainer.setLayout(alternativeContentLayout);
194:
195: // create composite for getting statement by number
196: byNumberComposite = new Composite(alternativeContentContainer,
197: 0);
198: byNumberComposite.setLayout(new GridLayout());
199:
200: Label numberLabel = new Label(byNumberComposite, SWT.WRAP);
201: numberLabel.setText("Number of the new statement:");
202: GridData numberLabelData = new GridData(
203: GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL
204: | GridData.HORIZONTAL_ALIGN_FILL
205: | GridData.VERTICAL_ALIGN_CENTER);
206: numberLabelData.widthHint = convertHorizontalDLUsToPixels(IDialogConstants.MINIMUM_MESSAGE_AREA_WIDTH);
207: numberLabel.setLayoutData(numberLabelData);
208: numberLabel.setFont(parent.getFont());
209:
210: text = new Text(byNumberComposite, SWT.SINGLE | SWT.BORDER);
211: text.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL
212: | GridData.HORIZONTAL_ALIGN_FILL));
213: text.addModifyListener(new ModifyListener() {
214: public void modifyText(ModifyEvent e) {
215: Pattern numberPattern = Pattern.compile("^(\\d){1,4}$");
216: Matcher m = numberPattern.matcher(text.getText());
217: boolean b = m.matches();
218: setErrorMessage(b ? null
219: : "Statement number must be a number in the range 1 to 9999");
220: }
221: });
222:
223: // create composite for getting statement by number
224: byDateComposite = new Composite(alternativeContentContainer, 0);
225: byDateComposite.setLayout(new GridLayout());
226:
227: Label dateLabel = new Label(byDateComposite, SWT.WRAP);
228: dateLabel.setText("Date of the new statement:");
229: GridData dateLabelData = new GridData(GridData.GRAB_HORIZONTAL
230: | GridData.GRAB_VERTICAL
231: | GridData.HORIZONTAL_ALIGN_FILL
232: | GridData.VERTICAL_ALIGN_CENTER);
233: dateLabelData.widthHint = convertHorizontalDLUsToPixels(IDialogConstants.MINIMUM_MESSAGE_AREA_WIDTH);
234: dateLabel.setLayoutData(dateLabelData);
235: dateLabel.setFont(parent.getFont());
236:
237: dateControl = new DateControl(byDateComposite/*, SWT.SINGLE | SWT.BORDER*/);
238: dateControl.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL
239: | GridData.HORIZONTAL_ALIGN_FILL));
240: dateControl.addModifyListener(new ModifyListener() {
241: public void modifyText(ModifyEvent e) {
242: boolean isDateValid = (dateControl.getDate() != null);
243: setErrorMessage(isDateValid ? null
244: : "Statement date must be in the format "
245: + JMoneyPlugin.getDefault()
246: .getDateFormat());
247: }
248: });
249:
250: errorMessageText = new Text(composite, SWT.READ_ONLY);
251: errorMessageText.setLayoutData(new GridData(
252: GridData.GRAB_HORIZONTAL
253: | GridData.HORIZONTAL_ALIGN_FILL));
254: errorMessageText.setBackground(errorMessageText.getDisplay()
255: .getSystemColor(SWT.COLOR_WIDGET_BACKGROUND));
256:
257: if (lastStatement != null) {
258: if (lastStatement.isNumber()) {
259: statementIdType = BY_NUMBER;
260: text.setText(Integer
261: .toString(lastStatement.getNumber() + 1));
262: } else {
263: statementIdType = BY_DATE;
264: Date initialDate = lastStatement.getStatementDate();
265:
266: Calendar calendar = Calendar.getInstance();
267: calendar.setTime(initialDate);
268: calendar.add(Calendar.MONTH, 1);
269: dateControl.setDate(calendar.getTime());
270: }
271: }
272:
273: switch (statementIdType) {
274: case BY_NUMBER:
275: alternativeContentLayout.show(byNumberComposite);
276: text.setFocus();
277: text.selectAll();
278: break;
279: case BY_DATE:
280: alternativeContentLayout.show(byDateComposite);
281: dateControl.setFocus();
282: break;
283: }
284:
285: applyDialogFont(composite);
286: return composite;
287: }
288:
289: /**
290: *
291: */
292: protected void promptForNumber() {
293: statementIdType = BY_NUMBER;
294: alternativeContentLayout.show(byNumberComposite);
295: }
296:
297: /**
298: *
299: */
300: protected void promptForDate() {
301: statementIdType = BY_DATE;
302: alternativeContentLayout.show(byDateComposite);
303: }
304:
305: /**
306: * Returns the string typed into this input dialog.
307: *
308: * @return the input string
309: */
310: public BankStatement getValue() {
311: return value;
312: }
313:
314: /**
315: * Sets or clears the error message.
316: * If not <code>null</code>, the OK button is disabled.
317: *
318: * @param errorMessage
319: * the error message, or <code>null</code> to clear
320: * @since 3.0
321: */
322: public void setErrorMessage(String errorMessage) {
323: errorMessageText
324: .setText(errorMessage == null ? "" : errorMessage); //$NON-NLS-1$
325:
326: // If called during createDialogArea, the okButton
327: // will not have been created yet.
328: if (okButton != null) {
329: okButton.setEnabled(errorMessage == null);
330: }
331: errorMessageText.getParent().update();
332: }
333: }
|