001    /* BorderLayout.java -- A layout manager class
002       Copyright (C) 1999, 2002, 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    
039    package java.awt;
040    
041    
042    /**
043      * This class implements a layout manager that positions components
044      * in certain sectors of the parent container.
045      *
046      * @author Aaron M. Renn (arenn@urbanophile.com)
047      * @author Rolf W. Rasmussen  (rolfwr@ii.uib.no)
048      */
049    public class BorderLayout implements LayoutManager2, java.io.Serializable
050    {
051    
052      /**
053       * Constant indicating the top of the container
054       */
055      public static final String NORTH = "North";
056    
057      /**
058       * Constant indicating the bottom of the container
059       */
060      public static final String SOUTH = "South";
061    
062      /**
063       * Constant indicating the right side of the container
064       */
065      public static final String EAST = "East";
066    
067      /**
068       * Constant indicating the left side of the container
069       */
070      public static final String WEST = "West";
071    
072      /**
073       * Constant indicating the center of the container
074       */
075      public static final String CENTER = "Center";
076    
077    
078      /**
079       * The constant indicating the position before the first line of the
080       * layout.  The exact position depends on the writing system: For a
081       * top-to-bottom orientation, it is the same as {@link #NORTH}, for
082       * a bottom-to-top orientation, it is the same as {@link #SOUTH}.
083       *
084       * <p>This constant is an older name for {@link #PAGE_START} which
085       * has exactly the same value.
086       *
087       * @since 1.2
088       */
089      public static final String BEFORE_FIRST_LINE = "First";
090    
091      /**
092       * The constant indicating the position after the last line of the
093       * layout.  The exact position depends on the writing system: For a
094       * top-to-bottom orientation, it is the same as {@link #SOUTH}, for
095       * a bottom-to-top orientation, it is the same as {@link #NORTH}.
096       *
097       * <p>This constant is an older name for {@link #PAGE_END} which
098       * has exactly the same value.
099       *
100       * @since 1.2
101       */
102      public static final String AFTER_LAST_LINE = "Last";
103    
104      /**
105       * The constant indicating the position before the first item of the
106       * layout.  The exact position depends on the writing system: For a
107       * left-to-right orientation, it is the same as {@link #WEST}, for
108       * a right-to-left orientation, it is the same as {@link #EAST}.
109       *
110       * <p>This constant is an older name for {@link #LINE_START} which
111       * has exactly the same value.
112       *
113       * @since 1.2
114       */
115      public static final String BEFORE_LINE_BEGINS = "Before";
116    
117      /**
118       * The constant indicating the position after the last item of the
119       * layout.  The exact position depends on the writing system: For a
120       * left-to-right orientation, it is the same as {@link #EAST}, for
121       * a right-to-left orientation, it is the same as {@link #WEST}.
122       *
123       * <p>This constant is an older name for {@link #LINE_END} which
124       * has exactly the same value.
125       *
126       * @since 1.2
127       */
128      public static final String AFTER_LINE_ENDS = "After";
129    
130      /**
131       * The constant indicating the position before the first line of the
132       * layout.  The exact position depends on the writing system: For a
133       * top-to-bottom orientation, it is the same as {@link #NORTH}, for
134       * a bottom-to-top orientation, it is the same as {@link #SOUTH}.
135       *
136       * @since 1.4
137       */
138      public static final String PAGE_START = BEFORE_FIRST_LINE;
139    
140      /**
141       * The constant indicating the position after the last line of the
142       * layout.  The exact position depends on the writing system: For a
143       * top-to-bottom orientation, it is the same as {@link #SOUTH}, for
144       * a bottom-to-top orientation, it is the same as {@link #NORTH}.
145       *
146       * @since 1.4
147       */
148      public static final String PAGE_END = AFTER_LAST_LINE;
149    
150      /**
151       * The constant indicating the position before the first item of the
152       * layout.  The exact position depends on the writing system: For a
153       * left-to-right orientation, it is the same as {@link #WEST}, for
154       * a right-to-left orientation, it is the same as {@link #EAST}.
155       *
156       * @since 1.4
157       */
158      public static final String LINE_START = BEFORE_LINE_BEGINS;
159    
160      /**
161       * The constant indicating the position after the last item of the
162       * layout.  The exact position depends on the writing system: For a
163       * left-to-right orientation, it is the same as {@link #EAST}, for
164       * a right-to-left orientation, it is the same as {@link #WEST}.
165       *
166       * @since 1.4
167       */
168      public static final String LINE_END = AFTER_LINE_ENDS;
169    
170    
171      /**
172       * Serialization constant.
173       */
174      private static final long serialVersionUID = -8658291919501921765L;
175    
176    
177      /**
178       * @serial
179       */
180      private Component north;
181    
182      /**
183       * @serial
184       */
185      private Component south;
186    
187      /**
188       * @serial
189       */
190      private Component east;
191    
192      /**
193       * @serial
194       */
195      private Component west;
196    
197      /**
198       * @serial
199      */
200      private Component center;
201    
202      /**
203       * @serial
204       */
205      private Component firstLine;
206    
207      /**
208       * @serial
209       */
210      private Component lastLine;
211    
212      /**
213       * @serial
214       */
215      private Component firstItem;
216    
217      /**
218       * @serial
219       */
220      private Component lastItem;
221    
222      /**
223       * @serial The horizontal gap between components
224       */
225      private int hgap;
226    
227      /**
228       * @serial The vertical gap between components
229       */
230      private int vgap;
231    
232    
233      // Some constants for use with calcSize().
234      private static final int MIN = 0;
235      private static final int MAX = 1;
236      private static final int PREF = 2;
237    
238    
239      /**
240       * Initializes a new instance of <code>BorderLayout</code> with no
241       * horiztonal or vertical gaps between components.
242       */
243      public BorderLayout()
244      {
245        this(0,0);
246      }
247    
248      /**
249       * Initializes a new instance of <code>BorderLayout</code> with the
250       * specified horiztonal and vertical gaps between components.
251       *
252       * @param hgap The horizontal gap between components.
253       * @param vgap The vertical gap between components.
254       */
255      public BorderLayout(int hgap, int vgap)
256      {
257        this.hgap = hgap;
258        this.vgap = vgap;
259      }
260    
261      /**
262       * Returns the horitzontal gap value.
263       *
264       * @return The horitzontal gap value.
265       */
266      public int getHgap()
267      {
268        return(hgap);
269      }
270    
271      /**
272       * Sets the horizontal gap to the specified value.
273       *
274       * @param hgap The new horizontal gap.
275       */
276      public void setHgap(int hgap)
277      {
278        this.hgap = hgap;
279      }
280    
281      /**
282       * Returns the vertical gap value.
283       *
284       * @return The vertical gap value.
285       */
286      public int getVgap()
287      {
288        return(vgap);
289      }
290    
291      /**
292       * Sets the vertical gap to the specified value.
293       *
294       * @param vgap The new vertical gap value.
295       */
296      public void setVgap(int vgap)
297      {
298        this.vgap = vgap;
299      }
300    
301      /**
302       * Adds a component to the layout in the specified constraint position, 
303       * which must be one of the string constants defined in this class.
304       *
305       * @param component The component to add.
306       * @param constraints The constraint string.
307       *
308       * @exception IllegalArgumentException If the constraint object is not
309       * a string, or is not one of the specified constants in this class.
310       */
311      public void addLayoutComponent(Component component, Object constraints)
312      {
313        if (constraints != null && ! (constraints instanceof String))
314          throw new IllegalArgumentException("Constraint must be a string");
315    
316        addLayoutComponent((String) constraints, component);
317      }
318    
319      /**
320       * Adds a component to the layout in the specified constraint position, 
321       * which must be one of the string constants defined in this class.
322       *
323       * @param constraints The constraint string.
324       * @param component The component to add.
325       *
326       * @exception IllegalArgumentException If the constraint object is not
327       *            one of the specified constants in this class.
328       *
329       * @deprecated This method is deprecated in favor of
330       *             <code>addLayoutComponent(Component, Object)</code>.
331       */
332      public void addLayoutComponent(String constraints, Component component)
333      {
334        String str = constraints;
335    
336        if (str == null || str.equals(CENTER))
337          center = component;
338        else if (str.equals(NORTH))
339          north = component;
340        else if (str.equals(SOUTH))
341          south = component;
342        else if (str.equals(EAST))
343          east = component;
344        else if (str.equals(WEST))
345          west = component;
346        else if (str.equals(BEFORE_FIRST_LINE))
347          firstLine = component;
348        else if (str.equals(AFTER_LAST_LINE))
349          lastLine = component;
350        else if (str.equals(BEFORE_LINE_BEGINS))
351          firstItem = component;
352        else if (str.equals(AFTER_LINE_ENDS))
353          lastItem = component;
354        else
355          throw new IllegalArgumentException("Constraint value not valid: " + str);
356      }
357    
358      /**
359       * Removes the specified component from the layout.
360       *
361       * @param component The component to remove from the layout.
362       */
363      public void removeLayoutComponent(Component component)
364      {
365        if (north == component)
366          north = null;
367        if (south == component)
368          south = null;
369        if (east == component)
370          east = null;
371        if (west == component)
372          west = null;
373        if (center == component)
374          center = null;
375        if (firstItem == component)
376          firstItem = null;
377        if (lastItem == component)
378          lastItem = null;
379        if (firstLine == component)
380          firstLine = null;
381        if (lastLine == component)
382          lastLine = null;
383      }
384    
385      /**
386       * Returns the minimum size of the specified container using this layout.
387       *
388       * @param target The container to calculate the minimum size for.
389       *
390       * @return The minimum size of the container
391       */
392      public Dimension minimumLayoutSize(Container target)
393      {
394        return calcSize(target, MIN);
395      }
396    
397      /**
398       * Returns the preferred size of the specified container using this layout.
399       *
400       * @param target The container to calculate the preferred size for.
401       *
402       * @return The preferred size of the container
403       */
404      public Dimension preferredLayoutSize(Container target)
405      {
406        return calcSize(target, PREF);
407      }
408    
409      /**
410       * Returns the maximum size of the specified container using this layout.
411       *
412       * @param target The container to calculate the maximum size for.
413       *
414       * @return The maximum size of the container
415       */
416      public Dimension maximumLayoutSize(Container target)
417      {
418        return new Dimension (Integer.MAX_VALUE, Integer.MAX_VALUE);
419      }
420    
421      /**
422       * Returns the X axis alignment, which is a <code>float</code> indicating
423       * where along the X axis this container wishs to position its layout.
424       * 0 indicates align to the left, 1 indicates align to the right, and 0.5
425       * indicates align to the center.
426       *
427       * @param parent The parent container.
428       *
429       * @return The X alignment value.
430       */
431      public float getLayoutAlignmentX(Container parent)
432      {
433        return 0.5F;
434      }
435    
436      /**
437       * Returns the Y axis alignment, which is a <code>float</code> indicating
438       * where along the Y axis this container wishs to position its layout.
439       * 0 indicates align to the top, 1 indicates align to the bottom, and 0.5
440       * indicates align to the center.
441       *
442       * @param parent The parent container.
443       *
444       * @return The Y alignment value.
445       */
446      public float getLayoutAlignmentY(Container parent)
447      {
448        return 0.5F;
449      }
450    
451      /**
452       * Instructs this object to discard any layout information it might
453       * have cached.
454       *
455       * @param parent The parent container.
456       */
457      public void invalidateLayout(Container parent)
458      {
459        // Nothing to do here.
460      }
461    
462      /**
463       * Lays out the specified container according to the constraints in this
464       * object.
465       * 
466       * @param target The container to lay out.
467       */
468      public void layoutContainer(Container target)
469      {
470        synchronized (target.getTreeLock())
471          {
472            Insets i = target.getInsets();
473            int top = i.top;
474            int bottom = target.height - i.bottom;
475            int left = i.left;
476            int right = target.width - i.right;
477    
478            boolean left_to_right = target.getComponentOrientation().isLeftToRight();
479    
480            Component my_north = north;
481            Component my_east = east;
482            Component my_south = south;
483            Component my_west = west;
484    
485            // Note that we currently don't handle vertical layouts.
486            // Neither does JDK 1.3.
487            if (firstLine != null)
488              my_north = firstLine;
489            if (lastLine != null)
490              my_south = lastLine;
491            if (firstItem != null)
492              {
493                if (left_to_right)
494                  my_west = firstItem;
495                else
496                  my_east = firstItem;
497              }
498            if (lastItem != null)
499              {
500                if (left_to_right)
501                  my_east = lastItem;
502                else
503                  my_west = lastItem;
504              }
505    
506            if (my_north != null)
507              {
508                Dimension n = calcCompSize(my_north, PREF);
509                my_north.setBounds(left, top, right - left, n.height);
510                top += n.height + vgap;
511              }
512    
513            if (my_south != null)
514              {
515                Dimension s = calcCompSize(my_south, PREF);
516                my_south.setBounds(left, bottom - s.height, right - left, s.height);
517                bottom -= s.height + vgap;
518              }
519    
520            if (my_east != null)
521              {
522                Dimension e = calcCompSize(my_east, PREF);
523                my_east.setBounds(right - e.width, top, e.width, bottom - top);
524                right -= e.width + hgap;
525              }
526    
527            if (my_west != null)
528              {
529                Dimension w = calcCompSize(my_west, PREF);
530                my_west.setBounds(left, top, w.width, bottom - top);
531                left += w.width + hgap;
532              }
533    
534            if (center != null)
535              center.setBounds(left, top, right - left, bottom - top);
536          }
537      }
538    
539      /**
540       * Returns a string representation of this layout manager.
541       * 
542       * @return A string representation of this object.
543       */
544      public String toString()
545      {
546        return getClass().getName() + "[hgap=" + hgap + ",vgap=" + vgap + "]";
547      }
548    
549      private Dimension calcCompSize(Component comp, int what)
550      {
551        if (comp == null || ! comp.isVisible())
552          return new Dimension(0, 0);
553        if (what == MIN)
554          return comp.getMinimumSize();
555        else if (what == MAX)
556          return comp.getMaximumSize();
557        return comp.getPreferredSize();
558      }
559    
560      /**
561       * This is a helper function used to compute the various sizes for this
562       * layout.
563       */
564      private Dimension calcSize(Container target, int what)
565      {
566        synchronized (target.getTreeLock())
567          {
568            Insets ins = target.getInsets();
569    
570            ComponentOrientation orient = target.getComponentOrientation ();
571            boolean left_to_right = orient.isLeftToRight ();
572    
573            Component my_north = north;
574            Component my_east = east;
575            Component my_south = south;
576            Component my_west = west;
577    
578            // Note that we currently don't handle vertical layouts.  Neither
579            // does JDK 1.3.
580            if (firstLine != null)
581              my_north = firstLine;
582            if (lastLine != null)
583              my_south = lastLine;
584            if (firstItem != null)
585              {
586                if (left_to_right)
587                  my_west = firstItem;
588                else
589                  my_east = firstItem;
590              }
591            if (lastItem != null)
592              {
593                if (left_to_right)
594                  my_east = lastItem;
595                else
596                  my_west = lastItem;
597              }
598    
599            Dimension ndim = calcCompSize(my_north, what);
600            Dimension sdim = calcCompSize(my_south, what);
601            Dimension edim = calcCompSize(my_east, what);
602            Dimension wdim = calcCompSize(my_west, what);
603            Dimension cdim = calcCompSize(center, what);
604    
605            int width = edim.width + cdim.width + wdim.width + (hgap * 2);
606            // Check for overflow.
607            if (width < edim.width || width < cdim.width || width < cdim.width)
608              width = Integer.MAX_VALUE;
609    
610            if (ndim.width > width)
611              width = ndim.width;
612            if (sdim.width > width)
613              width = sdim.width;
614    
615            width += (ins.left + ins.right);
616    
617            int height = edim.height;
618            if (cdim.height > height)
619              height = cdim.height;
620            if (wdim.height > height)
621              height = wdim.height;
622    
623            int addedHeight = height + (ndim.height + sdim.height + (vgap * 2)
624                                        + ins.top + ins.bottom);
625            // Check for overflow.
626            if (addedHeight < height)
627              height = Integer.MAX_VALUE;
628            else
629              height = addedHeight;
630    
631            return(new Dimension(width, height));
632          }
633      }
634    
635      /**
636       * Return the component at the indicated location, or null if no component
637       * is at that location.  The constraints argument must be one of the 
638       * location constants specified by this class.   
639       * @param constraints the location
640       * @return the component at that location, or null
641       * @throws IllegalArgumentException if the constraints argument is not 
642       * recognized
643       * @since 1.5
644       */
645      public Component getLayoutComponent(Object constraints)
646      {
647        if (constraints == CENTER)
648          return center;
649        if (constraints == NORTH)
650          return north;
651        if (constraints == EAST)
652          return east;
653        if (constraints == SOUTH)
654          return south;
655        if (constraints == WEST)
656          return west;
657        if (constraints == PAGE_START)
658          return firstLine;
659        if (constraints == PAGE_END)
660          return lastLine;
661        if (constraints == LINE_START)
662          return firstItem;
663        if (constraints == LINE_END)
664          return lastItem;
665        throw new IllegalArgumentException("constraint " + constraints 
666                                           + " is not recognized");
667      }
668    
669      /**
670       * Return the component at the specified location, which must be one
671       * of the absolute constants such as CENTER or SOUTH.  The container's
672       * orientation is used to map this location to the correct corresponding
673       * component, so for instance in a right-to-left container, a request
674       * for the EAST component could return the LINE_END component.  This will
675       * return null if no component is available at the given location.
676       * @param container the container whose orientation is used
677       * @param constraints the absolute location of the component
678       * @return the component at the location, or null
679       * @throws IllegalArgumentException if the constraint is not recognized
680       */
681      public Component getLayoutComponent(Container container, Object constraints)
682      {
683        ComponentOrientation orient = container.getComponentOrientation();
684        if (constraints == CENTER)
685          return center;
686        // Note that we don't support vertical layouts.
687        if (constraints == NORTH)
688          return north;
689        if (constraints == SOUTH)
690          return south;
691        if (constraints == WEST)
692          {
693            // Note that relative layout takes precedence.
694            if (orient.isLeftToRight())
695              return firstItem == null ? west : firstItem;
696            return lastItem == null ? west : lastItem;
697          }
698        if (constraints == EAST)
699          {
700            // Note that relative layout takes precedence.
701            if (orient.isLeftToRight())
702              return lastItem == null ? east : lastItem;
703            return firstItem == null ? east : firstItem;
704          }
705        throw new IllegalArgumentException("constraint " + constraints
706                                           + " is not recognized");
707      }
708    
709      /**
710       * Return the constraint corresponding to a component in this layout.
711       * If the component is null, or is not in this layout, returns null.
712       * Otherwise, this will return one of the constraint constants defined
713       * in this class.
714       * @param c the component
715       * @return the constraint, or null
716       * @since 1.5
717       */
718      public Object getConstraints(Component c)
719      {
720        if (c == null)
721          return null;
722        if (c == center)
723          return CENTER;
724        if (c == north)
725          return NORTH;
726        if (c == east)
727          return EAST;
728        if (c == south)
729          return SOUTH;
730        if (c == west)
731          return WEST;
732        if (c == firstLine)
733          return PAGE_START;
734        if (c == lastLine)
735          return PAGE_END;
736        if (c == firstItem)
737          return LINE_START;
738        if (c == lastItem)
739          return LINE_END;
740        return null;
741      }
742    }