001: /*
002: * Copyright (c) 2002-2007 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: import java.util.Locale;
038:
039: /**
040: * An implementation of the {@link Size} interface that represents constant
041: * sizes described by a value and unit, for example:
042: * 10 pixel, 15 point or 4 dialog units.
043: * You can get instances of <code>ConstantSize</code> using
044: * the factory methods and constants in the {@link Sizes} class.
045: * Logical constant sizes that vary with the current layout style
046: * are delivered by the {@link com.jgoodies.forms.util.LayoutStyle} class.<p>
047: *
048: * This class supports different size units:
049: * <table>
050: * <tr><td><b>Unit</b>
051: * </td><td> <b>Abbreviation</b> </td><td>
052: * <b>Size</b></td></tr>
053: * <tr><td>Millimeter</td><td>mm</td><td>0.1 cm</td></tr>
054: * <tr><td>Centimeter</td><td>cm</td><td>10.0 mm</td></tr>
055: * <tr><td>Inch</td><td>in</td><td>25.4 mm</td></tr>
056: * <tr><td>DTP Point</td><td>pt</td><td>1/72 in</td></tr>
057: * <tr><td>Pixel</td><td>px</td><td>1/(resolution in dpi) in</td></tr>
058: * <tr><td>Dialog Unit</td><td>dlu</td><td>honors l&f, resolution, and
059: * dialog font size</td></tr>
060: * </table><p>
061: *
062: * <strong>Examples:</strong><pre>
063: * Sizes.ZERO;
064: * Sizes.DLUX9;
065: * Sizes.dluX(42);
066: * Sizes.pixel(99);
067: * </pre>
068: *
069: * @author Karsten Lentzsch
070: * @version $Revision: 1.7 $
071: *
072: * @see Size
073: * @see Sizes
074: */
075: public final class ConstantSize implements Size, Serializable {
076:
077: // Public Units *********************************************************
078:
079: public static final Unit PIXEL = new Unit("Pixel", "px", true);
080: public static final Unit POINT = new Unit("Point", "pt", true);
081: public static final Unit DIALOG_UNITS_X = new Unit(
082: "Dialog units X", "dluX", true);
083: public static final Unit DLUX = DIALOG_UNITS_X;
084: public static final Unit DIALOG_UNITS_Y = new Unit(
085: "Dialog units Y", "dluY", true);
086: public static final Unit DLUY = DIALOG_UNITS_Y;
087: public static final Unit MILLIMETER = new Unit("Millimeter", "mm",
088: false);
089: public static final Unit MM = MILLIMETER;
090: public static final Unit CENTIMETER = new Unit("Centimeter", "cm",
091: false);
092: public static final Unit CM = CENTIMETER;
093: public static final Unit INCH = new Unit("Inch", "in", false);
094: public static final Unit IN = INCH;
095:
096: /**
097: * An array of all enumeration values used to canonicalize
098: * deserialized units.
099: */
100: private static final Unit[] VALUES = { PIXEL, POINT,
101: DIALOG_UNITS_X, DIALOG_UNITS_Y, MILLIMETER, CENTIMETER,
102: INCH };
103:
104: // Fields ***************************************************************
105:
106: private final double value;
107: private final Unit unit;
108:
109: // Instance Creation ****************************************************
110:
111: /**
112: * Constructs a ConstantSize for the given size and unit.
113: *
114: * @param value the size value interpreted in the given units
115: * @param unit the size's unit
116: *
117: * @since 1.1
118: */
119: public ConstantSize(int value, Unit unit) {
120: this .value = value;
121: this .unit = unit;
122: }
123:
124: /**
125: * Constructs a ConstantSize for the given size and unit.
126: *
127: * @param value the size value interpreted in the given units
128: * @param unit the size's unit
129: *
130: * @since 1.1
131: */
132: public ConstantSize(double value, Unit unit) {
133: this .value = value;
134: this .unit = unit;
135: }
136:
137: /**
138: * Creates and returns a ConstantSize from the given encoded size
139: * and unit description.
140: *
141: * @param encodedValueAndUnit the size's value and unit as string
142: * @param horizontal true for horizontal, false for vertical
143: * @return a constant size for the given encoding and unit description
144: *
145: * @throws IllegalArgumentException if the unit requires integer
146: * but the value is not an integer
147: */
148: static ConstantSize valueOf(String encodedValueAndUnit,
149: boolean horizontal) {
150: String split[] = ConstantSize
151: .splitValueAndUnit(encodedValueAndUnit);
152: String encodedValue = split[0];
153: String encodedUnit = split[1];
154: Unit unit = Unit.valueOf(encodedUnit, horizontal);
155: double value = Double.parseDouble(encodedValue);
156: if (unit.requiresIntegers) {
157: if (value != (int) value)
158: throw new IllegalArgumentException(unit.toString()
159: + " value " + encodedValue
160: + " must be an integer.");
161: }
162: return new ConstantSize(value, unit);
163: }
164:
165: /**
166: * Creates and returns a ConstantSize for the specified size value
167: * in horizontal dialog units.
168: *
169: * @param value size value in horizontal dialog units
170: * @return the associated Size instance
171: */
172: static ConstantSize dluX(int value) {
173: return new ConstantSize(value, DLUX);
174: }
175:
176: /**
177: * Creates and returns a ConstantSize for the specified size value
178: * in vertical dialog units.
179: *
180: * @param value size value in vertical dialog units
181: * @return the associated Size instance
182: */
183: static ConstantSize dluY(int value) {
184: return new ConstantSize(value, DLUY);
185: }
186:
187: // Accessors ************************************************************
188:
189: /**
190: * Returns this size's value.
191: *
192: * @return the size value
193: *
194: * @since 1.1
195: */
196: public double getValue() {
197: return value;
198: }
199:
200: /**
201: * Returns this size's unit.
202: *
203: * @return the size unit
204: *
205: * @since 1.1
206: */
207: public Unit getUnit() {
208: return unit;
209: }
210:
211: // Accessing the Value **************************************************
212:
213: /**
214: * Converts the size if necessary and returns the value in pixels.
215: *
216: * @param component the associated component
217: * @return the size in pixels
218: */
219: public int getPixelSize(Component component) {
220: if (unit == PIXEL)
221: return intValue();
222: else if (unit == POINT)
223: return Sizes.pointAsPixel(intValue(), component);
224: else if (unit == INCH)
225: return Sizes.inchAsPixel(value, component);
226: else if (unit == MILLIMETER)
227: return Sizes.millimeterAsPixel(value, component);
228: else if (unit == CENTIMETER)
229: return Sizes.centimeterAsPixel(value, component);
230: else if (unit == DIALOG_UNITS_X)
231: return Sizes.dialogUnitXAsPixel(intValue(), component);
232: else if (unit == DIALOG_UNITS_Y)
233: return Sizes.dialogUnitYAsPixel(intValue(), component);
234: else
235: throw new IllegalStateException("Invalid unit " + unit);
236: }
237:
238: // Implementing the Size Interface **************************************
239:
240: /**
241: * Returns this size as pixel size. Neither requires the component
242: * list nor the specified measures.<p>
243: *
244: * Invoked by {@link com.jgoodies.forms.layout.FormSpec} to determine
245: * the size of a column or row.
246: *
247: * @param container the layout container
248: * @param components the list of components used to compute the size
249: * @param minMeasure the measure that determines the minimum sizes
250: * @param prefMeasure the measure that determines the preferred sizes
251: * @param defaultMeasure the measure that determines the default sizes
252: * @return the computed maximum size in pixel
253: */
254: public int maximumSize(Container container, List components,
255: FormLayout.Measure minMeasure,
256: FormLayout.Measure prefMeasure,
257: FormLayout.Measure defaultMeasure) {
258: return getPixelSize(container);
259: }
260:
261: /**
262: * Describes if this Size can be compressed, if container space gets scarce.
263: * Used by the FormLayout size computations in <code>#compressedSizes</code>
264: * to check whether a column or row can be compressed or not.<p>
265: *
266: * ConstantSizes are incompressible.
267: *
268: * @return <code>false</code>
269: *
270: * @since 1.1
271: */
272: public boolean compressible() {
273: return false;
274: }
275:
276: // Overriding Object Behavior *******************************************
277:
278: /**
279: * Indicates whether some other ConstantSize is "equal to" this one.
280: *
281: * @param o the Object with which to compare
282: * @return <code>true</code> if this object is the same as the obj
283: * argument; <code>false</code> otherwise.
284: *
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
299: * supported for 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: *
304: * @see java.lang.Object#equals(java.lang.Object)
305: * @see java.util.Hashtable
306: */
307: public int hashCode() {
308: return new Double(value).hashCode() + 37 * unit.hashCode();
309: }
310:
311: /**
312: * Returns a string representation of this size object.
313: *
314: * <strong>Note:</strong> The string representation may change
315: * at any time. It is strongly recommended to not use this string
316: * for parsing purposes.
317: *
318: * @return a string representation of the constant size
319: */
320: public String toString() {
321: return (value == intValue()) ? Integer.toString(intValue())
322: + unit.abbreviation() : Double.toString(value)
323: + unit.abbreviation();
324: }
325:
326: // Helper Code **********************************************************
327:
328: private int intValue() {
329: return (int) Math.round(value);
330: }
331:
332: /**
333: * Splits a string that encodes size with unit into the size and unit
334: * substrings. Returns an array of two strings.
335: *
336: * @param encodedValueAndUnit a strings that represents a size with unit
337: * @return the first element is size, the second is unit
338: */
339: static String[] splitValueAndUnit(String encodedValueAndUnit) {
340: String[] result = new String[2];
341: int len = encodedValueAndUnit.length();
342: int firstLetterIndex = len;
343: while (firstLetterIndex > 0
344: && Character.isLetter(encodedValueAndUnit
345: .charAt(firstLetterIndex - 1))) {
346: firstLetterIndex--;
347: }
348: result[0] = encodedValueAndUnit.substring(0, firstLetterIndex);
349: result[1] = encodedValueAndUnit.substring(firstLetterIndex);
350: return result;
351: }
352:
353: // Helper Class *********************************************************
354:
355: /**
356: * An ordinal-based serializable typesafe enumeration for units
357: * as used in instances of {@link ConstantSize}.
358: */
359: public static final class Unit implements Serializable {
360:
361: private final transient String name;
362: private final transient String abbreviation;
363: final transient boolean requiresIntegers;
364:
365: private Unit(String name, String abbreviation,
366: boolean requiresIntegers) {
367: this .name = name;
368: this .abbreviation = abbreviation;
369: this .requiresIntegers = requiresIntegers;
370: }
371:
372: /**
373: * Returns a Unit that corresponds to the specified string.
374: *
375: * @param str the encoded unit
376: * @param horizontal true for a horizontal unit, false for vertical
377: * @return the corresponding Unit
378: * @throws IllegalArgumentException if no Unit exists for the string
379: */
380: static Unit valueOf(String str, boolean horizontal) {
381: String lowerCase = str.toLowerCase(Locale.ENGLISH);
382: if (lowerCase.equals("px") || lowerCase.length() == 0)
383: return PIXEL;
384: else if (lowerCase.equals("dlu"))
385: return horizontal ? DIALOG_UNITS_X : DIALOG_UNITS_Y;
386: else if (lowerCase.equals("pt"))
387: return POINT;
388: else if (lowerCase.equals("in"))
389: return INCH;
390: else if (lowerCase.equals("mm"))
391: return MILLIMETER;
392: else if (lowerCase.equals("cm"))
393: return CENTIMETER;
394: else
395: throw new IllegalArgumentException(
396: "Invalid unit name '" + str
397: + "'. Must be one of: "
398: + "px, dlu, pt, mm, cm, in");
399: }
400:
401: public String toString() {
402: return name;
403: }
404:
405: /**
406: * Returns the first character of this Unit's name.
407: * Used to identify it in short format strings.
408: *
409: * @return the first character of this Unit's name.
410: */
411: public String abbreviation() {
412: return abbreviation;
413: }
414:
415: // Serialization *****************************************************
416:
417: private static int nextOrdinal = 0;
418:
419: private final int ordinal = nextOrdinal++;
420:
421: private Object readResolve() {
422: return VALUES[ordinal]; // Canonicalize
423: }
424:
425: }
426:
427: }
|