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