001: /*************************************************************************
002: * *
003: * 1) This source code file, in unmodified form, and compiled classes *
004: * derived from it can be used and distributed without restriction, *
005: * including for commercial use. (Attribution is not required *
006: * but is appreciated.) *
007: * *
008: * 2) Modified versions of this file can be made and distributed *
009: * provided: the modified versions are put into a Java package *
010: * different from the original package, edu.hws; modified *
011: * versions are distributed under the same terms as the original; *
012: * and the modifications are documented in comments. (Modification *
013: * here does not include simply making subclasses that belong to *
014: * a package other than edu.hws, which can be done without any *
015: * restriction.) *
016: * *
017: * David J. Eck *
018: * Department of Mathematics and Computer Science *
019: * Hobart and William Smith Colleges *
020: * Geneva, New York 14456, USA *
021: * Email: eck@hws.edu WWW: http://math.hws.edu/eck/ *
022: * *
023: *************************************************************************/package edu.hws.jcm.awt;
024:
025: import java.awt.*;
026: import edu.hws.jcm.data.*;
027:
028: /**
029: * A DisplayLabel is a label that can display numbers embedded in
030: * strings. The text for the label can include '#' characters, which
031: * are substituted by values of specified Value objects. (A doubled
032: * ## is interpreted as a single literal # to be displayed rather than
033: * substituted.) You should provide as many Values as there
034: * are #'s in the text. However, no errors are generated if this
035: * is not the case. Extra expressions are ignored; extra #'s are
036: * shown as "undefined" in the display. In fact, DisplayLabels
037: * do not ever generat JCMErrors. Note that Value objects include
038: * objects of type Constant, Variable, and Expression, for example. Value
039: * is just an interface defined in package edu.hws.jcm.data.
040: *
041: * <p>The values displayed in a DisplayLabel are recomputed when
042: * the Label's compute() method is called. Usually, this is
043: * done by a Controller that the DisplayLabel is registered with.
044: * See the Controller class for more information.
045: */
046: public class DisplayLabel extends Label implements Computable {
047:
048: /**
049: * Unsubstituted text for display.
050: */
051: protected String text;
052:
053: /**
054: * Desired maximum number of characters in displayed numbers.
055: */
056: protected int numSize = 10;
057:
058: /**
059: * Value objects whose values will be
060: * substituted for #'s in text.
061: */
062: protected Value[] values;
063:
064: /**
065: * Create a label with no expressions set up to display a
066: * single number. Initial value is "undefined"; Use the
067: * setValue() method to set the value to be displayed.
068: */
069: public DisplayLabel() {
070: this (null, (Value[]) null);
071: }
072:
073: /**
074: * Convenience method for making a DisplayLabel with just one value to display.
075: *
076: * @param text Text to display. It shoud contain a single '#', which will be substituted by the value.
077: * @param val a Value object whose value is substituted for the # in the text.
078: */
079: public DisplayLabel(String text, Value val) {
080: this (text, (val == null) ? null : new Value[] { val });
081: }
082:
083: /**
084: * Create a DisplayLabel to display one or more values.
085: * Text and vals can be null. If not, text should have
086: * as many (single) #'s as there are expressions. The
087: * values of the Value objects are substituted for the
088: * #'s in the display.
089: *
090: * @param text The text to display. If this is null, it is set to "#".
091: * @param vals The Value object(s) whose values are substituted for #'s in the text. If this is null,
092: * the values shoud be set later by calling the setValues() method.
093: */
094: public DisplayLabel(String text, Value[] vals) {
095: this .text = (text == null) ? "#" : text;
096: setValues(vals);
097: }
098:
099: /**
100: * The compute method recalculates the displayed Values
101: * and changes the text of the label to show the new values.
102: * This is usually called by a Controller.
103: */
104: public void compute() {
105: super .setText(getSubstitutedText());
106: }
107:
108: /**
109: * Get the array of Value objects whose values are displayed
110: * in this DisplayLabel.
111: */
112: public Value[] getValues() {
113: return values;
114: }
115:
116: /**
117: * A convenience method that can be used when the display string contains
118: * just a single #. This sets the Value object whose value is substituted
119: * for that #.
120: */
121: public void setValue(Value val) {
122: if (val == null)
123: values = null;
124: else
125: values = new Value[] { val };
126: super .setText(getSubstitutedText());
127: }
128:
129: /**
130: * Set the array of Value objects whose values are displayed
131: * in this DisplayLabel, and change the display to show
132: * the new values. (The contents of the array, vals, are
133: * copied into a newly created array.)
134: */
135: public void setValues(Value[] vals) {
136: if (vals == null)
137: values = null;
138: else {
139: values = new Value[vals.length];
140: System.arraycopy(vals, 0, values, 0, vals.length);
141: }
142: super .setText(getSubstitutedText());
143: }
144:
145: /**
146: * Set the desired maximum number of characters in displayed numbers.
147: * Actual size might be larger. Value is clamped to the range
148: * 6 to 25.
149: */
150: public void setNumSize(int size) {
151: numSize = Math.min(Math.max(size, 6), 25);
152: }
153:
154: /**
155: * Return the desired maximum number of characters in displayed numbers.
156: */
157: public int getNumSize() {
158: return numSize;
159: }
160:
161: /**
162: * Return the basic text, including the #'s where Values
163: * are inserted in the displayed text. Note that the
164: * getText() method from the Label class will return the actual
165: * displayed text, including the substitited values.
166: */
167: public String getBaseText() {
168: return text;
169: }
170:
171: /**
172: * Compute the string that is obtained by substituting values for #'s in text.
173: * Will NOT throw any errors. (Any errors that occur when the
174: * Value objects are evaluated are caught and translated
175: * into "undefined" values.)
176: */
177: private String getSubstitutedText() {
178: StringBuffer b = new StringBuffer();
179: int valCt = 0;
180: for (int i = 0; i < text.length(); i++) {
181: if (text.charAt(i) == '#') {
182: if (i != text.length() - 1 && text.charAt(i + 1) == '#') {
183: b.append('#');
184: i++;
185: } else if (values == null || valCt >= values.length)
186: b.append("undefined");
187: else {
188: try {
189: b.append(NumUtils.realToString(values[valCt]
190: .getVal(), numSize));
191: } catch (JCMError e) {
192: b.append("undefined");
193: }
194: valCt++;
195: }
196: } else
197: b.append(text.charAt(i));
198: }
199: return b.toString();
200: }
201:
202: /**
203: * Set text for display -- text should include as many (single) #'s
204: * as there are values to display.
205: */
206: public void setText(String text) {
207: this .text = text;
208: super .setText(getSubstitutedText());
209: }
210:
211: /**
212: * Return the preferred size of this DisplayLabel.
213: * Allow space for up to numSize (or 8, whichever is larger) characters for
214: * each (single) # in the text. This is not meant to be called directly.
215: */
216: public Dimension getPreferredSize() {
217: Dimension size = super .getPreferredSize();
218: int ct = 0; // Number of (single) #'1 in the text.
219: if (text == null || text.length() == 0)
220: ct = 1;
221: else {
222: for (int i = 0; i < text.length(); i++) {
223: char ch = text.charAt(i);
224: if (ch == '#')
225: if (i < text.length() - 1
226: && text.charAt(i + 1) == '#')
227: i++;
228: else
229: ct++;
230: }
231: }
232: FontMetrics fm = getFontMetrics(getFont());
233: int perChar = fm.charWidth('0');
234: int w = 10 + (int) (perChar * Math.max(8, numSize) * ct + fm
235: .stringWidth(text)); // allowing extra space for numbers
236: return new Dimension(w, size.height);
237: }
238:
239: } // end class DisplayLabel
|