1 /*
   2  * Copyright (c) 1996, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package sun.awt;
  26 
  27 import java.awt.*;
  28 
  29 /**
  30  * Extends the FlowLayout class to support both vertical and horizontal
  31  * layout of components.  Orientation can be changed dynamically after
  32  * creation by calling either of the methods @method orientHorizontally or
  33  * @method orientVertically.  Separate values for alignment, vertical gap,
  34  * and horizontal gap can be specified for horizontal and vertical
  35  * orientation.
  36  *
  37  * @author Terry Cline
  38  */
  39 public class OrientableFlowLayout extends FlowLayout {
  40     /**
  41      * The horizontal orientation constant.
  42      */
  43     public static final int HORIZONTAL = 0;
  44 
  45     /**
  46      * The vertical orientation constant.
  47      */
  48     public static final int VERTICAL   = 1;
  49 
  50     /**
  51      * The top vertical alignment constant.
  52      */
  53     public static final int TOP        = 0;
  54 
  55     /**
  56      * The bottom vertical alignment constant.
  57      */
  58     public static final int BOTTOM     = 2; // CENTER == 1
  59 
  60     int orientation;
  61     int vAlign;
  62     int vHGap;
  63     int vVGap;
  64 
  65     /**
  66      * Constructs a new flow layout with a horizontal orientation and
  67      * centered alignment.
  68      */
  69     public OrientableFlowLayout() {
  70         this(HORIZONTAL, CENTER, CENTER, 5, 5, 5, 5);
  71     }
  72 
  73     /**
  74      * Constructs a new flow layout with the specified orientation and
  75      * a centered alignment.
  76      *
  77      * @param orientation the orientation, one of HORIZONTAL or VERTICAL.
  78      */
  79     public OrientableFlowLayout(int orientation) {
  80         this(orientation, CENTER, CENTER, 5, 5, 5, 5);
  81     }
  82 
  83     /**
  84      * Constructs a new flow layout with the specified orientation and
  85      * alignment.
  86      *
  87      * @param orientation the orientation, one of HORIZONTAL or VERTICAL.
  88      * @param hAlign the horizontal alignment, one of LEFT, CENTER, or RIGHT.
  89      * @param vAlign the vertical alignment, one of TOP, CENTER, or BOTTOM.
  90      */
  91     public OrientableFlowLayout(int orientation, int hAlign, int vAlign) {
  92         this(orientation, hAlign, vAlign, 5, 5, 5, 5);
  93     }
  94 
  95     /**
  96      * Constructs a new flow layout with the specified orientation,
  97      * alignment, and gap values.
  98      *
  99      * @param orientation the orientation, one of HORIZONTAL or VERTICAL.
 100      * @param hAlign the horizontal alignment, one of LEFT, CENTER, or RIGHT.
 101      * @param vAlign the vertical alignment, one of TOP, CENTER, or BOTTOM.
 102      * @param hHGap the horizontal gap between components in HORIZONTAL.
 103      * @param hVGap the vertical gap between components in HORIZONTAL.
 104      * @param vHGap the horizontal gap between components in VERTICAL.
 105      * @param vVGap the vertical gap between components in VERTICAL.
 106      */
 107     public OrientableFlowLayout(int orientation, int hAlign, int vAlign, int hHGap, int hVGap, int vHGap, int vVGap) {
 108         super(hAlign, hHGap, hVGap);
 109         this.orientation = orientation;
 110         this.vAlign      = vAlign;
 111         this.vHGap       = vHGap;
 112         this.vVGap       = vVGap;
 113     }
 114 
 115     /**
 116      * Set the layout's current orientation to horizontal.
 117      */
 118     public synchronized void orientHorizontally() {
 119         orientation = HORIZONTAL;
 120     }
 121 
 122     /**
 123      * Set the layout's current orientation to vertical.
 124      */
 125     public synchronized void orientVertically() {
 126         orientation = VERTICAL;
 127     }
 128 
 129     /**
 130      * Returns the preferred dimensions for this layout given the
 131      * components in the specified target container.
 132      *
 133      * @param target the component which needs to be laid out.
 134      * @see Container
 135      * @see FlowLayout
 136      * @see #minimumLayoutSize
 137      */
 138     public Dimension preferredLayoutSize(Container target) {
 139         if (orientation == HORIZONTAL) {
 140             return super.preferredLayoutSize(target);
 141         }
 142         else {
 143             Dimension dim = new Dimension(0, 0);
 144 
 145             int n = target.countComponents();
 146             for (int i = 0; i < n; i++) {
 147                 Component c = target.getComponent(i);
 148                 if (c.isVisible()) {
 149                     Dimension cDim = c.preferredSize();
 150                     dim.width = Math.max(dim.width, cDim.width);
 151                     if (i > 0) {
 152                         dim.height += vVGap;
 153                     }
 154                     dim.height += cDim.height;
 155                 }
 156             }
 157 
 158             Insets insets = target.insets();;
 159             dim.width  += insets.left + insets.right  + vHGap*2;
 160             dim.height += insets.top  + insets.bottom + vVGap*2;
 161 
 162             return dim;
 163         }
 164     }
 165 
 166     /**
 167      * Returns the minimum dimensions needed to layout the components
 168      * contained in the specified target container.
 169      *
 170      * @param target the component which needs to be laid out.
 171      * @see #preferredLayoutSize.
 172      */
 173     public Dimension minimumLayoutSize(Container target) {
 174         if (orientation == HORIZONTAL) {
 175             return super.minimumLayoutSize(target);
 176         }
 177         else {
 178             Dimension dim = new Dimension(0, 0);
 179 
 180             int n = target.countComponents();
 181             for (int i = 0; i < n; i++) {
 182                 Component c = target.getComponent(i);
 183                 if (c.isVisible()) {
 184                     Dimension cDim = c.minimumSize();
 185                     dim.width = Math.max(dim.width, cDim.width);
 186                     if (i > 0) {
 187                         dim.height += vVGap;
 188                     }
 189                     dim.height += cDim.height;
 190                 }
 191             }
 192 
 193             Insets insets = target.insets();
 194             dim.width  += insets.left + insets.right  + vHGap*2;
 195             dim.height += insets.top  + insets.bottom + vVGap*2;
 196 
 197             return dim;
 198         }
 199     }
 200 
 201     /**
 202      * Lays out the container.  This method will reshape the
 203      * components in the target to satisfy the constraints of the
 204      * layout.
 205      *
 206      * @param target the specified component being laid out.
 207      * @see Container.
 208      */
 209     public void layoutContainer(Container target) {
 210         if (orientation == HORIZONTAL) {
 211             super.layoutContainer(target);
 212         }
 213         else {
 214             Insets insets = target.insets();
 215             Dimension targetDim = target.size();
 216             int maxHeight = targetDim.height - (insets.top + insets.bottom + vVGap*2);
 217             int x = insets.left + vHGap;
 218             int y = 0;
 219             int colWidth = 0;
 220             int start = 0;
 221 
 222             int n = target.countComponents();
 223             for (int i = 0; i < n; i++) {
 224                 Component c = target.getComponent(i);
 225                 if (c.isVisible()) {
 226                     Dimension cDim = c.preferredSize();
 227                     c.resize(cDim.width, cDim.height);
 228 
 229                     if ((y == 0) || ((y + cDim.height) <= maxHeight)) {
 230                         if (y > 0) {
 231                             y += vVGap;
 232                         }
 233                         y += cDim.height;
 234                         colWidth = Math.max(colWidth, cDim.width);
 235                     }
 236                     else {
 237                         moveComponents(target,
 238                                        x,
 239                                        insets.top + vVGap,
 240                                        colWidth,
 241                                        maxHeight - y,
 242                                        start,
 243                                        i);
 244                         x += vHGap + colWidth;
 245                         y = cDim.width;
 246                         colWidth = cDim.width;
 247                         start = i;
 248                     }
 249                 }
 250             }
 251 
 252             moveComponents(target,
 253                            x,
 254                            insets.top + vVGap,
 255                            colWidth,
 256                            maxHeight - y,
 257                            start,
 258                            n);
 259         }
 260     }
 261 
 262     /**
 263      * Aligns the components vertically if there is any slack.
 264      *
 265      * @param target the container whose components need to be moved.
 266      * @param x the x coordinate.
 267      * @param y the y coordinate.
 268      * @param width the width available.
 269      * @param height the height available.
 270      * @param colStart the beginning of the column.
 271      * @param colEnd the end of the column.
 272      */
 273     private void moveComponents(Container target, int x, int y, int width, int height, int colStart, int colEnd) {
 274         switch (vAlign) {
 275         case TOP:
 276             break;
 277         case CENTER:
 278             y += height/2;
 279             break;
 280         case BOTTOM:
 281             y += height;
 282         }
 283 
 284         for (int i = colStart; i < colEnd; i++) {
 285             Component c = target.getComponent(i);
 286             Dimension cDim = c.size();
 287             if (c.isVisible()) {
 288                 c.move(x + (width - cDim.width)/2, y);
 289                 y += vVGap + cDim.height;
 290             }
 291         }
 292     }
 293 
 294     /**
 295      * Returns the String representation of this layout's values.
 296      */
 297     public String toString() {
 298         String str = "";
 299         switch (orientation) {
 300         case HORIZONTAL:
 301             str = "orientation=horizontal, ";
 302             break;
 303         case VERTICAL:
 304             str = "orientation=vertical, ";
 305             break;
 306         }
 307 
 308         return getClass().getName() + "[" + str + super.toString() + "]";
 309     }
 310 }