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 }