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: }
|