001: /*
002: * Copyright 2000,2005 wingS development team.
003: *
004: * This file is part of wingS (http://wingsframework.org).
005: *
006: * wingS is free software; you can redistribute it and/or modify
007: * it under the terms of the GNU Lesser General Public License
008: * as published by the Free Software Foundation; either version 2.1
009: * of the License, or (at your option) any later version.
010: *
011: * Please see COPYING for the complete licence.
012: */
013: package wingset;
014:
015: import org.wings.SButton;
016: import org.wings.SComponent;
017: import org.wings.SConstants;
018: import org.wings.SDimension;
019: import org.wings.SGridLayout;
020: import org.wings.SLabel;
021: import org.wings.SPanel;
022: import org.wings.STextField;
023: import org.wings.script.JavaScriptEvent;
024: import org.wings.script.JavaScriptListener;
025: import org.wings.style.CSSProperty;
026:
027: import java.awt.*;
028: import java.awt.event.ActionEvent;
029: import java.text.DecimalFormat;
030: import java.text.DecimalFormatSymbols;
031: import java.text.NumberFormat;
032: import java.util.Locale;
033:
034: /**
035: * Create some text fields and add two listeners: a standard server side
036: * listener, that does a numerical calculation on the fields and a client
037: * side JavaScript Listener that allows to calculate these fields as well.
038: *
039: * @author <a href="mailto:H.Zeller@acm.org">Henner Zeller</a>
040: */
041: public class JavaScriptListenerExample extends WingSetPane {
042: /**
043: * The JavaScript code that is executed on any change of input fields.
044: * The curly braces with a number in it are replaced by the numbered
045: * SComponent argument.
046: */
047: private final static String JS_ADD_SCRIPT = "self.add = function() { "
048: + " document.getElementById('{2}').value"
049: + " = ((1.0 * document.getElementById('{0}').value)"
050: + " + (1.0 * document.getElementById('{1}').value));"
051: + " }";
052:
053: private final static DecimalFormatSymbols DSYM = new DecimalFormatSymbols(
054: Locale.US); // '.' as fraction separator
055:
056: protected SComponent createControls() {
057: return null;
058: }
059:
060: public SComponent createExample() {
061: final STextField firstField = createNumberField();
062: final STextField secondField = createNumberField();
063: final STextField sumField = createNumberField();
064: SButton serverCalcButton = new SButton("sum");
065:
066: firstField.setFocusTraversalIndex(1);
067: secondField.setFocusTraversalIndex(2);
068:
069: SGridLayout gridLayout = new SGridLayout(2);
070: gridLayout.setHgap(4);
071: gridLayout.setVgap(4);
072:
073: SPanel panel = new SPanel(gridLayout);
074: panel.add(new SLabel("Value #1"));
075: panel.add(firstField);
076: panel.add(new SLabel("Value #2"));
077: panel.add(secondField);
078: panel.add(new SLabel("Sum"));
079: panel.add(sumField);
080: panel.add(new SLabel("Server calculation"));
081: panel.add(serverCalcButton);
082:
083: /*
084: * The server side listener
085: */
086: serverCalcButton
087: .addActionListener(new java.awt.event.ActionListener() {
088: public void actionPerformed(ActionEvent ev) {
089: doCalculation(firstField, secondField, sumField);
090: }
091: });
092:
093: /*
094: * add the client side script listener. The variables
095: * in curly braces are replaced by the actual IDs of the components.
096: */
097: SComponent[] jsParams = new SComponent[] { firstField,
098: secondField, sumField };
099: JavaScriptListener jsListener = new JavaScriptListener(
100: JavaScriptEvent.ON_CHANGE, "add()", JS_ADD_SCRIPT,
101: jsParams);
102:
103: firstField.addScriptListener(jsListener);
104: secondField.addScriptListener(jsListener);
105:
106: // any change to the sum field: no way, recalculate from source fields
107: sumField.addScriptListener(jsListener);
108:
109: SPanel p = new SPanel(new SGridLayout(2, 1, 10, 10));
110: p.setPreferredSize(SDimension.FULLWIDTH);
111: p
112: .add(new SLabel(
113: "The client side can handle simple events by JavaScript listeners.\n"
114: + "In this example, numbers are added locally inside the browser.\n",
115: SConstants.CENTER_ALIGN));
116: p.add(panel);
117: return p;
118: }
119:
120: /**
121: * do the calculation and normalize the output fields.
122: */
123: private void doCalculation(STextField a, STextField b,
124: STextField sum) {
125: double aNum = parseNumber(a);
126: double bNum = parseNumber(b);
127: if (Double.isNaN(aNum) || Double.isNaN(bNum)) {
128: sum.setBackground(Color.RED);
129: sum.setText("?");
130: } else {
131: sum.setBackground(null);
132: /*
133: * normalize the output: set the same number of decimal
134: * digits for all fields.
135: */
136: int decimalsNeeded = Math.max(fractionDecimals(aNum),
137: fractionDecimals(bNum));
138: NumberFormat fmt;
139: fmt = new DecimalFormat("#.#", DSYM);
140: fmt.setMinimumFractionDigits(decimalsNeeded);
141:
142: a.setText(fmt.format(aNum));
143: b.setText(fmt.format(bNum));
144: sum.setText(fmt.format(aNum + bNum));
145: }
146: }
147:
148: /**
149: * returns the number of decimals needed to display the given
150: * number
151: */
152: private int fractionDecimals(double number) {
153: // is there a simple and more efficient way ?
154: NumberFormat fmt = new DecimalFormat("#.########");
155: String fractionStr = fmt
156: .format(Math.IEEEremainder(number, 1.0));
157: return fractionStr.length() - 2;
158: }
159:
160: /**
161: * parse a number in a text field. Assume an empty text field
162: * to be '0', non-parseable values are NaN.
163: */
164: private double parseNumber(STextField field) {
165: String text = field.getText().trim();
166: if (text.length() == 0) {
167: text = "0";
168: }
169: double result = Double.NaN;
170: try {
171: result = Double.parseDouble(text);
172: field.setBackground(null);
173: } catch (Exception e) {
174: field.setBackground(Color.RED);
175: }
176: return result;
177: }
178:
179: private STextField createNumberField() {
180: STextField field = new STextField();
181: field.setAttribute(CSSProperty.TEXT_ALIGN, "right");
182: return field;
183: }
184: }
|