1. /*
  2. * @(#)CardLayout.java 1.40 04/05/18
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.awt;
  8. import java.util.Hashtable;
  9. import java.util.Vector;
  10. import java.util.Enumeration;
  11. import java.io.Serializable;
  12. import java.io.ObjectInputStream;
  13. import java.io.ObjectOutputStream;
  14. import java.io.ObjectStreamField;
  15. import java.io.IOException;
  16. /**
  17. * A <code>CardLayout</code> object is a layout manager for a
  18. * container. It treats each component in the container as a card.
  19. * Only one card is visible at a time, and the container acts as
  20. * a stack of cards. The first component added to a
  21. * <code>CardLayout</code> object is the visible component when the
  22. * container is first displayed.
  23. * <p>
  24. * The ordering of cards is determined by the container's own internal
  25. * ordering of its component objects. <code>CardLayout</code>
  26. * defines a set of methods that allow an application to flip
  27. * through these cards sequentially, or to show a specified card.
  28. * The {@link CardLayout#addLayoutComponent}
  29. * method can be used to associate a string identifier with a given card
  30. * for fast random access.
  31. *
  32. * @version 1.40 05/18/04
  33. * @author Arthur van Hoff
  34. * @see java.awt.Container
  35. * @since JDK1.0
  36. */
  37. public class CardLayout implements LayoutManager2,
  38. Serializable {
  39. private static final long serialVersionUID = -4328196481005934313L;
  40. /*
  41. * This creates a Vector to store associated
  42. * pairs of components and their names.
  43. * @see java.util.Vector
  44. */
  45. Vector vector = new Vector();
  46. /*
  47. * A pair of Component and String that represents its name.
  48. */
  49. class Card implements Serializable {
  50. static final long serialVersionUID = 6640330810709497518L;
  51. public String name;
  52. public Component comp;
  53. public Card(String cardName, Component cardComponent) {
  54. name = cardName;
  55. comp = cardComponent;
  56. }
  57. }
  58. /*
  59. * Index of Component currently displayed by CardLayout.
  60. */
  61. int currentCard = 0;
  62. /*
  63. * A cards horizontal Layout gap (inset). It specifies
  64. * the space between the left and right edges of a
  65. * container and the current component.
  66. * This should be a non negative Integer.
  67. * @see getHgap()
  68. * @see setHgap()
  69. */
  70. int hgap;
  71. /*
  72. * A cards vertical Layout gap (inset). It specifies
  73. * the space between the top and bottom edges of a
  74. * container and the current component.
  75. * This should be a non negative Integer.
  76. * @see getVgap()
  77. * @see setVgap()
  78. */
  79. int vgap;
  80. /**
  81. * @serialField tab Hashtable
  82. * deprectated, for forward compatibility only
  83. * @serialField hgap int
  84. * @serialField vgap int
  85. * @serialField vector Vector
  86. * @serialField currentCard int
  87. */
  88. private static final ObjectStreamField[] serialPersistentFields = {
  89. new ObjectStreamField("tab", Hashtable.class),
  90. new ObjectStreamField("hgap", Integer.TYPE),
  91. new ObjectStreamField("vgap", Integer.TYPE),
  92. new ObjectStreamField("vector", Vector.class),
  93. new ObjectStreamField("currentCard", Integer.TYPE)
  94. };
  95. /**
  96. * Creates a new card layout with gaps of size zero.
  97. */
  98. public CardLayout() {
  99. this(0, 0);
  100. }
  101. /**
  102. * Creates a new card layout with the specified horizontal and
  103. * vertical gaps. The horizontal gaps are placed at the left and
  104. * right edges. The vertical gaps are placed at the top and bottom
  105. * edges.
  106. * @param hgap the horizontal gap.
  107. * @param vgap the vertical gap.
  108. */
  109. public CardLayout(int hgap, int vgap) {
  110. this.hgap = hgap;
  111. this.vgap = vgap;
  112. }
  113. /**
  114. * Gets the horizontal gap between components.
  115. * @return the horizontal gap between components.
  116. * @see java.awt.CardLayout#setHgap(int)
  117. * @see java.awt.CardLayout#getVgap()
  118. * @since JDK1.1
  119. */
  120. public int getHgap() {
  121. return hgap;
  122. }
  123. /**
  124. * Sets the horizontal gap between components.
  125. * @param hgap the horizontal gap between components.
  126. * @see java.awt.CardLayout#getHgap()
  127. * @see java.awt.CardLayout#setVgap(int)
  128. * @since JDK1.1
  129. */
  130. public void setHgap(int hgap) {
  131. this.hgap = hgap;
  132. }
  133. /**
  134. * Gets the vertical gap between components.
  135. * @return the vertical gap between components.
  136. * @see java.awt.CardLayout#setVgap(int)
  137. * @see java.awt.CardLayout#getHgap()
  138. */
  139. public int getVgap() {
  140. return vgap;
  141. }
  142. /**
  143. * Sets the vertical gap between components.
  144. * @param vgap the vertical gap between components.
  145. * @see java.awt.CardLayout#getVgap()
  146. * @see java.awt.CardLayout#setHgap(int)
  147. * @since JDK1.1
  148. */
  149. public void setVgap(int vgap) {
  150. this.vgap = vgap;
  151. }
  152. /**
  153. * Adds the specified component to this card layout's internal
  154. * table of names. The object specified by <code>constraints</code>
  155. * must be a string. The card layout stores this string as a key-value
  156. * pair that can be used for random access to a particular card.
  157. * By calling the <code>show</code> method, an application can
  158. * display the component with the specified name.
  159. * @param comp the component to be added.
  160. * @param constraints a tag that identifies a particular
  161. * card in the layout.
  162. * @see java.awt.CardLayout#show(java.awt.Container, java.lang.String)
  163. * @exception IllegalArgumentException if the constraint is not a string.
  164. */
  165. public void addLayoutComponent(Component comp, Object constraints) {
  166. synchronized (comp.getTreeLock()) {
  167. if (constraints instanceof String) {
  168. addLayoutComponent((String)constraints, comp);
  169. } else {
  170. throw new IllegalArgumentException("cannot add to layout: constraint must be a string");
  171. }
  172. }
  173. }
  174. /**
  175. * @deprecated replaced by
  176. * <code>addLayoutComponent(Component, Object)</code>.
  177. */
  178. @Deprecated
  179. public void addLayoutComponent(String name, Component comp) {
  180. synchronized (comp.getTreeLock()) {
  181. if (!vector.isEmpty()) {
  182. comp.setVisible(false);
  183. }
  184. for (int i=0; i < vector.size(); i++) {
  185. if (((Card)vector.get(i)).name.equals(name)) {
  186. ((Card)vector.get(i)).comp = comp;
  187. return;
  188. }
  189. }
  190. vector.add(new Card(name, comp));
  191. }
  192. }
  193. /**
  194. * Removes the specified component from the layout.
  195. * @param comp the component to be removed.
  196. * @see java.awt.Container#remove(java.awt.Component)
  197. * @see java.awt.Container#removeAll()
  198. */
  199. public void removeLayoutComponent(Component comp) {
  200. synchronized (comp.getTreeLock()) {
  201. for (int i = 0; i < vector.size(); i++) {
  202. if (((Card)vector.get(i)).comp == comp) {
  203. // if we remove current component we should show next one
  204. if (comp.isVisible() && (comp.getParent() != null)) {
  205. next(comp.getParent());
  206. }
  207. vector.remove(i);
  208. // correct currentCard if this is necessary
  209. if (currentCard > i) {
  210. currentCard--;
  211. }
  212. break;
  213. }
  214. }
  215. }
  216. }
  217. /**
  218. * Determines the preferred size of the container argument using
  219. * this card layout.
  220. * @param parent the parent container in which to do the layout
  221. * @return the preferred dimensions to lay out the subcomponents
  222. * of the specified container
  223. * @see java.awt.Container#getPreferredSize
  224. * @see java.awt.CardLayout#minimumLayoutSize
  225. */
  226. public Dimension preferredLayoutSize(Container parent) {
  227. synchronized (parent.getTreeLock()) {
  228. Insets insets = parent.getInsets();
  229. int ncomponents = parent.getComponentCount();
  230. int w = 0;
  231. int h = 0;
  232. for (int i = 0 ; i < ncomponents ; i++) {
  233. Component comp = parent.getComponent(i);
  234. Dimension d = comp.getPreferredSize();
  235. if (d.width > w) {
  236. w = d.width;
  237. }
  238. if (d.height > h) {
  239. h = d.height;
  240. }
  241. }
  242. return new Dimension(insets.left + insets.right + w + hgap*2,
  243. insets.top + insets.bottom + h + vgap*2);
  244. }
  245. }
  246. /**
  247. * Calculates the minimum size for the specified panel.
  248. * @param parent the parent container in which to do the layout
  249. * @return the minimum dimensions required to lay out the
  250. * subcomponents of the specified container
  251. * @see java.awt.Container#doLayout
  252. * @see java.awt.CardLayout#preferredLayoutSize
  253. */
  254. public Dimension minimumLayoutSize(Container parent) {
  255. synchronized (parent.getTreeLock()) {
  256. Insets insets = parent.getInsets();
  257. int ncomponents = parent.getComponentCount();
  258. int w = 0;
  259. int h = 0;
  260. for (int i = 0 ; i < ncomponents ; i++) {
  261. Component comp = parent.getComponent(i);
  262. Dimension d = comp.getMinimumSize();
  263. if (d.width > w) {
  264. w = d.width;
  265. }
  266. if (d.height > h) {
  267. h = d.height;
  268. }
  269. }
  270. return new Dimension(insets.left + insets.right + w + hgap*2,
  271. insets.top + insets.bottom + h + vgap*2);
  272. }
  273. }
  274. /**
  275. * Returns the maximum dimensions for this layout given the components
  276. * in the specified target container.
  277. * @param target the component which needs to be laid out
  278. * @see Container
  279. * @see #minimumLayoutSize
  280. * @see #preferredLayoutSize
  281. */
  282. public Dimension maximumLayoutSize(Container target) {
  283. return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
  284. }
  285. /**
  286. * Returns the alignment along the x axis. This specifies how
  287. * the component would like to be aligned relative to other
  288. * components. The value should be a number between 0 and 1
  289. * where 0 represents alignment along the origin, 1 is aligned
  290. * the furthest away from the origin, 0.5 is centered, etc.
  291. */
  292. public float getLayoutAlignmentX(Container parent) {
  293. return 0.5f;
  294. }
  295. /**
  296. * Returns the alignment along the y axis. This specifies how
  297. * the component would like to be aligned relative to other
  298. * components. The value should be a number between 0 and 1
  299. * where 0 represents alignment along the origin, 1 is aligned
  300. * the furthest away from the origin, 0.5 is centered, etc.
  301. */
  302. public float getLayoutAlignmentY(Container parent) {
  303. return 0.5f;
  304. }
  305. /**
  306. * Invalidates the layout, indicating that if the layout manager
  307. * has cached information it should be discarded.
  308. */
  309. public void invalidateLayout(Container target) {
  310. }
  311. /**
  312. * Lays out the specified container using this card layout.
  313. * <p>
  314. * Each component in the <code>parent</code> container is reshaped
  315. * to be the size of the container, minus space for surrounding
  316. * insets, horizontal gaps, and vertical gaps.
  317. *
  318. * @param parent the parent container in which to do the layout
  319. * @see java.awt.Container#doLayout
  320. */
  321. public void layoutContainer(Container parent) {
  322. synchronized (parent.getTreeLock()) {
  323. Insets insets = parent.getInsets();
  324. int ncomponents = parent.getComponentCount();
  325. Component comp = null;
  326. boolean currentFound = false;
  327. for (int i = 0 ; i < ncomponents ; i++) {
  328. comp = parent.getComponent(i);
  329. comp.setBounds(hgap + insets.left, vgap + insets.top,
  330. parent.width - (hgap*2 + insets.left + insets.right),
  331. parent.height - (vgap*2 + insets.top + insets.bottom));
  332. if (comp.isVisible()) {
  333. currentFound = true;
  334. }
  335. }
  336. if (!currentFound && ncomponents > 0) {
  337. parent.getComponent(0).setVisible(true);
  338. }
  339. }
  340. }
  341. /**
  342. * Make sure that the Container really has a CardLayout installed.
  343. * Otherwise havoc can ensue!
  344. */
  345. void checkLayout(Container parent) {
  346. if (parent.getLayout() != this) {
  347. throw new IllegalArgumentException("wrong parent for CardLayout");
  348. }
  349. }
  350. /**
  351. * Flips to the first card of the container.
  352. * @param parent the parent container in which to do the layout
  353. * @see java.awt.CardLayout#last
  354. */
  355. public void first(Container parent) {
  356. synchronized (parent.getTreeLock()) {
  357. checkLayout(parent);
  358. int ncomponents = parent.getComponentCount();
  359. for (int i = 0 ; i < ncomponents ; i++) {
  360. Component comp = parent.getComponent(i);
  361. if (comp.isVisible()) {
  362. comp.setVisible(false);
  363. break;
  364. }
  365. }
  366. if (ncomponents > 0) {
  367. currentCard = 0;
  368. parent.getComponent(0).setVisible(true);
  369. parent.validate();
  370. }
  371. }
  372. }
  373. /**
  374. * Flips to the next card of the specified container. If the
  375. * currently visible card is the last one, this method flips to the
  376. * first card in the layout.
  377. * @param parent the parent container in which to do the layout
  378. * @see java.awt.CardLayout#previous
  379. */
  380. public void next(Container parent) {
  381. synchronized (parent.getTreeLock()) {
  382. checkLayout(parent);
  383. int ncomponents = parent.getComponentCount();
  384. for (int i = 0 ; i < ncomponents ; i++) {
  385. Component comp = parent.getComponent(i);
  386. if (comp.isVisible()) {
  387. comp.setVisible(false);
  388. currentCard = (i + 1) % ncomponents;
  389. comp = parent.getComponent(currentCard);
  390. comp.setVisible(true);
  391. parent.validate();
  392. return;
  393. }
  394. }
  395. showDefaultComponent(parent);
  396. }
  397. }
  398. /**
  399. * Flips to the previous card of the specified container. If the
  400. * currently visible card is the first one, this method flips to the
  401. * last card in the layout.
  402. * @param parent the parent container in which to do the layout
  403. * @see java.awt.CardLayout#next
  404. */
  405. public void previous(Container parent) {
  406. synchronized (parent.getTreeLock()) {
  407. checkLayout(parent);
  408. int ncomponents = parent.getComponentCount();
  409. for (int i = 0 ; i < ncomponents ; i++) {
  410. Component comp = parent.getComponent(i);
  411. if (comp.isVisible()) {
  412. comp.setVisible(false);
  413. currentCard = ((i > 0) ? i-1 : ncomponents-1);
  414. comp = parent.getComponent(currentCard);
  415. comp.setVisible(true);
  416. parent.validate();
  417. return;
  418. }
  419. }
  420. showDefaultComponent(parent);
  421. }
  422. }
  423. void showDefaultComponent(Container parent) {
  424. if (parent.getComponentCount() > 0) {
  425. currentCard = 0;
  426. parent.getComponent(0).setVisible(true);
  427. parent.validate();
  428. }
  429. }
  430. /**
  431. * Flips to the last card of the container.
  432. * @param parent the parent container in which to do the layout
  433. * @see java.awt.CardLayout#first
  434. */
  435. public void last(Container parent) {
  436. synchronized (parent.getTreeLock()) {
  437. checkLayout(parent);
  438. int ncomponents = parent.getComponentCount();
  439. for (int i = 0 ; i < ncomponents ; i++) {
  440. Component comp = parent.getComponent(i);
  441. if (comp.isVisible()) {
  442. comp.setVisible(false);
  443. break;
  444. }
  445. }
  446. if (ncomponents > 0) {
  447. currentCard = ncomponents - 1;
  448. parent.getComponent(currentCard).setVisible(true);
  449. parent.validate();
  450. }
  451. }
  452. }
  453. /**
  454. * Flips to the component that was added to this layout with the
  455. * specified <code>name</code>, using <code>addLayoutComponent</code>.
  456. * If no such component exists, then nothing happens.
  457. * @param parent the parent container in which to do the layout
  458. * @param name the component name
  459. * @see java.awt.CardLayout#addLayoutComponent(java.awt.Component, java.lang.Object)
  460. */
  461. public void show(Container parent, String name) {
  462. synchronized (parent.getTreeLock()) {
  463. checkLayout(parent);
  464. Component next = null;
  465. int ncomponents = vector.size();
  466. for (int i = 0; i < ncomponents; i++) {
  467. Card card = (Card)vector.get(i);
  468. if (card.name.equals(name)) {
  469. next = card.comp;
  470. currentCard = i;
  471. break;
  472. }
  473. }
  474. if ((next != null) && !next.isVisible()) {
  475. 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. break;
  481. }
  482. }
  483. next.setVisible(true);
  484. parent.validate();
  485. }
  486. }
  487. }
  488. /**
  489. * Returns a string representation of the state of this card layout.
  490. * @return a string representation of this card layout.
  491. */
  492. public String toString() {
  493. return getClass().getName() + "[hgap=" + hgap + ",vgap=" + vgap + "]";
  494. }
  495. /**
  496. * Reads serializable fields from stream.
  497. */
  498. private void readObject(ObjectInputStream s)
  499. throws ClassNotFoundException, IOException
  500. {
  501. ObjectInputStream.GetField f = s.readFields();
  502. hgap = f.get("hgap", 0);
  503. vgap = f.get("vgap", 0);
  504. if (f.defaulted("vector")) {
  505. // pre-1.4 stream
  506. Hashtable tab = (Hashtable)f.get("tab", null);
  507. vector = new Vector();
  508. if (tab != null && !tab.isEmpty()) {
  509. for (Enumeration e = tab.keys() ; e.hasMoreElements() ; ) {
  510. String key = (String)e.nextElement();
  511. Component comp = (Component)tab.get(key);
  512. vector.add(new Card(key, comp));
  513. if (comp.isVisible()) {
  514. currentCard = vector.size() - 1;
  515. }
  516. }
  517. }
  518. } else {
  519. vector = (Vector)f.get("vector", null);
  520. currentCard = f.get("currentCard", 0);
  521. }
  522. }
  523. /**
  524. * Writes serializable fields to stream.
  525. */
  526. private void writeObject(ObjectOutputStream s)
  527. throws IOException
  528. {
  529. Hashtable tab = new Hashtable();
  530. int ncomponents = vector.size();
  531. for (int i = 0; i < ncomponents; i++) {
  532. Card card = (Card)vector.get(i);
  533. tab.put(card.name, card.comp);
  534. }
  535. ObjectOutputStream.PutField f = s.putFields();
  536. f.put("hgap", hgap);
  537. f.put("vgap", vgap);
  538. f.put("vector", vector);
  539. f.put("currentCard", currentCard);
  540. f.put("tab", tab);
  541. s.writeFields();
  542. }
  543. }