1. /*
  2. * @(#)CardLayout.java 1.37 03/01/23
  3. *
  4. * Copyright 2003 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.37 01/23/03
  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. public void addLayoutComponent(String name, Component comp) {
  179. synchronized (comp.getTreeLock()) {
  180. if (!vector.isEmpty()) {
  181. comp.setVisible(false);
  182. }
  183. for (int i=0; i < vector.size(); i++) {
  184. if (((Card)vector.get(i)).name.equals(name)) {
  185. ((Card)vector.get(i)).comp = comp;
  186. return;
  187. }
  188. }
  189. vector.add(new Card(name, comp));
  190. }
  191. }
  192. /**
  193. * Removes the specified component from the layout.
  194. * @param comp the component to be removed.
  195. * @see java.awt.Container#remove(java.awt.Component)
  196. * @see java.awt.Container#removeAll()
  197. */
  198. public void removeLayoutComponent(Component comp) {
  199. synchronized (comp.getTreeLock()) {
  200. for (int i = 0; i < vector.size(); i++) {
  201. if (((Card)vector.get(i)).comp == comp) {
  202. // if we remove current component we should show next one
  203. if (comp.isVisible() && (comp.getParent() != null)) {
  204. next(comp.getParent());
  205. }
  206. vector.remove(i);
  207. // correct currentCard if this is necessary
  208. if (currentCard > i) {
  209. currentCard--;
  210. }
  211. break;
  212. }
  213. }
  214. }
  215. }
  216. /**
  217. * Determines the preferred size of the container argument using
  218. * this card layout.
  219. * @param parent the name of the parent container.
  220. * @return the preferred dimensions to lay out the subcomponents
  221. * of the specified container.
  222. * @see java.awt.Container#getPreferredSize
  223. * @see java.awt.CardLayout#minimumLayoutSize
  224. */
  225. public Dimension preferredLayoutSize(Container parent) {
  226. synchronized (parent.getTreeLock()) {
  227. Insets insets = parent.getInsets();
  228. int ncomponents = parent.getComponentCount();
  229. int w = 0;
  230. int h = 0;
  231. for (int i = 0 ; i < ncomponents ; i++) {
  232. Component comp = parent.getComponent(i);
  233. Dimension d = comp.getPreferredSize();
  234. if (d.width > w) {
  235. w = d.width;
  236. }
  237. if (d.height > h) {
  238. h = d.height;
  239. }
  240. }
  241. return new Dimension(insets.left + insets.right + w + hgap*2,
  242. insets.top + insets.bottom + h + vgap*2);
  243. }
  244. }
  245. /**
  246. * Calculates the minimum size for the specified panel.
  247. * @param parent the name of the parent container
  248. * 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 name of the parent container
  319. * in which to do the layout.
  320. * @see java.awt.Container#doLayout
  321. */
  322. public void layoutContainer(Container parent) {
  323. synchronized (parent.getTreeLock()) {
  324. Insets insets = parent.getInsets();
  325. int ncomponents = parent.getComponentCount();
  326. Component comp = null;
  327. boolean currentFound = false;
  328. for (int i = 0 ; i < ncomponents ; i++) {
  329. comp = parent.getComponent(i);
  330. comp.setBounds(hgap + insets.left, vgap + insets.top,
  331. parent.width - (hgap*2 + insets.left + insets.right),
  332. parent.height - (vgap*2 + insets.top + insets.bottom));
  333. if (comp.isVisible()) {
  334. currentFound = true;
  335. }
  336. }
  337. if (!currentFound && ncomponents > 0) {
  338. parent.getComponent(0).setVisible(true);
  339. }
  340. }
  341. }
  342. /**
  343. * Make sure that the Container really has a CardLayout installed.
  344. * Otherwise havoc can ensue!
  345. */
  346. void checkLayout(Container parent) {
  347. if (parent.getLayout() != this) {
  348. throw new IllegalArgumentException("wrong parent for CardLayout");
  349. }
  350. }
  351. /**
  352. * Flips to the first card of the container.
  353. * @param parent the name of the parent container
  354. * in which to do the layout.
  355. * @see java.awt.CardLayout#last
  356. */
  357. public void first(Container parent) {
  358. synchronized (parent.getTreeLock()) {
  359. checkLayout(parent);
  360. int ncomponents = parent.getComponentCount();
  361. for (int i = 0 ; i < ncomponents ; i++) {
  362. Component comp = parent.getComponent(i);
  363. if (comp.isVisible()) {
  364. comp.setVisible(false);
  365. break;
  366. }
  367. }
  368. if (ncomponents > 0) {
  369. currentCard = 0;
  370. parent.getComponent(0).setVisible(true);
  371. parent.validate();
  372. }
  373. }
  374. }
  375. /**
  376. * Flips to the next card of the specified container. If the
  377. * currently visible card is the last one, this method flips to the
  378. * first card in the layout.
  379. * @param parent the name of the parent container
  380. * in which to do the layout.
  381. * @see java.awt.CardLayout#previous
  382. */
  383. public void next(Container parent) {
  384. synchronized (parent.getTreeLock()) {
  385. checkLayout(parent);
  386. int ncomponents = parent.getComponentCount();
  387. for (int i = 0 ; i < ncomponents ; i++) {
  388. Component comp = parent.getComponent(i);
  389. if (comp.isVisible()) {
  390. comp.setVisible(false);
  391. currentCard = (i + 1) % ncomponents;
  392. comp = parent.getComponent(currentCard);
  393. comp.setVisible(true);
  394. parent.validate();
  395. return;
  396. }
  397. }
  398. showDefaultComponent(parent);
  399. }
  400. }
  401. /**
  402. * Flips to the previous card of the specified container. If the
  403. * currently visible card is the first one, this method flips to the
  404. * last card in the layout.
  405. * @param parent the name of the parent container
  406. * in which to do the layout.
  407. * @see java.awt.CardLayout#next
  408. */
  409. public void previous(Container parent) {
  410. synchronized (parent.getTreeLock()) {
  411. checkLayout(parent);
  412. int ncomponents = parent.getComponentCount();
  413. for (int i = 0 ; i < ncomponents ; i++) {
  414. Component comp = parent.getComponent(i);
  415. if (comp.isVisible()) {
  416. comp.setVisible(false);
  417. currentCard = ((i > 0) ? i-1 : ncomponents-1);
  418. comp = parent.getComponent(currentCard);
  419. comp.setVisible(true);
  420. parent.validate();
  421. return;
  422. }
  423. }
  424. showDefaultComponent(parent);
  425. }
  426. }
  427. void showDefaultComponent(Container parent) {
  428. if (parent.getComponentCount() > 0) {
  429. currentCard = 0;
  430. parent.getComponent(0).setVisible(true);
  431. parent.validate();
  432. }
  433. }
  434. /**
  435. * Flips to the last card of the container.
  436. * @param parent the name of the parent container
  437. * in which to do the layout.
  438. * @see java.awt.CardLayout#first
  439. */
  440. public void last(Container parent) {
  441. synchronized (parent.getTreeLock()) {
  442. checkLayout(parent);
  443. int ncomponents = parent.getComponentCount();
  444. for (int i = 0 ; i < ncomponents ; i++) {
  445. Component comp = parent.getComponent(i);
  446. if (comp.isVisible()) {
  447. comp.setVisible(false);
  448. break;
  449. }
  450. }
  451. if (ncomponents > 0) {
  452. currentCard = ncomponents - 1;
  453. parent.getComponent(currentCard).setVisible(true);
  454. parent.validate();
  455. }
  456. }
  457. }
  458. /**
  459. * Flips to the component that was added to this layout with the
  460. * specified <code>name</code>, using <code>addLayoutComponent</code>.
  461. * If no such component exists, then nothing happens.
  462. * @param parent the name of the parent container
  463. * in which to do the layout.
  464. * @param name the component name.
  465. * @see java.awt.CardLayout#addLayoutComponent(java.awt.Component, java.lang.Object)
  466. */
  467. public void show(Container parent, String name) {
  468. synchronized (parent.getTreeLock()) {
  469. checkLayout(parent);
  470. Component next = null;
  471. int ncomponents = vector.size();
  472. for (int i = 0; i < ncomponents; i++) {
  473. Card card = (Card)vector.get(i);
  474. if (card.name.equals(name)) {
  475. next = card.comp;
  476. currentCard = i;
  477. break;
  478. }
  479. }
  480. if ((next != null) && !next.isVisible()) {
  481. ncomponents = parent.getComponentCount();
  482. for (int i = 0; i < ncomponents; i++) {
  483. Component comp = parent.getComponent(i);
  484. if (comp.isVisible()) {
  485. comp.setVisible(false);
  486. break;
  487. }
  488. }
  489. next.setVisible(true);
  490. parent.validate();
  491. }
  492. }
  493. }
  494. /**
  495. * Returns a string representation of the state of this card layout.
  496. * @return a string representation of this card layout.
  497. */
  498. public String toString() {
  499. return getClass().getName() + "[hgap=" + hgap + ",vgap=" + vgap + "]";
  500. }
  501. /**
  502. * Reads serializable fields from stream.
  503. */
  504. private void readObject(ObjectInputStream s)
  505. throws ClassNotFoundException, IOException
  506. {
  507. ObjectInputStream.GetField f = s.readFields();
  508. hgap = f.get("hgap", 0);
  509. vgap = f.get("vgap", 0);
  510. if (f.defaulted("vector")) {
  511. // pre-1.4 stream
  512. Hashtable tab = (Hashtable)f.get("tab", null);
  513. vector = new Vector();
  514. if (tab != null && !tab.isEmpty()) {
  515. for (Enumeration e = tab.keys() ; e.hasMoreElements() ; ) {
  516. String key = (String)e.nextElement();
  517. Component comp = (Component)tab.get(key);
  518. vector.add(new Card(key, comp));
  519. if (comp.isVisible()) {
  520. currentCard = vector.size() - 1;
  521. }
  522. }
  523. }
  524. } else {
  525. vector = (Vector)f.get("vector", null);
  526. currentCard = f.get("currentCard", 0);
  527. }
  528. }
  529. /**
  530. * Writes serializable fields to stream.
  531. */
  532. private void writeObject(ObjectOutputStream s)
  533. throws IOException
  534. {
  535. Hashtable tab = new Hashtable();
  536. int ncomponents = vector.size();
  537. for (int i = 0; i < ncomponents; i++) {
  538. Card card = (Card)vector.get(i);
  539. tab.put(card.name, card.comp);
  540. }
  541. ObjectOutputStream.PutField f = s.putFields();
  542. f.put("hgap", hgap);
  543. f.put("vgap", vgap);
  544. f.put("vector", vector);
  545. f.put("currentCard", currentCard);
  546. f.put("tab", tab);
  547. s.writeFields();
  548. }
  549. }