Coverage Report - net.miginfocom.swt.MigLayout
 
Classes in this File Line Coverage Branch Coverage Complexity
MigLayout
N/A
N/A
2.639
MigLayout$1
N/A
N/A
2.639
MigLayout$MyDebugRepaintTask
N/A
N/A
2.639
MigLayout$MyDebugRepaintTask$1
N/A
N/A
2.639
 
 1  
 package net.miginfocom.swt;
 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  
 import net.miginfocom.layout.*;
 36  
 import org.eclipse.swt.graphics.Point;
 37  
 import org.eclipse.swt.graphics.Rectangle;
 38  
 import org.eclipse.swt.widgets.Composite;
 39  
 import org.eclipse.swt.widgets.Control;
 40  
 import org.eclipse.swt.widgets.Display;
 41  
 import org.eclipse.swt.widgets.Layout;
 42  
 
 43  
 import java.io.*;
 44  
 import java.lang.ref.WeakReference;
 45  
 import java.util.*;
 46  
 
 47  
 /** A very flexible layout manager.
 48  
  * <p>
 49  
  * Read the documentation that came with this layout manager for information on usage.
 50  
  */
 51  
 public final class MigLayout extends Layout implements Externalizable
 52  
 {
 53  
         static {
 54  
                 // Mac Swing defaults to 96 DPI from Java6 though SWT is still on 72.
 55  
                 if (PlatformDefaults.getPlatform() == PlatformDefaults.MAC_OSX)
 56  
                         PlatformDefaults.setDefaultDPI(72);
 57  
         }
 58  
 
 59  
         // ******** Instance part ********
 60  
 
 61  
         /** The component to string constraints mappings.
 62  
          */
 63  
         private final Map<Control, Object> scrConstrMap = new IdentityHashMap<Control, Object>(8);
 64  
 
 65  
         /** Hold the serializable text representation of the constraints.
 66  
          */
 67  
         private Object layoutConstraints = "", colConstraints = "", rowConstraints = "";    // Should never be null!
 68  
 
 69  
         // ******** Transient part ********
 70  
 
 71  
         private transient ContainerWrapper cacheParentW = null;
 72  
 
 73  
         private transient final Map<ComponentWrapper, CC> ccMap = new HashMap<ComponentWrapper, CC>(8);
 74  
 
 75  
         private transient LC lc = null;
 76  
         private transient AC colSpecs = null, rowSpecs = null;
 77  
         private transient Grid grid = null;
 78  
 
 79  
         private transient java.util.Timer debugTimer = null;
 80  
         private transient long curDelay = -1;
 81  
         private transient int lastModCount = PlatformDefaults.getModCount();
 82  
         private transient int lastHash = -1;
 83  
 
 84  
         private transient ArrayList<LayoutCallback> callbackList = null;
 85  
 
 86  
         /** Constructor with no constraints.
 87  
          */
 88  
         public MigLayout()
 89  
         {
 90  
                 this("", "", "");
 91  
         }
 92  
 
 93  
         /** Constructor.
 94  
          * @param layoutConstraints The constraints that concern the whole layout. <code>null</code> will be treated as "".
 95  
          */
 96  
         public MigLayout(String layoutConstraints)
 97  
         {
 98  
                 this(layoutConstraints, "", "");
 99  
         }
 100  
 
 101  
         /** Constructor.
 102  
          * @param layoutConstraints The constraints that concern the whole layout. <code>null</code> will be treated as "".
 103  
          * @param colConstraints The constraints for the columns in the grid. <code>null</code> will be treated as "".
 104  
          */
 105  
         public MigLayout(String layoutConstraints, String colConstraints)
 106  
         {
 107  
                 this(layoutConstraints, colConstraints, "");
 108  
         }
 109  
 
 110  
         /** Constructor.
 111  
          * @param layoutConstraints The constraints that concern the whole layout. <code>null</code> will be treated as "".
 112  
          * @param colConstraints The constraints for the columns in the grid. <code>null</code> will be treated as "".
 113  
          * @param rowConstraints The constraints for the rows in the grid. <code>null</code> will be treated as "".
 114  
          */
 115  
         public MigLayout(String layoutConstraints, String colConstraints, String rowConstraints)
 116  
         {
 117  
                 setLayoutConstraints(layoutConstraints);
 118  
                 setColumnConstraints(colConstraints);
 119  
                 setRowConstraints(rowConstraints);
 120  
         }
 121  
 
 122  
         /** Constructor.
 123  
          * @param layoutConstraints The constraints that concern the whole layout. <code>null</code> will be treated as an empty cosntraint.
 124  
          */
 125  
         public MigLayout(LC layoutConstraints)
 126  
         {
 127  
                 this(layoutConstraints, null, null);
 128  
         }
 129  
 
 130  
         /** Constructor.
 131  
          * @param layoutConstraints The constraints that concern the whole layout. <code>null</code> will be treated as an empty cosntraint.
 132  
          * @param colConstraints The constraints for the columns in the grid. <code>null</code> will be treated as an empty constraint.
 133  
          */
 134  
         public MigLayout(LC layoutConstraints, AC colConstraints)
 135  
         {
 136  
                 this(layoutConstraints, colConstraints, null);
 137  
         }
 138  
 
 139  
         /** Constructor.
 140  
          * @param layoutConstraints The constraints that concern the whole layout. <code>null</code> will be treated as an empty cosntraint.
 141  
          * @param colConstraints The constraints for the columns in the grid. <code>null</code> will be treated as an empty constraint.
 142  
          * @param rowConstraints The constraints for the rows in the grid. <code>null</code> will be treated as an empty constraint.
 143  
          */
 144  
         public MigLayout(LC layoutConstraints, AC colConstraints, AC rowConstraints)
 145  
         {
 146  
                 setLayoutConstraints(layoutConstraints);
 147  
                 setColumnConstraints(colConstraints);
 148  
                 setRowConstraints(rowConstraints);
 149  
         }
 150  
 
 151  
         /** Returns layout constraints either as a <code>String</code> or {@link net.miginfocom.layout.LC} depending what was sent in
 152  
          * to the constructor or set with {@link #setLayoutConstraints(Object)}.
 153  
          * @return The layout constraints eighter as a <code>String</code> or {@link net.miginfocom.layout.LC} depending what was sent in
 154  
          * to the constructor or set with {@link #setLayoutConstraints(Object)}. Never <code>null</code>.
 155  
          */
 156  
         public Object getLayoutConstraints()
 157  
         {
 158  
                 return layoutConstraints;
 159  
         }
 160  
 
 161  
         /** Sets the layout constraints for the layout manager instance as a String.
 162  
          * <p>
 163  
          * See the class JavaDocs for information on how this string is formatted.
 164  
          * @param s The layout constraints as a String representation. <code>null</code> is converted to <code>""</code> for storage.
 165  
          * @throws RuntimeException if the constraint was not valid.
 166  
          */
 167  
         public void setLayoutConstraints(Object s)
 168  
         {
 169  
                 if (s == null || s instanceof String) {
 170  
                         s = ConstraintParser.prepare((String) s);
 171  
                         lc = ConstraintParser.parseLayoutConstraint((String) s);
 172  
                 } else if (s instanceof LC) {
 173  
                         lc = (LC) s;
 174  
                 } else {
 175  
                         throw new IllegalArgumentException("Illegal constraint type: " + s.getClass().toString());
 176  
                 }
 177  
                 layoutConstraints = s;
 178  
                 grid = null;
 179  
         }
 180  
 
 181  
         /** Returns the column layout constraints either as a <code>String</code> or {@link net.miginfocom.layout.AC}.
 182  
          * @return The column constraints eighter as a <code>String</code> or {@link net.miginfocom.layout.LC} depending what was sent in
 183  
          * to the constructor or set with {@link #setLayoutConstraints(Object)}. Never <code>null</code>.
 184  
          */
 185  
         public Object getColumnConstraints()
 186  
         {
 187  
                 return colConstraints;
 188  
         }
 189  
 
 190  
         /** Sets the column layout constraints for the layout manager instance as a String.
 191  
          * <p>
 192  
          * See the class JavaDocs for information on how this string is formatted.
 193  
          * @param constr The column layout constraints as a String representation. <code>null</code> is converted to <code>""</code> for storage.
 194  
          * @throws RuntimeException if the constraint was not valid.
 195  
          */
 196  
         public void setColumnConstraints(Object constr)
 197  
         {
 198  
                 if (constr == null || constr instanceof String) {
 199  
                         constr = ConstraintParser.prepare((String) constr);
 200  
                         colSpecs = ConstraintParser.parseColumnConstraints((String) constr);
 201  
                 } else if (constr instanceof AC) {
 202  
                         colSpecs = (AC) constr;
 203  
                 } else {
 204  
                         throw new IllegalArgumentException("Illegal constraint type: " + constr.getClass().toString());
 205  
                 }
 206  
                 colConstraints = constr;
 207  
                 grid = null;
 208  
         }
 209  
 
 210  
         /** Returns the row layout constraints as a String representation. This string is the exact string as set with {@link #setRowConstraints(Object)}
 211  
          * or sent into the constructor.
 212  
          * <p>
 213  
          * See the class JavaDocs for information on how this string is formatted.
 214  
          * @return The row layout constraints as a String representation. Never <code>null</code>.
 215  
          */
 216  
         public Object getRowConstraints()
 217  
         {
 218  
                 return rowConstraints;
 219  
         }
 220  
 
 221  
         /** Sets the row layout constraints for the layout manager instance as a String.
 222  
          * <p>
 223  
          * See the class JavaDocs for information on how this string is formatted.
 224  
          * @param constr The row layout constraints as a String representation. <code>null</code> is converted to <code>""</code> for storage.
 225  
          * @throws RuntimeException if the constaint was not valid.
 226  
          */
 227  
         public void setRowConstraints(Object constr)
 228  
         {
 229  
                 if (constr == null || constr instanceof String) {
 230  
                         constr = ConstraintParser.prepare((String) constr);
 231  
                         rowSpecs = ConstraintParser.parseRowConstraints((String) constr);
 232  
                 } else if (constr instanceof AC) {
 233  
                         rowSpecs = (AC) constr;
 234  
                 } else {
 235  
                         throw new IllegalArgumentException("Illegal constraint type: " + constr.getClass().toString());
 236  
                 }
 237  
                 rowConstraints = constr;
 238  
                 grid = null;
 239  
         }
 240  
 
 241  
         /** Returns a shallow copy of the constraints map.
 242  
          * @return A  shallow copy of the constraints map. Never <code>null</code>.
 243  
          */
 244  
         public Map<Control, Object> getConstraintMap()
 245  
         {
 246  
                 return new IdentityHashMap<Control, Object>(scrConstrMap);
 247  
         }
 248  
 
 249  
         /** Sets the constraints map.
 250  
          * @param map The map. Will be copied.
 251  
          */
 252  
         public void setConstraintMap(Map<Control, Object> map)
 253  
         {
 254  
                 scrConstrMap.clear();
 255  
                 ccMap.clear();
 256  
                 for (Map.Entry<Control, Object> e : map.entrySet())
 257  
                         setComponentConstraintsImpl(e.getKey(), e.getValue(), true);
 258  
         }
 259  
 
 260  
         /** Sets the component constraint for the component that already must be handleded by this layout manager.
 261  
          * <p>
 262  
          * See the class JavaDocs for information on how this string is formatted.
 263  
          * @param constr The component constraints as a String or {@link net.miginfocom.layout.CC}. <code>null</code> is ok.
 264  
          * @param comp The component to set the constraints for.
 265  
          * @param noCheck Doesn't check if control already is managed.
 266  
          * @throws RuntimeException if the constaint was not valid.
 267  
          * @throws IllegalArgumentException If the component is not handling the component.
 268  
          */
 269  
         private void setComponentConstraintsImpl(Control comp, Object constr, boolean noCheck)
 270  
         {
 271  
                 if (noCheck == false && scrConstrMap.containsKey(comp) == false)
 272  
                         throw new IllegalArgumentException("Component must already be added to parent!");
 273  
 
 274  
                 ComponentWrapper cw = new SwtComponentWrapper(comp);
 275  
 
 276  
                 if (constr == null || constr instanceof String) {
 277  
                         String cStr = ConstraintParser.prepare((String) constr);
 278  
 
 279  
                         scrConstrMap.put(comp, constr);
 280  
                         ccMap.put(cw, ConstraintParser.parseComponentConstraint(cStr));
 281  
 
 282  
                 } else if (constr instanceof CC) {
 283  
 
 284  
                         scrConstrMap.put(comp, constr);
 285  
                         ccMap.put(cw, (CC) constr);
 286  
 
 287  
                 } else {
 288  
                         throw new IllegalArgumentException("Constraint must be String or ComponentConstraint: " + constr.getClass().toString());
 289  
                 }
 290  
                 grid = null;
 291  
         }
 292  
 
 293  
         /** Returns if this layout manager is currently managing this component.
 294  
          * @param c The component to check. If <code>null</code> then <code>false</code> will be returned.
 295  
          * @return If this layout manager is currently managing this component.
 296  
          */
 297  
         public boolean isManagingComponent(Control c)
 298  
         {
 299  
                 return scrConstrMap.containsKey(c);
 300  
         }
 301  
 
 302  
         /** Adds the callback function that will be called at different stages of the layout cycle.
 303  
          * @param callback The callback. Not <code>null</code>.
 304  
          */
 305  
         public void addLayoutCallback(LayoutCallback callback)
 306  
         {
 307  
                 if (callback == null)
 308  
                         throw new NullPointerException();
 309  
 
 310  
                 if (callbackList == null)
 311  
                         callbackList = new ArrayList<LayoutCallback>(1);
 312  
 
 313  
                 callbackList.add(callback);
 314  
         }
 315  
 
 316  
         /** Removes the callback if it exists.
 317  
          * @param callback The callback. May be <code>null</code>.
 318  
          */
 319  
         public void removeLayoutCallback(LayoutCallback callback)
 320  
         {
 321  
                 if (callbackList != null)
 322  
                         callbackList.remove(callback);
 323  
         }
 324  
 
 325  
         /** Sets the debugging state for this layout manager instance. If debug is turned on a timer will repaint the last laid out parent
 326  
          * with debug information on top.
 327  
          * <p>
 328  
          * Red fill and dashed dark red outline is used to indicate occupied cells in the grid. Blue dashed outline indicate
 329  
          * component bounds set.
 330  
          * <p>
 331  
          * 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
 332  
          * JavaDocs for information.
 333  
          * @param parentW The parent. Never <code>null</code>.
 334  
          * @param b <code>true</code> means debug is turned on.
 335  
          */
 336  
         private synchronized void setDebug(final ComponentWrapper parentW, boolean b)
 337  
         {
 338  
                 if (b && (debugTimer == null || curDelay != getDebugMillis())) {
 339  
                         if (debugTimer != null)
 340  
                                 debugTimer.cancel();
 341  
 
 342  
                         debugTimer = new Timer(true);
 343  
                         curDelay = getDebugMillis();
 344  
                         debugTimer.schedule(new MyDebugRepaintTask(this), curDelay, curDelay);
 345  
 
 346  
                         ContainerWrapper pCW = parentW.getParent();
 347  
                         Composite parent = pCW != null ? (Composite) pCW.getComponent() : null;
 348  
                         if (parent != null)
 349  
                                 parent.layout();
 350  
 
 351  
                 } else if (!b && debugTimer != null) {
 352  
                         debugTimer.cancel();
 353  
                         debugTimer = null;
 354  
                 }
 355  
         }
 356  
 
 357  
         /** Returns the current debugging state.
 358  
          * @return The current debugging state.
 359  
          */
 360  
         private boolean getDebug()
 361  
         {
 362  
                 return debugTimer != null;
 363  
         }
 364  
 
 365  
         /** Returns the debug millis. Combines the value from {@link net.miginfocom.layout.LC#getDebugMillis()} and {@link net.miginfocom.layout.LayoutUtil#getGlobalDebugMillis()}
 366  
          * @return The combined value.
 367  
          */
 368  
         private int getDebugMillis()
 369  
         {
 370  
                 int globalDebugMillis = LayoutUtil.getGlobalDebugMillis();
 371  
                 return globalDebugMillis > 0 ? globalDebugMillis : lc.getDebugMillis();
 372  
         }
 373  
 
 374  
         /** Check if something has changed and if so recreate it to the cached objects.
 375  
          * @param parent The parent that is the target for this layout manager.
 376  
          */
 377  
         private void checkCache(Composite parent)
 378  
         {
 379  
                 if (parent == null)
 380  
                         return;
 381  
 
 382  
                 checkConstrMap(parent);
 383  
 
 384  
                 ContainerWrapper par = checkParent(parent);
 385  
 
 386  
                 // Check if the grid is valid
 387  
                 int mc = PlatformDefaults.getModCount();
 388  
                 if (lastModCount != mc) {
 389  
                         grid = null;
 390  
                         lastModCount = mc;
 391  
                 }
 392  
 
 393  
                 int hash = parent.getSize().hashCode();
 394  
                 for (ComponentWrapper cw : ccMap.keySet()) {
 395  
                         hash ^= cw.getLayoutHashCode();
 396  
                         hash += 285134905;
 397  
                 }
 398  
 
 399  
                 if (hash != lastHash) {
 400  
                         grid = null;
 401  
                         lastHash = hash;
 402  
                 }
 403  
 
 404  
                 setDebug(par, getDebugMillis() > 0);
 405  
 
 406  
                 if (grid == null)
 407  
                         grid = new Grid(par, lc, rowSpecs, colSpecs, ccMap, callbackList);
 408  
         }
 409  
 
 410  
         private boolean checkConstrMap(Composite parent)
 411  
         {
 412  
                 Control[] comps = parent.getChildren();
 413  
                 boolean changed = comps.length != scrConstrMap.size();
 414  
 
 415  
                 if (changed == false) {
 416  
                         for (Control c : comps) {
 417  
                                 if (scrConstrMap.get(c) != c.getLayoutData()) {
 418  
                                         changed = true;
 419  
                                         break;
 420  
                                 }
 421  
                         }
 422  
                 }
 423  
 
 424  
                 if (changed) {
 425  
                         scrConstrMap.clear();
 426  
                         for (Control c : comps)
 427  
                                 setComponentConstraintsImpl(c, c.getLayoutData(), true);
 428  
                 }
 429  
                 return changed;
 430  
         }
 431  
 
 432  
         private ContainerWrapper checkParent(Composite parent)
 433  
         {
 434  
                 if (parent == null)
 435  
                         return null;
 436  
 
 437  
                 if (cacheParentW == null || cacheParentW.getComponent() != parent)
 438  
                         cacheParentW = new SwtContainerWrapper(parent);
 439  
 
 440  
                 return cacheParentW;
 441  
         }
 442  
 
 443  
         public float getLayoutAlignmentX(Composite parent)
 444  
         {
 445  
                 return lc != null && lc.getAlignX() != null ? lc.getAlignX().getPixels(1, checkParent(parent), null) : 0;
 446  
         }
 447  
 
 448  
         public float getLayoutAlignmentY(Composite parent)
 449  
         {
 450  
                 return lc != null && lc.getAlignY() != null ? lc.getAlignY().getPixels(1, checkParent(parent), null) : 0;
 451  
         }
 452  
 
 453  
         protected Point computeSize(Composite parent, int wHint, int hHint, boolean flushCache)
 454  
         {
 455  
                 checkCache(parent);
 456  
 
 457  
                 int w = LayoutUtil.getSizeSafe(grid != null ? grid.getWidth() : null, LayoutUtil.PREF);
 458  
                 int h = LayoutUtil.getSizeSafe(grid != null ? grid.getHeight() : null, LayoutUtil.PREF);
 459  
 
 460  
                 return new Point(w, h);
 461  
         }
 462  
 
 463  
         protected void layout(Composite parent, boolean flushCache)
 464  
         {
 465  
                 checkCache(parent);
 466  
 
 467  
                 Rectangle r = parent.getClientArea();
 468  
                 int[] b = new int[] {r.x, r.y, r.width, r.height};
 469  
 
 470  
                 final boolean layoutAgain = grid.layout(b, lc.getAlignX(), lc.getAlignY(), getDebug(), true);
 471  
 
 472  
                 if (layoutAgain) {
 473  
                         grid = null;
 474  
                         checkCache(parent);
 475  
                         grid.layout(b, lc.getAlignX(), lc.getAlignY(), getDebug(), false);
 476  
                 }
 477  
         }
 478  
 
 479  
         protected boolean flushCache(Control control)
 480  
         {
 481  
                 grid = null;
 482  
                 return true;
 483  
         }
 484  
 
 485  
         // ************************************************
 486  
         // Persistence Delegate and Serializable combined.
 487  
         // ************************************************
 488  
 
 489  
         private Object readResolve() throws ObjectStreamException
 490  
         {
 491  
                 return LayoutUtil.getSerializedObject(this);
 492  
         }
 493  
 
 494  
         public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
 495  
         {
 496  
                 LayoutUtil.setSerializedObject(this, LayoutUtil.readAsXML(in));
 497  
         }
 498  
 
 499  
         public void writeExternal(ObjectOutput out) throws IOException
 500  
         {
 501  
                 if (getClass() == MigLayout.class)
 502  
                         LayoutUtil.writeAsXML(out, this);
 503  
         }
 504  
 
 505  
         private static class MyDebugRepaintTask extends TimerTask
 506  
         {
 507  
                 private final WeakReference<MigLayout> layoutRef;
 508  
 
 509  
                 private MyDebugRepaintTask(MigLayout layout)
 510  
                 {
 511  
                         this.layoutRef = new WeakReference<MigLayout>(layout);
 512  
                 }
 513  
 
 514  
                 public void run()
 515  
                 {
 516  
                         final MigLayout layout = layoutRef.get();
 517  
                         if (layout != null && layout.grid != null) {
 518  
                                 Display.getDefault().asyncExec(new Runnable () {
 519  
                                         public void run () {
 520  
                                                 if (layout.grid != null)
 521  
                                                         layout.grid.paintDebug();
 522  
                                         }
 523  
                                 });
 524  
                         }
 525  
                 }
 526  
         }
 527  
 }