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 */ CardLayout()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 */ CardLayout(int hgap, int vgap)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 */ addLayoutComponent(Component comp, Object constraints)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 */ addLayoutComponent(String name, Component comp)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 */ first(Container parent)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 */ getHgap()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 */ getLayoutAlignmentX(Container parent)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 */ getLayoutAlignmentY(Container parent)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 */ getVgap()168 public int getVgap () 169 { 170 return vgap; 171 } 172 173 /** 174 * Invalidate this layout manager's state. 175 */ invalidateLayout(Container target)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 */ last(Container parent)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 */ layoutContainer(Container parent)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 */ maximumLayoutSize(Container target)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 */ minimumLayoutSize(Container target)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 */ next(Container parent)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 */ preferredLayoutSize(Container parent)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 */ previous(Container parent)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 */ removeLayoutComponent(Component comp)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 */ setHgap(int hgap)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 */ setVgap(int vgap)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 */ show(Container parent, String name)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 */ toString()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 */ gotoComponent(Container parent, int what)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. getSize(Container parent, int 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 } 502