001    /* BoxLayout.java -- A layout for swing components.
002       Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    package javax.swing;
039    
040    import java.awt.AWTError;
041    import java.awt.Component;
042    import java.awt.ComponentOrientation;
043    import java.awt.Container;
044    import java.awt.Dimension;
045    import java.awt.Insets;
046    import java.awt.LayoutManager2;
047    import java.io.Serializable;
048    
049    /**
050     * A layout that stacks the children of a container in a Box, either
051     * horizontally or vertically.
052     *
053     * @author Ronald Veldema (rveldema@cs.vu.nl)
054     * @author Roman Kennke (roman@kennke.org)
055     */
056    public class BoxLayout implements LayoutManager2, Serializable
057    {
058    
059      /**
060       * Specifies that components are laid out left to right.
061       */
062      public static final int X_AXIS = 0;
063    
064      /**
065       * Specifies that components are laid out top to bottom.
066       */
067      public static final int Y_AXIS = 1;
068    
069      /**
070       * Specifies that components are laid out in the direction of a line of text.
071       */
072      public static final int LINE_AXIS = 2;
073    
074      /**
075       * Sepcifies that components are laid out in the direction of the line flow.
076       */
077      public static final int PAGE_AXIS = 3;
078    
079      /*
080       * Needed for serialization.
081       */
082      private static final long serialVersionUID = -2474455742719112368L;
083    
084      /*
085       * The container given to the constructor.
086       */
087      private Container container;
088      
089      /**
090       * Current type of component layouting. Defaults to X_AXIS.
091       */
092      private int way = X_AXIS;
093    
094      /**
095       * The size requirements of the containers children for the X direction.
096       */
097      private SizeRequirements[] xChildren;
098    
099      /**
100       * The size requirements of the containers children for the Y direction.
101       */
102      private SizeRequirements[] yChildren;
103    
104      /**
105       * The size requirements of the container to be laid out for the X direction.
106       */
107      private SizeRequirements xTotal;
108    
109      /**
110       * The size requirements of the container to be laid out for the Y direction.
111       */
112      private SizeRequirements yTotal;
113    
114      /**
115       * The offsets of the child components in the X direction.
116       */
117      private int[] offsetsX;
118    
119      /**
120       * The offsets of the child components in the Y direction.
121       */
122      private int[] offsetsY;
123    
124      /**
125       * The spans of the child components in the X direction.
126       */
127      private int[] spansX;
128    
129      /**
130       * The spans of the child components in the Y direction.
131       */
132      private int[] spansY;
133    
134      /**
135       * Constructs a <code>BoxLayout</code> object.
136       *
137       * @param container The container that needs to be laid out.
138       * @param way The orientation of the components.
139       *
140       * @exception AWTError If way has an invalid value.
141       */
142      public BoxLayout(Container container, int way)
143      {
144        if (way != X_AXIS && way != Y_AXIS && way != LINE_AXIS && way != PAGE_AXIS)
145          throw new AWTError("Invalid axis");
146    
147        int width = 0;
148        int height = 0;
149        this.container = container;
150        this.way = way;
151      }
152    
153      /**
154       * Adds a component to the layout. Not used in BoxLayout.
155       *
156       * @param name The name of the component to add.
157       * @param component the component to add to the layout.
158       */
159      public void addLayoutComponent(String name, Component component)
160      {
161        // Nothing to do here.
162      }
163    
164      /**
165       * Removes a component from the layout. Not used in BoxLayout.
166       *
167       * @param component The component to remove from the layout.
168       */
169      public void removeLayoutComponent(Component component)
170      {
171        // Nothing to do here.
172      }
173    
174      private boolean isHorizontalIn(Container parent)
175      {
176        ComponentOrientation orientation = parent.getComponentOrientation();
177        return this.way == X_AXIS 
178          || (this.way == LINE_AXIS 
179              && orientation.isHorizontal())
180          || (this.way == PAGE_AXIS
181              && (!orientation.isHorizontal()));
182      }
183    
184      
185    
186      /**
187       * Returns the preferred size of the layout.
188       *
189       * @param parent The container that needs to be laid out.
190       *
191       * @return The dimension of the layout.
192       */
193      public Dimension preferredLayoutSize(Container parent)
194      {
195        synchronized (container.getTreeLock())
196          {
197            if (container != parent)
198              throw new AWTError("BoxLayout can't be shared");
199    
200            checkTotalRequirements();
201            Insets i = container.getInsets();
202            return new Dimension(xTotal.preferred + i.left + i.right,
203                                 yTotal.preferred + i.top + i.bottom);
204          }
205      }
206    
207      /**
208       * Returns the minimum size of the layout.
209       *
210       * @param parent The container that needs to be laid out.
211       *
212       * @return The dimension of the layout.
213       */
214      public Dimension minimumLayoutSize(Container parent)
215      {
216        synchronized (container.getTreeLock())
217          {
218            if (container != parent)
219              throw new AWTError("BoxLayout can't be shared");
220    
221            checkTotalRequirements();
222            Insets i = container.getInsets();
223            return new Dimension(xTotal.minimum + i.left + i.right,
224                                 yTotal.minimum + i.top + i.bottom);
225          }
226      }
227    
228      /**
229       * Lays out the specified container using this layout.
230       *
231       * @param parent The container that needs to be laid out.
232       */
233      public void layoutContainer(Container parent)
234      {
235        synchronized (container.getTreeLock())
236          {
237            if (container != parent)
238              throw new AWTError("BoxLayout can't be shared");
239          
240            checkLayout();
241            Component[] children = container.getComponents();
242            Insets in = container.getInsets();
243            for (int i = 0; i < children.length; i++)
244              children[i].setBounds(offsetsX[i] + in.left, offsetsY[i] + in.top,
245                                    spansX[i], spansY[i]);
246          }
247      }
248    
249      /**
250       * Adds a component to the layout. Not used in BoxLayout
251       *
252       * @param child The component to add to the layout.
253       * @param constraints The constraints for the component in the layout.
254       */
255      public void addLayoutComponent(Component child, Object constraints)
256      {
257        // Nothing to do here.
258      }
259    
260      /**
261       * Returns the alignment along the X axis for the container.
262       *
263       * @param parent The container that needs to be laid out.
264       *
265       * @return The alignment.
266       */
267      public float getLayoutAlignmentX(Container parent)
268      {
269        synchronized (container.getTreeLock())
270          {
271            if (container != parent)
272              throw new AWTError("BoxLayout can't be shared");
273    
274            checkTotalRequirements();
275            return xTotal.alignment;
276          }
277      }
278    
279      /**
280       * Returns the alignment along the Y axis for the container.
281       *
282       * @param parent The container that needs to be laid out.
283       *
284       * @return The alignment.
285       */
286      public float getLayoutAlignmentY(Container parent)
287      {
288        synchronized (container.getTreeLock())
289          {
290            if (container != parent)
291              throw new AWTError("BoxLayout can't be shared");
292    
293            checkTotalRequirements();
294            return yTotal.alignment;
295          }
296      }
297    
298      /**
299       * Invalidates the layout.
300       *
301       * @param parent The container that needs to be laid out.
302       */
303      public void invalidateLayout(Container parent)
304      {
305        if (container != parent)
306          throw new AWTError("BoxLayout can't be shared");
307    
308        synchronized (container.getTreeLock())
309          {
310            xChildren = null;
311            yChildren = null;
312            xTotal = null;
313            yTotal = null;
314            offsetsX = null;
315            offsetsY = null;
316            spansX = null;
317            spansY = null;
318          }
319      }
320    
321      /**
322       * Returns the maximum size of the layout gived the components
323       * in the given container.
324       *
325       * @param parent The container that needs to be laid out.
326       *
327       * @return The dimension of the layout.
328       */
329      public Dimension maximumLayoutSize(Container parent)
330      {
331        synchronized (container.getTreeLock())
332          {
333            if (container != parent)
334              throw new AWTError("BoxLayout can't be shared");
335    
336            checkTotalRequirements();
337            Insets i = container.getInsets();
338            int xDim = xTotal.maximum + i.left + i.right;
339            int yDim = yTotal.maximum + i.top + i.bottom;
340            
341            // Check for overflow
342            if (xDim < xTotal.maximum)
343              xDim = Integer.MAX_VALUE;
344            if (yDim < yTotal.maximum)
345              yDim = Integer.MAX_VALUE;
346            return new Dimension(xDim, yDim);
347          }
348      }
349    
350      /**
351       * Makes sure that the xTotal and yTotal fields are set up correctly. A call
352       * to {@link #invalidateLayout} sets these fields to null and they have to be
353       * recomputed.
354       */
355      private void checkTotalRequirements()
356      {
357        if (xTotal == null || yTotal == null)
358          {
359            checkRequirements();
360            if (isHorizontalIn(container))
361              {
362                xTotal = SizeRequirements.getTiledSizeRequirements(xChildren);
363                yTotal = SizeRequirements.getAlignedSizeRequirements(yChildren);
364              }
365            else
366              {
367                xTotal = SizeRequirements.getAlignedSizeRequirements(xChildren);
368                yTotal = SizeRequirements.getTiledSizeRequirements(yChildren);
369              }
370          }
371      }
372    
373      /**
374       * Makes sure that the xChildren and yChildren fields are correctly set up.
375       * A call to {@link #invalidateLayout(Container)} sets these fields to null,
376       * so they have to be set up again.
377       */
378      private void checkRequirements()
379      {
380        if (xChildren == null || yChildren == null)
381          {
382            Component[] children = container.getComponents();
383            xChildren = new SizeRequirements[children.length];
384            yChildren = new SizeRequirements[children.length];
385            for (int i = 0; i < children.length; i++)
386              {
387                if (! children[i].isVisible())
388                  {
389                    xChildren[i] = new SizeRequirements();
390                    yChildren[i] = new SizeRequirements();
391                  }
392                else
393                  {
394                    xChildren[i] =
395                      new SizeRequirements(children[i].getMinimumSize().width,
396                                           children[i].getPreferredSize().width,
397                                           children[i].getMaximumSize().width,
398                                           children[i].getAlignmentX());
399                    yChildren[i] =
400                      new SizeRequirements(children[i].getMinimumSize().height,
401                                           children[i].getPreferredSize().height,
402                                           children[i].getMaximumSize().height,
403                                           children[i].getAlignmentY());
404                  }
405              }
406          }
407      }
408    
409      /**
410       * Makes sure that the offsetsX, offsetsY, spansX and spansY fields are set
411       * up correctly. A call to {@link #invalidateLayout} sets these fields
412       * to null and they have to be recomputed.
413       */
414      private void checkLayout()
415      {
416        if (offsetsX == null || offsetsY == null || spansX == null
417            || spansY == null)
418          {
419            checkRequirements();
420            checkTotalRequirements();
421            int len = container.getComponents().length;
422            offsetsX = new int[len];
423            offsetsY = new int[len];
424            spansX = new int[len];
425            spansY = new int[len];
426    
427            Insets in = container.getInsets();
428            int width = container.getWidth() - in.left - in.right;
429            int height = container.getHeight() - in.top - in.bottom;
430    
431            if (isHorizontalIn(container))
432              {
433                SizeRequirements.calculateTiledPositions(width,
434                                                         xTotal, xChildren,
435                                                         offsetsX, spansX);
436                SizeRequirements.calculateAlignedPositions(height,
437                                                           yTotal, yChildren,
438                                                           offsetsY, spansY);
439              }
440            else
441              {
442                SizeRequirements.calculateAlignedPositions(width,
443                                                           xTotal, xChildren,
444                                                           offsetsX, spansX);
445                SizeRequirements.calculateTiledPositions(height,
446                                                         yTotal, yChildren,
447                                                         offsetsY, spansY);
448              }
449          }
450      }
451    }