Merge branch 'Jalview-JS/jim/JAL-3253-JAL-3418' into Jalview-JS/JAL-3253-applet
[jalview.git] / unused / net / miginfocom / swing / MigLayout.java
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 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 or {@link net.miginfocom.layout.LC} 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.AC} depending what was sent in
179          * to the constructor or set with {@link #setColumnConstraints(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 or {@link net.miginfocom.layout.AC} 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 either as a <code>String</code> or {@link net.miginfocom.layout.AC}.
207          * @return The row constraints either as a <code>String</code> or {@link net.miginfocom.layout.AC} depending what was sent in
208          * to the constructor or set with {@link #setRowConstraints(Object)}. Never <code>null</code>.
209          */
210         public Object getRowConstraints()
211         {
212                 return rowConstraints;
213         }
214
215         /** Sets the row layout constraints for the layout manager instance as a String.
216          * <p>
217          * See the class JavaDocs for information on how this string is formatted.
218          * @param constr The row layout constraints as a String or {@link net.miginfocom.layout.AC} representation. <code>null</code> is converted to <code>""</code> for storage.
219          * @throws RuntimeException if the constraint was not valid.
220          */
221         public void setRowConstraints(Object constr)
222         {
223                 if (constr == null || constr instanceof String) {
224                         constr = ConstraintParser.prepare((String) constr);
225                         rowSpecs = ConstraintParser.parseRowConstraints((String) constr);
226                 } else if (constr instanceof AC) {
227                         rowSpecs = (AC) constr;
228                 } else {
229                         throw new IllegalArgumentException("Illegal constraint type: " + constr.getClass().toString());
230                 }
231                 rowConstraints = constr;
232                 dirty = true;
233         }
234
235         /** Returns a shallow copy of the constraints map.
236          * @return A  shallow copy of the constraints map. Never <code>null</code>.
237          */
238         public Map<Component, Object> getConstraintMap()
239         {
240                 return new IdentityHashMap<Component, Object>(scrConstrMap);
241         }
242
243         /** Sets the constraints map.
244          * @param map The map. Will be copied.
245          */
246         public void setConstraintMap(Map<Component, Object> map)
247         {
248                 scrConstrMap.clear();
249                 ccMap.clear();
250                 for (Map.Entry<Component, Object> e : map.entrySet())
251                         setComponentConstraintsImpl(e.getKey(), e.getValue(), true);
252         }
253
254         /** Returns the component constraints as a String representation. This string is the exact string as set with {@link #setComponentConstraints(java.awt.Component, Object)}
255          * or set when adding the component to the parent component.
256          * <p>
257          * See the class JavaDocs for information on how this string is formatted.
258          * @param comp The component to return the constraints for.
259          * @return The component constraints as a String representation or <code>null</code> if the component is not registered
260          * with this layout manager. The returned values is either a String or a {@link net.miginfocom.layout.CC}
261          * depending on what constraint was sent in when the component was added. May be <code>null</code>.
262          */
263         public Object getComponentConstraints(Component comp)
264         {
265                 synchronized(comp.getParent().getTreeLock()) {
266                         return scrConstrMap.get(comp);
267                 }
268         }
269
270         /** Sets the component constraint for the component that already must be handled by this layout manager.
271          * <p>
272          * See the class JavaDocs for information on how this string is formatted.
273          * @param constr The component constraints as a String or {@link net.miginfocom.layout.CC}. <code>null</code> is ok.
274          * @param comp The component to set the constraints for.
275          * @throws RuntimeException if the constraint was not valid.
276          * @throws IllegalArgumentException If the component is not handling the component.
277          */
278         public void setComponentConstraints(Component comp, Object constr)
279         {
280                 setComponentConstraintsImpl(comp, constr, false);
281         }
282
283         /** Sets the component constraint for the component that already must be handled by this layout manager.
284          * <p>
285          * See the class JavaDocs for information on how this string is formatted.
286          * @param constr The component constraints as a String or {@link net.miginfocom.layout.CC}. <code>null</code> is ok.
287          * @param comp The component to set the constraints for.
288          * @param noCheck Does not check if the component is handled if true
289          * @throws RuntimeException if the constraint was not valid.
290          * @throws IllegalArgumentException If the component is not handling the component.
291          */
292         private void setComponentConstraintsImpl(Component comp, Object constr, boolean noCheck)
293         {
294                 Container parent = comp.getParent();
295                 synchronized(parent != null ? parent.getTreeLock() : new Object()) { // 3.7.2. No sync if not added to a hierarchy. Defeats a NPE.
296                         if (noCheck == false && scrConstrMap.containsKey(comp) == false)
297                                 throw new IllegalArgumentException("Component must already be added to parent!");
298
299                         ComponentWrapper cw = new SwingComponentWrapper(comp);
300
301                         if (constr == null || constr instanceof String) {
302                                 String cStr = ConstraintParser.prepare((String) constr);
303
304                                 scrConstrMap.put(comp, constr);
305                                 ccMap.put(cw, ConstraintParser.parseComponentConstraint(cStr));
306
307                         } else if (constr instanceof CC) {
308
309                                 scrConstrMap.put(comp, constr);
310                                 ccMap.put(cw, (CC) constr);
311
312                         } else {
313                                 throw new IllegalArgumentException("Constraint must be String or ComponentConstraint: " + constr.getClass().toString());
314                         }
315
316                         dirty = true;
317                 }
318         }
319
320         /** Returns if this layout manager is currently managing this component.
321          * @param c The component to check. If <code>null</code> then <code>false</code> will be returned.
322          * @return If this layout manager is currently managing this component.
323          */
324         public boolean isManagingComponent(Component c)
325         {
326                 return scrConstrMap.containsKey(c);
327         }
328
329         /** Adds the callback function that will be called at different stages of the layout cycle.
330          * @param callback The callback. Not <code>null</code>.
331          */
332         public void addLayoutCallback(LayoutCallback callback)
333         {
334                 if (callback == null)
335                         throw new NullPointerException();
336
337                 if (callbackList == null)
338                         callbackList = new ArrayList<LayoutCallback>(1);
339
340                 callbackList.add(callback);
341
342                 grid = null;
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                                         @Override
379                                         public void run() {
380                                                 Container p = parent.getParent();
381                                                 if (p != null) {
382                                                         if (p instanceof JComponent) {
383                                                                 ((JComponent) p).revalidate();
384                                                         } else {
385                                                                 parent.invalidate();
386                                                                 p.validate();
387                                                         }
388                                                 }
389                                         }
390                                 });
391                         }
392
393                         debugTimer.setInitialDelay(100);
394                         debugTimer.start();
395
396                 } else if (!b && debugTimer != null) {
397                         debugTimer.stop();
398                         debugTimer = null;
399                 }
400         }
401
402         /** Returns the current debugging state.
403          * @return The current debugging state.
404          */
405         private boolean getDebug()
406         {
407                 return debugTimer != null;
408         }
409
410         /** Returns the debug millis. Combines the value from {@link net.miginfocom.layout.LC#getDebugMillis()} and {@link net.miginfocom.layout.LayoutUtil#getGlobalDebugMillis()}
411          * @return The combined value.
412          */
413         private int getDebugMillis()
414         {
415                 int globalDebugMillis = LayoutUtil.getGlobalDebugMillis();
416                 return globalDebugMillis > 0 ? globalDebugMillis : lc.getDebugMillis();
417         }
418
419         /** Check if something has changed and if so recreate it to the cached objects.
420          * @param parent The parent that is the target for this layout manager.
421          */
422         private void checkCache(Container parent)
423         {
424                 if (parent == null)
425                         return;
426
427                 if (dirty)
428                         grid = null;
429
430                 cleanConstraintMaps(parent);
431
432                 // Check if the grid is valid
433                 int mc = PlatformDefaults.getModCount();
434                 if (lastModCount != mc) {
435                         grid = null;
436                         lastModCount = mc;
437                 }
438
439                 if (!parent.isValid()) {
440                         if (!lastWasInvalid) {
441                                 lastWasInvalid = true;
442
443                                 int hash = 0;
444                                 boolean resetLastInvalidOnParent = false; // Added in 3.7.3 to resolve a timing regression introduced in 3.7.1
445                                 for (ComponentWrapper wrapper : ccMap.keySet()) {
446                                         Object component = wrapper.getComponent();
447                                         if (component instanceof JTextArea || component instanceof JEditorPane)
448                                                 resetLastInvalidOnParent = true;
449
450                                         hash ^= wrapper.getLayoutHashCode();
451                                         hash += 285134905;
452                                 }
453                                 if (resetLastInvalidOnParent)
454                                         resetLastInvalidOnParent(parent);
455
456                                 if (hash != lastHash) {
457                                         grid = null;
458                                         lastHash = hash;
459                                 }
460
461                                 Dimension ps = parent.getSize();
462                                 if (lastInvalidSize == null || !lastInvalidSize.equals(ps)) {
463                                         grid = null;
464                                         lastInvalidSize = ps;
465                                 }
466                         }
467                 } else {
468                         lastWasInvalid = false;
469                 }
470
471                 ContainerWrapper par = checkParent(parent);
472
473                 setDebug(par, getDebugMillis() > 0);
474
475                 if (grid == null)
476                         grid = new Grid(par, lc, rowSpecs, colSpecs, ccMap, callbackList);
477
478                 dirty = false;
479         }
480
481         /** Checks so all components in ccMap actually exist in the parent's collection. Removes
482          * any references that don't.
483          * @param parent The parent to compare ccMap against. Never null.
484          */
485         private void cleanConstraintMaps(Container parent)
486         {
487                 HashSet<Component> parentCompSet = new HashSet<Component>(Arrays.asList(parent.getComponents()));
488
489                 Iterator<Map.Entry<ComponentWrapper, CC>> it = ccMap.entrySet().iterator();
490                 while(it.hasNext()) {
491                         Component c = (Component) it.next().getKey().getComponent();
492                         if (parentCompSet.contains(c) == false) {
493                                 it.remove();
494                                 scrConstrMap.remove(c);
495                         }
496                 }
497         }
498
499         /**
500          * @since 3.7.3
501          */
502         private void resetLastInvalidOnParent(Container parent)
503         {
504                 while (parent != null) {
505                         LayoutManager layoutManager = parent.getLayout();
506                         if (layoutManager instanceof MigLayout) {
507                                 ((MigLayout) layoutManager).lastWasInvalid = false;
508                         }
509                         parent = parent.getParent();
510                 }
511         }
512
513         private ContainerWrapper checkParent(Container parent)
514         {
515                 if (parent == null)
516                         return null;
517
518                 if (cacheParentW == null || cacheParentW.getComponent() != parent)
519                         cacheParentW = new SwingContainerWrapper(parent);
520
521                 return cacheParentW;
522         }
523
524         private long lastSize = 0;
525
526         @Override
527         public void layoutContainer(final Container parent)
528         {
529                 synchronized(parent.getTreeLock()) {
530                         checkCache(parent);
531
532                         Insets i = parent.getInsets();
533                         int[] b = new int[] {
534                                         i.left,
535                                         i.top,
536                                         parent.getWidth() - i.left - i.right,
537                                         parent.getHeight() - i.top - i.bottom
538                         };
539
540                         if (grid.layout(b, lc.getAlignX(), lc.getAlignY(), getDebug())) {
541                                 grid = null;
542                                 checkCache(parent);
543                                 grid.layout(b, lc.getAlignX(), lc.getAlignY(), getDebug());
544                         }
545
546                         long newSize = grid.getHeight()[1] + (((long) grid.getWidth()[1]) << 32);
547                         if (lastSize != newSize) {
548                                 lastSize = newSize;
549                                 final ContainerWrapper containerWrapper = checkParent(parent);
550                                 Window win = ((Window) SwingUtilities.getAncestorOfClass(Window.class, (Component)containerWrapper.getComponent()));
551                                 if (win != null) {
552                                    if (win.isVisible()) {
553                                            SwingUtilities.invokeLater(new Runnable() {
554                                                    @Override
555                                                    public void run() {
556                                                            adjustWindowSize(containerWrapper);
557                                                    }
558                                            });
559                                    } else {
560                                            adjustWindowSize(containerWrapper);
561                                    }
562                                 }
563                         }
564                         lastInvalidSize = null;
565                 }
566         }
567
568         /** Checks the parent window/popup if its size is within parameters as set by the LC.
569          * @param parent The parent who's window to possibly adjust the size for.
570          */
571         private void adjustWindowSize(ContainerWrapper parent)
572         {
573                 BoundSize wBounds = lc.getPackWidth();
574                 BoundSize hBounds = lc.getPackHeight();
575
576                 if (wBounds == BoundSize.NULL_SIZE && hBounds == BoundSize.NULL_SIZE)
577                         return;
578
579                 Container packable = getPackable((Component) parent.getComponent());
580
581                 if (packable != null) {
582
583                         Component pc = (Component) parent.getComponent();
584
585                         Container c = pc instanceof Container ? (Container) pc : pc.getParent();
586                         for (; c != null; c = c.getParent()) {
587                                 LayoutManager layout = c.getLayout();
588                                 if (layout instanceof BoxLayout || layout instanceof OverlayLayout)
589                                         ((LayoutManager2) layout).invalidateLayout(c);
590                         }
591
592                         Dimension prefSize = packable.getPreferredSize();
593                         int targW = constrain(checkParent(packable), packable.getWidth(), prefSize.width, wBounds);
594                         int targH = constrain(checkParent(packable), packable.getHeight(), prefSize.height, hBounds);
595
596                         Point p = packable.isShowing() ? packable.getLocationOnScreen() : packable.getLocation();
597
598                         int x = Math.round(p.x - ((targW - packable.getWidth()) * (1 - lc.getPackWidthAlign())));
599                         int y = Math.round(p.y - ((targH - packable.getHeight()) * (1 - lc.getPackHeightAlign())));
600
601                         if (packable instanceof JPopupMenu) {
602                                 JPopupMenu popupMenu = (JPopupMenu) packable;
603                                 popupMenu.setVisible(false);
604                                 popupMenu.setPopupSize(targW, targH);
605                                 Component invoker = popupMenu.getInvoker();
606                                 Point popPoint = new Point(x, y);
607                                 SwingUtilities.convertPointFromScreen(popPoint, invoker);
608                                 ((JPopupMenu) packable).show(invoker, popPoint.x, popPoint.y);
609
610                                 packable.setPreferredSize(null); // Reset preferred size so we don't read it again.
611
612                         } else {
613                                 packable.setBounds(x, y, targW, targH);
614                         }
615                 }
616         }
617
618         /** Returns a high level window or popup to pack, if any.
619          * @return May be null.
620          */
621         private Container getPackable(Component comp)
622         {
623                 JPopupMenu popup = findType(JPopupMenu.class, comp);
624                 if (popup != null) { // Lightweight/HeavyWeight popup must be handled separately
625                         Container popupComp = popup;
626                         while (popupComp != null) {
627                                 if (popupComp.getClass().getName().contains("HeavyWeightWindow"))
628                                         return popupComp; // Return the heavy weight window for normal processing
629                                 popupComp = popupComp.getParent();
630                         }
631                         return popup; // Return the JPopup.
632                 }
633
634                 return findType(Window.class, comp);
635         }
636
637         public static <E> E findType(Class<E> clazz, Component comp)
638         {
639                 while (comp != null && !clazz.isInstance(comp))
640                         comp = comp.getParent();
641
642                 return (E) comp;
643         }
644
645
646         private int constrain(ContainerWrapper parent, int winSize, int prefSize, BoundSize constrain)
647         {
648                 if (constrain == null)
649                         return winSize;
650
651                 int retSize = winSize;
652                 UnitValue wUV = constrain.getPreferred();
653                 if (wUV != null)
654                         retSize = wUV.getPixels(prefSize, parent, parent);
655
656                 retSize = constrain.constrain(retSize, prefSize, parent);
657
658                 return constrain.getGapPush() ? Math.max(winSize, retSize) : retSize;
659         }
660
661         @Override
662         public Dimension minimumLayoutSize(Container parent)
663         {
664                 synchronized(parent.getTreeLock()) {
665                         return getSizeImpl(parent, LayoutUtil.MIN);
666                 }
667         }
668
669         @Override
670         public Dimension preferredLayoutSize(Container parent)
671         {
672                 synchronized(parent.getTreeLock()) {
673            if (lastParentSize == null || !parent.getSize().equals(lastParentSize)) {
674                for (ComponentWrapper wrapper : ccMap.keySet()) {
675                        if (wrapper.getContentBias() != -1) {
676                        layoutContainer(parent);
677                        break;
678                    }
679                }
680            }
681
682            lastParentSize = parent.getSize();
683            return getSizeImpl(parent, LayoutUtil.PREF);
684                 }
685         }
686
687         @Override
688         public Dimension maximumLayoutSize(Container parent)
689         {
690                 return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
691         }
692
693         // Implementation method that does the job.
694         private Dimension getSizeImpl(Container parent, int sizeType)
695         {
696                 checkCache(parent);
697
698                 Insets i = parent.getInsets();
699
700                 int w = LayoutUtil.getSizeSafe(grid != null ? grid.getWidth() : null, sizeType) + i.left + i.right;
701                 int h = LayoutUtil.getSizeSafe(grid != null ? grid.getHeight() : null, sizeType) + i.top + i.bottom;
702
703                 return new Dimension(w, h);
704         }
705
706         @Override
707         public float getLayoutAlignmentX(Container parent)
708         {
709                 return lc != null && lc.getAlignX() != null ? lc.getAlignX().getPixels(1, checkParent(parent), null) : 0;
710         }
711
712         @Override
713         public float getLayoutAlignmentY(Container parent)
714         {
715                 return lc != null && lc.getAlignY() != null ? lc.getAlignY().getPixels(1, checkParent(parent), null) : 0;
716         }
717
718         @Override
719         public void addLayoutComponent(String s, Component comp)
720         {
721                 addLayoutComponent(comp, s);
722         }
723
724         @Override
725         public void addLayoutComponent(Component comp, Object constraints)
726         {
727                 synchronized(comp.getParent().getTreeLock()) {
728                         setComponentConstraintsImpl(comp, constraints, true);
729                 }
730         }
731
732         @Override
733         public void removeLayoutComponent(Component comp)
734         {
735                 synchronized(comp.getParent().getTreeLock()) {
736                         scrConstrMap.remove(comp);
737                         ccMap.remove(new SwingComponentWrapper(comp));
738                         grid = null; // To clear references
739                 }
740         }
741
742         @Override
743         public void invalidateLayout(Container target)
744         {
745                 dirty = true;
746         }
747
748         // ************************************************
749         // Persistence Delegate and Serializable combined.
750         // ************************************************
751
752         private Object readResolve() throws ObjectStreamException
753         {
754                 return LayoutUtil.getSerializedObject(this);
755         }
756
757         @Override
758         public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
759         {
760                 LayoutUtil.setSerializedObject(this, LayoutUtil.readAsXML(in));
761         }
762
763         @Override
764         public void writeExternal(ObjectOutput out) throws IOException
765         {
766                 if (getClass() == MigLayout.class)
767                         LayoutUtil.writeAsXML(out, this);
768         }
769
770         private class MyDebugRepaintListener implements ActionListener
771         {
772                 @Override
773                 public void actionPerformed(ActionEvent e)
774                 {
775                         if (grid != null) {
776                                 Component comp = (Component) grid.getContainer().getComponent();
777                                 if (comp.isShowing()) {
778                                         grid.paintDebug();
779                                         return;
780                                 }
781                         }
782                         debugTimer.stop();
783                         debugTimer = null;
784                 }
785         }
786 }