View Javadoc

1   /*
2    * Copyright 2006-2016 The JGUIraffe Team.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License")
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package net.sf.jguiraffe.gui.layout;
17  
18  import java.util.Locale;
19  
20  /**
21   * <p>
22   * A GUI library independent implementation of the AWT layout manager
23   * <em>BorderLayout</em>.
24   * </p>
25   * <p>
26   * This layout manager implements the <code>BorderLayout</code> functionality
27   * based on the <code>PercentLayout</code> layout manager. Because of that it
28   * can be used for all platforms for which an adapter is available.
29   * </p>
30   * <p>
31   * This implementation differs from the original <code>BorderLayout</code> in
32   * only a few points: Margins can be defined around the hosting container and
33   * individual gaps are supported between all hosted components. These margins
34   * and gaps can be specified using all supported units.
35   * </p>
36   *
37   * @author Oliver Heger
38   * @version $Id: BorderLayout.java 205 2012-01-29 18:29:57Z oheger $
39   */
40  public class BorderLayout extends PercentLayoutBase
41  {
42      /** Constant for the layout constraints <em>North</em>. */
43      public static final String NORTH = "NORTH";
44  
45      /** Constant for the layout constraints <em>East</em>. */
46      public static final String EAST = "EAST";
47  
48      /** Constant for the layout constraints <em>South</em>. */
49      public static final String SOUTH = "SOUTH";
50  
51      /** Constant for the layout constraints <em>West</em>. */
52      public static final String WEST = "WEST";
53  
54      /** Constant for the layout constraints <em>Center</em>. */
55      public static final String CENTER = "CENTER";
56  
57      /**
58       * The serial version UID.
59       */
60      private static final long serialVersionUID = 20090730L;
61  
62      /** Constant for the number of columns and rows. */
63      private static final int SIZE = 7;
64  
65      /** Constant for the index of the 1st cell. */
66      private static final int IDX_1 = 1;
67  
68      /** Constant for the index of the 2nd cell. */
69      private static final int IDX_2 = 2;
70  
71      /** Constant for the index of the 3rd cell. */
72      private static final int IDX_3 = 3;
73  
74      /** Constant for the index of the 4th cell. */
75      private static final int IDX_4 = 4;
76  
77      /** Constant for the index of the 5th cell. */
78      private static final int IDX_5 = 5;
79  
80      /** Constant for the index of the 6th cell. */
81      private static final int IDX_6 = 6;
82  
83      /** Constant for the maximum weight factor. */
84      private static final int WEIGHT_FULL = 100;
85  
86      /** A builder for percent data objects. */
87      private final PercentData.Builder pcb;
88  
89      /** Stores the top margin. */
90      private NumberWithUnit topMargin;
91  
92      /** Stores the left margin. */
93      private NumberWithUnit leftMargin;
94  
95      /** Stores the bottom margin. */
96      private NumberWithUnit bottomMargin;
97  
98      /** Stores the right margin. */
99      private NumberWithUnit rightMargin;
100 
101     /** Stores the north gap. */
102     private NumberWithUnit northGap;
103 
104     /** Stores the east gap. */
105     private NumberWithUnit eastGap;
106 
107     /** Stores the south gap. */
108     private NumberWithUnit southGap;
109 
110     /** Stores the west gap. */
111     private NumberWithUnit westGap;
112 
113     /**
114      * Creates a new instance of {@code BorderLayout}.
115      */
116     public BorderLayout()
117     {
118         pcb = new PercentData.Builder();
119     }
120 
121     /**
122      * Returns the bottom margin.
123      *
124      * @return the bottom margin
125      */
126     public NumberWithUnit getBottomMargin()
127     {
128         return NumberWithUnit.nonNull(bottomMargin);
129     }
130 
131     /**
132      * Sets the bottom margin.
133      *
134      * @param bottomMargin the bottom margin
135      */
136     public void setBottomMargin(NumberWithUnit bottomMargin)
137     {
138         this.bottomMargin = bottomMargin;
139     }
140 
141     /**
142      * Returns the east gap.
143      *
144      * @return the east gap
145      */
146     public NumberWithUnit getEastGap()
147     {
148         return NumberWithUnit.nonNull(eastGap);
149     }
150 
151     /**
152      * Sets the east gap. This is a gap between the center and the east
153      * component.
154      *
155      * @param eastGap the east gap
156      */
157     public void setEastGap(NumberWithUnit eastGap)
158     {
159         this.eastGap = eastGap;
160     }
161 
162     /**
163      * Returns the left margin.
164      *
165      * @return the left margin
166      */
167     public NumberWithUnit getLeftMargin()
168     {
169         return NumberWithUnit.nonNull(leftMargin);
170     }
171 
172     /**
173      * Sets the left margin.
174      *
175      * @param leftMargin the left margin
176      */
177     public void setLeftMargin(NumberWithUnit leftMargin)
178     {
179         this.leftMargin = leftMargin;
180     }
181 
182     /**
183      * Returns the north gap.
184      *
185      * @return the north gap
186      */
187     public NumberWithUnit getNorthGap()
188     {
189         return NumberWithUnit.nonNull(northGap);
190     }
191 
192     /**
193      * Sets the north gap. This is a gap between the north and the center
194      * component.
195      *
196      * @param northGap the north gap
197      */
198     public void setNorthGap(NumberWithUnit northGap)
199     {
200         this.northGap = northGap;
201     }
202 
203     /**
204      * Returns the right margin.
205      *
206      * @return the right margin
207      */
208     public NumberWithUnit getRightMargin()
209     {
210         return NumberWithUnit.nonNull(rightMargin);
211     }
212 
213     /**
214      * Sets the right margin.
215      *
216      * @param rightMargin the right margin
217      */
218     public void setRightMargin(NumberWithUnit rightMargin)
219     {
220         this.rightMargin = rightMargin;
221     }
222 
223     /**
224      * Returns the south gap.
225      *
226      * @return the south gap
227      */
228     public NumberWithUnit getSouthGap()
229     {
230         return NumberWithUnit.nonNull(southGap);
231     }
232 
233     /**
234      * Sets the south gap. This is a gap between the south and the center
235      * component.
236      *
237      * @param southGap the south gap
238      */
239     public void setSouthGap(NumberWithUnit southGap)
240     {
241         this.southGap = southGap;
242     }
243 
244     /**
245      * Returns the top margin.
246      *
247      * @return the top margin
248      */
249     public NumberWithUnit getTopMargin()
250     {
251         return NumberWithUnit.nonNull(topMargin);
252     }
253 
254     /**
255      * Sets the top margin.
256      *
257      * @param topMargin the top margin
258      */
259     public void setTopMargin(NumberWithUnit topMargin)
260     {
261         this.topMargin = topMargin;
262     }
263 
264     /**
265      * Returns the west gap.
266      *
267      * @return the west gap
268      */
269     public NumberWithUnit getWestGap()
270     {
271         return NumberWithUnit.nonNull(westGap);
272     }
273 
274     /**
275      * Sets the west gap. This is a gap between the west and the center
276      * component.
277      *
278      * @param westGap the west gap
279      */
280     public void setWestGap(NumberWithUnit westGap)
281     {
282         this.westGap = westGap;
283     }
284 
285     /**
286      * Initializes the percent layout. This implementation creates a layout with
287      * 7 columns and 7 rows and places the contained components in the
288      * appropriate cells according to their constraints.
289      *
290      * @param adapter the platform adapter
291      */
292     @Override
293     protected void initCells(PercentLayoutPlatformAdapter adapter)
294     {
295         initDimensions(SIZE, SIZE);
296         clearCells(SIZE, SIZE);
297 
298         int centerIndex = -1;
299         boolean north = false;
300         boolean west = false;
301         boolean south = false;
302         boolean east = false;
303 
304         for (int i = 0; i < adapter.getComponentCount(); i++)
305         {
306             Object o = adapter.getConstraints(i);
307             if (o == null)
308             {
309                 throw new IllegalStateException(
310                         "A constraints object must be defined for BorderLayout!");
311             }
312             String c = o.toString().toUpperCase(Locale.ENGLISH);
313             PercentData pd;
314 
315             if (NORTH.equals(c))
316             {
317                 pd = initHorizontalCell(IDX_1, west, east);
318                 north = true;
319             }
320             else if (SOUTH.equals(c))
321             {
322                 pd = initHorizontalCell(IDX_5, west, east);
323                 south = true;
324             }
325             else if (WEST.equals(c))
326             {
327                 pd = initVerticalCell(IDX_1, north, south);
328                 west = true;
329             }
330             else if (EAST.equals(c))
331             {
332                 pd = initVerticalCell(IDX_5, north, south);
333                 east = true;
334             }
335             else if (CENTER.equals(c))
336             {
337                 centerIndex = i;
338                 pd = null;
339             }
340 
341             else
342             {
343                 throw new IllegalStateException("Invalid constraints object: "
344                         + c);
345             }
346 
347             if (pd != null)
348             {
349                 initCell(adapter.getComponent(i), pd);
350             }
351         }
352 
353         if (centerIndex >= 0)
354         {
355             initCell(adapter.getComponent(centerIndex), initCenterCell(north,
356                     south, west, east));
357         }
358 
359         initConstraints(north, south, west, east);
360     }
361 
362     /**
363      * Initializes the column and row constraints for the percent layout. They
364      * depend on the occupied positions in the border layout.
365      *
366      * @param north flag whether the north position is occupied
367      * @param south flag whether the south position is occupied
368      * @param west flag whether the west position is occupied
369      * @param east flag whether the east position is occupied
370      */
371     protected void initConstraints(boolean north, boolean south, boolean west,
372             boolean east)
373     {
374         CellConstraints.Builder cBuilder = getConstraintsBuilder();
375 
376         setColumnConstraints(0, cBuilder.withMinimumSize(getLeftMargin())
377                 .create());
378         setColumnConstraints(IDX_1, cBuilder.withCellSize(CellSize.PREFERRED)
379                 .create());
380         setColumnConstraints(IDX_2, cBuilder.withMinimumSize(
381                 west ? getWestGap() : NumberWithUnit.ZERO).create());
382         setColumnConstraints(IDX_3, cBuilder.withCellSize(CellSize.PREFERRED)
383                 .withWeight(WEIGHT_FULL).create());
384         setColumnConstraints(IDX_4, cBuilder.withMinimumSize(
385                 east ? getEastGap() : NumberWithUnit.ZERO).create());
386         setColumnConstraints(IDX_5, cBuilder.withCellSize(CellSize.PREFERRED)
387                 .create());
388         setColumnConstraints(IDX_6, cBuilder.withMinimumSize(getRightMargin())
389                 .create());
390 
391         setRowConstraints(0, cBuilder.withMinimumSize(getTopMargin()).create());
392         setRowConstraints(IDX_1, cBuilder.withCellAlignment(CellAlignment.FULL)
393                 .withCellSize(CellSize.PREFERRED).create());
394         setRowConstraints(IDX_2, cBuilder.withMinimumSize(
395                 north ? getNorthGap() : NumberWithUnit.ZERO).create());
396         setRowConstraints(IDX_3, cBuilder.withCellAlignment(CellAlignment.FULL)
397                 .withCellSize(CellSize.PREFERRED).withWeight(WEIGHT_FULL).create());
398         setRowConstraints(IDX_4, cBuilder.withMinimumSize(
399                 south ? getSouthGap() : NumberWithUnit.ZERO).create());
400         setRowConstraints(IDX_5, cBuilder.withCellAlignment(CellAlignment.FULL)
401                 .withCellSize(CellSize.PREFERRED).create());
402         setRowConstraints(IDX_6, cBuilder.withMinimumSize(getBottomMargin())
403                 .create());
404     }
405 
406     /**
407      * Initializes a constraints object for the NORTH or SOUTH cell.
408      *
409      * @param row the row number
410      * @param west the west flag
411      * @param east the east flag
412      * @return the constraints
413      */
414     private PercentData initHorizontalCell(int row, boolean west, boolean east)
415     {
416         PercentData pd = pcb.xy(calcPosition(west, east), row).spanX(
417                 calcSpan(west, east)).withTargetColumn(IDX_3).create();
418         return pd;
419     }
420 
421     /**
422      * Initializes a constraints object for the WEST or EAST cell.
423      *
424      * @param col the column index
425      * @param north the north flag
426      * @param south the south flag
427      * @return the constraints
428      */
429     private PercentData initVerticalCell(int col, boolean north, boolean south)
430     {
431         PercentData pd = pcb.xy(col, calcPosition(north, south)).spanY(
432                 calcSpan(north, south)).withTargetRow(IDX_3).create();
433         return pd;
434     }
435 
436     /**
437      * Initializes a constraints object for the CENTER cell.
438      *
439      * @param north the north flag
440      * @param south the south flag
441      * @param west the west flag
442      * @param east the east flag
443      * @return the constraints
444      */
445     private PercentData initCenterCell(boolean north, boolean south,
446             boolean west, boolean east)
447     {
448         PercentData pd =
449                 pcb.xy(calcPosition(west, east), calcPosition(north, south))
450                         .span(calcSpan(east, west), calcSpan(north, south))
451                         .withTargetColumn(IDX_3).withTargetRow(IDX_3).create();
452         return pd;
453     }
454 
455     /**
456      * Determines the position of a component depending on already available
457      * components.
458      *
459      * @param f1 flag for the first adjacent component
460      * @param f2 flag for the second adjacent component
461      * @return the position
462      */
463     private static int calcPosition(boolean f1, boolean f2)
464     {
465         return f1 ? IDX_3 : IDX_1;
466     }
467 
468     /**
469      * Determines the span of a component depending on already available
470      * components.
471      *
472      * @param f1 flag for the first adjacent component
473      * @param f2 flag for the second adjacent component
474      * @return the span
475      */
476     private static int calcSpan(boolean f1, boolean f2)
477     {
478         int span = 1;
479         if (!f1)
480         {
481             span += 2;
482         }
483         if (!f2)
484         {
485             span += 2;
486         }
487         return span;
488     }
489 }