1 /*
2 * Copyright 1995-2006 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any 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 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;
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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
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 */
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 */
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 */
556 public String toString() {
557 return getClass().getName() + "[hgap=" + hgap + ",vgap=" + vgap + "]";
558 }
559
560 /**
561 * Reads serializable fields from stream.
562 */
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 tab = (Hashtable)f.get("tab", null);
574 vector = new Vector();
575 if (tab != null && !tab.isEmpty()) {
576 for (Enumeration 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 */
594 private void writeObject(ObjectOutputStream s)
595 throws IOException
596 {
597 Hashtable 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 }