Coverage Report - net.miginfocom.swing.MigLayout
 
Classes in this File Line Coverage Branch Coverage Complexity
MigLayout
N/A
N/A
2.891
MigLayout$1
N/A
N/A
2.891
MigLayout$2
N/A
N/A
2.891
MigLayout$MyDebugRepaintListener
N/A
N/A
2.891
 
 1  
 package net.miginfocom.swing;
 2  
 /*
 3  
  * License (BSD):
 4  
  * ==============
 5  
  *
 6  
  * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
 7  
  * All rights reserved.
 8  
  *
 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.
 19  
  *
 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
 29  
  * OF SUCH DAMAGE.
 30  
  *
 31  
  * @version 1.0
 32  
  * @author Mikael Grev, MiG InfoCom AB
 33  
  *         Date: 2006-sep-08
 34  
  */
 35  
 
 36  
 import net.miginfocom.layout.*;
 37  
 
 38  
 import javax.swing.*;
 39  
 import javax.swing.Timer;
 40  
 import java.awt.*;
 41  
 import java.awt.event.ActionEvent;
 42  
 import java.awt.event.ActionListener;
 43  
 import java.io.*;
 44  
 import java.util.*;
 45  
 
 46  
 /** A very flexible layout manager.
 47  
  * <p>
 48  
  * Read the documentation that came with this layout manager for information on usage.
 49  
  */
 50  
 public final class MigLayout implements LayoutManager2, Externalizable
 51  
 {
 52  
         // ******** Instance part ********
 53  
 
 54  
         /** The component to string constraints mappings.
 55  
          */
 56  
         private final Map<Component, Object> scrConstrMap = new IdentityHashMap<Component, Object>(8);
 57  
 
 58  
         /** Hold the serializable text representation of the constraints.
 59  
          */
 60  
         private Object layoutConstraints = "", colConstraints = "", rowConstraints = "";    // Should never be null!
 61  
 
 62  
         // ******** Transient part ********
 63  
 
 64  
         private transient ContainerWrapper cacheParentW = null;
 65  
 
 66  
         private transient final Map<ComponentWrapper, CC> ccMap = new HashMap<ComponentWrapper, CC>(8);
 67  
         private transient javax.swing.Timer debugTimer = null;
 68  
 
 69  
         private transient LC lc = null;
 70  
         private transient AC colSpecs = null, rowSpecs = null;
 71  
         private transient Grid grid = null;
 72  
         private transient int lastModCount = PlatformDefaults.getModCount();
 73  
         private transient int lastHash = -1;
 74  
         private transient Dimension lastInvalidSize = null;
 75  
         private transient boolean lastWasInvalid = false;  // Added in 3.7.1. May have regressions
 76  
         private transient Dimension lastParentSize = null;
 77  
 
 78  
         private transient ArrayList<LayoutCallback> callbackList = null;
 79  
 
 80  
         private transient boolean dirty = true;
 81  
 
 82  
         /** Constructor with no constraints.
 83  
          */
 84  
         public MigLayout()
 85  
         {
 86  
                 this("", "", "");
 87  
         }
 88  
 
 89  
         /** Constructor.
 90  
          * @param layoutConstraints The constraints that concern the whole layout. <code>null</code> will be treated as "".
 91  
          */
 92  
         public MigLayout(String layoutConstraints)
 93  
         {
 94  
                 this(layoutConstraints, "", "");
 95  
         }
 96  
 
 97  
         /** Constructor.
 98  
          * @param layoutConstraints The constraints that concern the whole layout. <code>null</code> will be treated as "".
 99  
          * @param colConstraints The constraints for the columns in the grid. <code>null</code> will be treated as "".
 100  
          */
 101  
         public MigLayout(String layoutConstraints, String colConstraints)
 102  
         {
 103  
                 this(layoutConstraints, colConstraints, "");
 104  
         }
 105  
 
 106  
         /** Constructor.
 107  
          * @param layoutConstraints The constraints that concern the whole layout. <code>null</code> will be treated as "".
 108  
          * @param colConstraints The constraints for the columns in the grid. <code>null</code> will be treated as "".
 109  
          * @param rowConstraints The constraints for the rows in the grid. <code>null</code> will be treated as "".
 110  
          */
 111  
         public MigLayout(String layoutConstraints, String colConstraints, String rowConstraints)
 112  
         {
 113  
                 setLayoutConstraints(layoutConstraints);
 114  
                 setColumnConstraints(colConstraints);
 115  
                 setRowConstraints(rowConstraints);
 116  
         }
 117  
 
 118  
         /** Constructor.
 119  
          * @param layoutConstraints The constraints that concern the whole layout. <code>null</code> will be treated as an empty constraint.
 120  
          */
 121  
         public MigLayout(LC layoutConstraints)
 122  
         {
 123  
                 this(layoutConstraints, null, null);
 124  
         }
 125  
 
 126  
         /** Constructor.
 127  
          * @param layoutConstraints The constraints that concern the whole layout. <code>null</code> will be treated as an empty constraint.
 128  
          * @param colConstraints The constraints for the columns in the grid. <code>null</code> will be treated as an empty constraint.
 129  
          */
 130  
         public MigLayout(LC layoutConstraints, AC colConstraints)
 131  
         {
 132  
                 this(layoutConstraints, colConstraints, null);
 133  
         }
 134  
 
 135  
         /** Constructor.
 136  
          * @param layoutConstraints The constraints that concern the whole layout. <code>null</code> will be treated as an empty constraint.
 137  
          * @param colConstraints The constraints for the columns in the grid. <code>null</code> will be treated as an empty constraint.
 138  
          * @param rowConstraints The constraints for the rows in the grid. <code>null</code> will be treated as an empty constraint.
 139  
          */
 140  
         public MigLayout(LC layoutConstraints, AC colConstraints, AC rowConstraints)
 141  
         {
 142  
                 setLayoutConstraints(layoutConstraints);
 143  
                 setColumnConstraints(colConstraints);
 144  
                 setRowConstraints(rowConstraints);
 145  
         }
 146  
 
 147  
         /** Returns layout constraints either as a <code>String</code> or {@link net.miginfocom.layout.LC} depending what was sent in
 148  
          * to the constructor or set with {@link #setLayoutConstraints(Object)}.
 149  
          * @return The layout constraints either as a <code>String</code> or {@link net.miginfocom.layout.LC} depending what was sent in
 150  
          * to the constructor or set with {@link #setLayoutConstraints(Object)}. Never <code>null</code>.
 151  
          */
 152  
         public Object getLayoutConstraints()
 153  
         {
 154  
                 return layoutConstraints;
 155  
         }
 156  
 
 157  
         /** Sets the layout constraints for the layout manager instance as a String.
 158  
          * <p>
 159  
          * See the class JavaDocs for information on how this string is formatted.
 160  
          * @param constr The layout constraints as a String representation. <code>null</code> is converted to <code>""</code> for storage.
 161  
          * @throws RuntimeException if the constraint was not valid.
 162  
          */
 163  
         public void setLayoutConstraints(Object constr)
 164  
         {
 165  
                 if (constr == null || constr instanceof String) {
 166  
                         constr = ConstraintParser.prepare((String) constr);
 167  
                         lc = ConstraintParser.parseLayoutConstraint((String) constr);
 168  
                 } else if (constr instanceof LC) {
 169  
                         lc = (LC) constr;
 170  
                 } else {
 171  
                         throw new IllegalArgumentException("Illegal constraint type: " + constr.getClass().toString());
 172  
                 }
 173  
                 layoutConstraints = constr;
 174  
                 dirty = true;
 175  
         }
 176  
 
 177  
         /** Returns the column layout constraints either as a <code>String</code> or {@link net.miginfocom.layout.AC}.
 178  
          * @return The column constraints either as a <code>String</code> or {@link net.miginfocom.layout.LC} depending what was sent in
 179  
          * to the constructor or set with {@link #setLayoutConstraints(Object)}. Never <code>null</code>.
 180  
          */
 181  
         public Object getColumnConstraints()
 182  
         {
 183  
                 return colConstraints;
 184  
         }
 185  
 
 186  
         /** Sets the column layout constraints for the layout manager instance as a String.
 187  
          * <p>
 188  
          * See the class JavaDocs for information on how this string is formatted.
 189  
          * @param constr The column layout constraints as a String representation. <code>null</code> is converted to <code>""</code> for storage.
 190  
          * @throws RuntimeException if the constraint was not valid.
 191  
          */
 192  
         public void setColumnConstraints(Object constr)
 193  
         {
 194  
                 if (constr == null || constr instanceof String) {
 195  
                         constr = ConstraintParser.prepare((String) constr);
 196  
                         colSpecs = ConstraintParser.parseColumnConstraints((String) constr);
 197  
                 } else if (constr instanceof AC) {
 198  
                         colSpecs = (AC) constr;
 199  
                 } else {
 200  
                         throw new IllegalArgumentException("Illegal constraint type: " + constr.getClass().toString());
 201  
                 }
 202  
                 colConstraints = constr;
 203  
                 dirty = true;
 204  
         }
 205  
 
 206  
         /** Returns the row layout constraints as a String representation. This string is the exact string as set with {@link #setRowConstraints(Object)}
 207  
          * or sent into the constructor.
 208  
          * <p>
 209  
          * See the class JavaDocs for information on how this string is formatted.
 210  
          * @return The row layout constraints as a String representation. Never <code>null</code>.
 211  
          */
 212  
         public Object getRowConstraints()
 213  
         {
 214  
                 return rowConstraints;
 215  
         }
 216  
 
 217  
         /** Sets the row layout constraints for the layout manager instance as a String.
 218  
          * <p>
 219  
          * See the class JavaDocs for information on how this string is formatted.
 220  
          * @param constr The row layout constraints as a String representation. <code>null</code> is converted to <code>""</code> for storage.
 221  
          * @throws RuntimeException if the constraint was not valid.
 222  
          */
 223  
         public void setRowConstraints(Object constr)
 224  
         {
 225  
                 if (constr == null || constr instanceof String) {
 226  
                         constr = ConstraintParser.prepare((String) constr);
 227  
                         rowSpecs = ConstraintParser.parseRowConstraints((String) constr);
 228  
                 } else if (constr instanceof AC) {
 229  
                         rowSpecs = (AC) constr;
 230  
                 } else {
 231  
                         throw new IllegalArgumentException("Illegal constraint type: " + constr.getClass().toString());
 232  
                 }
 233  
                 rowConstraints = constr;
 234  
                 dirty = true;
 235  
         }
 236  
 
 237  
         /** Returns a shallow copy of the constraints map.
 238  
          * @return A  shallow copy of the constraints map. Never <code>null</code>.
 239  
          */
 240  
         public Map<Component, Object> getConstraintMap()
 241  
         {
 242  
                 return new IdentityHashMap<Component, Object>(scrConstrMap);
 243  
         }
 244  
 
 245  
         /** Sets the constraints map.
 246  
          * @param map The map. Will be copied.
 247  
          */
 248  
         public void setConstraintMap(Map<Component, Object> map)
 249  
         {
 250  
                 scrConstrMap.clear();
 251  
                 ccMap.clear();
 252  
                 for (Map.Entry<Component, Object> e : map.entrySet())
 253  
                         setComponentConstraintsImpl(e.getKey(), e.getValue(), true);
 254  
         }
 255  
 
 256  
         /** Returns the component constraints as a String representation. This string is the exact string as set with {@link #setComponentConstraints(java.awt.Component, Object)}
 257  
          * or set when adding the component to the parent component.
 258  
          * <p>
 259  
          * See the class JavaDocs for information on how this string is formatted.
 260  
          * @param comp The component to return the constraints for.
 261  
          * @return The component constraints as a String representation or <code>null</code> if the component is not registered
 262  
          * with this layout manager. The returned values is either a String or a {@link net.miginfocom.layout.CC}
 263  
          * depending on what constraint was sent in when the component was added. May be <code>null</code>.
 264  
          */
 265  
         public Object getComponentConstraints(Component comp)
 266  
         {
 267  
                 synchronized(comp.getParent().getTreeLock()) {
 268  
                         return scrConstrMap.get(comp);
 269  
                 }
 270  
         }
 271  
 
 272  
         /** Sets the component constraint for the component that already must be handled by this layout manager.
 273  
          * <p>
 274  
          * See the class JavaDocs for information on how this string is formatted.
 275  
          * @param constr The component constraints as a String or {@link net.miginfocom.layout.CC}. <code>null</code> is ok.
 276  
          * @param comp The component to set the constraints for.
 277  
          * @throws RuntimeException if the constraint was not valid.
 278  
          * @throws IllegalArgumentException If the component is not handling the component.
 279  
          */
 280  
         public void setComponentConstraints(Component comp, Object constr)
 281  
         {
 282  
                 setComponentConstraintsImpl(comp, constr, false);
 283  
         }
 284  
 
 285  
         /** Sets the component constraint for the component that already must be handled by this layout manager.
 286  
          * <p>
 287  
          * See the class JavaDocs for information on how this string is formatted.
 288  
          * @param constr The component constraints as a String or {@link net.miginfocom.layout.CC}. <code>null</code> is ok.
 289  
          * @param comp The component to set the constraints for.
 290  
          * @param noCheck Doe not check if the component is handled if true
 291  
          * @throws RuntimeException if the constraint was not valid.
 292  
          * @throws IllegalArgumentException If the component is not handling the component.
 293  
          */
 294  
         private void setComponentConstraintsImpl(Component comp, Object constr, boolean noCheck)
 295  
         {
 296  
                 Container parent = comp.getParent();
 297  
                 synchronized(parent != null ? parent.getTreeLock() : new Object()) { // 3.7.2. No sync if not added to a hierarchy. Defeats a NPE.
 298  
                         if (noCheck == false && scrConstrMap.containsKey(comp) == false)
 299  
                                 throw new IllegalArgumentException("Component must already be added to parent!");
 300  
 
 301  
                         ComponentWrapper cw = new SwingComponentWrapper(comp);
 302  
 
 303  
                         if (constr == null || constr instanceof String) {
 304  
                                 String cStr = ConstraintParser.prepare((String) constr);
 305  
 
 306  
                                 scrConstrMap.put(comp, constr);
 307  
                                 ccMap.put(cw, ConstraintParser.parseComponentConstraint(cStr));
 308  
 
 309  
                         } else if (constr instanceof CC) {
 310  
 
 311  
                                 scrConstrMap.put(comp, constr);
 312  
                                 ccMap.put(cw, (CC) constr);
 313  
 
 314  
                         } else {
 315  
                                 throw new IllegalArgumentException("Constraint must be String or ComponentConstraint: " + constr.getClass().toString());
 316  
                         }
 317  
 
 318  
                         dirty = true;
 319  
                 }
 320  
         }
 321  
 
 322  
         /** Returns if this layout manager is currently managing this component.
 323  
          * @param c The component to check. If <code>null</code> then <code>false</code> will be returned.
 324  
          * @return If this layout manager is currently managing this component.
 325  
          */
 326  
         public boolean isManagingComponent(Component c)
 327  
         {
 328  
                 return scrConstrMap.containsKey(c);
 329  
         }
 330  
 
 331  
         /** Adds the callback function that will be called at different stages of the layout cylce.
 332  
          * @param callback The callback. Not <code>null</code>.
 333  
          */
 334  
         public void addLayoutCallback(LayoutCallback callback)
 335  
         {
 336  
                 if (callback == null)
 337  
                         throw new NullPointerException();
 338  
 
 339  
                 if (callbackList == null)
 340  
                         callbackList = new ArrayList<LayoutCallback>(1);
 341  
 
 342  
                 callbackList.add(callback);
 343  
         }
 344  
 
 345  
         /** Removes the callback if it exists.
 346  
          * @param callback The callback. May be <code>null</code>.
 347  
          */
 348  
         public void removeLayoutCallback(LayoutCallback callback)
 349  
         {
 350  
                 if (callbackList != null)
 351  
                         callbackList.remove(callback);
 352  
         }
 353  
 
 354  
         /** Sets the debugging state for this layout manager instance. If debug is turned on a timer will repaint the last laid out parent
 355  
          * with debug information on top.
 356  
          * <p>
 357  
          * Red fill and dashed red outline is used to indicate occupied cells in the grid. Blue dashed outline indicate
 358  
          * component bounds set.
 359  
          * <p>
 360  
          * Note that debug can also be set on the layout constraints. There it will be persisted. The value set here will not. See the class
 361  
          * JavaDocs for information.
 362  
          * @param parentW The parent to set debug for.
 363  
          * @param b <code>true</code> means debug is turned on.
 364  
          */
 365  
         private void setDebug(final ComponentWrapper parentW, boolean b)
 366  
         {
 367  
                 if (b && (debugTimer == null || debugTimer.getDelay() != getDebugMillis())) {
 368  
                         if (debugTimer != null)
 369  
                                 debugTimer.stop();
 370  
 
 371  
                         ContainerWrapper pCW = parentW.getParent();
 372  
                         final Component parent = pCW != null ? (Component) pCW.getComponent() : null;
 373  
 
 374  
                         debugTimer = new Timer(getDebugMillis(), new MyDebugRepaintListener());
 375  
 
 376  
                         if (parent != null) {
 377  
                                 SwingUtilities.invokeLater(new Runnable() {
 378  
                                         public void run() {
 379  
                                                 Container p = parent.getParent();
 380  
                                                 if (p != null) {
 381  
                                                         if (p instanceof JComponent) {
 382  
                                                                 ((JComponent) p).revalidate();
 383  
                                                         } else {
 384  
                                                                 parent.invalidate();
 385  
                                                                 p.validate();
 386  
                                                         }
 387  
                                                 }
 388  
                                         }
 389  
                                 });
 390  
                         }
 391  
 
 392  
                         debugTimer.setInitialDelay(100);
 393  
                         debugTimer.start();
 394  
 
 395  
                 } else if (!b && debugTimer != null) {
 396  
                         debugTimer.stop();
 397  
                         debugTimer = null;
 398  
                 }
 399  
         }
 400  
 
 401  
         /** Returns the current debugging state.
 402  
          * @return The current debugging state.
 403  
          */
 404  
         private boolean getDebug()
 405  
         {
 406  
                 return debugTimer != null;
 407  
         }
 408  
 
 409  
         /** Returns the debug millis. Combines the value from {@link net.miginfocom.layout.LC#getDebugMillis()} and {@link net.miginfocom.layout.LayoutUtil#getGlobalDebugMillis()}
 410  
          * @return The combined value.
 411  
          */
 412  
         private int getDebugMillis()
 413  
         {
 414  
                 int globalDebugMillis = LayoutUtil.getGlobalDebugMillis();
 415  
                 return globalDebugMillis > 0 ? globalDebugMillis : lc.getDebugMillis();
 416  
         }
 417  
 
 418  
         /** Check if something has changed and if so recreate it to the cached objects.
 419  
          * @param parent The parent that is the target for this layout manager.
 420  
          */
 421  
         private void checkCache(Container parent)
 422  
         {
 423  
                 if (parent == null)
 424  
                         return;
 425  
 
 426  
                 if (dirty)
 427  
                         grid = null;
 428  
 
 429  
                 // Check if the grid is valid
 430  
                 int mc = PlatformDefaults.getModCount();
 431  
                 if (lastModCount != mc) {
 432  
                         grid = null;
 433  
                         lastModCount = mc;
 434  
                 }
 435  
 
 436  
                 if (parent.isValid() == false) {
 437  
                         if (lastWasInvalid == false) {
 438  
                                 lastWasInvalid = true;
 439  
 
 440  
                                 int hash = 0;
 441  
                                 boolean resetLastInvalidOnParent = false; // Added in 3.7.3 to resolve a timing regression introduced in 3.7.1
 442  
                                 for (ComponentWrapper wrapper : ccMap.keySet()) {
 443  
                                         Object component = wrapper.getComponent();
 444  
                                         if (component instanceof JTextArea || component instanceof JEditorPane)
 445  
                                                 resetLastInvalidOnParent = true;
 446  
                                         hash ^= wrapper.getLayoutHashCode();
 447  
                                         hash += 285134905;
 448  
                                 }
 449  
                                 if (resetLastInvalidOnParent)
 450  
                                         resetLastInvalidOnParent(parent);
 451  
 
 452  
                                 if (hash != lastHash) {
 453  
                                         grid = null;
 454  
                                         lastHash = hash;
 455  
                                 }
 456  
 
 457  
                                 Dimension ps = parent.getSize();
 458  
                                 if (lastInvalidSize == null || !lastInvalidSize.equals(ps)) {
 459  
                                         if (grid != null)
 460  
                                                 grid.invalidateContainerSize();
 461  
                                         lastInvalidSize = ps;
 462  
                                 }
 463  
                         }
 464  
                 } else {
 465  
                         lastWasInvalid = false;
 466  
                 }
 467  
 
 468  
                 ContainerWrapper par = checkParent(parent);
 469  
 
 470  
                 setDebug(par, getDebugMillis() > 0);
 471  
 
 472  
                 if (grid == null)
 473  
                         grid = new Grid(par, lc, rowSpecs, colSpecs, ccMap, callbackList);
 474  
 
 475  
                 dirty = false;
 476  
         }
 477  
 
 478  
         /**
 479  
          * @since 3.7.3
 480  
          */
 481  
         private void resetLastInvalidOnParent(Container parent)
 482  
         {
 483  
                 while (parent != null) {
 484  
                         LayoutManager layoutManager = parent.getLayout();
 485  
                         if (layoutManager instanceof MigLayout) {
 486  
                                 ((MigLayout) layoutManager).lastWasInvalid = false;
 487  
                         }
 488  
                         parent = parent.getParent();
 489  
                 }
 490  
         }
 491  
 
 492  
         private ContainerWrapper checkParent(Container parent)
 493  
         {
 494  
                 if (parent == null)
 495  
                         return null;
 496  
 
 497  
                 if (cacheParentW == null || cacheParentW.getComponent() != parent)
 498  
                         cacheParentW = new SwingContainerWrapper(parent);
 499  
 
 500  
                 return cacheParentW;
 501  
         }
 502  
 
 503  
         private long lastSize = 0;
 504  
 
 505  
         public void layoutContainer(final Container parent)
 506  
         {
 507  
                 synchronized(parent.getTreeLock()) {
 508  
                         checkCache(parent);
 509  
 
 510  
                         Insets i = parent.getInsets();
 511  
                         int[] b = new int[] {
 512  
                                         i.left,
 513  
                                         i.top,
 514  
                                         parent.getWidth() - i.left - i.right,
 515  
                                         parent.getHeight() - i.top - i.bottom
 516  
                         };
 517  
 
 518  
                         if (grid.layout(b, lc.getAlignX(), lc.getAlignY(), getDebug(), true)) {
 519  
                                 grid = null;
 520  
                                 checkCache(parent);
 521  
                                 grid.layout(b, lc.getAlignX(), lc.getAlignY(), getDebug(), false);
 522  
                         }
 523  
 
 524  
                         long newSize = grid.getHeight()[1] + (((long) grid.getWidth()[1]) << 32);
 525  
                         if (lastSize != newSize) {
 526  
                                 lastSize = newSize;
 527  
                                 final ContainerWrapper containerWrapper = checkParent(parent);
 528  
                                 Window win = ((Window) SwingUtilities.getAncestorOfClass(Window.class, (Component)containerWrapper.getComponent()));
 529  
                                 if (win != null) {
 530  
                                    if (win.isVisible()) {
 531  
                                            SwingUtilities.invokeLater(new Runnable() {
 532  
                                                    public void run() {
 533  
                                                            adjustWindowSize(containerWrapper);
 534  
                                                    }
 535  
                                            });
 536  
                                    } else {
 537  
                                            adjustWindowSize(containerWrapper);
 538  
                                    }
 539  
                                 }
 540  
                         }
 541  
                         lastInvalidSize = null;
 542  
                 }
 543  
         }
 544  
 
 545  
         /** Checks the parent window if its size is within parameters as set by the LC.
 546  
          * @param parent The parent who's window to possibly adjust the size for.
 547  
          */
 548  
         private void adjustWindowSize(ContainerWrapper parent)
 549  
         {
 550  
                 BoundSize wBounds = lc.getPackWidth();
 551  
                 BoundSize hBounds = lc.getPackHeight();
 552  
 
 553  
                 if (wBounds == null && hBounds == null)
 554  
                         return;
 555  
 
 556  
                 Window win = ((Window) SwingUtilities.getAncestorOfClass(Window.class, (Component) parent.getComponent()));
 557  
                 if (win == null)
 558  
                         return;
 559  
 
 560  
                 Dimension prefSize = win.getPreferredSize();
 561  
                 int targW = constrain(checkParent(win), win.getWidth(), prefSize.width, wBounds);
 562  
                 int targH = constrain(checkParent(win), win.getHeight(), prefSize.height, hBounds);
 563  
 
 564  
                 int x = Math.round(win.getX() - ((targW - win.getWidth()) * (1 - lc.getPackWidthAlign())));
 565  
                 int y = Math.round(win.getY() - ((targH - win.getHeight()) * (1 - lc.getPackHeightAlign())));
 566  
 
 567  
                 win.setBounds(x, y, targW, targH);
 568  
         }
 569  
 
 570  
         private int constrain(ContainerWrapper parent, int winSize, int prefSize, BoundSize constrain)
 571  
         {
 572  
                 if (constrain == null)
 573  
                         return winSize;
 574  
 
 575  
                 int retSize = winSize;
 576  
                 UnitValue wUV = constrain.getPreferred();
 577  
                 if (wUV != null)
 578  
                         retSize = wUV.getPixels(prefSize, parent, parent);
 579  
 
 580  
                 retSize = constrain.constrain(retSize, prefSize, parent);
 581  
 
 582  
                 return constrain.getGapPush() ? Math.max(winSize, retSize) : retSize;
 583  
         }
 584  
 
 585  
         public Dimension minimumLayoutSize(Container parent)
 586  
         {
 587  
                 synchronized(parent.getTreeLock()) {
 588  
                         return getSizeImpl(parent, LayoutUtil.MIN);
 589  
                 }
 590  
         }
 591  
 
 592  
         public Dimension preferredLayoutSize(Container parent)
 593  
         {
 594  
                 synchronized(parent.getTreeLock()) {
 595  
            if (lastParentSize == null || !parent.getSize().equals(lastParentSize)) {
 596  
                for (ComponentWrapper wrapper : ccMap.keySet()) {
 597  
                    Component c = (Component) wrapper.getComponent();
 598  
                    if (c instanceof JTextArea || c instanceof JEditorPane || (c instanceof JComponent && Boolean.TRUE.equals(((JComponent)c).getClientProperty("migLayout.dynamicAspectRatio")))) {
 599  
                        layoutContainer(parent);
 600  
                        break;
 601  
                    }
 602  
                }
 603  
            }
 604  
 
 605  
            lastParentSize = parent.getSize();
 606  
            return getSizeImpl(parent, LayoutUtil.PREF);
 607  
                 }
 608  
         }
 609  
 
 610  
         public Dimension maximumLayoutSize(Container parent)
 611  
         {
 612  
                 return new Dimension(Short.MAX_VALUE, Short.MAX_VALUE);
 613  
         }
 614  
 
 615  
         // Implementation method that does the job.
 616  
         private Dimension getSizeImpl(Container parent, int sizeType)
 617  
         {
 618  
                 checkCache(parent);
 619  
 
 620  
                 Insets i = parent.getInsets();
 621  
 
 622  
                 int w = LayoutUtil.getSizeSafe(grid != null ? grid.getWidth() : null, sizeType) + i.left + i.right;
 623  
                 int h = LayoutUtil.getSizeSafe(grid != null ? grid.getHeight() : null, sizeType) + i.top + i.bottom;
 624  
 
 625  
                 return new Dimension(w, h);
 626  
         }
 627  
 
 628  
         public float getLayoutAlignmentX(Container parent)
 629  
         {
 630  
                 return lc != null && lc.getAlignX() != null ? lc.getAlignX().getPixels(1, checkParent(parent), null) : 0;
 631  
         }
 632  
 
 633  
         public float getLayoutAlignmentY(Container parent)
 634  
         {
 635  
                 return lc != null && lc.getAlignY() != null ? lc.getAlignY().getPixels(1, checkParent(parent), null) : 0;
 636  
         }
 637  
 
 638  
         public void addLayoutComponent(String s, Component comp)
 639  
         {
 640  
                 addLayoutComponent(comp, s);
 641  
         }
 642  
 
 643  
         public void addLayoutComponent(Component comp, Object constraints)
 644  
         {
 645  
                 synchronized(comp.getParent().getTreeLock()) {
 646  
                         setComponentConstraintsImpl(comp, constraints, true);
 647  
                 }
 648  
         }
 649  
 
 650  
         public void removeLayoutComponent(Component comp)
 651  
         {
 652  
                 synchronized(comp.getParent().getTreeLock()) {
 653  
                         scrConstrMap.remove(comp);
 654  
                         ccMap.remove(new SwingComponentWrapper(comp));
 655  
                 }
 656  
         }
 657  
 
 658  
         public void invalidateLayout(Container target)
 659  
         {
 660  
 //                if (lc.isNoCache())  // Commented for 3.5 since there was too often that the "nocache" was needed and the user did not know.
 661  
                 dirty = true;
 662  
 
 663  
                 // the validity of components is maintained automatically.
 664  
         }
 665  
 
 666  
         // ************************************************
 667  
         // Persistence Delegate and Serializable combined.
 668  
         // ************************************************
 669  
 
 670  
         private Object readResolve() throws ObjectStreamException
 671  
         {
 672  
                 return LayoutUtil.getSerializedObject(this);
 673  
         }
 674  
 
 675  
         public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
 676  
         {
 677  
                 LayoutUtil.setSerializedObject(this, LayoutUtil.readAsXML(in));
 678  
         }
 679  
 
 680  
         public void writeExternal(ObjectOutput out) throws IOException
 681  
         {
 682  
                 if (getClass() == MigLayout.class)
 683  
                         LayoutUtil.writeAsXML(out, this);
 684  
         }
 685  
 
 686  
         private class MyDebugRepaintListener implements ActionListener
 687  
         {
 688  
                 public void actionPerformed(ActionEvent e)
 689  
                 {
 690  
                         if (grid != null) {
 691  
                                 Component comp = (Component) grid.getContainer().getComponent();
 692  
                                 if (comp.isShowing()) {
 693  
                                         grid.paintDebug();
 694  
                                         return;
 695  
                                 }
 696  
                         }
 697  
                         debugTimer.stop();
 698  
                         debugTimer = null;
 699  
                 }
 700  
         }
 701  
 }