001: /*
002: * @(#)JideBorderLayout.java
003: *
004: * Copyright 2002 JIDE Software Inc. All rights reserved.
005:
006: * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
007: * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
008: */
009:
010: package com.jidesoft.swing;
011:
012: import java.awt.*;
013:
014: /**
015: * This is a modified version of <code>BorderLayout</code>. Different from
016: * <code>BorderLayout</code>, the TOP and BOTTOM component's width are same
017: * as CENTER compoennt's width. In BorderLayout, their width are the same
018: * as the container's width.
019: *
020: * @see BorderLayout
021: */
022: public class JideBorderLayout implements LayoutManager2,
023: java.io.Serializable {
024:
025: /**
026: * Constructs a border layout with the horizontal gaps
027: * between components.
028: * The horizontal gap is specified by <code>hgap</code>.
029: */
030: int hgap;
031:
032: /**
033: * Constructs a border layout with the vertical gaps
034: * between components.
035: * The vertical gap is specified by <code>vgap</code>.
036: */
037: int vgap;
038:
039: /**
040: * Constant to specify components location to be the
041: * north portion of the border layout.
042: *
043: * @serial
044: * @see #addLayoutComponent
045: * @see #removeLayoutComponent
046: */
047: Component north;
048: /**
049: * Constant to specify components location to be the
050: * west portion of the border layout.
051: *
052: * @serial
053: * @see #addLayoutComponent
054: * @see #removeLayoutComponent
055: */
056: Component west;
057: /**
058: * Constant to specify components location to be the
059: * east portion of the border layout.
060: *
061: * @serial
062: * @see #addLayoutComponent
063: * @see #removeLayoutComponent
064: */
065: Component east;
066: /**
067: * Constant to specify components location to be the
068: * south portion of the border layout.
069: *
070: * @serial
071: * @see #addLayoutComponent
072: * @see #removeLayoutComponent
073: */
074: Component south;
075: /**
076: * Constant to specify components location to be the
077: * center portion of the border layout.
078: *
079: * @serial
080: * @see #addLayoutComponent
081: * @see #removeLayoutComponent
082: */
083: Component center;
084:
085: /**
086: * A relative positioning constant, that can be used instead of
087: * north, south, east, west or center.
088: * mixing the two types of constants can lead to unpredicable results. If
089: * you use both types, the relative constants will take precedence.
090: * For example, if you add components using both the <code>NORTH</code>
091: * and <code>BEFORE_FIRST_LINE</code> constants in a container whose
092: * orientation is <code>LEFT_TO_RIGHT</code>, only the
093: * <code>BEFORE_FIRST_LINE</code> will be layed out.
094: * This will be the same for lastLine, firstItem, lastItem.
095: *
096: * @serial
097: */
098: Component firstLine;
099: /**
100: * A relative positioning constant, that can be used instead of
101: * north, south, east, west or center.
102: * Please read Description for firstLine.
103: *
104: * @serial
105: */
106: Component lastLine;
107: /**
108: * A relative positioning constant, that can be used instead of
109: * north, south, east, west or center.
110: * Please read Description for firstLine.
111: *
112: * @serial
113: */
114: Component firstItem;
115: /**
116: * A relative positioning constant, that can be used instead of
117: * north, south, east, west or center.
118: * Please read Description for firstLine.
119: *
120: * @serial
121: */
122: Component lastItem;
123:
124: /**
125: * The north layout constraint (top of container).
126: */
127: public static final String NORTH = "North";
128:
129: /**
130: * The south layout constraint (bottom of container).
131: */
132: public static final String SOUTH = "South";
133:
134: /**
135: * The east layout constraint (right side of container).
136: */
137: public static final String EAST = "East";
138:
139: /**
140: * The west layout constraint (left side of container).
141: */
142: public static final String WEST = "West";
143:
144: /**
145: * The center layout constraint (middle of container).
146: */
147: public static final String CENTER = "Center";
148:
149: /**
150: * Synonym for PAGE_START. Exists for compatibility with previous
151: * versions. PAGE_START is preferred.
152: *
153: * @see #PAGE_START
154: * @since 1.2
155: */
156: public static final String BEFORE_FIRST_LINE = "First";
157:
158: /**
159: * Synonym for PAGE_END. Exists for compatibility with previous
160: * versions. PAGE_END is preferred.
161: *
162: * @see #PAGE_END
163: * @since 1.2
164: */
165: public static final String AFTER_LAST_LINE = "Last";
166:
167: /**
168: * Synonym for LINE_START. Exists for compatibility with previous
169: * versions. LINE_START is preferred.
170: *
171: * @see #LINE_START
172: * @since 1.2
173: */
174: public static final String BEFORE_LINE_BEGINS = "Before";
175:
176: /**
177: * Synonym for LINE_END. Exists for compatibility with previous
178: * versions. LINE_END is preferred.
179: *
180: * @see #LINE_END
181: * @since 1.2
182: */
183: public static final String AFTER_LINE_ENDS = "After";
184:
185: /**
186: * The component comes before the first line of the layout's content.
187: * For Western, left-to-right and top-to-bottom orientations, this is
188: * equivalent to NORTH.
189: *
190: * @see Component#getComponentOrientation
191: * @since 1.4
192: */
193: public static final String PAGE_START = BEFORE_FIRST_LINE;
194:
195: /**
196: * The component comes after the last line of the layout's content.
197: * For Western, left-to-right and top-to-bottom orientations, this is
198: * equivalent to SOUTH.
199: *
200: * @see Component#getComponentOrientation
201: * @since 1.4
202: */
203: public static final String PAGE_END = AFTER_LAST_LINE;
204:
205: /**
206: * The component goes at the beginning of the line direction for the
207: * layout. For Western, left-to-right and top-to-bottom orientations,
208: * this is equivalent to WEST.
209: *
210: * @see Component#getComponentOrientation
211: * @since 1.4
212: */
213: public static final String LINE_START = BEFORE_LINE_BEGINS;
214:
215: /**
216: * The component goes at the end of the line direction for the
217: * layout. For Western, left-to-right and top-to-bottom orientations,
218: * this is equivalent to EAST.
219: *
220: * @see Component#getComponentOrientation
221: * @since 1.4
222: */
223: public static final String LINE_END = AFTER_LINE_ENDS;
224:
225: /*
226: * JDK 1.1 serialVersionUID
227: */
228: private static final long serialVersionUID = -8658291919501921765L;
229:
230: /**
231: * Constructs a new border layout with
232: * no gaps between components.
233: */
234: public JideBorderLayout() {
235: this (0, 0);
236: }
237:
238: /**
239: * Constructs a border layout with the specified gaps
240: * between components.
241: * The horizontal gap is specified by <code>hgap</code>
242: * and the vertical gap is specified by <code>vgap</code>.
243: *
244: * @param hgap the horizontal gap.
245: * @param vgap the vertical gap.
246: */
247: public JideBorderLayout(int hgap, int vgap) {
248: this .hgap = hgap;
249: this .vgap = vgap;
250: }
251:
252: /**
253: * Returns the horizontal gap between components.
254: *
255: * @return the horizontal gap between components
256: */
257: public int getHgap() {
258: return hgap;
259: }
260:
261: /**
262: * Sets the horizontal gap between components.
263: *
264: * @param hgap the horizontal gap between components
265: * @since JDK1.1
266: */
267: public void setHgap(int hgap) {
268: this .hgap = hgap;
269: }
270:
271: /**
272: * Returns the vertical gap between components.
273: *
274: * @return the vertical gap between components
275: */
276: public int getVgap() {
277: return vgap;
278: }
279:
280: /**
281: * Sets the vertical gap between components.
282: *
283: * @param vgap the vertical gap between components
284: * @since JDK1.1
285: */
286: public void setVgap(int vgap) {
287: this .vgap = vgap;
288: }
289:
290: /**
291: * Adds the specified component to the layout, using the specified
292: * constraint object. For border layouts, the constraint must be
293: * one of the following constants: <code>NORTH</code>,
294: * <code>SOUTH</code>, <code>EAST</code>,
295: * <code>WEST</code>, or <code>CENTER</code>.
296: * <p/>
297: * Most applications do not call this method directly. This method
298: * is called when a component is added to a container using the
299: * <code>Container.add</code> method with the same argument types.
300: *
301: * @param comp the component to be added.
302: * @param constraints an object that specifies how and where
303: * the component is added to the layout.
304: * @throws IllegalArgumentException if the constraint object is not
305: * a string, or if it not one of the five specified
306: * constants.
307: * @see Container#add(Component,Object)
308: * @since JDK1.1
309: */
310: public void addLayoutComponent(Component comp, Object constraints) {
311: synchronized (comp.getTreeLock()) {
312: if ((constraints == null)
313: || (constraints instanceof String)) {
314: addLayoutComponent((String) constraints, comp);
315: } else {
316: throw new IllegalArgumentException(
317: "cannot add to layout: constraint must be a string (or null)");
318: }
319: }
320: }
321:
322: public void addLayoutComponent(String name, Component comp) {
323: synchronized (comp.getTreeLock()) {
324: /* Special case: treat null the same as "Center". */
325: if (name == null) {
326: name = "Center";
327: }
328:
329: /* Assign the component to one of the known regions of the layout.
330: */
331: if ("Center".equals(name)) {
332: center = comp;
333: } else if ("North".equals(name)) {
334: north = comp;
335: } else if ("South".equals(name)) {
336: south = comp;
337: } else if ("East".equals(name)) {
338: east = comp;
339: } else if ("West".equals(name)) {
340: west = comp;
341: } else if (BEFORE_FIRST_LINE.equals(name)) {
342: firstLine = comp;
343: } else if (AFTER_LAST_LINE.equals(name)) {
344: lastLine = comp;
345: } else if (BEFORE_LINE_BEGINS.equals(name)) {
346: firstItem = comp;
347: } else if (AFTER_LINE_ENDS.equals(name)) {
348: lastItem = comp;
349: } else {
350: throw new IllegalArgumentException(
351: "cannot add to layout: unknown constraint: "
352: + name);
353: }
354: }
355: }
356:
357: /**
358: * Removes the specified component from this border layout. This
359: * method is called when a container calls its <code>remove</code> or
360: * <code>removeAll</code> methods. Most applications do not call this
361: * method directly.
362: *
363: * @param comp the component to be removed.
364: * @see Container#remove(Component)
365: * @see Container#removeAll()
366: */
367: public void removeLayoutComponent(Component comp) {
368: synchronized (comp.getTreeLock()) {
369: if (comp == center) {
370: center = null;
371: } else if (comp == north) {
372: north = null;
373: } else if (comp == south) {
374: south = null;
375: } else if (comp == east) {
376: east = null;
377: } else if (comp == west) {
378: west = null;
379: }
380: if (comp == firstLine) {
381: firstLine = null;
382: } else if (comp == lastLine) {
383: lastLine = null;
384: } else if (comp == firstItem) {
385: firstItem = null;
386: } else if (comp == lastItem) {
387: lastItem = null;
388: }
389: }
390: }
391:
392: /**
393: * Determines the minimum size of the <code>target</code> container
394: * using this layout manager.
395: * <p/>
396: * This method is called when a container calls its
397: * <code>getMinimumSize</code> method. Most applications do not call
398: * this method directly.
399: *
400: * @param target the container in which to do the layout.
401: * @return the minimum dimensions needed to lay out the subcomponents
402: * of the specified container.
403: * @see Container
404: * @see Container#getMinimumSize()
405: */
406: public Dimension minimumLayoutSize(Container target) {
407: synchronized (target.getTreeLock()) {
408: Dimension dim = new Dimension(0, 0);
409:
410: boolean ltr = target.getComponentOrientation()
411: .isLeftToRight();
412: Component c;
413:
414: if ((c = getChild(CENTER, ltr)) != null) {
415: Dimension d = c.getMinimumSize();
416: dim.width += d.width;
417: dim.height = Math.max(d.height, dim.height);
418: }
419: if ((c = getChild(NORTH, ltr)) != null) {
420: Dimension d = c.getMinimumSize();
421: dim.width = Math.max(d.width, dim.width);
422: dim.height += d.height + vgap;
423: }
424: if ((c = getChild(SOUTH, ltr)) != null) {
425: Dimension d = c.getMinimumSize();
426: dim.width = Math.max(d.width, dim.width);
427: dim.height += d.height + vgap;
428: }
429: if ((c = getChild(EAST, ltr)) != null) {
430: Dimension d = c.getMinimumSize();
431: dim.width += d.width + hgap;
432: dim.height = Math.max(d.height, dim.height);
433: }
434: if ((c = getChild(WEST, ltr)) != null) {
435: Dimension d = c.getMinimumSize();
436: dim.width += d.width + hgap;
437: dim.height = Math.max(d.height, dim.height);
438: }
439:
440: Insets insets = target.getInsets();
441: dim.width += insets.left + insets.right;
442: dim.height += insets.top + insets.bottom;
443:
444: return dim;
445: }
446: }
447:
448: /**
449: * Determines the preferred size of the <code>target</code>
450: * container using this layout manager, based on the components
451: * in the container.
452: * <p/>
453: * Most applications do not call this method directly. This method
454: * is called when a container calls its <code>getPreferredSize</code>
455: * method.
456: *
457: * @param target the container in which to do the layout.
458: * @return the preferred dimensions to lay out the subcomponents
459: * of the specified container.
460: * @see Container
461: * @see Container#getPreferredSize()
462: */
463: public Dimension preferredLayoutSize(Container target) {
464: synchronized (target.getTreeLock()) {
465: Dimension dim = new Dimension(0, 0);
466:
467: boolean ltr = target.getComponentOrientation()
468: .isLeftToRight();
469: Component c;
470:
471: if ((c = getChild(CENTER, ltr)) != null) {
472: Dimension d = c.getPreferredSize();
473: dim.width += d.width;
474: dim.height = Math.max(d.height, dim.height);
475: }
476: if ((c = getChild(NORTH, ltr)) != null) {
477: Dimension d = c.getPreferredSize();
478: dim.width = Math.max(d.width, dim.width);
479: dim.height += d.height + vgap;
480: }
481: if ((c = getChild(SOUTH, ltr)) != null) {
482: Dimension d = c.getPreferredSize();
483: dim.width = Math.max(d.width, dim.width);
484: dim.height += d.height + vgap;
485: }
486: if ((c = getChild(EAST, ltr)) != null) {
487: Dimension d = c.getPreferredSize();
488: dim.width += d.width + hgap;
489: dim.height = Math.max(d.height, dim.height);
490: }
491: if ((c = getChild(WEST, ltr)) != null) {
492: Dimension d = c.getPreferredSize();
493: dim.width += d.width + hgap;
494: dim.height = Math.max(d.height, dim.height);
495: }
496:
497: Insets insets = target.getInsets();
498: dim.width += insets.left + insets.right;
499: dim.height += insets.top + insets.bottom;
500:
501: return dim;
502: }
503: }
504:
505: /**
506: * Returns the maximum dimensions for this layout given the components
507: * in the specified target container.
508: *
509: * @param target the component which needs to be laid out
510: * @see Container
511: * @see #minimumLayoutSize
512: * @see #preferredLayoutSize
513: */
514: public Dimension maximumLayoutSize(Container target) {
515: return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
516: }
517:
518: /**
519: * Returns the alignment along the x axis. This specifies how
520: * the component would like to be aligned relative to other
521: * components. The value should be a number between 0 and 1
522: * where 0 represents alignment along the origin, 1 is aligned
523: * the furthest away from the origin, 0.5 is centered, etc.
524: */
525: public float getLayoutAlignmentX(Container parent) {
526: return 0.5f;
527: }
528:
529: /**
530: * Returns the alignment along the y axis. This specifies how
531: * the component would like to be aligned relative to other
532: * components. The value should be a number between 0 and 1
533: * where 0 represents alignment along the origin, 1 is aligned
534: * the furthest away from the origin, 0.5 is centered, etc.
535: */
536: public float getLayoutAlignmentY(Container parent) {
537: return 0.5f;
538: }
539:
540: /**
541: * Invalidates the layout, indicating that if the layout manager
542: * has cached information it should be discarded.
543: */
544: public void invalidateLayout(Container target) {
545: }
546:
547: /**
548: * Lays out the container argument using this border layout.
549: * <p/>
550: * This method actually reshapes the components in the specified
551: * container in order to satisfy the constraints of this
552: * <code>BorderLayout</code> object. The <code>NORTH</code>
553: * and <code>SOUTH</code> components, if any, are placed at
554: * the top and bottom of the container, respectively. The
555: * <code>WEST</code> and <code>EAST</code> components are
556: * then placed on the left and right, respectively. Finally,
557: * the <code>CENTER</code> object is placed in any remaining
558: * space in the middle.
559: * <p/>
560: * Most applications do not call this method directly. This method
561: * is called when a container calls its <code>doLayout</code> method.
562: *
563: * @param target the container in which to do the layout.
564: * @see Container
565: * @see Container#doLayout()
566: */
567: public void layoutContainer(Container target) {
568: synchronized (target.getTreeLock()) {
569: Insets insets = target.getInsets();
570: int top = insets.top;
571: int bottom = target.getHeight() - insets.bottom;
572: int left = insets.left;
573: int right = target.getWidth() - insets.right;
574:
575: boolean ltr = target.getComponentOrientation()
576: .isLeftToRight();
577:
578: Component north = getChild(NORTH, ltr);
579: Component south = getChild(SOUTH, ltr);
580: Component east = getChild(EAST, ltr);
581: Component west = getChild(WEST, ltr);
582: Component center = getChild(CENTER, ltr);
583:
584: /*
585: * Include the hgap here since if the east or west components
586: * do not exist then there should be no gap applied
587: */
588: int westGap = (west != null ? west.getPreferredSize().width
589: + hgap : 0);
590: int eastGap = (east != null ? east.getPreferredSize().width
591: + hgap : 0);
592:
593: if (north != null) {
594: // north.setSize(right - left - westGap - eastGap/* - 2 * hgap*/, north.getHeight());
595: Dimension d = north.getPreferredSize();
596: north.setBounds(left + westGap/* + hgap*/, top, right
597: - left - westGap - eastGap/* - 2 * hgap*/,
598: d.height);
599: top += d.height + vgap;
600: }
601: if (south != null) {
602: // south.setSize(right - left - westGap - eastGap/* - 2 * hgap*/, south.getHeight());
603: Dimension d = south.getPreferredSize();
604: south
605: .setBounds(left + westGap/* + hgap*/, bottom
606: - d.height, right - left - westGap
607: - eastGap/* - 2 * hgap*/, d.height);
608: bottom -= d.height + vgap;
609: }
610: if (east != null) {
611: east.setSize(east.getWidth(), bottom - top);
612: Dimension d = east.getPreferredSize();
613: east.setBounds(right - d.width, top, d.width, bottom
614: - top);
615: right -= d.width + hgap;
616: }
617: if (west != null) {
618: west.setSize(west.getWidth(), bottom - top);
619: Dimension d = west.getPreferredSize();
620: west.setBounds(left, top, d.width, bottom - top);
621: left += d.width + hgap;
622: }
623: if (center != null) {
624: center.setBounds(left, top, right - left, bottom - top);
625: }
626: }
627: }
628:
629: /**
630: * Get the component that corresponds to the given constraint location
631: *
632: * @param key The desired absolute position,
633: * either NORTH, SOUTH, EAST, or WEST.
634: * @param ltr Is the component line direction left-to-right?
635: * @return the child component.
636: */
637: private Component getChild(String key, boolean ltr) {
638: Component result = null;
639:
640: if (key.equals(NORTH)) {
641: result = (firstLine != null) ? firstLine : north;
642: } else if (key.equals(SOUTH)) {
643: result = (lastLine != null) ? lastLine : south;
644: } else if (key.equals(WEST)) {
645: result = ltr ? firstItem : lastItem;
646: if (result == null) {
647: result = west;
648: }
649: } else if (key.equals(EAST)) {
650: result = ltr ? lastItem : firstItem;
651: if (result == null) {
652: result = east;
653: }
654: } else if (key.equals(CENTER)) {
655: result = center;
656: }
657: if (result != null && !result.isVisible()) {
658: result = null;
659: }
660: return result;
661: }
662:
663: /**
664: * Returns a string representation of the state of this border layout.
665: *
666: * @return a string representation of this border layout.
667: */
668: @Override
669: public String toString() {
670: return getClass().getName() + "[hgap=" + hgap + ",vgap=" + vgap
671: + "]";
672: }
673: }
|