1 /* 2 * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.awt; 27 28 import java.util.Hashtable; 29 import java.util.Vector; 30 import java.util.Enumeration; 31 32 import java.io.Serializable; 33 import java.io.ObjectInputStream; 34 import java.io.ObjectOutputStream; 35 import java.io.ObjectStreamField; 36 import java.io.IOException; 37 38 /** 39 * A <code>CardLayout</code> object is a layout manager for a 40 * container. It treats each component in the container as a card. 41 * Only one card is visible at a time, and the container acts as 42 * a stack of cards. The first component added to a 43 * <code>CardLayout</code> object is the visible component when the 44 * container is first displayed. 45 * <p> 46 * The ordering of cards is determined by the container's own internal 47 * ordering of its component objects. <code>CardLayout</code> 48 * defines a set of methods that allow an application to flip 49 * through these cards sequentially, or to show a specified card. 50 * The {@link CardLayout#addLayoutComponent} 51 * method can be used to associate a string identifier with a given card 52 * for fast random access. 53 * 54 * @author Arthur van Hoff 55 * @see java.awt.Container 56 * @since JDK1.0 57 */ 58 59 public class CardLayout implements LayoutManager2, 60 Serializable { 61 62 private static final long serialVersionUID = -4328196481005934313L; 63 64 /* 65 * This creates a Vector to store associated 66 * pairs of components and their names. 67 * @see java.util.Vector 68 */ 69 Vector<Card> vector = new Vector<>(); 70 71 /* 72 * A pair of Component and String that represents its name. 73 */ 74 class Card implements Serializable { 75 static final long serialVersionUID = 6640330810709497518L; 76 public String name; 77 public Component comp; Card(String cardName, Component cardComponent)78 public Card(String cardName, Component cardComponent) { 79 name = cardName; 80 comp = cardComponent; 81 } 82 } 83 84 /* 85 * Index of Component currently displayed by CardLayout. 86 */ 87 int currentCard = 0; 88 89 90 /* 91 * A cards horizontal Layout gap (inset). It specifies 92 * the space between the left and right edges of a 93 * container and the current component. 94 * This should be a non negative Integer. 95 * @see getHgap() 96 * @see setHgap() 97 */ 98 int hgap; 99 100 /* 101 * A cards vertical Layout gap (inset). It specifies 102 * the space between the top and bottom edges of a 103 * container and the current component. 104 * This should be a non negative Integer. 105 * @see getVgap() 106 * @see setVgap() 107 */ 108 int vgap; 109 110 /** 111 * @serialField tab Hashtable 112 * deprectated, for forward compatibility only 113 * @serialField hgap int 114 * @serialField vgap int 115 * @serialField vector Vector 116 * @serialField currentCard int 117 */ 118 private static final ObjectStreamField[] serialPersistentFields = { 119 new ObjectStreamField("tab", Hashtable.class), 120 new ObjectStreamField("hgap", Integer.TYPE), 121 new ObjectStreamField("vgap", Integer.TYPE), 122 new ObjectStreamField("vector", Vector.class), 123 new ObjectStreamField("currentCard", Integer.TYPE) 124 }; 125 126 /** 127 * Creates a new card layout with gaps of size zero. 128 */ CardLayout()129 public CardLayout() { 130 this(0, 0); 131 } 132 133 /** 134 * Creates a new card layout with the specified horizontal and 135 * vertical gaps. The horizontal gaps are placed at the left and 136 * right edges. The vertical gaps are placed at the top and bottom 137 * edges. 138 * @param hgap the horizontal gap. 139 * @param vgap the vertical gap. 140 */ CardLayout(int hgap, int vgap)141 public CardLayout(int hgap, int vgap) { 142 this.hgap = hgap; 143 this.vgap = vgap; 144 } 145 146 /** 147 * Gets the horizontal gap between components. 148 * @return the horizontal gap between components. 149 * @see java.awt.CardLayout#setHgap(int) 150 * @see java.awt.CardLayout#getVgap() 151 * @since JDK1.1 152 */ getHgap()153 public int getHgap() { 154 return hgap; 155 } 156 157 /** 158 * Sets the horizontal gap between components. 159 * @param hgap the horizontal gap between components. 160 * @see java.awt.CardLayout#getHgap() 161 * @see java.awt.CardLayout#setVgap(int) 162 * @since JDK1.1 163 */ setHgap(int hgap)164 public void setHgap(int hgap) { 165 this.hgap = hgap; 166 } 167 168 /** 169 * Gets the vertical gap between components. 170 * @return the vertical gap between components. 171 * @see java.awt.CardLayout#setVgap(int) 172 * @see java.awt.CardLayout#getHgap() 173 */ getVgap()174 public int getVgap() { 175 return vgap; 176 } 177 178 /** 179 * Sets the vertical gap between components. 180 * @param vgap the vertical gap between components. 181 * @see java.awt.CardLayout#getVgap() 182 * @see java.awt.CardLayout#setHgap(int) 183 * @since JDK1.1 184 */ setVgap(int vgap)185 public void setVgap(int vgap) { 186 this.vgap = vgap; 187 } 188 189 /** 190 * Adds the specified component to this card layout's internal 191 * table of names. The object specified by <code>constraints</code> 192 * must be a string. The card layout stores this string as a key-value 193 * pair that can be used for random access to a particular card. 194 * By calling the <code>show</code> method, an application can 195 * display the component with the specified name. 196 * @param comp the component to be added. 197 * @param constraints a tag that identifies a particular 198 * card in the layout. 199 * @see java.awt.CardLayout#show(java.awt.Container, java.lang.String) 200 * @exception IllegalArgumentException if the constraint is not a string. 201 */ addLayoutComponent(Component comp, Object constraints)202 public void addLayoutComponent(Component comp, Object constraints) { 203 synchronized (comp.getTreeLock()) { 204 if (constraints == null){ 205 constraints = ""; 206 } 207 if (constraints instanceof String) { 208 addLayoutComponent((String)constraints, comp); 209 } else { 210 throw new IllegalArgumentException("cannot add to layout: constraint must be a string"); 211 } 212 } 213 } 214 215 /** 216 * @deprecated replaced by 217 * <code>addLayoutComponent(Component, Object)</code>. 218 */ 219 @Deprecated addLayoutComponent(String name, Component comp)220 public void addLayoutComponent(String name, Component comp) { 221 synchronized (comp.getTreeLock()) { 222 if (!vector.isEmpty()) { 223 comp.setVisible(false); 224 } 225 for (int i=0; i < vector.size(); i++) { 226 if (((Card)vector.get(i)).name.equals(name)) { 227 ((Card)vector.get(i)).comp = comp; 228 return; 229 } 230 } 231 vector.add(new Card(name, comp)); 232 } 233 } 234 235 /** 236 * Removes the specified component from the layout. 237 * If the card was visible on top, the next card underneath it is shown. 238 * @param comp the component to be removed. 239 * @see java.awt.Container#remove(java.awt.Component) 240 * @see java.awt.Container#removeAll() 241 */ removeLayoutComponent(Component comp)242 public void removeLayoutComponent(Component comp) { 243 synchronized (comp.getTreeLock()) { 244 for (int i = 0; i < vector.size(); i++) { 245 if (((Card)vector.get(i)).comp == comp) { 246 // if we remove current component we should show next one 247 if (comp.isVisible() && (comp.getParent() != null)) { 248 next(comp.getParent()); 249 } 250 251 vector.remove(i); 252 253 // correct currentCard if this is necessary 254 if (currentCard > i) { 255 currentCard--; 256 } 257 break; 258 } 259 } 260 } 261 } 262 263 /** 264 * Determines the preferred size of the container argument using 265 * this card layout. 266 * @param parent the parent container in which to do the layout 267 * @return the preferred dimensions to lay out the subcomponents 268 * of the specified container 269 * @see java.awt.Container#getPreferredSize 270 * @see java.awt.CardLayout#minimumLayoutSize 271 */ preferredLayoutSize(Container parent)272 public Dimension preferredLayoutSize(Container parent) { 273 synchronized (parent.getTreeLock()) { 274 Insets insets = parent.getInsets(); 275 int ncomponents = parent.getComponentCount(); 276 int w = 0; 277 int h = 0; 278 279 for (int i = 0 ; i < ncomponents ; i++) { 280 Component comp = parent.getComponent(i); 281 Dimension d = comp.getPreferredSize(); 282 if (d.width > w) { 283 w = d.width; 284 } 285 if (d.height > h) { 286 h = d.height; 287 } 288 } 289 return new Dimension(insets.left + insets.right + w + hgap*2, 290 insets.top + insets.bottom + h + vgap*2); 291 } 292 } 293 294 /** 295 * Calculates the minimum size for the specified panel. 296 * @param parent the parent container in which to do the layout 297 * @return the minimum dimensions required to lay out the 298 * subcomponents of the specified container 299 * @see java.awt.Container#doLayout 300 * @see java.awt.CardLayout#preferredLayoutSize 301 */ minimumLayoutSize(Container parent)302 public Dimension minimumLayoutSize(Container parent) { 303 synchronized (parent.getTreeLock()) { 304 Insets insets = parent.getInsets(); 305 int ncomponents = parent.getComponentCount(); 306 int w = 0; 307 int h = 0; 308 309 for (int i = 0 ; i < ncomponents ; i++) { 310 Component comp = parent.getComponent(i); 311 Dimension d = comp.getMinimumSize(); 312 if (d.width > w) { 313 w = d.width; 314 } 315 if (d.height > h) { 316 h = d.height; 317 } 318 } 319 return new Dimension(insets.left + insets.right + w + hgap*2, 320 insets.top + insets.bottom + h + vgap*2); 321 } 322 } 323 324 /** 325 * Returns the maximum dimensions for this layout given the components 326 * in the specified target container. 327 * @param target the component which needs to be laid out 328 * @see Container 329 * @see #minimumLayoutSize 330 * @see #preferredLayoutSize 331 */ maximumLayoutSize(Container target)332 public Dimension maximumLayoutSize(Container target) { 333 return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); 334 } 335 336 /** 337 * Returns the alignment along the x axis. This specifies how 338 * the component would like to be aligned relative to other 339 * components. The value should be a number between 0 and 1 340 * where 0 represents alignment along the origin, 1 is aligned 341 * the furthest away from the origin, 0.5 is centered, etc. 342 */ getLayoutAlignmentX(Container parent)343 public float getLayoutAlignmentX(Container parent) { 344 return 0.5f; 345 } 346 347 /** 348 * Returns the alignment along the y axis. This specifies how 349 * the component would like to be aligned relative to other 350 * components. The value should be a number between 0 and 1 351 * where 0 represents alignment along the origin, 1 is aligned 352 * the furthest away from the origin, 0.5 is centered, etc. 353 */ getLayoutAlignmentY(Container parent)354 public float getLayoutAlignmentY(Container parent) { 355 return 0.5f; 356 } 357 358 /** 359 * Invalidates the layout, indicating that if the layout manager 360 * has cached information it should be discarded. 361 */ invalidateLayout(Container target)362 public void invalidateLayout(Container target) { 363 } 364 365 /** 366 * Lays out the specified container using this card layout. 367 * <p> 368 * Each component in the <code>parent</code> container is reshaped 369 * to be the size of the container, minus space for surrounding 370 * insets, horizontal gaps, and vertical gaps. 371 * 372 * @param parent the parent container in which to do the layout 373 * @see java.awt.Container#doLayout 374 */ layoutContainer(Container parent)375 public void layoutContainer(Container parent) { 376 synchronized (parent.getTreeLock()) { 377 Insets insets = parent.getInsets(); 378 int ncomponents = parent.getComponentCount(); 379 Component comp = null; 380 boolean currentFound = false; 381 382 for (int i = 0 ; i < ncomponents ; i++) { 383 comp = parent.getComponent(i); 384 comp.setBounds(hgap + insets.left, vgap + insets.top, 385 parent.width - (hgap*2 + insets.left + insets.right), 386 parent.height - (vgap*2 + insets.top + insets.bottom)); 387 if (comp.isVisible()) { 388 currentFound = true; 389 } 390 } 391 392 if (!currentFound && ncomponents > 0) { 393 parent.getComponent(0).setVisible(true); 394 } 395 } 396 } 397 398 /** 399 * Make sure that the Container really has a CardLayout installed. 400 * Otherwise havoc can ensue! 401 */ checkLayout(Container parent)402 void checkLayout(Container parent) { 403 if (parent.getLayout() != this) { 404 throw new IllegalArgumentException("wrong parent for CardLayout"); 405 } 406 } 407 408 /** 409 * Flips to the first card of the container. 410 * @param parent the parent container in which to do the layout 411 * @see java.awt.CardLayout#last 412 */ first(Container parent)413 public void first(Container parent) { 414 synchronized (parent.getTreeLock()) { 415 checkLayout(parent); 416 int ncomponents = parent.getComponentCount(); 417 for (int i = 0 ; i < ncomponents ; i++) { 418 Component comp = parent.getComponent(i); 419 if (comp.isVisible()) { 420 comp.setVisible(false); 421 break; 422 } 423 } 424 if (ncomponents > 0) { 425 currentCard = 0; 426 parent.getComponent(0).setVisible(true); 427 parent.validate(); 428 } 429 } 430 } 431 432 /** 433 * Flips to the next card of the specified container. If the 434 * currently visible card is the last one, this method flips to the 435 * first card in the layout. 436 * @param parent the parent container in which to do the layout 437 * @see java.awt.CardLayout#previous 438 */ next(Container parent)439 public void next(Container parent) { 440 synchronized (parent.getTreeLock()) { 441 checkLayout(parent); 442 int ncomponents = parent.getComponentCount(); 443 for (int i = 0 ; i < ncomponents ; i++) { 444 Component comp = parent.getComponent(i); 445 if (comp.isVisible()) { 446 comp.setVisible(false); 447 currentCard = (i + 1) % ncomponents; 448 comp = parent.getComponent(currentCard); 449 comp.setVisible(true); 450 parent.validate(); 451 return; 452 } 453 } 454 showDefaultComponent(parent); 455 } 456 } 457 458 /** 459 * Flips to the previous card of the specified container. If the 460 * currently visible card is the first one, this method flips to the 461 * last card in the layout. 462 * @param parent the parent container in which to do the layout 463 * @see java.awt.CardLayout#next 464 */ previous(Container parent)465 public void previous(Container parent) { 466 synchronized (parent.getTreeLock()) { 467 checkLayout(parent); 468 int ncomponents = parent.getComponentCount(); 469 for (int i = 0 ; i < ncomponents ; i++) { 470 Component comp = parent.getComponent(i); 471 if (comp.isVisible()) { 472 comp.setVisible(false); 473 currentCard = ((i > 0) ? i-1 : ncomponents-1); 474 comp = parent.getComponent(currentCard); 475 comp.setVisible(true); 476 parent.validate(); 477 return; 478 } 479 } 480 showDefaultComponent(parent); 481 } 482 } 483 showDefaultComponent(Container parent)484 void showDefaultComponent(Container parent) { 485 if (parent.getComponentCount() > 0) { 486 currentCard = 0; 487 parent.getComponent(0).setVisible(true); 488 parent.validate(); 489 } 490 } 491 492 /** 493 * Flips to the last card of the container. 494 * @param parent the parent container in which to do the layout 495 * @see java.awt.CardLayout#first 496 */ last(Container parent)497 public void last(Container parent) { 498 synchronized (parent.getTreeLock()) { 499 checkLayout(parent); 500 int ncomponents = parent.getComponentCount(); 501 for (int i = 0 ; i < ncomponents ; i++) { 502 Component comp = parent.getComponent(i); 503 if (comp.isVisible()) { 504 comp.setVisible(false); 505 break; 506 } 507 } 508 if (ncomponents > 0) { 509 currentCard = ncomponents - 1; 510 parent.getComponent(currentCard).setVisible(true); 511 parent.validate(); 512 } 513 } 514 } 515 516 /** 517 * Flips to the component that was added to this layout with the 518 * specified <code>name</code>, using <code>addLayoutComponent</code>. 519 * If no such component exists, then nothing happens. 520 * @param parent the parent container in which to do the layout 521 * @param name the component name 522 * @see java.awt.CardLayout#addLayoutComponent(java.awt.Component, java.lang.Object) 523 */ show(Container parent, String name)524 public void show(Container parent, String name) { 525 synchronized (parent.getTreeLock()) { 526 checkLayout(parent); 527 Component next = null; 528 int ncomponents = vector.size(); 529 for (int i = 0; i < ncomponents; i++) { 530 Card card = (Card)vector.get(i); 531 if (card.name.equals(name)) { 532 next = card.comp; 533 currentCard = i; 534 break; 535 } 536 } 537 if ((next != null) && !next.isVisible()) { 538 ncomponents = parent.getComponentCount(); 539 for (int i = 0; i < ncomponents; i++) { 540 Component comp = parent.getComponent(i); 541 if (comp.isVisible()) { 542 comp.setVisible(false); 543 break; 544 } 545 } 546 next.setVisible(true); 547 parent.validate(); 548 } 549 } 550 } 551 552 /** 553 * Returns a string representation of the state of this card layout. 554 * @return a string representation of this card layout. 555 */ toString()556 public String toString() { 557 return getClass().getName() + "[hgap=" + hgap + ",vgap=" + vgap + "]"; 558 } 559 560 /** 561 * Reads serializable fields from stream. 562 */ readObject(ObjectInputStream s)563 private void readObject(ObjectInputStream s) 564 throws ClassNotFoundException, IOException 565 { 566 ObjectInputStream.GetField f = s.readFields(); 567 568 hgap = f.get("hgap", 0); 569 vgap = f.get("vgap", 0); 570 571 if (f.defaulted("vector")) { 572 // pre-1.4 stream 573 Hashtable<String, Component> tab = (Hashtable)f.get("tab", null); 574 vector = new Vector<>(); 575 if (tab != null && !tab.isEmpty()) { 576 for (Enumeration<String> e = tab.keys() ; e.hasMoreElements() ; ) { 577 String key = (String)e.nextElement(); 578 Component comp = (Component)tab.get(key); 579 vector.add(new Card(key, comp)); 580 if (comp.isVisible()) { 581 currentCard = vector.size() - 1; 582 } 583 } 584 } 585 } else { 586 vector = (Vector)f.get("vector", null); 587 currentCard = f.get("currentCard", 0); 588 } 589 } 590 591 /** 592 * Writes serializable fields to stream. 593 */ writeObject(ObjectOutputStream s)594 private void writeObject(ObjectOutputStream s) 595 throws IOException 596 { 597 Hashtable<String, Component> tab = new Hashtable<>(); 598 int ncomponents = vector.size(); 599 for (int i = 0; i < ncomponents; i++) { 600 Card card = (Card)vector.get(i); 601 tab.put(card.name, card.comp); 602 } 603 604 ObjectOutputStream.PutField f = s.putFields(); 605 f.put("hgap", hgap); 606 f.put("vgap", vgap); 607 f.put("vector", vector); 608 f.put("currentCard", currentCard); 609 f.put("tab", tab); 610 s.writeFields(); 611 } 612 } 613