001: package net.sf.jmoney.stocks;
002:
003: import java.util.ArrayList;
004: import java.util.List;
005:
006: import net.sf.jmoney.model2.Currency;
007: import net.sf.jmoney.stocks.RatesTable.Band;
008:
009: import org.eclipse.swt.SWT;
010: import org.eclipse.swt.events.FocusAdapter;
011: import org.eclipse.swt.events.FocusEvent;
012: import org.eclipse.swt.events.FocusListener;
013: import org.eclipse.swt.events.SelectionAdapter;
014: import org.eclipse.swt.events.SelectionEvent;
015: import org.eclipse.swt.layout.FillLayout;
016: import org.eclipse.swt.layout.GridData;
017: import org.eclipse.swt.layout.GridLayout;
018: import org.eclipse.swt.widgets.Button;
019: import org.eclipse.swt.widgets.Composite;
020: import org.eclipse.swt.widgets.Control;
021: import org.eclipse.swt.widgets.Label;
022: import org.eclipse.swt.widgets.Text;
023:
024: /**
025: * The implementation for the composite control that contains
026: * the account property controls.
027: */
028: class RatesEditorControl extends Composite {
029: private class RowControls {
030: public RowControls(Composite parent) {
031: label1 = new Label(parent, SWT.NONE);
032: percentageText = new Text(parent, SWT.BORDER);
033: label2 = new Label(parent, SWT.NONE);
034: upperBoundText = new Text(parent, SWT.BORDER);
035:
036: label1.setText("plus");
037: percentageText.setLayoutData(new GridData(30, SWT.DEFAULT));
038: upperBoundText.setLayoutData(new GridData(30, SWT.DEFAULT));
039:
040: FocusListener listener = new FocusAdapter() {
041: @Override
042: public void focusGained(FocusEvent e) {
043: lastFocusRow = RowControls.this ;
044: }
045: };
046:
047: percentageText.addFocusListener(listener);
048: upperBoundText.addFocusListener(listener);
049: }
050:
051: Label label1;
052: Text percentageText;
053: Label label2;
054: Text upperBoundText;
055:
056: public void configureAsMiddleRow() {
057: label2.setText("percent of the amount up to");
058: upperBoundText.setVisible(true);
059: }
060:
061: public void configureAsLastRow() {
062: label2.setText("percent of the rest");
063: upperBoundText.setVisible(false);
064: }
065:
066: public void configureAsOnlyRow() {
067: label2.setText("percent of the amount");
068: upperBoundText.setVisible(false);
069: }
070: }
071:
072: // RatesTable ratesTable;
073:
074: private Text fixedAmountControl;
075:
076: private ArrayList<RowControls> rows = new ArrayList<RowControls>();
077:
078: // The grid in which the rate bands are displayed.
079: private Composite middleRow;
080:
081: /*
082: * The last row which has focus. This is used only for determining which
083: * row to delete or where to insert a new row.
084: */
085: private RowControls lastFocusRow;
086:
087: private Currency currencyForFormatting;
088:
089: private Button addRowButton;
090: private Button removeRowButton;
091:
092: /**
093: * @param parent
094: */
095: public RatesEditorControl(Composite parent) {
096: super (parent, SWT.BORDER);
097:
098: GridLayout layout = new GridLayout();
099: layout.numColumns = 1;
100: setLayout(layout);
101:
102: // Create the controls to edit the properties.
103: // Only the controls in the first line are created at
104: // this time. The rest are created when the account is set.
105:
106: // Create the top row
107: Composite topRow = createFixedAmountRow();
108:
109: GridData topRowData = new GridData();
110: topRowData.verticalAlignment = GridData.FILL;
111: topRowData.horizontalAlignment = GridData.FILL;
112: topRow.setLayoutData(topRowData);
113:
114: // Add the center control
115: createBandRows();
116:
117: middleRow.setLayoutData(new GridData(GridData.FILL,
118: GridData.FILL, false, false));
119:
120: // Add the buttons
121: createButtonsArea();
122: }
123:
124: private Composite createButtonsArea() {
125: Composite buttonRow = new Composite(this , SWT.NONE);
126:
127: FillLayout buttonLayout = new FillLayout();
128: buttonLayout.marginHeight = 0;
129: buttonLayout.marginWidth = 0;
130: buttonRow.setLayout(buttonLayout);
131:
132: addRowButton = new Button(buttonRow, SWT.PUSH);
133: addRowButton.setText("Add Band");
134: addRowButton.addSelectionListener(new SelectionAdapter() {
135: @Override
136: public void widgetSelected(SelectionEvent e) {
137: // Add a new row to the table
138: RowControls newRow = new RowControls(middleRow);
139:
140: int lastFocusRowIndex = rows.indexOf(lastFocusRow);
141:
142: rows.add(lastFocusRowIndex + 1, newRow);
143: if (lastFocusRowIndex != rows.size() - 2) {
144: Control followingControl = rows
145: .get(lastFocusRowIndex + 2).label1;
146: newRow.label1.moveAbove(followingControl);
147: newRow.percentageText.moveAbove(followingControl);
148: newRow.label2.moveAbove(followingControl);
149: newRow.upperBoundText.moveAbove(followingControl);
150:
151: newRow.configureAsMiddleRow();
152: if (rows.size() == 2) {
153: // Existing row was only row, now it is last row
154: RowControls lastRow = rows.get(1);
155: lastRow.configureAsLastRow();
156: }
157: } else {
158: RowControls previousRow = rows
159: .get(lastFocusRowIndex);
160: previousRow.configureAsMiddleRow();
161: newRow.configureAsLastRow();
162: }
163:
164: removeRowButton.setEnabled(true);
165:
166: middleRow.pack(true);
167: pack(true);
168: getParent().pack(true);
169: }
170: });
171:
172: removeRowButton = new Button(buttonRow, SWT.PUSH);
173: removeRowButton.setText("Remove Band");
174: removeRowButton.addSelectionListener(new SelectionAdapter() {
175: @Override
176: public void widgetSelected(SelectionEvent e) {
177: // Remove a row from the table
178: int lastFocusRowIndex = rows.indexOf(lastFocusRow);
179: RowControls row = rows.remove(lastFocusRowIndex);
180: row.label1.dispose();
181: row.percentageText.dispose();
182: row.label2.dispose();
183: row.upperBoundText.dispose();
184:
185: if (lastFocusRowIndex == rows.size()) {
186: // We removed the last row
187: if (rows.size() == 1) {
188: rows.get(0).configureAsOnlyRow();
189: removeRowButton.setEnabled(false);
190: } else {
191: rows.get(lastFocusRowIndex - 1)
192: .configureAsLastRow();
193: }
194: }
195:
196: middleRow.pack(true);
197: pack(true);
198: getParent().pack(true);
199: }
200: });
201:
202: return buttonRow;
203: }
204:
205: private Composite createFixedAmountRow() {
206: Composite topRow = new Composite(this , SWT.NONE);
207:
208: GridLayout topRowLayout = new GridLayout();
209: topRowLayout.numColumns = 2;
210: topRowLayout.marginHeight = 0;
211: topRowLayout.marginWidth = 0;
212: topRow.setLayout(topRowLayout);
213:
214: new Label(topRow, SWT.NONE).setText("Fixed amount");
215: fixedAmountControl = new Text(topRow, SWT.BORDER);
216:
217: return topRow;
218: }
219:
220: private void createBandRows() {
221: middleRow = new Composite(this , SWT.NONE);
222:
223: GridLayout middleRowLayout = new GridLayout();
224: middleRowLayout.numColumns = 4;
225: middleRowLayout.marginHeight = 0;
226: middleRowLayout.marginWidth = 0;
227:
228: middleRow.setLayout(middleRowLayout);
229:
230: // No sub-controls are added at this time.
231: }
232:
233: /**
234: * @param account
235: */
236: public void setRatesTable(RatesTable ratesTable,
237: final Currency currencyForFormatting) {
238: this .currencyForFormatting = currencyForFormatting;
239:
240: // Destroy all the controls in the middle grid.
241: for (RowControls row : rows) {
242: row.label1.dispose();
243: row.percentageText.dispose();
244: row.label2.dispose();
245: row.upperBoundText.dispose();
246: }
247:
248: // Note: Either ratesTable is null, or it is completely valid.
249:
250: if (ratesTable != null) {
251: fixedAmountControl.setText(currencyForFormatting
252: .format(ratesTable.getFixedAmount()));
253:
254: // Add a row for each row in the table
255:
256: List<Band> bands = ratesTable.getBands();
257:
258: if (bands.size() == 0) {
259: RowControls row = new RowControls(middleRow);
260: rows.add(row);
261:
262: row.configureAsOnlyRow();
263: } else if (bands.size() == 1) {
264: RowControls row = new RowControls(middleRow);
265: rows.add(row);
266:
267: double percentage = bands.get(0).getPercentage();
268: row.percentageText.setText(Double.toString(percentage));
269:
270: row.configureAsOnlyRow();
271: } else {
272: for (int rowIndex = 0; rowIndex < bands.size(); rowIndex++) {
273: Band band = bands.get(rowIndex);
274:
275: final RowControls row = new RowControls(middleRow);
276: rows.add(row);
277:
278: double percentage = band.getPercentage();
279: row.percentageText.setText(Double
280: .toString(percentage));
281:
282: if (rowIndex != bands.size() - 1) {
283: Band nextBand = bands.get(rowIndex + 1);
284:
285: row.configureAsMiddleRow();
286:
287: row.upperBoundText
288: .setText(currencyForFormatting
289: .format(nextBand.getBandStart()));
290: // row.upperBoundText.addFocusListener(
291: // new FocusAdapter() {
292: // @Override
293: // public void focusLost(FocusEvent e) {
294: // long amount = currencyForFormatting.parse(row.upperBoundText.getText());
295: // // Update any controls here?
296: // }
297: // });
298: } else {
299: row.configureAsLastRow();
300: }
301: }
302: }
303: } else {
304: RowControls row = new RowControls(middleRow);
305: rows.add(row);
306:
307: row.configureAsOnlyRow();
308: }
309:
310: removeRowButton.setEnabled(rows.size() > 1);
311:
312: // TODO: what is this?
313: middleRow.pack();
314: pack();
315: getParent().pack(true);
316:
317: // We must listen for changes to the currency so that
318: // we can change the format of the amount.
319: /* TODO: complete this. It may be this should be handled within an amount editor control which
320: * we should use here.
321:
322: account.getSession().addSessionChangeListener(
323: new SessionChangeAdapter() {
324: public void accountChanged(Account changedAccount, PropertyAccessor changedProperty, Object oldValue, Object newValue) {
325: if (changedAccount == RatesEditor.this.account
326: && changedProperty == CapitalAccountInfo.getCurrencyAccessor()) {
327: // Get the current text from the control and try to re-format
328: // it for the new currency.
329: // However, if the property can take null values and the control
330: // contains the empty string then set the amount to null.
331: // (The currency amount parser returns a zero amount for the
332: // empty string).
333: // Amounts can be represented by 'Long' or by 'long'.
334: // 'Long' values can be null, 'long' values cannot be null.
335: // If the text in the control now translates to a different long
336: // value as a result of the new currency, update the new long value
337: // in the datastore.
338:
339: // It is probably not necessary for us to set the control text here,
340: // because this will be done by our listener if we are changing
341: // the amount. However, to protect against a future possibility
342: // that a currency change may change the format without changing the amount,
343: // we set the control text ourselves first.
344:
345: String amountString = propertyControl.getText();
346: if (!amountString.equals("")) {
347: Currency newCurrency = (Currency)newValue;
348: long amount = newCurrency.parse(amountString);
349: propertyControl.setText(newCurrency.format(amount));
350: changedAccount.setLongPropertyValue(amountPropertyAccessor, amount);
351: }
352: }
353:
354: }
355: });
356: */
357: }
358:
359: public RatesTable getRatesTable() {
360: long fixedAmount = currencyForFormatting
361: .parse(fixedAmountControl.getText());
362:
363: /*
364: * Note that the Band objects contain the start of the range in which the
365: * percentage applies, whereas the rows of controls contain the upper end of
366: * the range to which the percentage applies. This code therefore uses the
367: * value from the previous row (or zero for the first row) when constructing
368: * each band.
369: */
370: long bandStart = 0;
371:
372: ArrayList<Band> bands = new ArrayList<Band>();
373: for (RowControls row : rows) {
374: double percentage;
375:
376: try {
377: percentage = Double.parseDouble(row.percentageText
378: .getText());
379: } catch (NumberFormatException e) {
380: percentage = 0.0;
381: }
382:
383: bands.add(new Band(bandStart, percentage));
384:
385: try {
386: bandStart = currencyForFormatting
387: .parse(row.upperBoundText.getText());
388: } catch (NumberFormatException e) {
389: bandStart = 0;
390: }
391: }
392:
393: return new RatesTable(fixedAmount, bands);
394: }
395: }
|