Frames | No Frames |
1: /* CardLayout.java -- Card-based layout engine 2: Copyright (C) 1999, 2000, 2002, 2003, 2004 Free Software Foundation 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: 39: package java.awt; 40: 41: import java.io.Serializable; 42: import java.util.Enumeration; 43: import java.util.Hashtable; 44: 45: /** 46: * This class implements a card-based layout scheme. Each included 47: * component is treated as a card. Only one card can be shown at a 48: * time. This class includes methods for changing which card is 49: * shown. 50: * 51: * @author Tom Tromey (tromey@redhat.com) 52: * @author Aaron M. Renn (arenn@urbanophile.com) 53: */ 54: public class CardLayout implements LayoutManager2, Serializable 55: { 56: private static final long serialVersionUID = -4328196481005934313L; 57: 58: /** 59: * Initializes a new instance of <code>CardLayout</code> with horizontal 60: * and vertical gaps of 0. 61: */ 62: public CardLayout () 63: { 64: this (0, 0); 65: } 66: 67: /** 68: * Create a new <code>CardLayout</code> object with the specified 69: * horizontal and vertical gaps. 70: * 71: * @param hgap The horizontal gap 72: * @param vgap The vertical gap 73: */ 74: public CardLayout (int hgap, int vgap) 75: { 76: this.hgap = hgap; 77: this.vgap = vgap; 78: this.tab = new Hashtable (); 79: } 80: 81: /** 82: * Add a new component to the layout. The constraint must be a 83: * string which is used to name the component. This string can 84: * later be used to refer to the particular component. 85: * 86: * @param comp The component to add 87: * @param constraints The name by which the component can later be called 88: * 89: * @exception IllegalArgumentException If `constraints' is not a 90: * <code>String</code> 91: */ 92: public void addLayoutComponent (Component comp, Object constraints) 93: { 94: if (! (constraints instanceof String)) 95: throw new IllegalArgumentException ("Object " + constraints 96: + " is not a string"); 97: addLayoutComponent ((String) constraints, comp); 98: } 99: 100: /** 101: * Add a new component to the layout. The name can be used later 102: * to refer to the component. 103: * 104: * @param name The name by which the component can later be called 105: * @param comp The component to add 106: * 107: * @deprecated This method is deprecated in favor of 108: * <code>addLayoutComponent(Component, Object)</code>. 109: */ 110: public void addLayoutComponent (String name, Component comp) 111: { 112: tab.put (name, comp); 113: // First component added is the default component. 114: comp.setVisible(tab.size() == 1); 115: } 116: 117: /** 118: * Cause the first component in the container to be displayed. 119: * 120: * @param parent The parent container, not <code>null</code>. 121: */ 122: public void first (Container parent) 123: { 124: gotoComponent (parent, FIRST); 125: } 126: 127: /** 128: * Return this layout manager's horizontal gap. 129: * 130: * @return the horizontal gap 131: */ 132: public int getHgap () 133: { 134: return hgap; 135: } 136: 137: /** 138: * Return this layout manager's x alignment. This method always 139: * returns Component.CENTER_ALIGNMENT. 140: * 141: * @param parent Container using this layout manager instance 142: * 143: * @return the x-axis alignment 144: */ 145: public float getLayoutAlignmentX (Container parent) 146: { 147: return Component.CENTER_ALIGNMENT; 148: } 149: 150: /** 151: * Returns this layout manager's y alignment. This method always 152: * returns Component.CENTER_ALIGNMENT. 153: * 154: * @param parent Container using this layout manager instance 155: * 156: * @return the y-axis alignment 157: */ 158: public float getLayoutAlignmentY (Container parent) 159: { 160: return Component.CENTER_ALIGNMENT; 161: } 162: 163: /** 164: * Return this layout manager's vertical gap. 165: * 166: * @return the vertical gap 167: */ 168: public int getVgap () 169: { 170: return vgap; 171: } 172: 173: /** 174: * Invalidate this layout manager's state. 175: */ 176: public void invalidateLayout (Container target) 177: { 178: // Do nothing. 179: } 180: 181: /** 182: * Cause the last component in the container to be displayed. 183: * 184: * @param parent The parent container, not <code>null</code>. 185: */ 186: public void last (Container parent) 187: { 188: gotoComponent (parent, LAST); 189: } 190: 191: /** 192: * Lays out the container. This is done by resizing the child components 193: * to be the same size as the parent, less insets and gaps. 194: * 195: * @param parent The parent container. 196: */ 197: public void layoutContainer (Container parent) 198: { 199: synchronized (parent.getTreeLock ()) 200: { 201: int width = parent.width; 202: int height = parent.height; 203: 204: Insets ins = parent.getInsets (); 205: 206: int num = parent.ncomponents; 207: Component[] comps = parent.component; 208: 209: int x = ins.left + hgap; 210: int y = ins.top + vgap; 211: width = width - 2 * hgap - ins.left - ins.right; 212: height = height - 2 * vgap - ins.top - ins.bottom; 213: 214: for (int i = 0; i < num; ++i) 215: comps[i].setBounds (x, y, width, height); 216: } 217: } 218: 219: /** 220: * Get the maximum layout size of the container. 221: * 222: * @param target The parent container 223: * 224: * @return the maximum layout size 225: */ 226: public Dimension maximumLayoutSize (Container target) 227: { 228: // The JCL says that this returns Integer.MAX_VALUE for both 229: // dimensions. But that just seems wrong to me. 230: return getSize (target, MAX); 231: } 232: 233: /** 234: * Get the minimum layout size of the container. 235: * 236: * @param target The parent container 237: * 238: * @return the minimum layout size 239: */ 240: public Dimension minimumLayoutSize (Container target) 241: { 242: return getSize (target, MIN); 243: } 244: 245: /** 246: * Cause the next component in the container to be displayed. If 247: * this current card is the last one in the deck, the first 248: * component is displayed. 249: * 250: * @param parent The parent container, not <code>null</code>. 251: */ 252: public void next (Container parent) 253: { 254: gotoComponent (parent, NEXT); 255: } 256: 257: /** 258: * Get the preferred layout size of the container. 259: * 260: * @param parent The parent container 261: * 262: * @return the preferred layout size 263: */ 264: public Dimension preferredLayoutSize (Container parent) 265: { 266: return getSize (parent, PREF); 267: } 268: 269: /** 270: * Cause the previous component in the container to be displayed. 271: * If this current card is the first one in the deck, the last 272: * component is displayed. 273: * 274: * @param parent The parent container, not <code>null</code>. 275: */ 276: public void previous (Container parent) 277: { 278: gotoComponent (parent, PREV); 279: } 280: 281: /** 282: * Remove the indicated component from this layout manager. 283: * 284: * @param comp The component to remove 285: */ 286: public void removeLayoutComponent (Component comp) 287: { 288: Enumeration e = tab.keys (); 289: while (e.hasMoreElements ()) 290: { 291: Object key = e.nextElement (); 292: if (tab.get (key) == comp) 293: { 294: tab.remove (key); 295: Container parent = comp.getParent(); 296: next(parent); 297: break; 298: } 299: } 300: } 301: 302: /** 303: * Set this layout manager's horizontal gap. 304: * 305: * @param hgap The new gap 306: */ 307: public void setHgap (int hgap) 308: { 309: this.hgap = hgap; 310: } 311: 312: /** 313: * Set this layout manager's vertical gap. 314: * 315: * @param vgap The new gap 316: */ 317: public void setVgap (int vgap) 318: { 319: this.vgap = vgap; 320: } 321: 322: /** 323: * Cause the named component to be shown. If the component name is 324: * unknown or <code>null</code>, this method does nothing. 325: * 326: * @param parent The parent container, not <code>null</code>. 327: * @param name The name of the component to show 328: */ 329: public void show (Container parent, String name) 330: { 331: if (name == null) 332: return; 333: 334: if (parent.getLayout() != this) 335: throw new IllegalArgumentException("parent's layout is not this CardLayout"); 336: 337: Object target = tab.get (name); 338: if (target != null) 339: { 340: int num = parent.ncomponents; 341: // This is more efficient than calling getComponents(). 342: Component[] comps = parent.component; 343: for (int i = 0; i < num; ++i) 344: { 345: if (comps[i].isVisible()) 346: { 347: if (target == comps[i]) 348: return; 349: comps[i].setVisible (false); 350: } 351: } 352: ((Component) target).setVisible (true); 353: parent.validate(); 354: } 355: } 356: 357: /** 358: * Returns a string representation of this layout manager. 359: * 360: * @return A string representation of this object. 361: */ 362: public String toString () 363: { 364: return getClass ().getName () + "[" + hgap + "," + vgap + "]"; 365: } 366: 367: /** 368: * This implements first(), last(), next(), and previous(). 369: * 370: * @param parent The parent container 371: * @param what The type of goto: FIRST, LAST, NEXT or PREV 372: * 373: * @throws IllegalArgumentException if parent has not this 374: * CardLayout set as its layout. 375: */ 376: private void gotoComponent (Container parent, int what) 377: { 378: if (parent.getLayout() != this) 379: throw new IllegalArgumentException("parent's layout is not this CardLayout"); 380: 381: synchronized (parent.getTreeLock ()) 382: { 383: int num = parent.ncomponents; 384: // This is more efficient than calling getComponents(). 385: Component[] comps = parent.component; 386: 387: if (num == 1) 388: { 389: comps[0].setVisible(true); 390: return; 391: } 392: 393: int choice = -1; 394: 395: if (what == FIRST) 396: choice = 0; 397: else if (what == LAST) 398: choice = num - 1; 399: 400: for (int i = 0; i < num; ++i) 401: { 402: if (comps[i].isVisible ()) 403: { 404: if (what == NEXT) 405: { 406: choice = i + 1; 407: if (choice == num) 408: choice = 0; 409: } 410: else if (what == PREV) 411: { 412: choice = i - 1; 413: if (choice < 0) 414: choice = num - 1; 415: } 416: else if (choice == i) 417: { 418: // Do nothing if we're already looking at the right 419: // component. 420: return; 421: } 422: comps[i].setVisible (false); 423: 424: if (choice >= 0) 425: break; 426: } 427: } 428: 429: if (choice >= 0 && choice < num) 430: comps[choice].setVisible (true); 431: } 432: } 433: 434: // Compute the size according to WHAT. 435: private Dimension getSize (Container parent, int what) 436: { 437: synchronized (parent.getTreeLock ()) 438: { 439: int w = 0, h = 0, num = parent.ncomponents; 440: Component[] comps = parent.component; 441: 442: for (int i = 0; i < num; ++i) 443: { 444: Dimension d; 445: 446: if (what == MIN) 447: d = comps[i].getMinimumSize (); 448: else if (what == MAX) 449: d = comps[i].getMaximumSize (); 450: else 451: d = comps[i].getPreferredSize (); 452: 453: w = Math.max (d.width, w); 454: h = Math.max (d.height, h); 455: } 456: 457: Insets i = parent.getInsets (); 458: w += 2 * hgap + i.right + i.left; 459: h += 2 * vgap + i.bottom + i.top; 460: 461: // Handle overflow. 462: if (w < 0) 463: w = Integer.MAX_VALUE; 464: if (h < 0) 465: h = Integer.MAX_VALUE; 466: 467: return new Dimension (w, h); 468: } 469: } 470: 471: /** 472: * @serial Horizontal gap value. 473: */ 474: private int hgap; 475: 476: /** 477: * @serial Vertical gap value. 478: */ 479: private int vgap; 480: 481: /** 482: * @serial Table of named components. 483: */ 484: private Hashtable tab; 485: 486: // These constants are used by the private gotoComponent method. 487: private static final int FIRST = 0; 488: private static final int LAST = 1; 489: private static final int NEXT = 2; 490: private static final int PREV = 3; 491: 492: // These constants are used by the private getSize method. 493: private static final int MIN = 0; 494: private static final int MAX = 1; 495: private static final int PREF = 2; 496: }