1 package net.miginfocom.layout;
6 * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
9 * Redistribution and use in source and binary forms, with or without modification,
10 * are permitted provided that the following conditions are met:
11 * Redistributions of source code must retain the above copyright notice, this list
12 * of conditions and the following disclaimer.
13 * Redistributions in binary form must reproduce the above copyright notice, this
14 * list of conditions and the following disclaimer in the documentation and/or other
15 * materials provided with the distribution.
16 * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
17 * used to endorse or promote products derived from this software without specific
18 * prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
26 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
32 * @author Mikael Grev, MiG InfoCom AB
36 import java.beans.Encoder;
37 import java.beans.Expression;
38 import java.beans.PersistenceDelegate;
40 import java.util.ArrayList;
41 import java.util.HashMap;
43 public final class UnitValue implements Serializable
45 private static final HashMap<String, Integer> UNIT_MAP = new HashMap<String, Integer>(32);
47 private static final ArrayList<UnitConverter> CONVERTERS = new ArrayList<UnitConverter>();
49 /** An operation indicating a static value.
51 public static final int STATIC = 100;
53 /** An operation indicating a addition of two sub units.
55 public static final int ADD = 101; // Must have "sub-unit values"
57 /** An operation indicating a subtraction of two sub units
59 public static final int SUB = 102; // Must have "sub-unit values"
61 /** An operation indicating a multiplication of two sub units.
63 public static final int MUL = 103; // Must have "sub-unit values"
65 /** An operation indicating a division of two sub units.
67 public static final int DIV = 104; // Must have "sub-unit values"
69 /** An operation indicating the minimum of two sub units
71 public static final int MIN = 105; // Must have "sub-unit values"
73 /** An operation indicating the maximum of two sub units
75 public static final int MAX = 106; // Must have "sub-unit values"
77 /** An operation indicating the middle value of two sub units
79 public static final int MID = 107; // Must have "sub-unit values"
84 /** A unit indicating pixels.
86 public static final int PIXEL = 0;
88 /** A unit indicating logical horizontal pixels.
90 public static final int LPX = 1;
92 /** A unit indicating logical vertical pixels.
94 public static final int LPY = 2;
96 /** A unit indicating millimeters.
98 public static final int MM = 3;
100 /** A unit indicating centimeters.
102 public static final int CM = 4;
104 /** A unit indicating inches.
106 public static final int INCH = 5;
108 /** A unit indicating percent.
110 public static final int PERCENT = 6;
112 /** A unit indicating points.
114 public static final int PT = 7;
116 /** A unit indicating screen percentage width.
118 public static final int SPX = 8;
120 /** A unit indicating screen percentage height.
122 public static final int SPY = 9;
124 /** A unit indicating alignment.
126 public static final int ALIGN = 12;
128 /** A unit indicating minimum size.
130 public static final int MIN_SIZE = 13;
132 /** A unit indicating preferred size.
134 public static final int PREF_SIZE = 14;
136 /** A unit indicating maximum size.
138 public static final int MAX_SIZE = 15;
140 /** A unit indicating button size.
142 public static final int BUTTON = 16;
144 /** A unit indicating linking to x.
146 public static final int LINK_X = 18; // First link
148 /** A unit indicating linking to y.
150 public static final int LINK_Y = 19;
152 /** A unit indicating linking to width.
154 public static final int LINK_W = 20;
156 /** A unit indicating linking to height.
158 public static final int LINK_H = 21;
160 /** A unit indicating linking to x2.
162 public static final int LINK_X2 = 22;
164 /** A unit indicating linking to y2.
166 public static final int LINK_Y2 = 23;
168 /** A unit indicating linking to x position on screen.
170 public static final int LINK_XPOS = 24;
172 /** A unit indicating linking to y position on screen.
174 public static final int LINK_YPOS = 25; // Last link
176 /** A unit indicating a lookup.
178 public static final int LOOKUP = 26;
180 /** A unit indicating label alignment.
182 public static final int LABEL_ALIGN = 27;
184 private static final int IDENTITY = -1;
187 UNIT_MAP.put("px", PIXEL);
188 UNIT_MAP.put("lpx", LPX);
189 UNIT_MAP.put("lpy", LPY);
190 UNIT_MAP.put("%", PERCENT);
191 UNIT_MAP.put("cm", CM);
192 UNIT_MAP.put("in", INCH);
193 UNIT_MAP.put("spx", SPX);
194 UNIT_MAP.put("spy", SPY);
195 UNIT_MAP.put("al", ALIGN);
196 UNIT_MAP.put("mm", MM);
197 UNIT_MAP.put("pt", PT);
198 UNIT_MAP.put("min", MIN_SIZE);
199 UNIT_MAP.put("minimum", MIN_SIZE);
200 UNIT_MAP.put("p", PREF_SIZE);
201 UNIT_MAP.put("pref", PREF_SIZE);
202 UNIT_MAP.put("max", MAX_SIZE);
203 UNIT_MAP.put("maximum", MAX_SIZE);
204 UNIT_MAP.put("button", BUTTON);
205 UNIT_MAP.put("label", LABEL_ALIGN);
208 static final UnitValue ZERO = new UnitValue(0, null, PIXEL, true, STATIC, null, null, "0px");
209 static final UnitValue TOP = new UnitValue(0, null, PERCENT, false, STATIC, null, null, "top");
210 static final UnitValue LEADING = new UnitValue(0, null, PERCENT, true, STATIC, null, null, "leading");
211 static final UnitValue LEFT = new UnitValue(0, null, PERCENT, true, STATIC, null, null, "left");
212 static final UnitValue CENTER = new UnitValue(50, null, PERCENT, true, STATIC, null, null, "center");
213 static final UnitValue TRAILING = new UnitValue(100, null, PERCENT, true, STATIC, null, null, "trailing");
214 static final UnitValue RIGHT = new UnitValue(100, null, PERCENT, true, STATIC, null, null, "right");
215 static final UnitValue BOTTOM = new UnitValue(100, null, PERCENT, false, STATIC, null, null, "bottom");
216 static final UnitValue LABEL = new UnitValue(0, null, LABEL_ALIGN, false, STATIC, null, null, "label");
218 static final UnitValue INF = new UnitValue(LayoutUtil.INF, null, PIXEL, true, STATIC, null, null, "inf");
220 static final UnitValue BASELINE_IDENTITY = new UnitValue(0, null, IDENTITY, false, STATIC, null, null, "baseline");
222 private final transient float value;
223 private final transient int unit;
224 private final transient int oper;
225 private final transient String unitStr;
226 private transient String linkId = null; // Should be final, but initializes in a sub method.
227 private final transient boolean isHor;
228 private final transient UnitValue[] subUnits;
231 public UnitValue(float value) // If hor/ver does not matter.
233 this(value, null, PIXEL, true, STATIC, null, null, value + "px");
236 public UnitValue(float value, int unit, String createString) // If hor/ver does not matter.
238 this(value, null, unit, true, STATIC, null, null, createString);
241 public UnitValue(float value, String unitStr, boolean isHor, int oper, String createString)
243 this(value, unitStr, -1, isHor, oper, null, null, createString);
246 UnitValue(boolean isHor, int oper, UnitValue sub1, UnitValue sub2, String createString)
248 this(0, "", -1, isHor, oper, sub1, sub2, createString);
249 if (sub1 == null || sub2 == null)
250 throw new IllegalArgumentException("Sub units is null!");
253 private UnitValue(float value, String unitStr, int unit, boolean isHor, int oper, UnitValue sub1, UnitValue sub2, String createString)
255 if (oper < STATIC || oper > MID)
256 throw new IllegalArgumentException("Unknown Operation: " + oper);
258 if (oper >= ADD && oper <= MID && (sub1 == null || sub2 == null))
259 throw new IllegalArgumentException(oper + " Operation may not have null sub-UnitValues.");
264 this.unitStr = unitStr;
265 this.unit = unitStr != null ? parseUnitString() : unit;
266 this.subUnits = sub1 != null && sub2 != null ? new UnitValue[] {sub1, sub2} : null;
268 LayoutUtil.putCCString(this, createString); // "this" escapes!! Safe though.
271 /** Returns the size in pixels rounded.
272 * @param refValue The reference value. Normally the size of the parent. For unit {@link #ALIGN} the current size of the component should be sent in.
273 * @param parent The parent. May be <code>null</code> for testing the validity of the value, but should normally not and are not
274 * required to return any usable value if <code>null</code>.
275 * @param comp The component, if any, that the value is for. Might be <code>null</code> if the value is not
276 * connected to any component.
277 * @return The size in pixels.
279 public final int getPixels(float refValue, ContainerWrapper parent, ComponentWrapper comp)
281 return Math.round(getPixelsExact(refValue, parent, comp));
284 private static final float[] SCALE = new float[] {25.4f, 2.54f, 1f, 0f, 72f};
285 /** Returns the size in pixels.
286 * @param refValue The reference value. Normally the size of the parent. For unit {@link #ALIGN} the current size of the component should be sent in.
287 * @param parent The parent. May be <code>null</code> for testing the validity of the value, but should normally not and are not
288 * required to return any usable value if <code>null</code>.
289 * @param comp The component, if any, that the value is for. Might be <code>null</code> if the value is not
290 * connected to any component.
291 * @return The size in pixels.
293 public final float getPixelsExact(float refValue, ContainerWrapper parent, ComponentWrapper comp)
298 if (oper == STATIC) {
305 return parent.getPixelUnitFactor(unit == LPX) * value;
311 float f = SCALE[unit - MM];
312 Float s = isHor ? PlatformDefaults.getHorizontalScaleFactor() : PlatformDefaults.getVerticalScaleFactor();
316 return (isHor ? parent.getHorizontalScreenDPI() : parent.getVerticalScreenDPI()) * value / f;
319 return value * refValue * 0.01f;
323 return (unit == SPX ? parent.getScreenWidth() : parent.getScreenHeight()) * value * 0.01f;
326 Integer st = LinkHandler.getValue(parent.getLayout(), "visual", isHor ? LinkHandler.X : LinkHandler.Y);
327 Integer sz = LinkHandler.getValue(parent.getLayout(), "visual", isHor ? LinkHandler.WIDTH : LinkHandler.HEIGHT);
328 if (st == null || sz == null)
330 return value * (Math.max(0, sz.intValue()) - refValue) + st;
335 return isHor ? comp.getMinimumWidth(comp.getHeight()) : comp.getMinimumHeight(comp.getWidth());
340 return isHor ? comp.getPreferredWidth(comp.getHeight()) : comp.getPreferredHeight(comp.getWidth());
345 return isHor ? comp.getMaximumWidth(comp.getHeight()) : comp.getMaximumHeight(comp.getWidth());
348 return PlatformDefaults.getMinimumButtonWidthIncludingPadding(refValue, parent, comp);
358 Integer v = LinkHandler.getValue(parent.getLayout(), getLinkTargetId(), unit - (unit >= LINK_XPOS ? LINK_XPOS : LINK_X));
362 if (unit == LINK_XPOS)
363 return parent.getScreenLocationX() + v;
364 if (unit == LINK_YPOS)
365 return parent.getScreenLocationY() + v;
370 float res = lookup(refValue, parent, comp);
371 if (res != UnitConverter.UNABLE)
375 return PlatformDefaults.getLabelAlignPercentage() * refValue;
379 throw new IllegalArgumentException("Unknown/illegal unit: " + unit + ", unitStr: " + unitStr);
382 if (subUnits != null && subUnits.length == 2) {
383 float r1 = subUnits[0].getPixelsExact(refValue, parent, comp);
384 float r2 = subUnits[1].getPixelsExact(refValue, parent, comp);
395 return r1 < r2 ? r1 : r2;
397 return r1 > r2 ? r1 : r2;
399 return (r1 + r2) * 0.5f;
403 throw new IllegalArgumentException("Internal: Unknown Oper: " + oper);
406 private float lookup(float refValue, ContainerWrapper parent, ComponentWrapper comp)
408 float res = UnitConverter.UNABLE;
409 for (int i = CONVERTERS.size() - 1; i >= 0; i--) {
410 res = CONVERTERS.get(i).convertToPixels(value, unitStr, isHor, refValue, parent, comp);
411 if (res != UnitConverter.UNABLE)
414 return PlatformDefaults.convertToPixels(value, unitStr, isHor, refValue, parent, comp);
417 private int parseUnitString()
419 int len = unitStr.length();
421 return isHor ? PlatformDefaults.getDefaultHorizontalUnit() : PlatformDefaults.getDefaultVerticalUnit();
423 Integer u = UNIT_MAP.get(unitStr);
425 if (!isHor && (u == BUTTON || u == LABEL_ALIGN))
426 throw new IllegalArgumentException("Not valid in vertical contexts: '" + unitStr + "'");
431 if (unitStr.equals("lp"))
432 return isHor ? LPX : LPY;
434 if (unitStr.equals("sp"))
435 return isHor ? SPX : SPY;
437 if (lookup(0, null, null) != UnitConverter.UNABLE) // To test so we can fail fast
440 // Only link left. E.g. "otherID.width"
442 int pIx = unitStr.indexOf('.');
444 linkId = unitStr.substring(0, pIx);
445 String e = unitStr.substring(pIx + 1);
451 if (e.equals("w") || e.equals("width"))
453 if (e.equals("h") || e.equals("height"))
459 if (e.equals("xpos"))
461 if (e.equals("ypos"))
465 throw new IllegalArgumentException("Unknown keyword: " + unitStr);
468 final boolean isAbsolute()
502 throw new IllegalArgumentException("Unknown/illegal unit: " + unit + ", unitStr: " + unitStr);
505 final boolean isAbsoluteDeep()
507 if (subUnits != null) {
508 for (UnitValue subUnit : subUnits) {
509 if (subUnit.isAbsoluteDeep())
516 final boolean isLinked()
518 return linkId != null;
521 final boolean isLinkedDeep()
523 if (subUnits != null) {
524 for (UnitValue subUnit : subUnits) {
525 if (subUnit.isLinkedDeep())
532 final String getLinkTargetId()
537 final UnitValue getSubUnitValue(int i)
542 final int getSubUnitCount()
544 return subUnits != null ? subUnits.length : 0;
547 public final UnitValue[] getSubUnits()
549 return subUnits != null ? subUnits.clone() : null;
552 public final int getUnit()
557 public final String getUnitString()
562 public final int getOperation()
567 public final float getValue()
572 public final boolean isHorizontal()
578 final public String toString()
580 return getClass().getName() + ". Value=" + value + ", unit=" + unit + ", unitString: " + unitStr + ", oper=" + oper + ", isHor: " + isHor;
583 /** Returns the creation string for this object. Note that {@link LayoutUtil#setDesignTime(ContainerWrapper, boolean)} must be
584 * set to <code>true</code> for the creation strings to be stored.
585 * @return The constraint string or <code>null</code> if none is registered.
587 public final String getConstraintString()
589 return LayoutUtil.getCCString(this);
593 public final int hashCode()
595 return (int) (value * 12345) + (oper >>> 5) + unit >>> 17;
598 /** Adds a global unit converter that can convert from some <code>unit</code> to pixels.
600 * This converter will be asked before the platform converter so the values for it (e.g. "related" and "unrelated")
601 * can be overridden. It is however not possible to override the built in ones (e.g. "mm", "pixel" or "lp").
602 * @param conv The converter. Not <code>null</code>.
604 public synchronized static void addGlobalUnitConverter(UnitConverter conv)
607 throw new NullPointerException();
608 CONVERTERS.add(conv);
611 /** Removed the converter.
612 * @param unit The converter.
613 * @return If there was a converter found and thus removed.
615 public synchronized static boolean removeGlobalUnitConverter(UnitConverter unit)
617 return CONVERTERS.remove(unit);
620 /** Returns the global converters currently registered. The platform converter will not be in this list.
621 * @return The converters. Never <code>null</code>.
623 public synchronized static UnitConverter[] getGlobalUnitConverters()
625 return CONVERTERS.toArray(new UnitConverter[CONVERTERS.size()]);
628 /** Returns the current default unit. The default unit is the unit used if no unit is set. E.g. "width 10".
629 * @return The current default unit.
632 * @deprecated Use {@link PlatformDefaults#getDefaultHorizontalUnit()} and {@link PlatformDefaults#getDefaultVerticalUnit()} instead.
634 public static int getDefaultUnit()
636 return PlatformDefaults.getDefaultHorizontalUnit();
639 /** Sets the default unit. The default unit is the unit used if no unit is set. E.g. "width 10".
640 * @param unit The new default unit.
643 * @deprecated Use {@link PlatformDefaults#setDefaultHorizontalUnit(int)} and {@link PlatformDefaults#setDefaultVerticalUnit(int)} instead.
645 public static void setDefaultUnit(int unit)
647 PlatformDefaults.setDefaultHorizontalUnit(unit);
648 PlatformDefaults.setDefaultVerticalUnit(unit);
652 // if(LayoutUtil.HAS_BEANS){
653 // LayoutUtil.setDelegate(UnitValue.class, new PersistenceDelegate() {
655 // protected Expression instantiate(Object oldInstance, Encoder out)
657 // UnitValue uv = (UnitValue) oldInstance;
658 // String cs = uv.getConstraintString();
660 // throw new IllegalStateException("Design time must be on to use XML persistence. See LayoutUtil.");
662 // return new Expression(oldInstance, ConstraintParser.class, "parseUnitValueOrAlign", new Object[] {
663 // uv.getConstraintString(), (uv.isHorizontal() ? Boolean.TRUE : Boolean.FALSE), null
670 // ************************************************
671 // Persistence Delegate and Serializable combined.
672 // ************************************************
674 private static final long serialVersionUID = 1L;
676 private Object readResolve() throws ObjectStreamException
678 return LayoutUtil.getSerializedObject(this);
681 private void writeObject(ObjectOutputStream out) throws IOException
683 if (getClass() == UnitValue.class)
684 LayoutUtil.writeAsXML(out, this);
687 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
689 LayoutUtil.setSerializedObject(this, LayoutUtil.readAsXML(in));