001 /*
002 * Copyright 1995-2006 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package java.awt;
027
028 import java.util.Hashtable;
029 import java.util.Vector;
030 import java.util.Enumeration;
031
032 import java.io.Serializable;
033 import java.io.ObjectInputStream;
034 import java.io.ObjectOutputStream;
035 import java.io.ObjectStreamField;
036 import java.io.IOException;
037
038 /**
039 * A <code>CardLayout</code> object is a layout manager for a
040 * container. It treats each component in the container as a card.
041 * Only one card is visible at a time, and the container acts as
042 * a stack of cards. The first component added to a
043 * <code>CardLayout</code> object is the visible component when the
044 * container is first displayed.
045 * <p>
046 * The ordering of cards is determined by the container's own internal
047 * ordering of its component objects. <code>CardLayout</code>
048 * defines a set of methods that allow an application to flip
049 * through these cards sequentially, or to show a specified card.
050 * The {@link CardLayout#addLayoutComponent}
051 * method can be used to associate a string identifier with a given card
052 * for fast random access.
053 *
054 * @version 1.49 05/05/07
055 * @author Arthur van Hoff
056 * @see java.awt.Container
057 * @since JDK1.0
058 */
059
060 public class CardLayout implements LayoutManager2, Serializable {
061
062 private static final long serialVersionUID = -4328196481005934313L;
063
064 /*
065 * This creates a Vector to store associated
066 * pairs of components and their names.
067 * @see java.util.Vector
068 */
069 Vector vector = new Vector();
070
071 /*
072 * A pair of Component and String that represents its name.
073 */
074 class Card implements Serializable {
075 static final long serialVersionUID = 6640330810709497518L;
076 public String name;
077 public Component comp;
078
079 public Card(String cardName, Component cardComponent) {
080 name = cardName;
081 comp = cardComponent;
082 }
083 }
084
085 /*
086 * Index of Component currently displayed by CardLayout.
087 */
088 int currentCard = 0;
089
090 /*
091 * A cards horizontal Layout gap (inset). It specifies
092 * the space between the left and right edges of a
093 * container and the current component.
094 * This should be a non negative Integer.
095 * @see getHgap()
096 * @see setHgap()
097 */
098 int hgap;
099
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 * Creates a new card layout with gaps of size zero.
127 */
128 public CardLayout() {
129 this (0, 0);
130 }
131
132 /**
133 * Creates a new card layout with the specified horizontal and
134 * vertical gaps. The horizontal gaps are placed at the left and
135 * right edges. The vertical gaps are placed at the top and bottom
136 * edges.
137 * @param hgap the horizontal gap.
138 * @param vgap the vertical gap.
139 */
140 public CardLayout(int hgap, int vgap) {
141 this .hgap = hgap;
142 this .vgap = vgap;
143 }
144
145 /**
146 * Gets the horizontal gap between components.
147 * @return the horizontal gap between components.
148 * @see java.awt.CardLayout#setHgap(int)
149 * @see java.awt.CardLayout#getVgap()
150 * @since JDK1.1
151 */
152 public int getHgap() {
153 return hgap;
154 }
155
156 /**
157 * Sets the horizontal gap between components.
158 * @param hgap the horizontal gap between components.
159 * @see java.awt.CardLayout#getHgap()
160 * @see java.awt.CardLayout#setVgap(int)
161 * @since JDK1.1
162 */
163 public void setHgap(int hgap) {
164 this .hgap = hgap;
165 }
166
167 /**
168 * Gets the vertical gap between components.
169 * @return the vertical gap between components.
170 * @see java.awt.CardLayout#setVgap(int)
171 * @see java.awt.CardLayout#getHgap()
172 */
173 public int getVgap() {
174 return vgap;
175 }
176
177 /**
178 * Sets the vertical gap between components.
179 * @param vgap the vertical gap between components.
180 * @see java.awt.CardLayout#getVgap()
181 * @see java.awt.CardLayout#setHgap(int)
182 * @since JDK1.1
183 */
184 public void setVgap(int vgap) {
185 this .vgap = vgap;
186 }
187
188 /**
189 * Adds the specified component to this card layout's internal
190 * table of names. The object specified by <code>constraints</code>
191 * must be a string. The card layout stores this string as a key-value
192 * pair that can be used for random access to a particular card.
193 * By calling the <code>show</code> method, an application can
194 * display the component with the specified name.
195 * @param comp the component to be added.
196 * @param constraints a tag that identifies a particular
197 * card in the layout.
198 * @see java.awt.CardLayout#show(java.awt.Container, java.lang.String)
199 * @exception IllegalArgumentException if the constraint is not a string.
200 */
201 public void addLayoutComponent(Component comp, Object constraints) {
202 synchronized (comp.getTreeLock()) {
203 if (constraints == null) {
204 constraints = "";
205 }
206 if (constraints instanceof String) {
207 addLayoutComponent((String) constraints, comp);
208 } else {
209 throw new IllegalArgumentException(
210 "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
290 * 2, 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
320 * 2, 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
385 .setBounds(
386 hgap + insets.left,
387 vgap + insets.top,
388 parent.width
389 - (hgap * 2 + insets.left + insets.right),
390 parent.height
391 - (vgap * 2 + insets.top + insets.bottom));
392 if (comp.isVisible()) {
393 currentFound = true;
394 }
395 }
396
397 if (!currentFound && ncomponents > 0) {
398 parent.getComponent(0).setVisible(true);
399 }
400 }
401 }
402
403 /**
404 * Make sure that the Container really has a CardLayout installed.
405 * Otherwise havoc can ensue!
406 */
407 void checkLayout(Container parent) {
408 if (parent.getLayout() != this ) {
409 throw new IllegalArgumentException(
410 "wrong parent for CardLayout");
411 }
412 }
413
414 /**
415 * Flips to the first card of the container.
416 * @param parent the parent container in which to do the layout
417 * @see java.awt.CardLayout#last
418 */
419 public void first(Container parent) {
420 synchronized (parent.getTreeLock()) {
421 checkLayout(parent);
422 int ncomponents = parent.getComponentCount();
423 for (int i = 0; i < ncomponents; i++) {
424 Component comp = parent.getComponent(i);
425 if (comp.isVisible()) {
426 comp.setVisible(false);
427 break;
428 }
429 }
430 if (ncomponents > 0) {
431 currentCard = 0;
432 parent.getComponent(0).setVisible(true);
433 parent.validate();
434 }
435 }
436 }
437
438 /**
439 * Flips to the next card of the specified container. If the
440 * currently visible card is the last one, this method flips to the
441 * first card in the layout.
442 * @param parent the parent container in which to do the layout
443 * @see java.awt.CardLayout#previous
444 */
445 public void next(Container parent) {
446 synchronized (parent.getTreeLock()) {
447 checkLayout(parent);
448 int ncomponents = parent.getComponentCount();
449 for (int i = 0; i < ncomponents; i++) {
450 Component comp = parent.getComponent(i);
451 if (comp.isVisible()) {
452 comp.setVisible(false);
453 currentCard = (i + 1) % ncomponents;
454 comp = parent.getComponent(currentCard);
455 comp.setVisible(true);
456 parent.validate();
457 return;
458 }
459 }
460 showDefaultComponent(parent);
461 }
462 }
463
464 /**
465 * Flips to the previous card of the specified container. If the
466 * currently visible card is the first one, this method flips to the
467 * last card in the layout.
468 * @param parent the parent container in which to do the layout
469 * @see java.awt.CardLayout#next
470 */
471 public void previous(Container parent) {
472 synchronized (parent.getTreeLock()) {
473 checkLayout(parent);
474 int ncomponents = parent.getComponentCount();
475 for (int i = 0; i < ncomponents; i++) {
476 Component comp = parent.getComponent(i);
477 if (comp.isVisible()) {
478 comp.setVisible(false);
479 currentCard = ((i > 0) ? i - 1 : ncomponents - 1);
480 comp = parent.getComponent(currentCard);
481 comp.setVisible(true);
482 parent.validate();
483 return;
484 }
485 }
486 showDefaultComponent(parent);
487 }
488 }
489
490 void showDefaultComponent(Container parent) {
491 if (parent.getComponentCount() > 0) {
492 currentCard = 0;
493 parent.getComponent(0).setVisible(true);
494 parent.validate();
495 }
496 }
497
498 /**
499 * Flips to the last card of the container.
500 * @param parent the parent container in which to do the layout
501 * @see java.awt.CardLayout#first
502 */
503 public void last(Container parent) {
504 synchronized (parent.getTreeLock()) {
505 checkLayout(parent);
506 int ncomponents = parent.getComponentCount();
507 for (int i = 0; i < ncomponents; i++) {
508 Component comp = parent.getComponent(i);
509 if (comp.isVisible()) {
510 comp.setVisible(false);
511 break;
512 }
513 }
514 if (ncomponents > 0) {
515 currentCard = ncomponents - 1;
516 parent.getComponent(currentCard).setVisible(true);
517 parent.validate();
518 }
519 }
520 }
521
522 /**
523 * Flips to the component that was added to this layout with the
524 * specified <code>name</code>, using <code>addLayoutComponent</code>.
525 * If no such component exists, then nothing happens.
526 * @param parent the parent container in which to do the layout
527 * @param name the component name
528 * @see java.awt.CardLayout#addLayoutComponent(java.awt.Component, java.lang.Object)
529 */
530 public void show(Container parent, String name) {
531 synchronized (parent.getTreeLock()) {
532 checkLayout(parent);
533 Component next = null;
534 int ncomponents = vector.size();
535 for (int i = 0; i < ncomponents; i++) {
536 Card card = (Card) vector.get(i);
537 if (card.name.equals(name)) {
538 next = card.comp;
539 currentCard = i;
540 break;
541 }
542 }
543 if ((next != null) && !next.isVisible()) {
544 ncomponents = parent.getComponentCount();
545 for (int i = 0; i < ncomponents; i++) {
546 Component comp = parent.getComponent(i);
547 if (comp.isVisible()) {
548 comp.setVisible(false);
549 break;
550 }
551 }
552 next.setVisible(true);
553 parent.validate();
554 }
555 }
556 }
557
558 /**
559 * Returns a string representation of the state of this card layout.
560 * @return a string representation of this card layout.
561 */
562 public String toString() {
563 return getClass().getName() + "[hgap=" + hgap + ",vgap=" + vgap
564 + "]";
565 }
566
567 /**
568 * Reads serializable fields from stream.
569 */
570 private void readObject(ObjectInputStream s)
571 throws ClassNotFoundException, IOException {
572 ObjectInputStream.GetField f = s.readFields();
573
574 hgap = f.get("hgap", 0);
575 vgap = f.get("vgap", 0);
576
577 if (f.defaulted("vector")) {
578 // pre-1.4 stream
579 Hashtable tab = (Hashtable) f.get("tab", null);
580 vector = new Vector();
581 if (tab != null && !tab.isEmpty()) {
582 for (Enumeration e = tab.keys(); e.hasMoreElements();) {
583 String key = (String) e.nextElement();
584 Component comp = (Component) tab.get(key);
585 vector.add(new Card(key, comp));
586 if (comp.isVisible()) {
587 currentCard = vector.size() - 1;
588 }
589 }
590 }
591 } else {
592 vector = (Vector) f.get("vector", null);
593 currentCard = f.get("currentCard", 0);
594 }
595 }
596
597 /**
598 * Writes serializable fields to stream.
599 */
600 private void writeObject(ObjectOutputStream s) throws IOException {
601 Hashtable tab = new Hashtable();
602 int ncomponents = vector.size();
603 for (int i = 0; i < ncomponents; i++) {
604 Card card = (Card) vector.get(i);
605 tab.put(card.name, card.comp);
606 }
607
608 ObjectOutputStream.PutField f = s.putFields();
609 f.put("hgap", hgap);
610 f.put("vgap", vgap);
611 f.put("vector", vector);
612 f.put("currentCard", currentCard);
613 f.put("tab", tab);
614 s.writeFields();
615 }
616 }
|