1. /*
  2. * @(#)JMenuBar.java 1.77 01/11/29
  3. *
  4. * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing;
  8. import java.awt.Component;
  9. import java.awt.Dimension;
  10. import java.awt.Graphics;
  11. import java.awt.Insets;
  12. import java.awt.Point;
  13. import java.awt.Rectangle;
  14. import java.awt.event.*;
  15. import java.util.Vector;
  16. import java.util.Enumeration;
  17. import java.io.Serializable;
  18. import java.io.ObjectOutputStream;
  19. import java.io.ObjectInputStream;
  20. import java.io.IOException;
  21. import javax.swing.event.*;
  22. import javax.swing.border.Border;
  23. import javax.swing.plaf.*;
  24. import javax.accessibility.*;
  25. /**
  26. * An implementation of a MenuBar. You add JMenu objects to the
  27. * menu bar to construct a menu. When the user selects a JMenu
  28. * object, its associated JPopupMenu is displayed, allowing the
  29. * user to select one of the JMenuItems on it.
  30. * <p>
  31. * For the keyboard keys used by this component in the standard Look and
  32. * Feel (L&F) renditions, see the
  33. * <a href="doc-files/Key-Index.html#JMenuBar">JMenuBar</a> key assignments.
  34. * <p>
  35. * <strong>Warning:</strong>
  36. * Serialized objects of this class will not be compatible with
  37. * future Swing releases. The current serialization support is appropriate
  38. * for short term storage or RMI between applications running the same
  39. * version of Swing. A future release of Swing will provide support for
  40. * long term persistence.
  41. *
  42. * @version 1.52 04/09/98
  43. * @author Georges Saab
  44. * @author David Karlton
  45. * @author Arnaud Weber
  46. * @see JMenu
  47. * @see JPopupMenu
  48. * @see JMenuItem
  49. */
  50. public class JMenuBar extends JComponent implements Accessible,MenuElement
  51. {
  52. /**
  53. * @see #getUIClassID
  54. * @see #readObject
  55. */
  56. private static final String uiClassID = "MenuBarUI";
  57. /*
  58. * Model for the selected subcontrol
  59. */
  60. private transient SingleSelectionModel selectionModel;
  61. private boolean paintBorder = true;
  62. private Insets margin = null;
  63. /**
  64. * Creates a new menu bar.
  65. */
  66. public JMenuBar() {
  67. super();
  68. setSelectionModel(new DefaultSingleSelectionModel());
  69. updateUI();
  70. }
  71. /**
  72. * Returns the menubar's current UI.
  73. * @see #setUI
  74. */
  75. public MenuBarUI getUI() {
  76. return (MenuBarUI)ui;
  77. }
  78. /**
  79. * Sets the L&F object that renders this component.
  80. *
  81. * @param ui the new MenuBarUI L&F object
  82. * @see UIDefaults#getUI
  83. */
  84. public void setUI(MenuBarUI ui) {
  85. super.setUI(ui);
  86. }
  87. /**
  88. * Notification from the UIFactory that the L&F has changed.
  89. * Called to replace the UI with the latest version from the
  90. * UIFactory.
  91. *
  92. * @see JComponent#updateUI
  93. */
  94. public void updateUI() {
  95. setUI((MenuBarUI)UIManager.getUI(this));
  96. }
  97. /**
  98. * Returns the name of the L&F class that renders this component.
  99. *
  100. * @return "MenuBarUI"
  101. * @see JComponent#getUIClassID
  102. * @see UIDefaults#getUI
  103. */
  104. public String getUIClassID() {
  105. return uiClassID;
  106. }
  107. /**
  108. * Returns the model object that handles single selections.
  109. *
  110. * @return the SingleSelectionModel in use
  111. * @see SingleSelectionModel
  112. */
  113. public SingleSelectionModel getSelectionModel() {
  114. return selectionModel;
  115. }
  116. /**
  117. * Set the model object to handle single selections.
  118. *
  119. * @param model the SingleSelectionModel to use
  120. * @see SingleSelectionModel
  121. * @beaninfo
  122. * bound: true
  123. * description: The selection model, recording which child is selected.
  124. */
  125. public void setSelectionModel(SingleSelectionModel model) {
  126. SingleSelectionModel oldValue = selectionModel;
  127. this.selectionModel = model;
  128. firePropertyChange("selectionModel", oldValue, selectionModel);
  129. }
  130. /**
  131. * Appends the specified menu to the end of the menu bar.
  132. *
  133. * @param c the JMenu component to add
  134. */
  135. public JMenu add(JMenu c) {
  136. super.add(c);
  137. return c;
  138. }
  139. /**
  140. * Gets the menu at the specified position in the menu bar.
  141. *
  142. * @param index an int giving the position in the menu bar, where
  143. * 0 is the first position
  144. * @return the JMenu at that position
  145. */
  146. public JMenu getMenu(int index) {
  147. Component c = getComponentAtIndex(index);
  148. if (c instanceof JMenu)
  149. return (JMenu) c;
  150. return null;
  151. }
  152. /**
  153. * Returns the number of items in the menu bar.
  154. *
  155. * @return the number of items in the menu bar
  156. */
  157. public int getMenuCount() {
  158. return getComponentCount();
  159. }
  160. /**
  161. * Sets the help menu that appears when the user selects the
  162. * "help" option in the menu bar. This method is not yet implemented.
  163. *
  164. * @param menu the JMenu that delivers help to the user
  165. */
  166. public void setHelpMenu(JMenu menu) {
  167. throw new Error("setHelpMenu() not yet implemented.");
  168. }
  169. /**
  170. * Gets the help menu for the menu bar.
  171. *
  172. * @return the JMenu that delivers help to the user
  173. */
  174. public JMenu getHelpMenu() {
  175. throw new Error("getHelpMenu() not yet implemented.");
  176. }
  177. /**
  178. * Returns the component at the specified index.
  179. * This method is obsolete, please use <code>getComponent(int i)</code> instead.
  180. *
  181. * @param i an int specifying the position, where 0 = first
  182. * @return the Component at the position, or null for an
  183. * invalid index
  184. */
  185. public Component getComponentAtIndex(int i) {
  186. return getComponent(i);
  187. }
  188. /**
  189. * Returns the index of the specified component.
  190. *
  191. * @param c the Component to find
  192. * @return an int giving the component's position, where 0 = first
  193. */
  194. public int getComponentIndex(Component c) {
  195. int ncomponents = this.getComponentCount();
  196. Component[] component = this.getComponents();
  197. for (int i = 0 ; i < ncomponents ; i++) {
  198. Component comp = component[i];
  199. if (comp == c)
  200. return i;
  201. }
  202. return -1;
  203. }
  204. /**
  205. * Sets the currently selected component, producing a
  206. * a change to the selection model.
  207. *
  208. * @param sel the Component to select
  209. */
  210. public void setSelected(Component sel) {
  211. SingleSelectionModel model = getSelectionModel();
  212. int index = getComponentIndex(sel);
  213. model.setSelectedIndex(index);
  214. }
  215. /**
  216. * Returns true if the MenuBar currently has a component selected
  217. *
  218. * @return true if a selection has been made, else false
  219. */
  220. public boolean isSelected() {
  221. return selectionModel.isSelected();
  222. }
  223. /**
  224. * Returns true if the Menubar's border should be painted.
  225. *
  226. * @return true if the border should be painted, else false
  227. */
  228. public boolean isBorderPainted() {
  229. return paintBorder;
  230. }
  231. /**
  232. * Sets whether the border should be painted.
  233. * @param b if true and border property is not null, the border is painted.
  234. * @see #isBorderPainted
  235. * @beaninfo
  236. * bound: true
  237. * attribute: visualUpdate true
  238. * description: Whether the border should be painted.
  239. */
  240. public void setBorderPainted(boolean b) {
  241. boolean oldValue = paintBorder;
  242. paintBorder = b;
  243. firePropertyChange("borderPainted", oldValue, paintBorder);
  244. if (b != oldValue) {
  245. revalidate();
  246. repaint();
  247. }
  248. }
  249. /**
  250. * Paint the menubar's border if BorderPainted property is true.
  251. *
  252. * @param g the Graphics context to use for painting
  253. * @see JComponent#paint
  254. * @see JComponent#setBorder
  255. */
  256. protected void paintBorder(Graphics g) {
  257. if (isBorderPainted()) {
  258. super.paintBorder(g);
  259. }
  260. }
  261. /**
  262. * Sets the margin between the menubar's border and
  263. * its menus. Setting to null will cause the menubar to
  264. * use the default margins.
  265. *
  266. * @param margin an Insets object containing the margin values
  267. * @see Insets
  268. * @beaninfo
  269. * bound: true
  270. * attribute: visualUpdate true
  271. * description: The space between the menubar's border and its contents
  272. */
  273. public void setMargin(Insets m) {
  274. Insets old = margin;
  275. this.margin = m;
  276. firePropertyChange("margin", old, m);
  277. if (old == null || !m.equals(old)) {
  278. revalidate();
  279. repaint();
  280. }
  281. }
  282. /**
  283. * Returns the margin between the menubar's border and
  284. * its menus.
  285. *
  286. * @return an Insets object containing the margin values
  287. * @see Insets
  288. */
  289. public Insets getMargin() {
  290. if(margin == null) {
  291. return new Insets(0,0,0,0);
  292. } else {
  293. return margin;
  294. }
  295. }
  296. /**
  297. * Implemented to be a MenuElement -- does nothing.
  298. *
  299. * @see #getSubElements
  300. */
  301. public void processMouseEvent(MouseEvent event,MenuElement path[],MenuSelectionManager manager) {
  302. }
  303. /**
  304. * Implemented to be a MenuElement -- does nothing.
  305. *
  306. * @see #getSubElements
  307. */
  308. public void processKeyEvent(KeyEvent e,MenuElement path[],MenuSelectionManager manager) {
  309. }
  310. /**
  311. * Implemented to be a MenuElement -- does nothing.
  312. *
  313. * @see #getSubElements
  314. */
  315. public void menuSelectionChanged(boolean isIncluded) {
  316. }
  317. /**
  318. * Implemented to be a MenuElement -- returns the menus in this menu
  319. * bar. This is the reason for implementing the MenuElement
  320. * interface -- so that the menu bar can be treated the same as
  321. * other menu elements.
  322. */
  323. public MenuElement[] getSubElements() {
  324. MenuElement result[];
  325. Vector tmp = new Vector();
  326. int c = getComponentCount();
  327. int i;
  328. Component m;
  329. for(i=0 ; i < c ; i++) {
  330. m = getComponent(i);
  331. if(m instanceof MenuElement)
  332. tmp.addElement(m);
  333. }
  334. result = new MenuElement[tmp.size()];
  335. for(i=0,c=tmp.size() ; i < c ; i++)
  336. result[i] = (MenuElement) tmp.elementAt(i);
  337. return result;
  338. }
  339. /**
  340. * Implemented to be a MenuElement. Returns this object.
  341. *
  342. * @return the current Component (this)
  343. * @see #getSubElements
  344. */
  345. public Component getComponent() {
  346. return this;
  347. }
  348. /**
  349. * Returns a string representation of this JMenuBar. This method
  350. * is intended to be used only for debugging purposes, and the
  351. * content and format of the returned string may vary between
  352. * implementations. The returned string may be empty but may not
  353. * be <code>null</code>.
  354. *
  355. * @return a string representation of this JMenuBar.
  356. */
  357. protected String paramString() {
  358. String paintBorderString = (paintBorder ?
  359. "true" : "false");
  360. String marginString = (margin != null ?
  361. margin.toString() : "");
  362. return super.paramString() +
  363. ",margin=" + marginString +
  364. ",paintBorder=" + paintBorderString;
  365. }
  366. /////////////////
  367. // Accessibility support
  368. ////////////////
  369. /**
  370. * Get the AccessibleContext associated with this JComponent
  371. *
  372. * @return the AccessibleContext of this JComponent
  373. */
  374. public AccessibleContext getAccessibleContext() {
  375. if (accessibleContext == null) {
  376. accessibleContext = new AccessibleJMenuBar();
  377. }
  378. return accessibleContext;
  379. }
  380. /**
  381. * The class used to obtain the accessible role for this object.
  382. * <p>
  383. * <strong>Warning:</strong>
  384. * Serialized objects of this class will not be compatible with
  385. * future Swing releases. The current serialization support is appropriate
  386. * for short term storage or RMI between applications running the same
  387. * version of Swing. A future release of Swing will provide support for
  388. * long term persistence.
  389. */
  390. protected class AccessibleJMenuBar extends AccessibleJComponent
  391. implements AccessibleSelection {
  392. /**
  393. * Get the accessible state set of this object.
  394. *
  395. * @return an instance of AccessibleState containing the current state
  396. * of the object
  397. */
  398. public AccessibleStateSet getAccessibleStateSet() {
  399. AccessibleStateSet states = super.getAccessibleStateSet();
  400. return states;
  401. }
  402. /**
  403. * Get the role of this object.
  404. *
  405. * @return an instance of AccessibleRole describing the role of the
  406. * object
  407. */
  408. public AccessibleRole getAccessibleRole() {
  409. return AccessibleRole.MENU_BAR;
  410. }
  411. /**
  412. * Get the AccessibleSelection associated with this object if one
  413. * exists. Otherwise return null.
  414. */
  415. public AccessibleSelection getAccessibleSelection() {
  416. return this;
  417. }
  418. /**
  419. * Returns 1 if a menu is currently selected in this menu bar.
  420. *
  421. * @return 1 if a menu is currently selected, else 0
  422. */
  423. public int getAccessibleSelectionCount() {
  424. if (isSelected()) {
  425. return 1;
  426. } else {
  427. return 0;
  428. }
  429. }
  430. /**
  431. * Returns the currently selected menu if one is selected,
  432. * otherwise null.
  433. */
  434. public Accessible getAccessibleSelection(int i) {
  435. if (isSelected()) {
  436. if (i != 0) { // single selection model for JMenuBar
  437. return null;
  438. }
  439. int j = getSelectionModel().getSelectedIndex();
  440. if (getComponentAtIndex(j) instanceof Accessible) {
  441. return (Accessible) getComponentAtIndex(j);
  442. }
  443. }
  444. return null;
  445. }
  446. /**
  447. * Returns true if the current child of this object is selected.
  448. *
  449. * @param i the zero-based index of the child in this Accessible
  450. * object.
  451. * @see AccessibleContext#getAccessibleChild
  452. */
  453. public boolean isAccessibleChildSelected(int i) {
  454. return (i == getSelectionModel().getSelectedIndex());
  455. }
  456. /**
  457. * Selects the nth menu in the menu bar, forcing it to
  458. * pop up. If another menu is popped up, this will force
  459. * it to close. If the nth menu is already selected, this
  460. * method has no effect.
  461. *
  462. * @param i the zero-based index of selectable items
  463. * @see #getAccessibleStateSet
  464. */
  465. public void addAccessibleSelection(int i) {
  466. // first close up any open menu
  467. int j = getSelectionModel().getSelectedIndex();
  468. if (i == j) {
  469. return;
  470. }
  471. if (j >= 0 && j < getMenuCount()) {
  472. JMenu menu = getMenu(j);
  473. if (menu != null) {
  474. MenuSelectionManager.defaultManager().setSelectedPath(null);
  475. // menu.setPopupMenuVisible(false);
  476. }
  477. }
  478. // now popup the new menu
  479. getSelectionModel().setSelectedIndex(i);
  480. JMenu menu = getMenu(i);
  481. if (menu != null) {
  482. MenuElement me[] = new MenuElement[3];
  483. me[0] = JMenuBar.this;
  484. me[1] = menu;
  485. me[2] = menu.getPopupMenu();
  486. MenuSelectionManager.defaultManager().setSelectedPath(me);
  487. // menu.setPopupMenuVisible(true);
  488. }
  489. }
  490. /**
  491. * Removes the nth selected item in the object from the object's
  492. * selection. If the nth item isn't currently selected, this
  493. * method has no effect. Otherwise, it closes the popup menu.
  494. *
  495. * @param i the zero-based index of selectable items
  496. */
  497. public void removeAccessibleSelection(int i) {
  498. if (i >= 0 && i < getMenuCount()) {
  499. JMenu menu = getMenu(i);
  500. if (menu != null) {
  501. MenuSelectionManager.defaultManager().setSelectedPath(null);
  502. // menu.setPopupMenuVisible(false);
  503. }
  504. getSelectionModel().setSelectedIndex(-1);
  505. }
  506. }
  507. /**
  508. * Clears the selection in the object, so that nothing in the
  509. * object is selected. This will close any open menu.
  510. */
  511. public void clearAccessibleSelection() {
  512. int i = getSelectionModel().getSelectedIndex();
  513. if (i >= 0 && i < getMenuCount()) {
  514. JMenu menu = getMenu(i);
  515. if (menu != null) {
  516. MenuSelectionManager.defaultManager().setSelectedPath(null);
  517. // menu.setPopupMenuVisible(false);
  518. }
  519. }
  520. getSelectionModel().setSelectedIndex(-1);
  521. }
  522. /**
  523. * Normally causes every selected item in the object to be selected
  524. * if the object supports multiple selections. This method
  525. * makes no sense in a menu bar, and so does nothing.
  526. */
  527. public void selectAllAccessibleSelection() {
  528. }
  529. } // internal class AccessibleJMenuBar
  530. /**
  531. * Returns true to indicate that this component manages focus
  532. * events internally.
  533. *
  534. * @return true
  535. */
  536. public boolean isManagingFocus() {
  537. return true;
  538. }
  539. KeyboardBinding bindingForKeyStroke(KeyStroke ks,int condition) {
  540. // Does it exist for the MenuBar?
  541. KeyboardBinding kbb = super.bindingForKeyStroke(ks, condition);
  542. if (kbb != null)
  543. return kbb;
  544. int i;
  545. Component subComponents[];
  546. subComponents = getComponents();
  547. for(i=0 ; i < subComponents.length ; i++) {
  548. // If
  549. if(subComponents[i] instanceof JMenu) {
  550. kbb = bindingForKeyStrokeRecursive(subComponents[i], ks, condition);
  551. }
  552. if (kbb != null)
  553. return kbb;
  554. }
  555. return null;
  556. }
  557. static KeyboardBinding bindingForKeyStrokeRecursive(Component c,
  558. KeyStroke ks,int condition) {
  559. KeyboardBinding kbb = null;
  560. if (c==null)
  561. return null;
  562. if (c instanceof JComponent) {
  563. kbb = ((JComponent)c).bindingForKeyStroke(ks, condition);
  564. if (kbb != null)
  565. return kbb;
  566. }
  567. if (c instanceof JMenu) {
  568. JMenu m = (JMenu)c;
  569. int i;
  570. Component subComponents[];
  571. subComponents = m.getMenuComponents();
  572. if (subComponents != null) {
  573. for(i=0 ; i < subComponents.length ; i++) {
  574. if(subComponents[i] instanceof JMenuItem) {
  575. kbb = bindingForKeyStrokeRecursive(subComponents[i],
  576. ks, condition);
  577. }
  578. if (kbb != null)
  579. return kbb;
  580. }
  581. }
  582. }
  583. return kbb;
  584. }
  585. /**
  586. * Overrides <code>JComponent.addNotify</code> to register this
  587. * menu bar with the current {@link KeyboardManager}.
  588. */
  589. public void addNotify() {
  590. super.addNotify();
  591. KeyboardManager.getCurrentManager().registerMenuBar(this);
  592. }
  593. /**
  594. * Overrides <code>JComponent.removeNotify</code> to unregister this
  595. * menu bar with the current {@link KeyboardManager}.
  596. */
  597. public void removeNotify() {
  598. super.removeNotify();
  599. KeyboardManager.getCurrentManager().unregisterMenuBar(this);
  600. }
  601. private void writeObject(ObjectOutputStream s) throws IOException {
  602. s.defaultWriteObject();
  603. Object[] kvData = new Object[4];
  604. int n = 0;
  605. if (selectionModel instanceof Serializable) {
  606. kvData[n++] = "selectionModel";
  607. kvData[n++] = selectionModel;
  608. }
  609. s.writeObject(kvData);
  610. }
  611. /**
  612. * See JComponent.readObject() for information about serialization
  613. * in Swing.
  614. */
  615. private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException
  616. {
  617. s.defaultReadObject();
  618. Object[] kvData = (Object[])(s.readObject());
  619. for(int i = 0; i < kvData.length; i += 2) {
  620. if (kvData[i] == null) {
  621. break;
  622. }
  623. else if (kvData[i].equals("selectionModel")) {
  624. selectionModel = (SingleSelectionModel)kvData[i + 1];
  625. }
  626. }
  627. if ((ui != null) && (getUIClassID().equals(uiClassID))) {
  628. ui.installUI(this);
  629. }
  630. }
  631. }