Coverage Report - net.wotonomy.ui.swing.components.BetterFlowLayout
 
Classes in this File Line Coverage Branch Coverage Complexity
BetterFlowLayout
0% 
0% 
3.263
 
 1  
 /*
 2  
 Wotonomy: OpenStep design patterns for pure Java applications.
 3  
 Copyright (C) 2000 Blacksmith, Inc.
 4  
 
 5  
 This library is free software; you can redistribute it and/or
 6  
 modify it under the terms of the GNU Lesser General Public
 7  
 License as published by the Free Software Foundation; either
 8  
 version 2.1 of the License, or (at your option) any later version.
 9  
 
 10  
 This library is distributed in the hope that it will be useful,
 11  
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 13  
 Lesser General Public License for more details.
 14  
 
 15  
 You should have received a copy of the GNU Lesser General Public
 16  
 License along with this library; if not, see http://www.gnu.org
 17  
 */
 18  
 
 19  
 package net.wotonomy.ui.swing.components;
 20  
 
 21  
 import java.awt.Component;
 22  
 import java.awt.Container;
 23  
 import java.awt.Dimension;
 24  
 import java.awt.FlowLayout;
 25  
 import java.awt.Insets;
 26  
 
 27  
 /**
 28  
  * BetterFlowLayout works just like FlowLayout, except that
 29  
  * you can specify a vertical orientation in addition to the
 30  
  * usual horizontal orientations.  You can also specify that
 31  
  * all the components be sized to the same height and/or width.
 32  
  * By default, the behavior is identical to FlowLayout.
 33  
  *
 34  
  * @author michael@mpowers.net
 35  
  * @author $Author: cgruber $
 36  
  * @version $Revision: 904 $
 37  
  * $Date: 2006-02-18 23:19:05 +0000 (Sat, 18 Feb 2006) $
 38  
  */
 39  
 public class BetterFlowLayout extends FlowLayout {
 40  
 
 41  
     /**
 42  
      * This value indicates vertical orientation and
 43  
      * that each column of components should be top-justified.
 44  
      */
 45  
     public static final int TOP         = 32;
 46  
 
 47  
     /**
 48  
      * This value indicates vertical orientation and
 49  
      * that each column of components should be centered.
 50  
      */
 51  
     public static final int CENTER_VERTICAL        = 16;
 52  
 
 53  
     /**
 54  
      * This value indicates vertical orientation and
 55  
      * that each column of components should be bottom-justified.
 56  
      */
 57  
     public static final int BOTTOM         = 8;
 58  
 
 59  
     /**
 60  
      * Tracks orientation.
 61  
      */
 62  0
     protected boolean isHorizontal = true;
 63  
 
 64  
     /**
 65  
      * Tracks component sizing of width.
 66  
      */
 67  0
     protected boolean isWidthUniform = false;
 68  
     /**
 69  
      * Tracks component sizing of height.
 70  
      */
 71  0
     protected boolean isHeightUniform = false;
 72  
 
 73  
     /**
 74  
      * Constructs a new Flow Layout with a centered alignment and a
 75  
      * default 5-unit horizontal and vertical gap.
 76  
      */
 77  
     public BetterFlowLayout() {
 78  0
         this(CENTER, 5, 5);
 79  0
     }
 80  
 
 81  
     /**
 82  
      * Constructs a new Flow Layout with the specified alignment and a
 83  
      * default 5-unit horizontal and vertical gap.
 84  
      * The value of the alignment argument must be one of
 85  
      * <code>BetterFlowLayout.LEFT</code>, <code>BetterFlowLayout.RIGHT</code>,
 86  
      * or <code>BetterFlowLayout.CENTER</code>.
 87  
      * @param align the alignment value
 88  
      */
 89  
     public BetterFlowLayout(int align) {
 90  0
         this(align, 5, 5);
 91  0
     }
 92  
 
 93  
     /**
 94  
      * Creates a new flow layout manager with the indicated alignment
 95  
      * and the indicated horizontal and vertical gaps.
 96  
      * <p>
 97  
      * The value of the alignment argument must be one of
 98  
      * <code>BetterFlowLayout.LEFT</code>, <code>BetterFlowLayout.RIGHT</code>,
 99  
      * or <code>BetterFlowLayout.CENTER</code>.
 100  
      * @param      align   the alignment value.
 101  
      * @param      hgap    the horizontal gap between components.
 102  
      * @param      vgap    the vertical gap between components.
 103  
      */
 104  0
     public BetterFlowLayout(int align, int hgap, int vgap) {
 105  0
             setHgap(hgap);
 106  0
             setVgap(vgap);
 107  0
         setAlignment(align);
 108  0
     }
 109  
 
 110  
     /**
 111  
      * Sets whether all components should have the same height.
 112  
      * @param isUniform the new value.
 113  
      * @see #isHeightUniform
 114  
      */
 115  
     public void setHeightUniform(boolean isUniform) {
 116  0
         isHeightUniform = isUniform;
 117  0
     }
 118  
 
 119  
     /**
 120  
      * Sets whether all components should have the same width.
 121  
      * @param isUniform the new value.
 122  
      * @see #isWidthUniform
 123  
      */
 124  
     public void setWidthUniform(boolean isUniform) {
 125  0
         isWidthUniform = isUniform;
 126  0
     }
 127  
 
 128  
     /**
 129  
      * Determines whether all components will have the same height.
 130  
      * The uniform height will be the maximum of the preferred heights
 131  
      * of all the components in the container.
 132  
      * This value defaults to false.
 133  
      * @return whether components will have the same height.
 134  
      */
 135  
     public boolean isHeightUniform() {
 136  0
         return isHeightUniform;
 137  
     }
 138  
 
 139  
     /**
 140  
      * Determines whether all components will have the same width.
 141  
      * The uniform height will be the maximum of the preferred widths
 142  
      * of all the components in the container.
 143  
      * This value defaults to false.
 144  
      * @return whether components will have the same width.
 145  
      */
 146  
     public boolean isWidthUniform() {
 147  0
         return isWidthUniform;
 148  
     }
 149  
 
 150  
     /**
 151  
      * Sets the alignment for this layout.
 152  
      * Possible values for horizontal orientation are <code>LEFT</code>,
 153  
      * <code>RIGHT</code>, and <code>CENTER</code>.
 154  
      * Possible values for vertical orientation are <code>TOP</code>,
 155  
      * <code>BOTTOM</code>, and <code>CENTER_VERTICAL</code>.
 156  
      * @param      align the alignment value.
 157  
      * @see        java.awt.FlowLayout#getAlignment
 158  
      */
 159  
     public void setAlignment(int align) {
 160  0
         if ( ( align == TOP ) || ( align == BOTTOM ) || ( align == CENTER_VERTICAL ) )
 161  
         {
 162  0
             isHorizontal = false;
 163  0
         }
 164  
         else
 165  
         {
 166  0
             isHorizontal = true;
 167  
         }
 168  
 
 169  0
         super.setAlignment( align );
 170  0
     }
 171  
 
 172  
     /**
 173  
      * Returns the preferred dimensions for this layout given the components
 174  
      * in the specified target container.
 175  
      * @param target the component which needs to be laid out
 176  
      * @return    the preferred dimensions to lay out the
 177  
      *                    subcomponents of the specified container.
 178  
      * @see Container
 179  
      * @see #minimumLayoutSize
 180  
      * @see       java.awt.Container#getPreferredSize
 181  
      */
 182  
     public Dimension preferredLayoutSize(Container target) {
 183  0
         if ( isHorizontal ) {
 184  0
             return preferredLayoutSizeHorizontal( target );
 185  
         } else {
 186  0
             return preferredLayoutSizeVertical( target );
 187  
         }
 188  
     }
 189  
 
 190  
     /**
 191  
      * Returns the preferred dimensions for this layout given the components
 192  
      * in the specified target container.
 193  
      * @param target the component which needs to be laid out
 194  
      * @return    the preferred dimensions to lay out the
 195  
      *                    subcomponents of the specified container.
 196  
      * @see Container
 197  
      * @see #minimumLayoutSize
 198  
      * @see       java.awt.Container#getPreferredSize
 199  
      */
 200  
     public Dimension preferredLayoutSizeHorizontal(Container target) {
 201  0
       synchronized (target.getTreeLock()) {
 202  0
         Dimension dim = new Dimension(0, 0);
 203  0
         int nmembers = target.getComponentCount();
 204  0
         int maxWidth = 0;
 205  
 
 206  0
         for (int i = 0 ; i < nmembers ; i++) {
 207  0
             Component m = target.getComponent(i);
 208  0
             if (m.isVisible()) {
 209  0
             Dimension d = m.getPreferredSize();
 210  0
             dim.height = Math.max(dim.height, d.height);
 211  0
             maxWidth = Math.max(maxWidth, d.width);
 212  0
             if (i > 0) {
 213  0
                 dim.width += getHgap();
 214  
             }
 215  0
             dim.width += d.width;
 216  
             }
 217  
         }
 218  0
         if ( isWidthUniform )
 219  0
             dim.width = ( maxWidth + getHgap() ) * nmembers - getHgap();
 220  0
         Insets insets = target.getInsets();
 221  0
         dim.width += insets.left + insets.right + getHgap()*2;
 222  0
         dim.height += insets.top + insets.bottom + getVgap()*2;
 223  0
         return dim;
 224  0
       }
 225  
     }
 226  
 
 227  
     /**
 228  
      * Returns the preferred dimensions for this layout given the components
 229  
      * in the specified target container.
 230  
      * @param target the component which needs to be laid out
 231  
      * @return    the preferred dimensions to lay out the
 232  
      *                    subcomponents of the specified container.
 233  
      * @see Container
 234  
      * @see #minimumLayoutSize
 235  
      * @see       java.awt.Container#getPreferredSize
 236  
      */
 237  
     public Dimension preferredLayoutSizeVertical(Container target) {
 238  0
       synchronized (target.getTreeLock()) {
 239  0
         Dimension dim = new Dimension(0, 0);
 240  0
         int nmembers = target.getComponentCount();
 241  0
         int maxHeight = 0;
 242  
 
 243  0
         for (int i = 0 ; i < nmembers ; i++) {
 244  0
             Component m = target.getComponent(i);
 245  0
             if (m.isVisible()) {
 246  0
             Dimension d = m.getPreferredSize();
 247  0
             dim.width = Math.max(dim.width, d.width);
 248  0
             maxHeight = Math.max(maxHeight, d.height);
 249  0
             if (i > 0) {
 250  0
                 dim.height += getVgap();
 251  
             }
 252  0
             dim.height += d.height;
 253  
             }
 254  
         }
 255  0
         if ( isHeightUniform )
 256  0
             dim.height = ( maxHeight + getVgap() ) * nmembers - getVgap();
 257  0
         Insets insets = target.getInsets();
 258  0
         dim.width += insets.left + insets.right + getHgap()*2;
 259  0
         dim.height += insets.top + insets.bottom + getVgap()*2;
 260  0
         return dim;
 261  0
       }
 262  
     }
 263  
 
 264  
     /**
 265  
      * Returns the minimum dimensions needed to layout the components
 266  
      * contained in the specified target container.
 267  
      * @param target the component which needs to be laid out
 268  
      * @return    the minimum dimensions to lay out the
 269  
      *                    subcomponents of the specified container.
 270  
      * @see #preferredLayoutSize
 271  
      * @see       java.awt.Container
 272  
      * @see       java.awt.Container#doLayout
 273  
      */
 274  
     public Dimension minimumLayoutSize(Container target) {
 275  
         // preferred size is also the minimum size
 276  0
         if ( isHorizontal ) {
 277  0
             return preferredLayoutSizeHorizontal( target );
 278  
         } else {
 279  0
             return preferredLayoutSizeVertical( target );
 280  
         }
 281  
     }
 282  
 
 283  
     /**
 284  
      * Lays out the container. This method lets each component take
 285  
      * its preferred size by reshaping the components in the
 286  
      * target container in order to satisfy the constraints of
 287  
      * this <code>BetterFlowLayout</code> object.
 288  
      * @param target the specified component being laid out.
 289  
      * @see Container
 290  
      * @see       java.awt.Container#doLayout
 291  
      */
 292  
     public void layoutContainer(Container target) {
 293  0
         if ( isHorizontal ) {
 294  0
             layoutContainerHorizontal( target );
 295  0
         } else {
 296  0
             layoutContainerVertical( target );
 297  
         }
 298  0
     }
 299  
 
 300  
     /**
 301  
      * Lays out the container. This method lets each component take
 302  
      * its preferred size by reshaping the components in the
 303  
      * target container in order to satisfy the constraints of
 304  
      * this <code>BetterFlowLayout</code> object.
 305  
      * @param target the specified component being laid out.
 306  
      * @see Container
 307  
      * @see       java.awt.Container#doLayout
 308  
      */
 309  
     protected void layoutContainerHorizontal(Container target) {
 310  0
       synchronized (target.getTreeLock()) {
 311  0
         Insets insets = target.getInsets();
 312  0
         int maxwidth = target.getSize().width - (insets.left + insets.right + getHgap()*2);
 313  0
         int nmembers = target.getComponentCount();
 314  0
         int x = 0, y = insets.top + getVgap();
 315  0
         int rowh = 0, start = 0;
 316  
 
 317  0
         boolean ltr = true; // target.getComponentOrientation().isLeftToRight();
 318  0
         Dimension uniform = getUniformDimension( target );
 319  
 
 320  0
         for (int i = 0 ; i < nmembers ; i++) {
 321  0
             Component m = target.getComponent(i);
 322  0
             if (m.isVisible()) {
 323  0
                 Dimension d = m.getPreferredSize();
 324  0
                 if ( isWidthUniform )
 325  0
                     d.width = uniform.width;
 326  0
                 if ( isHeightUniform )
 327  0
                     d.height = uniform.height;
 328  0
                 m.setSize(d.width, d.height);
 329  
 
 330  0
                 if ((x == 0) || ((x + d.width) <= maxwidth)) {
 331  0
                     if (x > 0) {
 332  0
                         x += getHgap();
 333  
                     }
 334  0
                     x += d.width;
 335  0
                     rowh = Math.max(rowh, d.height);
 336  0
                 } else {
 337  0
                     moveComponentsHorizontal(target, insets.left + getHgap(), y, maxwidth - x, rowh, start, i, ltr);
 338  0
                     x = d.width;
 339  0
                     y += getVgap() + rowh;
 340  0
                     rowh = d.height;
 341  0
                     start = i;
 342  
                 }
 343  
             }
 344  
         }
 345  0
         moveComponentsHorizontal(target, insets.left + getHgap(), y, maxwidth - x, rowh, start, nmembers, ltr);
 346  0
       }
 347  0
     }
 348  
 
 349  
     /**
 350  
      * Centers the elements in the specified row, if there is any slack.
 351  
      * @param target the component which needs to be moved
 352  
      * @param x the x coordinate
 353  
      * @param y the y coordinate
 354  
      * @param width the width dimensions
 355  
      * @param height the height dimensions
 356  
      * @param rowStart the beginning of the row
 357  
      * @param rowEnd the the ending of the row
 358  
      */
 359  
     private void moveComponentsHorizontal(Container target, int x, int y, int width, int height,
 360  
                                 int rowStart, int rowEnd, boolean ltr) {
 361  0
       synchronized (target.getTreeLock()) {
 362  0
         switch (getAlignment()) {
 363  
         case LEFT:
 364  0
             x += ltr ? 0 : width;
 365  0
             break;
 366  
         case CENTER:
 367  0
             x += width / 2;
 368  0
             break;
 369  
         case RIGHT:
 370  0
             x += ltr ? width : 0;
 371  
             break;
 372  
 //1.2        case LEADING:
 373  
 //1.2            break;
 374  
 //1.2        case TRAILING:
 375  
 //1.2            x += width;
 376  
 //1.2            break;
 377  
         }
 378  0
         for (int i = rowStart ; i < rowEnd ; i++) {
 379  0
             Component m = target.getComponent(i);
 380  0
             if (m.isVisible()) {
 381  0
                 if (ltr) {
 382  0
                     m.setLocation(x, y + (height - m.getBounds().height) / 2);
 383  0
                 } else {
 384  0
                     m.setLocation(target.getBounds().width - x - m.getBounds().width, y + (height - m.getBounds().height) / 2);
 385  
             }
 386  0
                 x += m.getBounds().width + getHgap();
 387  
             }
 388  
         }
 389  0
       }
 390  0
     }
 391  
 
 392  
     /**
 393  
      * Lays out the container. This method lets each component take
 394  
      * its preferred size by reshaping the components in the
 395  
      * target container in order to satisfy the constraints of
 396  
      * this <code>BetterFlowLayout</code> object.
 397  
      * @param target the specified component being laid out.
 398  
      * @see Container
 399  
      * @see       java.awt.Container#doLayout
 400  
      */
 401  
     protected void layoutContainerVertical(Container target) {
 402  0
       synchronized (target.getTreeLock()) {
 403  
 
 404  0
         Insets insets = target.getInsets();
 405  0
         int maxheight = target.getBounds().height - (insets.top + insets.bottom + getVgap()*2);
 406  0
         int nmembers = target.getComponentCount();
 407  0
         int y = 0, x = insets.left + getHgap();
 408  0
         int colw = 0, start = 0;
 409  
 
 410  0
         Dimension uniform = getUniformDimension( target );
 411  0
         for (int i = 0 ; i < nmembers ; i++) {
 412  0
             Component m = target.getComponent(i);
 413  0
             if (m.isVisible()) {
 414  0
                 Dimension d = m.getPreferredSize();
 415  0
                 if ( isWidthUniform )
 416  0
                     d.width = uniform.width;
 417  0
                 if ( isHeightUniform )
 418  0
                     d.height = uniform.height;
 419  0
                 m.setSize(d.width, d.height);
 420  
 
 421  0
                 if ((y == 0) || ((y + d.height) <= maxheight)) {
 422  0
                     if (y > 0) {
 423  0
                     y += getVgap();
 424  
                     }
 425  0
                     y += d.height;
 426  0
                     colw = Math.max(colw, d.width);
 427  0
                 } else {
 428  0
                     moveComponentsVertical(target, x, insets.top + getVgap(), colw, maxheight - y, start, i );
 429  0
                     y = d.height;
 430  0
                     x += getHgap() + colw;
 431  0
                     colw = d.width;
 432  0
                     start = i;
 433  
                 }
 434  
             }
 435  
         }
 436  0
         moveComponentsVertical(target, x, insets.top + getVgap(), colw, maxheight - y, start, nmembers );
 437  0
       }
 438  0
     }
 439  
 
 440  
     /**
 441  
      * Centers the elements in the specified row, if there is any slack.
 442  
      * @param target the component which needs to be moved
 443  
      * @param x the x coordinate
 444  
      * @param y the y coordinate
 445  
      * @param width the width dimensions
 446  
      * @param height the height dimensions
 447  
      * @param colStart the beginning of the column
 448  
      * @param colEnd the the ending of the column
 449  
      */
 450  
     private void moveComponentsVertical(Container target, int x, int y, int width, int height,
 451  
                                 int colStart, int colEnd) {
 452  0
       synchronized (target.getTreeLock()) {
 453  0
         switch (getAlignment()) {
 454  
         case TOP:
 455  0
             y += 0;
 456  0
             break;
 457  
         case CENTER_VERTICAL:
 458  0
             y += ( height / 2 ); // - preferredLayoutSize( target ).height ) / 2 );
 459  0
             break;
 460  
         case BOTTOM:
 461  0
             y += height;
 462  
             break;
 463  
         }
 464  0
         for (int i = colStart ; i < colEnd ; i++) {
 465  0
             Component m = target.getComponent(i);
 466  0
             if (m.isVisible()) {
 467  0
                 m.setLocation(x + (width - m.getBounds().width) / 2, y );
 468  
 //                m.setLocation(x, y );
 469  
 //                m.setSize( width, m.getBounds().height ); //!
 470  0
                 y += m.getBounds().height + getVgap();
 471  
             }
 472  
         }
 473  0
       }
 474  0
     }
 475  
 
 476  
     /**
 477  
      * Returns a dimension representing the maximum preferred
 478  
      * height and width of all the components in the container.
 479  
      * @param target the container to scan.
 480  
      * @return a dimension containing the maximum values.
 481  
      */
 482  
     protected Dimension getUniformDimension(Container target) {
 483  0
         Component m = null;
 484  0
         Dimension preferred = null;
 485  0
         int maxWidth = 0, maxHeight = 0;
 486  0
         int nmembers = target.getComponentCount();
 487  0
         for ( int i = 0; i < nmembers; i++ ) {
 488  0
             m = target.getComponent( i );
 489  0
             if ( m.isVisible() ) {
 490  0
                 preferred = m.getPreferredSize();
 491  0
                 maxWidth = Math.max( maxWidth, preferred.width );
 492  0
                 maxHeight = Math.max( maxHeight, preferred.height );
 493  
             }
 494  
         }
 495  0
         return new Dimension( maxWidth, maxHeight );
 496  
     }
 497  
 
 498  
     /**
 499  
      * Returns a string representation of this <code>BetterFlowLayout</code>
 500  
      * object and its values.
 501  
      * @return     a string representation of this layout.
 502  
      */
 503  
     public String toString() {
 504  0
         String str = "";
 505  0
         switch (getAlignment()) {
 506  0
           case TOP:        str = ",align=top"; break;
 507  0
           case CENTER_VERTICAL:      str = ",align=vertical"; break;
 508  0
           case BOTTOM:       str = ",align=bottom"; break;
 509  0
       default: return super.toString();
 510  
         }
 511  0
         return getClass().getName() + "[hgap=" + getHgap() + ",vgap=" + getVgap() + str + "]";
 512  
     }
 513  
 
 514  
 
 515  
 }