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: if (target == null || target.ncomponents == 0) 229: return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); 230: // The JCL says that this returns Integer.MAX_VALUE for both 231: // dimensions. But that just seems wrong to me. 232: return getSize (target, MAX); 233: } 234: 235: /** 236: * Get the minimum layout size of the container. 237: * 238: * @param target The parent container 239: * 240: * @return the minimum layout size 241: */ 242: public Dimension minimumLayoutSize (Container target) 243: { 244: return getSize (target, MIN); 245: } 246: 247: /** 248: * Cause the next component in the container to be displayed. If 249: * this current card is the last one in the deck, the first 250: * component is displayed. 251: * 252: * @param parent The parent container, not <code>null</code>. 253: */ 254: public void next (Container parent) 255: { 256: gotoComponent (parent, NEXT); 257: } 258: 259: /** 260: * Get the preferred layout size of the container. 261: * 262: * @param parent The parent container 263: * 264: * @return the preferred layout size 265: */ 266: public Dimension preferredLayoutSize (Container parent) 267: { 268: return getSize (parent, PREF); 269: } 270: 271: /** 272: * Cause the previous component in the container to be displayed. 273: * If this current card is the first one in the deck, the last 274: * component is displayed. 275: * 276: * @param parent The parent container, not <code>null</code>. 277: */ 278: public void previous (Container parent) 279: { 280: gotoComponent (parent, PREV); 281: } 282: 283: /** 284: * Remove the indicated component from this layout manager. 285: * 286: * @param comp The component to remove 287: */ 288: public void removeLayoutComponent (Component comp) 289: { 290: Enumeration e = tab.keys (); 291: while (e.hasMoreElements ()) 292: { 293: Object key = e.nextElement (); 294: if (tab.get (key) == comp) 295: { 296: tab.remove (key); 297: Container parent = comp.getParent(); 298: next(parent); 299: break; 300: } 301: } 302: } 303: 304: /** 305: * Set this layout manager's horizontal gap. 306: * 307: * @param hgap The new gap 308: */ 309: public void setHgap (int hgap) 310: { 311: this.hgap = hgap; 312: } 313: 314: /** 315: * Set this layout manager's vertical gap. 316: * 317: * @param vgap The new gap 318: */ 319: public void setVgap (int vgap) 320: { 321: this.vgap = vgap; 322: } 323: 324: /** 325: * Cause the named component to be shown. If the component name is 326: * unknown or <code>null</code>, this method does nothing. 327: * 328: * @param parent The parent container, not <code>null</code>. 329: * @param name The name of the component to show 330: */ 331: public void show (Container parent, String name) 332: { 333: if (name == null) 334: return; 335: 336: if (parent.getLayout() != this) 337: throw new IllegalArgumentException("parent's layout is not this CardLayout"); 338: 339: Object target = tab.get (name); 340: if (target != null) 341: { 342: int num = parent.ncomponents; 343: // This is more efficient than calling getComponents(). 344: Component[] comps = parent.component; 345: for (int i = 0; i < num; ++i) 346: { 347: if (comps[i].isVisible()) 348: { 349: if (target == comps[i]) 350: return; 351: comps[i].setVisible (false); 352: } 353: } 354: ((Component) target).setVisible (true); 355: parent.validate(); 356: } 357: } 358: 359: /** 360: * Returns a string representation of this layout manager. 361: * 362: * @return A string representation of this object. 363: */ 364: public String toString () 365: { 366: return getClass ().getName () + "[hgap=" + hgap + ",vgap=" + vgap + "]"; 367: } 368: 369: /** 370: * This implements first(), last(), next(), and previous(). 371: * 372: * @param parent The parent container 373: * @param what The type of goto: FIRST, LAST, NEXT or PREV 374: * 375: * @throws IllegalArgumentException if parent has not this 376: * CardLayout set as its layout. 377: */ 378: private void gotoComponent (Container parent, int what) 379: { 380: if (parent.getLayout() != this) 381: throw new IllegalArgumentException("parent's layout is not this CardLayout"); 382: 383: synchronized (parent.getTreeLock ()) 384: { 385: int num = parent.ncomponents; 386: // This is more efficient than calling getComponents(). 387: Component[] comps = parent.component; 388: 389: if (num == 1) 390: { 391: comps[0].setVisible(true); 392: return; 393: } 394: 395: int choice = -1; 396: 397: if (what == FIRST) 398: choice = 0; 399: else if (what == LAST) 400: choice = num - 1; 401: 402: for (int i = 0; i < num; ++i) 403: { 404: if (comps[i].isVisible ()) 405: { 406: if (choice == i) 407: { 408: // Do nothing if we're already looking at the right 409: // component. 410: return; 411: } 412: else if (what == PREV) 413: { 414: choice = i - 1; 415: if (choice < 0) 416: choice = num - 1; 417: } 418: else if (what == NEXT) 419: { 420: choice = i + 1; 421: if (choice == num) 422: choice = 0; 423: } 424: comps[i].setVisible (false); 425: 426: if (choice >= 0) 427: break; 428: } else 429: { 430: comps[i].setVisible(true); 431: } 432: } 433: 434: if (choice >= 0 && choice < num) 435: comps[choice].setVisible (true); 436: } 437: } 438: 439: // Compute the size according to WHAT. 440: private Dimension getSize (Container parent, int what) 441: { 442: synchronized (parent.getTreeLock ()) 443: { 444: int w = 0, h = 0, num = parent.ncomponents; 445: Component[] comps = parent.component; 446: 447: for (int i = 0; i < num; ++i) 448: { 449: Dimension d; 450: 451: if (what == MIN) 452: d = comps[i].getMinimumSize (); 453: else if (what == MAX) 454: d = comps[i].getMaximumSize (); 455: else 456: d = comps[i].getPreferredSize (); 457: 458: w = Math.max (d.width, w); 459: h = Math.max (d.height, h); 460: } 461: 462: Insets i = parent.getInsets (); 463: w += 2 * hgap + i.right + i.left; 464: h += 2 * vgap + i.bottom + i.top; 465: 466: // Handle overflow. 467: if (w < 0) 468: w = Integer.MAX_VALUE; 469: if (h < 0) 470: h = Integer.MAX_VALUE; 471: 472: return new Dimension (w, h); 473: } 474: } 475: 476: /** 477: * @serial Horizontal gap value. 478: */ 479: private int hgap; 480: 481: /** 482: * @serial Vertical gap value. 483: */ 484: private int vgap; 485: 486: /** 487: * @serial Table of named components. 488: */ 489: private Hashtable tab; 490: 491: // These constants are used by the private gotoComponent method. 492: private static final int FIRST = 0; 493: private static final int LAST = 1; 494: private static final int NEXT = 2; 495: private static final int PREV = 3; 496: 497: // These constants are used by the private getSize method. 498: private static final int MIN = 0; 499: private static final int MAX = 1; 500: private static final int PREF = 2; 501: }