001: /*
002: * Copyright (c) 2002-2004 JGoodies Karsten Lentzsch. All Rights Reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * o Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * o Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * o Neither the name of JGoodies Karsten Lentzsch nor the names of
015: * its contributors may be used to endorse or promote products derived
016: * from this software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
020: * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
021: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
022: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
025: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
026: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
027: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
028: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029: */
030:
031: package com.jgoodies.forms.layout;
032:
033: import java.awt.Component;
034: import java.awt.Container;
035: import java.io.Serializable;
036: import java.util.List;
037:
038: /**
039: * An implementation of the {@link Size} interface that represents constant
040: * sizes described by a value and unit, for example: 10 pixel,
041: * 15 point or 4 dialog units. You can get instances of
042: * <code>ConstantSize</code> using the factory methods and constants in the
043: * {@link Sizes} class. Logical constant sizes that vary with the current layout
044: * style are delivered by the {@link com.jgoodies.forms.util.LayoutStyle} class.
045: * <p>
046: *
047: * This class supports different size units: <table>
048: * <tr>
049: * <td><b>Unit</b> </td>
050: * <td> <b>Abbreviation</b> </td>
051: * <td> <b>Size</b></td>
052: * </tr>
053: * <tr>
054: * <td>Millimeter</td>
055: * <td>mm</td>
056: * <td>0.1 cm</td>
057: * </tr>
058: * <tr>
059: * <td>Centimeter</td>
060: * <td>cm</td>
061: * <td>10.0 mm</td>
062: * </tr>
063: * <tr>
064: * <td>Inch</td>
065: * <td>in</td>
066: * <td>25.4 mm</td>
067: * </tr>
068: * <tr>
069: * <td>DTP Point</td>
070: * <td>pt</td>
071: * <td>1/72 in</td>
072: * </tr>
073: * <tr>
074: * <td>Pixel</td>
075: * <td>px</td>
076: * <td>1/(resolution in dpi) in</td>
077: * </tr>
078: * <tr>
079: * <td>Dialog Unit</td>
080: * <td>dlu</td>
081: * <td>honors l&f, resolution, and dialog font size</td>
082: * </tr>
083: * </table>
084: * <p>
085: *
086: * <strong>Examples:</strong>
087: *
088: * <pre>
089: * Sizes.ZERO;
090: * Sizes.DLUX9;
091: * Sizes.dluX(42);
092: * Sizes.pixel(99);
093: * </pre>
094: *
095: * @author Karsten Lentzsch
096: * @version $Revision: 1.2 $
097: *
098: * @see Size
099: * @see Sizes
100: */
101:
102: public final class ConstantSize implements Size, Serializable {
103:
104: // Public Units *********************************************************
105:
106: public static final Unit PIXEL = new Unit("Pixel", "px", true);
107: public static final Unit POINT = new Unit("Point", "pt", true);
108: public static final Unit DIALOG_UNITS_X = new Unit(
109: "Dialog units X", "dluX", true);
110: public static final Unit DLUX = DIALOG_UNITS_X;
111: public static final Unit DIALOG_UNITS_Y = new Unit(
112: "Dialog units Y", "dluY", true);
113: public static final Unit DLUY = DIALOG_UNITS_Y;
114: public static final Unit MILLIMETER = new Unit("Millimeter", "mm",
115: false);
116: public static final Unit MM = MILLIMETER;
117: public static final Unit CENTIMETER = new Unit("Centimeter", "cm",
118: false);
119: public static final Unit CM = CENTIMETER;
120: public static final Unit INCH = new Unit("Inch", "in", false);
121: public static final Unit IN = INCH;
122:
123: /**
124: * An array of all enumeration values used to canonicalize deserialized
125: * units.
126: */
127: private static final Unit[] VALUES = { PIXEL, POINT,
128: DIALOG_UNITS_X, DIALOG_UNITS_Y, MILLIMETER, CENTIMETER,
129: INCH };
130:
131: // Fields ***************************************************************
132:
133: private final double value;
134: private final Unit unit;
135:
136: // Instance Creation ****************************************************
137:
138: /**
139: * Constructs an instance of <code>ConstantSize</code> from the given
140: * encoded size and unit description.
141: *
142: * @param value
143: * the size value interpreted in the given units
144: * @param unit
145: * the size's unit
146: */
147: ConstantSize(int value, Unit unit) {
148: this .value = value;
149: this .unit = unit;
150: }
151:
152: /**
153: * Constructs an instance of <code>ConstantSize</code> from the given
154: * encoded size and unit description.
155: *
156: * @param value
157: * the size value interpreted in the given units
158: * @param unit
159: * the size's unit
160: */
161: ConstantSize(double value, Unit unit) {
162: this .value = value;
163: this .unit = unit;
164: }
165:
166: /**
167: * Constructs an instance of <code>ConstantSize</code> from the given
168: * encoded size and unit description.
169: *
170: * @param encodedValueAndUnit
171: * the size's value and unit as string
172: * @param horizontal
173: * true for horizontal, false for vertical
174: * @return a constant size for the given encoding and unit description
175: * @throws IllegalArgumentException
176: * if the unit requires integer but the value is not an integer
177: */
178: static ConstantSize valueOf(String encodedValueAndUnit,
179: boolean horizontal) {
180: String split[] = ConstantSize
181: .splitValueAndUnit(encodedValueAndUnit);
182: String encodedValue = split[0];
183: String encodedUnit = split[1];
184: Unit unit = Unit.valueOf(encodedUnit, horizontal);
185: double value = Double.parseDouble(encodedValue);
186: if (unit.requiresIntegers) {
187: if (value != (int) value)
188: throw new IllegalArgumentException(unit.toString()
189: + " value " + encodedValue
190: + " must be an integer.");
191: }
192: return new ConstantSize(value, unit);
193: }
194:
195: /**
196: * Returns an instance of <code>Size</code> for the specified value in
197: * horizontal dialog units.
198: *
199: * @param value
200: * size value in horizontal dialog units
201: * @return the associated Size instance
202: */
203: static ConstantSize dluX(int value) {
204: return new ConstantSize(value, DLUX);
205: }
206:
207: /**
208: * Returns an instance of <code>Size</code> for the specified value in
209: * vertical dialog units.
210: *
211: * @param value
212: * size value in vertical dialog units
213: * @return the associated Size instance
214: */
215: static ConstantSize dluY(int value) {
216: return new ConstantSize(value, DLUY);
217: }
218:
219: // Accessing the Value **************************************************
220:
221: /**
222: * Converts the size if necessary and returns the value in pixels.
223: *
224: * @param component
225: * the associated component
226: * @return the size in pixels
227: */
228: public int getPixelSize(Component component) {
229: if (unit == PIXEL)
230: return intValue();
231: else if (unit == POINT)
232: return Sizes.pointAsPixel(intValue(), component);
233: else if (unit == INCH)
234: return Sizes.inchAsPixel(value, component);
235: else if (unit == MILLIMETER)
236: return Sizes.millimeterAsPixel(value, component);
237: else if (unit == CENTIMETER)
238: return Sizes.centimeterAsPixel(value, component);
239: else if (unit == DIALOG_UNITS_X)
240: return Sizes.dialogUnitXAsPixel(intValue(), component);
241: else if (unit == DIALOG_UNITS_Y)
242: return Sizes.dialogUnitYAsPixel(intValue(), component);
243: else
244: throw new IllegalStateException("Invalid unit " + unit);
245: }
246:
247: // Implementing the Size Interface **************************************
248:
249: /**
250: * Returns this size as pixel size. Neither requires the component list nor
251: * the specified measures.
252: * <p>
253: *
254: * Invoked by {@link com.jgoodies.forms.layout.FormSpec} to determine the
255: * size of a column or row.
256: *
257: * @param container
258: * the layout container
259: * @param components
260: * the list of components used to compute the size
261: * @param minMeasure
262: * the measure that determines the minimum sizes
263: * @param prefMeasure
264: * the measure that determines the preferred sizes
265: * @param defaultMeasure
266: * the measure that determines the default sizes
267: * @return the computed maximum size in pixel
268: */
269: public int maximumSize(Container container, List components,
270: FormLayout.Measure minMeasure,
271: FormLayout.Measure prefMeasure,
272: FormLayout.Measure defaultMeasure) {
273: return getPixelSize(container);
274: }
275:
276: // Overriding Object Behavior *******************************************
277:
278: /**
279: * Indicates whether some other ConstantSize is "equal to" this one.
280: *
281: * @param o
282: * the Object with which to compare
283: * @return <code>true</code> if this object is the same as the obj
284: * argument; <code>false</code> otherwise.
285: * @see java.lang.Object#hashCode()
286: * @see java.util.Hashtable
287: */
288: public boolean equals(Object o) {
289: if (this == o)
290: return true;
291: if (!(o instanceof ConstantSize))
292: return false;
293: ConstantSize size = (ConstantSize) o;
294: return this .value == size.value && this .unit == size.unit;
295: }
296:
297: /**
298: * Returns a hash code value for the object. This method is supported for
299: * the benefit of hashtables such as those provided by
300: * <code>java.util.Hashtable</code>.
301: *
302: * @return a hash code value for this object.
303: * @see java.lang.Object#equals(java.lang.Object)
304: * @see java.util.Hashtable
305: */
306: public int hashCode() {
307: return new Double(value).hashCode() + 37 * unit.hashCode();
308: }
309:
310: /**
311: * Returns a string representation of this size object.
312: *
313: * <strong>Note:</strong> The string representation may change at any time.
314: * It is strongly recommended to not use this string for parsing purposes.
315: *
316: * @return a string representation of the constant size
317: */
318: public String toString() {
319: return (value == intValue()) ? Integer.toString(intValue())
320: + unit.abbreviation() : Double.toString(value)
321: + unit.abbreviation();
322: }
323:
324: // Helper Code **********************************************************
325:
326: private int intValue() {
327: return (int) Math.round(value);
328: }
329:
330: /**
331: * Splits a string that encodes size with unit into the size and unit
332: * substrings. Returns an array of two strings.
333: *
334: * @param encodedValueAndUnit
335: * a strings that represents a size with unit
336: * @return the first element is size, the second is unit
337: */
338: static String[] splitValueAndUnit(String encodedValueAndUnit) {
339: String[] result = new String[2];
340: int len = encodedValueAndUnit.length();
341: int firstLetterIndex = len;
342: while (firstLetterIndex > 0
343: && Character.isLetter(encodedValueAndUnit
344: .charAt(firstLetterIndex - 1))) {
345: firstLetterIndex--;
346: }
347: result[0] = encodedValueAndUnit.substring(0, firstLetterIndex);
348: result[1] = encodedValueAndUnit.substring(firstLetterIndex);
349: return result;
350: }
351:
352: // Helper Class *********************************************************
353:
354: /**
355: * An ordinal-based serializable typesafe enumeration for units as used in
356: * instances of {@link ConstantSize}.
357: */
358: public static final class Unit implements Serializable {
359:
360: private final transient String name;
361: private final transient String abbreviation;
362: final transient boolean requiresIntegers;
363:
364: private Unit(String name, String abbreviation,
365: boolean requiresIntegers) {
366: this .name = name;
367: this .abbreviation = abbreviation;
368: this .requiresIntegers = requiresIntegers;
369: }
370:
371: /**
372: * Returns an instance of <code>Unit</code> that corresponds to the
373: * specified string.
374: *
375: * @param str
376: * the encoded unit
377: * @param horizontal
378: * true for a horizontal unit, false for vertical
379: * @return the corresponding Unit
380: * @throws IllegalArgumentException
381: * if no Unit exists for the string
382: */
383: static Unit valueOf(String str, boolean horizontal) {
384: String lowerCase = str.toLowerCase();
385: if (lowerCase.equals("px") || lowerCase.length() == 0)
386: return PIXEL;
387: else if (lowerCase.equals("dlu"))
388: return horizontal ? DIALOG_UNITS_X : DIALOG_UNITS_Y;
389: else if (lowerCase.equals("pt"))
390: return POINT;
391: else if (lowerCase.equals("in"))
392: return INCH;
393: else if (lowerCase.equals("mm"))
394: return MILLIMETER;
395: else if (lowerCase.equals("cm"))
396: return CENTIMETER;
397: else
398: throw new IllegalArgumentException(
399: "Invalid unit name '" + str
400: + "'. Must be one of: "
401: + "px, dlu, pt, mm, cm, in");
402: }
403:
404: public String toString() {
405: return name;
406: }
407:
408: /**
409: * Returns the first character of this Unit's name. Used to identify it
410: * in short format strings.
411: *
412: * @return the first character of this Unit's name.
413: */
414: public String abbreviation() {
415: return abbreviation;
416: }
417:
418: // Serialization *****************************************************
419:
420: private static int nextOrdinal = 0;
421:
422: private final int ordinal = nextOrdinal++;
423:
424: private Object readResolve() {
425: return VALUES[ordinal]; // Canonicalize
426: }
427:
428: }
429:
430: }
|