001: /*
002: * @(#)CardLayout.java 1.36 05/03/12
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package java.awt;
028:
029: import java.util.Hashtable;
030: import java.util.Vector;
031: import java.util.Enumeration;
032:
033: import java.io.Serializable;
034: import java.io.ObjectInputStream;
035: import java.io.ObjectOutputStream;
036: import java.io.ObjectStreamField;
037: import java.io.IOException;
038:
039: /**
040: * A <code>CardLayout</code> object is a layout manager for a
041: * container. It treats each component in the container as a card.
042: * Only one card is visible at a time, and the container acts as
043: * a stack of cards. The first component added to a
044: * <code>CardLayout</code> object is the visible component when the
045: * container is first displayed.
046: * <p>
047: * The ordering of cards is determined by the container's own internal
048: * ordering of its component objects. <code>CardLayout</code>
049: * defines a set of methods that allow an application to flip
050: * through these cards sequentially, or to show a specified card.
051: * The {@link CardLayout#addLayoutComponent}
052: * method can be used to associate a string identifier with a given card
053: * for fast random access.
054: *
055: * @version 1.37 01/23/03
056: * @author Arthur van Hoff
057: * @see java.awt.Container
058: * @since JDK1.0
059: */
060:
061: public class CardLayout implements LayoutManager2, Serializable {
062:
063: private static final long serialVersionUID = -4328196481005934313L;
064:
065: /*
066: * This creates a Vector to store associated
067: * pairs of components and their names.
068: * @see java.util.Vector
069: */
070: Vector vector = new Vector();
071:
072: /*
073: * A pair of Component and String that represents its name.
074: */
075: class Card implements Serializable {
076: static final long serialVersionUID = 6640330810709497518L;
077: public String name;
078: public Component comp;
079:
080: public Card(String cardName, Component cardComponent) {
081: name = cardName;
082: comp = cardComponent;
083: }
084: }
085:
086: /*
087: * Index of Component currently displayed by CardLayout.
088: */
089: int currentCard = 0;
090:
091: /*
092: * A cards horizontal Layout gap (inset). It specifies
093: * the space between the left and right edges of a
094: * container and the current component.
095: * This should be a non negative Integer.
096: * @see getHgap()
097: * @see setHgap()
098: */
099: int hgap;
100:
101: /*
102: * A cards vertical Layout gap (inset). It specifies
103: * the space between the top and bottom edges of a
104: * container and the current component.
105: * This should be a non negative Integer.
106: * @see getVgap()
107: * @see setVgap()
108: */
109: int vgap;
110:
111: /**
112: * @serialField tab Hashtable
113: * deprectated, for forward compatibility only
114: * @serialField hgap int
115: * @serialField vgap int
116: * @serialField vector Vector
117: * @serialField currentCard int
118: */
119: private static final ObjectStreamField[] serialPersistentFields = {
120: new ObjectStreamField("tab", Hashtable.class),
121: new ObjectStreamField("hgap", Integer.TYPE),
122: new ObjectStreamField("vgap", Integer.TYPE),
123: new ObjectStreamField("vector", Vector.class),
124: new ObjectStreamField("currentCard", Integer.TYPE) };
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 instanceof String) {
205: addLayoutComponent((String) constraints, comp);
206: } else {
207: throw new IllegalArgumentException(
208: "cannot add to layout: constraint must be a string");
209: }
210: }
211: }
212:
213: /**
214: * @deprecated replaced by
215: * <code>addLayoutComponent(Component, Object)</code>.
216: */
217: public void addLayoutComponent(String name, Component comp) {
218: synchronized (comp.getTreeLock()) {
219: if (!vector.isEmpty()) {
220: comp.setVisible(false);
221: }
222: for (int i = 0; i < vector.size(); i++) {
223: if (((Card) vector.get(i)).name.equals(name)) {
224: ((Card) vector.get(i)).comp = comp;
225: return;
226: }
227: }
228: vector.add(new Card(name, comp));
229: }
230: }
231:
232: /**
233: * Removes the specified component from the layout.
234: * @param comp the component to be removed.
235: * @see java.awt.Container#remove(java.awt.Component)
236: * @see java.awt.Container#removeAll()
237: */
238: public void removeLayoutComponent(Component comp) {
239: synchronized (comp.getTreeLock()) {
240: for (int i = 0; i < vector.size(); i++) {
241: if (((Card) vector.get(i)).comp == comp) {
242: // if we remove current component we should show next one
243: if (comp.isVisible() && (comp.getParent() != null)) {
244: next(comp.getParent());
245: }
246:
247: vector.remove(i);
248:
249: // correct currentCard if this is necessary
250: if (currentCard > i) {
251: currentCard--;
252: }
253: break;
254: }
255: }
256: }
257: }
258:
259: /**
260: * Determines the preferred size of the container argument using
261: * this card layout.
262: * @param parent the name of the parent container.
263: * @return the preferred dimensions to lay out the subcomponents
264: * of the specified container.
265: * @see java.awt.Container#getPreferredSize
266: * @see java.awt.CardLayout#minimumLayoutSize
267: */
268: public Dimension preferredLayoutSize(Container parent) {
269: synchronized (parent.getTreeLock()) {
270: Insets insets = parent.getInsets();
271: int ncomponents = parent.getComponentCount();
272: int w = 0;
273: int h = 0;
274:
275: for (int i = 0; i < ncomponents; i++) {
276: Component comp = parent.getComponent(i);
277: Dimension d = comp.getPreferredSize();
278: if (d.width > w) {
279: w = d.width;
280: }
281: if (d.height > h) {
282: h = d.height;
283: }
284: }
285: return new Dimension(insets.left + insets.right + w + hgap
286: * 2, insets.top + insets.bottom + h + vgap * 2);
287: }
288: }
289:
290: /**
291: * Calculates the minimum size for the specified panel.
292: * @param parent the name of the parent container
293: * in which to do the layout.
294: * @return the minimum dimensions required to lay out the
295: * subcomponents of the specified container.
296: * @see java.awt.Container#doLayout
297: * @see java.awt.CardLayout#preferredLayoutSize
298: */
299: public Dimension minimumLayoutSize(Container parent) {
300: synchronized (parent.getTreeLock()) {
301: Insets insets = parent.getInsets();
302: int ncomponents = parent.getComponentCount();
303: int w = 0;
304: int h = 0;
305:
306: for (int i = 0; i < ncomponents; i++) {
307: Component comp = parent.getComponent(i);
308: Dimension d = comp.getMinimumSize();
309: if (d.width > w) {
310: w = d.width;
311: }
312: if (d.height > h) {
313: h = d.height;
314: }
315: }
316: return new Dimension(insets.left + insets.right + w + hgap
317: * 2, insets.top + insets.bottom + h + vgap * 2);
318: }
319: }
320:
321: /**
322: * Returns the maximum dimensions for this layout given the components
323: * in the specified target container.
324: * @param target the component which needs to be laid out
325: * @see Container
326: * @see #minimumLayoutSize
327: * @see #preferredLayoutSize
328: */
329: public Dimension maximumLayoutSize(Container target) {
330: return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
331: }
332:
333: /**
334: * Returns the alignment along the x axis. This specifies how
335: * the component would like to be aligned relative to other
336: * components. The value should be a number between 0 and 1
337: * where 0 represents alignment along the origin, 1 is aligned
338: * the furthest away from the origin, 0.5 is centered, etc.
339: */
340: public float getLayoutAlignmentX(Container parent) {
341: return 0.5f;
342: }
343:
344: /**
345: * Returns the alignment along the y axis. This specifies how
346: * the component would like to be aligned relative to other
347: * components. The value should be a number between 0 and 1
348: * where 0 represents alignment along the origin, 1 is aligned
349: * the furthest away from the origin, 0.5 is centered, etc.
350: */
351: public float getLayoutAlignmentY(Container parent) {
352: return 0.5f;
353: }
354:
355: /**
356: * Invalidates the layout, indicating that if the layout manager
357: * has cached information it should be discarded.
358: */
359: public void invalidateLayout(Container target) {
360: }
361:
362: /**
363: * Lays out the specified container using this card layout.
364: * <p>
365: * Each component in the <code>parent</code> container is reshaped
366: * to be the size of the container, minus space for surrounding
367: * insets, horizontal gaps, and vertical gaps.
368: *
369: * @param parent the name of the parent container
370: * in which to do the layout.
371: * @see java.awt.Container#doLayout
372: */
373: public void layoutContainer(Container parent) {
374: synchronized (parent.getTreeLock()) {
375: Insets insets = parent.getInsets();
376: int ncomponents = parent.getComponentCount();
377: Component comp = null;
378: boolean currentFound = false;
379:
380: for (int i = 0; i < ncomponents; i++) {
381: comp = parent.getComponent(i);
382: comp
383: .setBounds(
384: hgap + insets.left,
385: vgap + insets.top,
386: parent.width
387: - (hgap * 2 + insets.left + insets.right),
388: parent.height
389: - (vgap * 2 + insets.top + insets.bottom));
390: if (comp.isVisible()) {
391: currentFound = true;
392: }
393: }
394:
395: if (!currentFound && ncomponents > 0) {
396: parent.getComponent(0).setVisible(true);
397: }
398: }
399: }
400:
401: /**
402: * Make sure that the Container really has a CardLayout installed.
403: * Otherwise havoc can ensue!
404: */
405: void checkLayout(Container parent) {
406: if (parent.getLayout() != this ) {
407: throw new IllegalArgumentException(
408: "wrong parent for CardLayout");
409: }
410: }
411:
412: /**
413: * Flips to the first card of the container.
414: * @param parent the name of the parent container
415: * in which to do the layout.
416: * @see java.awt.CardLayout#last
417: */
418: public void first(Container parent) {
419: synchronized (parent.getTreeLock()) {
420: checkLayout(parent);
421: int ncomponents = parent.getComponentCount();
422: for (int i = 0; i < ncomponents; i++) {
423: Component comp = parent.getComponent(i);
424: if (comp.isVisible()) {
425: comp.setVisible(false);
426: break;
427: }
428: }
429: if (ncomponents > 0) {
430: currentCard = 0;
431: parent.getComponent(0).setVisible(true);
432: parent.validate();
433: }
434: }
435: }
436:
437: /**
438: * Flips to the next card of the specified container. If the
439: * currently visible card is the last one, this method flips to the
440: * first card in the layout.
441: * @param parent the name of the parent container
442: * 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 name of the parent container
469: * in which to do the layout.
470: * @see java.awt.CardLayout#next
471: */
472: public void previous(Container parent) {
473: synchronized (parent.getTreeLock()) {
474: checkLayout(parent);
475: int ncomponents = parent.getComponentCount();
476: for (int i = 0; i < ncomponents; i++) {
477: Component comp = parent.getComponent(i);
478: if (comp.isVisible()) {
479: comp.setVisible(false);
480: currentCard = ((i > 0) ? i - 1 : ncomponents - 1);
481: comp = parent.getComponent(currentCard);
482: comp.setVisible(true);
483: parent.validate();
484: return;
485: }
486: }
487: showDefaultComponent(parent);
488: }
489: }
490:
491: void showDefaultComponent(Container parent) {
492: if (parent.getComponentCount() > 0) {
493: currentCard = 0;
494: parent.getComponent(0).setVisible(true);
495: parent.validate();
496: }
497: }
498:
499: /**
500: * Flips to the last card of the container.
501: * @param parent the name of the parent container
502: * in which to do the layout.
503: * @see java.awt.CardLayout#first
504: */
505: public void last(Container parent) {
506: synchronized (parent.getTreeLock()) {
507: checkLayout(parent);
508: int ncomponents = parent.getComponentCount();
509: for (int i = 0; i < ncomponents; i++) {
510: Component comp = parent.getComponent(i);
511: if (comp.isVisible()) {
512: comp.setVisible(false);
513: break;
514: }
515: }
516: if (ncomponents > 0) {
517: currentCard = ncomponents - 1;
518: parent.getComponent(currentCard).setVisible(true);
519: parent.validate();
520: }
521: }
522: }
523:
524: /**
525: * Flips to the component that was added to this layout with the
526: * specified <code>name</code>, using <code>addLayoutComponent</code>.
527: * If no such component exists, then nothing happens.
528: * @param parent the name of the parent container
529: * in which to do the layout.
530: * @param name the component name.
531: * @see java.awt.CardLayout#addLayoutComponent(java.awt.Component, java.lang.Object)
532: */
533: public void show(Container parent, String name) {
534: synchronized (parent.getTreeLock()) {
535: checkLayout(parent);
536: Component next = null;
537: int ncomponents = vector.size();
538: for (int i = 0; i < ncomponents; i++) {
539: Card card = (Card) vector.get(i);
540: if (card.name.equals(name)) {
541: next = card.comp;
542: currentCard = i;
543: break;
544: }
545: }
546: if ((next != null) && !next.isVisible()) {
547: ncomponents = parent.getComponentCount();
548: for (int i = 0; i < ncomponents; i++) {
549: Component comp = parent.getComponent(i);
550: if (comp.isVisible()) {
551: comp.setVisible(false);
552: break;
553: }
554: }
555: next.setVisible(true);
556: parent.validate();
557: }
558: }
559: }
560:
561: /**
562: * Returns a string representation of the state of this card layout.
563: * @return a string representation of this card layout.
564: */
565: public String toString() {
566: return getClass().getName() + "[hgap=" + hgap + ",vgap=" + vgap
567: + "]";
568: }
569:
570: /**
571: * Reads serializable fields from stream.
572: */
573: private void readObject(ObjectInputStream s)
574: throws ClassNotFoundException, IOException {
575: ObjectInputStream.GetField f = s.readFields();
576:
577: hgap = f.get("hgap", 0);
578: vgap = f.get("vgap", 0);
579:
580: if (f.defaulted("vector")) {
581: // pre-1.4 stream
582: Hashtable tab = (Hashtable) f.get("tab", null);
583: vector = new Vector();
584: if (tab != null && !tab.isEmpty()) {
585: for (Enumeration e = tab.keys(); e.hasMoreElements();) {
586: String key = (String) e.nextElement();
587: Component comp = (Component) tab.get(key);
588: vector.add(new Card(key, comp));
589: if (comp.isVisible()) {
590: currentCard = vector.size() - 1;
591: }
592: }
593: }
594: } else {
595: vector = (Vector) f.get("vector", null);
596: currentCard = f.get("currentCard", 0);
597: }
598: }
599:
600: /**
601: * Writes serializable fields to stream.
602: */
603: private void writeObject(ObjectOutputStream s) throws IOException {
604: Hashtable tab = new Hashtable();
605: int ncomponents = vector.size();
606: for (int i = 0; i < ncomponents; i++) {
607: Card card = (Card) vector.get(i);
608: tab.put(card.name, card.comp);
609: }
610:
611: ObjectOutputStream.PutField f = s.putFields();
612: f.put("hgap", hgap);
613: f.put("vgap", vgap);
614: f.put("vector", vector);
615: f.put("currentCard", currentCard);
616: f.put("tab", tab);
617: s.writeFields();
618: }
619: }
|