1. /*
  2. * @(#)JToolBar.java 1.88 00/04/06
  3. *
  4. * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. package javax.swing;
  11. import java.awt.Color;
  12. import java.awt.Component;
  13. import java.awt.Dimension;
  14. import java.awt.Graphics;
  15. import java.awt.Insets;
  16. import java.awt.FlowLayout;
  17. import java.awt.event.*;
  18. import java.beans.*;
  19. import javax.swing.border.Border;
  20. import javax.swing.plaf.*;
  21. import javax.accessibility.*;
  22. import java.io.Serializable;
  23. import java.io.ObjectOutputStream;
  24. import java.io.ObjectInputStream;
  25. import java.io.IOException;
  26. import java.util.Hashtable;
  27. /**
  28. * <code>JToolBar</code> provides a component that is useful for
  29. * displaying commonly used <code>Action</code>s or controls.
  30. * For examples and information on using tool bars see
  31. * <a href="http://java.sun.com/docs/books/tutorial/uiswing/components/toolbar.html">How to Use Tool Bars</a>,
  32. * a section in <em>The Java Tutorial</em>.
  33. *
  34. * <p>
  35. * A tool bar can be dragged out into a separate window
  36. * by the user (unless the floatable property is set to false). In order
  37. * for drag-out to work correctly, it is recommended that you add
  38. * <code>JToolBar</code> instances to one of the four 'sides' of a
  39. * container whose layout manager is a <code>BorderLayout</code>,
  40. * and do not add children to any of the other four 'sides'.
  41. * <p>
  42. * For the keyboard keys used by this component in the standard Look and
  43. * Feel (L&F) renditions, see the
  44. * <a href="doc-files/Key-Index.html#JToolBar">JToolBar</a> key assignments.
  45. * <p>
  46. * <strong>Warning:</strong>
  47. * Serialized objects of this class will not be compatible with
  48. * future Swing releases. The current serialization support is appropriate
  49. * for short term storage or RMI between applications running the same
  50. * version of Swing. A future release of Swing will provide support for
  51. * long term persistence.
  52. *
  53. * @beaninfo
  54. * attribute: isContainer true
  55. * description: A component which displays commonly used controls or Actions.
  56. *
  57. * @version 1.88 04/06/00
  58. * @author Georges Saab
  59. * @author Jeff Shapiro
  60. * @see Action
  61. */
  62. public class JToolBar extends JComponent implements SwingConstants, Accessible
  63. {
  64. /**
  65. * @see #getUIClassID
  66. * @see #readObject
  67. */
  68. private static final String uiClassID = "ToolBarUI";
  69. private boolean paintBorder = true;
  70. private Insets margin = null;
  71. private boolean floatable = true;
  72. private int orientation = HORIZONTAL;
  73. /**
  74. * Creates a new toolbar; orientation defaults to <code>HORIZONTAL</code>.
  75. */
  76. public JToolBar()
  77. {
  78. this( HORIZONTAL );
  79. }
  80. /**
  81. * Creates a new toolbar with the specified <code>orientation.
  82. * The <code>orientation</code> must be either <code>HORIZONTAL</code>
  83. * or <code>VERTICAL</code>.
  84. *
  85. * @param orientation the orientation desired
  86. */
  87. public JToolBar( int orientation )
  88. {
  89. this(null, orientation);
  90. }
  91. /**
  92. * Creates a new toolbar with the specified <code>name</code>. The
  93. * name is used as the title of the undocked toolbar. The default
  94. * orientation is <code>HORIZONTAL</code>.
  95. *
  96. * @param name the name of the toolbar
  97. * @since 1.3
  98. */
  99. public JToolBar( String name ) {
  100. this(name, HORIZONTAL);
  101. }
  102. /**
  103. * Creates a new toolbar with a specified <code>name</code> and
  104. * <code>orientation</code>.
  105. * All other constructors call this constructor.
  106. * If <code>orientation</code> is an invalid value, an exception will
  107. * be thrown.
  108. *
  109. * @param name the name of the toolbar
  110. * @param orientation the initial orientation -- it must be
  111. * either <code>HORIZONTAL</code> or <code>VERTICAL</code>
  112. * @exception IllegalArgumentException if orientation is neither
  113. * <code>HORIZONTAL</code> nor <code>VERTICAL</code>
  114. * @since 1.3
  115. */
  116. public JToolBar( String name , int orientation) {
  117. setName(name);
  118. checkOrientation( orientation );
  119. this.orientation = orientation;
  120. if ( orientation == VERTICAL )
  121. {
  122. this.setLayout( new BoxLayout( this, BoxLayout.Y_AXIS ) );
  123. }
  124. else
  125. {
  126. if( SwingUtilities.isLeftToRight(this) ) {
  127. this.setLayout( new BoxLayout( this, BoxLayout.X_AXIS ) );
  128. } else {
  129. this.setLayout( new RightToLeftToolBarLayout() );
  130. }
  131. }
  132. addPropertyChangeListener( new PropertyChangeHandler() );
  133. updateUI();
  134. }
  135. /**
  136. * Returns the toolbar's current UI.
  137. * @see #setUI
  138. */
  139. public ToolBarUI getUI() {
  140. return (ToolBarUI)ui;
  141. }
  142. /**
  143. * Sets the L&F object that renders this component.
  144. *
  145. * @param ui the <code>ToolBarUI</code> L&F object
  146. * @see UIDefaults#getUI
  147. * @beaninfo
  148. * description: The menu item's UI delegate
  149. * bound: true
  150. * expert: true
  151. * hidden: true
  152. */
  153. public void setUI(ToolBarUI ui) {
  154. super.setUI(ui);
  155. }
  156. /**
  157. * Notification from the <code>UIFactory</code> that the L&F has changed.
  158. * Called to replace the UI with the latest version from the
  159. * <code>UIFactory</code>.
  160. *
  161. * @see JComponent#updateUI
  162. */
  163. public void updateUI() {
  164. setUI((ToolBarUI)UIManager.getUI(this));
  165. invalidate();
  166. }
  167. /**
  168. * Returns the name of the L&F class that renders this component.
  169. *
  170. * @return the string "ToolBarUI"
  171. * @see JComponent#getUIClassID
  172. * @see UIDefaults#getUI
  173. */
  174. public String getUIClassID() {
  175. return uiClassID;
  176. }
  177. /**
  178. * Returns the index of the specified component.
  179. * (Note: Separators occupy index positions.)
  180. *
  181. * @param c the <code>Component</code> to find
  182. * @return an integer indicating the component's position,
  183. * where 0 is first
  184. */
  185. public int getComponentIndex(Component c) {
  186. int ncomponents = this.getComponentCount();
  187. Component[] component = this.getComponents();
  188. for (int i = 0 ; i < ncomponents ; i++) {
  189. Component comp = component[i];
  190. if (comp == c)
  191. return i;
  192. }
  193. return -1;
  194. }
  195. /**
  196. * Returns the component at the specified index.
  197. *
  198. * @param i the component's position, where 0 is first
  199. * @return the <code>Component</code> at that position,
  200. * or <code>null</code> for an invalid index
  201. *
  202. */
  203. public Component getComponentAtIndex(int i) {
  204. int ncomponents = this.getComponentCount();
  205. if ( i >= 0 && i < ncomponents) {
  206. Component[] component = this.getComponents();
  207. return component[i];
  208. }
  209. return null;
  210. }
  211. /**
  212. * Sets the margin between the toolbar's border and
  213. * its buttons. Setting to <code>null</code> causes the toolbar to
  214. * use the default margins. The toolbar's default <code>Border</code>
  215. * object uses this value to create the proper margin.
  216. * However, if a non-default border is set on the toolbar,
  217. * it is that <code>Border</code> object's responsibility to create the
  218. * appropriate margin space (otherwise this property will
  219. * effectively be ignored).
  220. *
  221. * @param m an <code>Insets</code> object that defines the space
  222. * between the border and the buttons
  223. * @see Insets
  224. * @beaninfo
  225. * description: The margin between the toolbar's border and contents
  226. * bound: true
  227. * expert: true
  228. */
  229. public void setMargin(Insets m)
  230. {
  231. Insets old = margin;
  232. margin = m;
  233. firePropertyChange("margin", old, m);
  234. revalidate();
  235. repaint();
  236. }
  237. /**
  238. * Returns the margin between the toolbar's border and
  239. * its buttons.
  240. *
  241. * @return an <code>Insets</code> object containing the margin values
  242. * @see Insets
  243. */
  244. public Insets getMargin()
  245. {
  246. if(margin == null) {
  247. return new Insets(0,0,0,0);
  248. } else {
  249. return margin;
  250. }
  251. }
  252. /**
  253. * Checks whether the border should be painted.
  254. *
  255. * @return true if the border should be painted, else false
  256. * @see #setBorderPainted
  257. */
  258. public boolean isBorderPainted()
  259. {
  260. return paintBorder;
  261. }
  262. /**
  263. * Sets whether the border should be painted.
  264. *
  265. * @param b if true, the border is painted
  266. * @see #isBorderPainted
  267. * @beaninfo
  268. * description: Does the toolbar paint its borders?
  269. * bound: true
  270. * expert: true
  271. */
  272. public void setBorderPainted(boolean b)
  273. {
  274. if ( paintBorder != b )
  275. {
  276. boolean old = paintBorder;
  277. paintBorder = b;
  278. firePropertyChange("borderPainted", old, b);
  279. revalidate();
  280. repaint();
  281. }
  282. }
  283. /**
  284. * Paint the toolbar's border if <code>BorderPainted</code> property
  285. * is true.
  286. *
  287. * @param g the <code>Graphics</code> context in which the painting
  288. * is done
  289. * @see JComponent#paint
  290. * @see JComponent#setBorder
  291. */
  292. protected void paintBorder(Graphics g)
  293. {
  294. if (isBorderPainted())
  295. {
  296. super.paintBorder(g);
  297. }
  298. }
  299. /**
  300. * Returns true if the <code>JToolbar</code> can be dragged out by the user.
  301. *
  302. * @return true if the <code>JToolbar</code> can be dragged
  303. * out by the user, false otherwise
  304. */
  305. public boolean isFloatable()
  306. {
  307. return floatable;
  308. }
  309. /**
  310. * Sets whether the toolbar can be made to float.
  311. *
  312. * @param b if true, the toolbar can be dragged out; false otherwise
  313. * @see #isFloatable
  314. * @beaninfo
  315. * description: Can the toolbar be made to float by the user?
  316. * bound: true
  317. * preferred: true
  318. */
  319. public void setFloatable( boolean b )
  320. {
  321. if ( floatable != b )
  322. {
  323. boolean old = floatable;
  324. floatable = b;
  325. firePropertyChange("floatable", old, b);
  326. revalidate();
  327. repaint();
  328. }
  329. }
  330. /**
  331. * Returns the current orientation of the toolbar. The value is either
  332. * <code>HORIZONTAL</code> or <code>VERTICAL</code>.
  333. *
  334. * @return an integer representing the current orientation -- either
  335. * <code>HORIZONTAL</code> or <code>VERTICAL</code>
  336. * @see #setOrientation
  337. */
  338. public int getOrientation()
  339. {
  340. return this.orientation;
  341. }
  342. /**
  343. * Sets the orientation of the toolbar. The orientation must have
  344. * either the value <code>HORIZONTAL</code> or <code>VERTICAL</code>.
  345. * If <code>orientation</code> is
  346. * an invalid value, an exception will be thrown.
  347. *
  348. * @param o the new orientation -- either <code>HORIZONTAL</code> or
  349. * </code>VERTICAL</code>
  350. * @exception IllegalArgumentException if orientation is neither
  351. * <code>HORIZONTAL</code> nor <code>VERTICAL</code>
  352. * @see #getOrientation
  353. * @beaninfo
  354. * description: The current orientation of the toolbar
  355. * bound: true
  356. * preferred: true
  357. */
  358. public void setOrientation( int o )
  359. {
  360. checkOrientation( o );
  361. if ( orientation != o )
  362. {
  363. int old = orientation;
  364. orientation = o;
  365. if ( o == VERTICAL )
  366. setLayout( new BoxLayout( this, BoxLayout.Y_AXIS ) );
  367. else {
  368. if( SwingUtilities.isLeftToRight(this) ) {
  369. setLayout( new BoxLayout( this, BoxLayout.X_AXIS ) );
  370. } else {
  371. setLayout( new RightToLeftToolBarLayout() );
  372. }
  373. }
  374. firePropertyChange("orientation", old, o);
  375. revalidate();
  376. repaint();
  377. }
  378. }
  379. private void checkOrientation( int orientation )
  380. {
  381. switch ( orientation )
  382. {
  383. case VERTICAL:
  384. case HORIZONTAL:
  385. break;
  386. default:
  387. throw new IllegalArgumentException( "orientation must be one of: VERTICAL, HORIZONTAL" );
  388. }
  389. }
  390. /**
  391. * Appends a toolbar separator of default size to the end of the toolbar.
  392. * The default size is determined by the current look and feel.
  393. */
  394. public void addSeparator()
  395. {
  396. JToolBar.Separator s = new JToolBar.Separator();
  397. add(s);
  398. }
  399. /**
  400. * Appends a toolbar separator of a specified size to the end
  401. * of the toolbar.
  402. *
  403. * @param size the <code>Dimension</code> of the separator
  404. */
  405. public void addSeparator( Dimension size )
  406. {
  407. JToolBar.Separator s = new JToolBar.Separator( size );
  408. add(s);
  409. }
  410. /**
  411. * Adds a new <code>JButton</code> which dispatches the action.
  412. *
  413. * <p>
  414. * As of 1.3, this is no longer the preferred method for adding
  415. * <code>Action</code>s to a container. Instead it is recommended
  416. * to configure a control with an action using
  417. * using <code>setAction</code>, and then add that control directly
  418. * to the <code>Container</code>.
  419. *
  420. * @param a the <code>Action</code> object to add as a new menu item
  421. * @return the new button which dispatches the action
  422. */
  423. public JButton add(Action a) {
  424. JButton b = createActionComponent(a);
  425. b.setAction(a);
  426. add(b);
  427. return b;
  428. }
  429. /**
  430. * Factory method which creates the <code>JButton</code> for
  431. * <code>Action</code>s added to the <code>JToolBar</code>.
  432. * The default name is empty if a <code>null</code> action is passed.
  433. *
  434. * <p>
  435. * As of 1.3, this is no longer the preferred method for adding
  436. * <code>Action</code>s to a <code>Container</code>.
  437. * Instead it is recommended to configure a control with an action
  438. * using <code>setAction</code>, and then add that control directly
  439. * to the <code>Container</code>.
  440. *
  441. * @param a the <code>Action</code> for the button to be added
  442. * @return the newly created button
  443. * @see Action
  444. */
  445. protected JButton createActionComponent(Action a) {
  446. String text = (String)a.getValue(Action.NAME);
  447. Icon icon = (Icon)a.getValue(Action.SMALL_ICON);
  448. JButton b = new JButton(text, icon) {
  449. protected PropertyChangeListener createActionPropertyChangeListener(Action a) {
  450. PropertyChangeListener pcl = createActionChangeListener(this);
  451. if (pcl==null) {
  452. pcl = super.createActionPropertyChangeListener(a);
  453. }
  454. return pcl;
  455. }
  456. };
  457. if (icon !=null) {
  458. b.putClientProperty("hideActionText", Boolean.TRUE);
  459. }
  460. b.setHorizontalTextPosition(JButton.CENTER);
  461. b.setVerticalTextPosition(JButton.BOTTOM);
  462. b.setEnabled(a.isEnabled());
  463. b.setToolTipText((String)a.getValue(Action.SHORT_DESCRIPTION));
  464. return b;
  465. }
  466. /**
  467. * Returns a properly configured <code>PropertyChangeListener</code>
  468. * which updates the control as changes to the <code>Action</code> occur,
  469. * or <code>null</code> if the default
  470. * property change listener for the control is desired.
  471. *
  472. * <p>
  473. * As of 1.3, this is no longer the preferred method for adding
  474. * <code>Action</code>s to a <code>Container</code>.
  475. * Instead it is recommended to configure a control with an action
  476. * using <code>setAction</code>, and then add that control directly
  477. * to the <code>Container</code>.
  478. * @return <code>null</code>
  479. */
  480. protected PropertyChangeListener createActionChangeListener(JButton b) {
  481. return null;
  482. }
  483. /**
  484. * If a <code>JButton</code> is being added, it is initially
  485. * set to be disabled.
  486. *
  487. * @param comp the component to be enhanced
  488. * @param constraints the constraints to be enforced on the component
  489. * @param index the index of the component
  490. *
  491. */
  492. protected void addImpl(Component comp, Object constraints, int index) {
  493. super.addImpl(comp, constraints, index);
  494. if (comp instanceof JButton) {
  495. ((JButton)comp).setDefaultCapable(false);
  496. }
  497. }
  498. /**
  499. * A toolbar-specific separator. An object with dimension but
  500. * no contents used to divide buttons on a toolbar into groups.
  501. */
  502. static public class Separator extends JSeparator
  503. {
  504. private Dimension separatorSize;
  505. /**
  506. * Creates a new toolbar separator with the default size
  507. * as defined by the current look and feel.
  508. */
  509. public Separator()
  510. {
  511. this( null ); // let the UI define the default size
  512. }
  513. /**
  514. * Creates a new toolbar separator with the specified size.
  515. *
  516. * @param size the <code>Dimension</code> of the separator
  517. */
  518. public Separator( Dimension size )
  519. {
  520. super( JSeparator.HORIZONTAL );
  521. setSeparatorSize(size);
  522. }
  523. /**
  524. * Returns the name of the L&F class that renders this component.
  525. *
  526. * @return the string "ToolBarSeparatorUI"
  527. * @see JComponent#getUIClassID
  528. * @see UIDefaults#getUI
  529. */
  530. public String getUIClassID()
  531. {
  532. return "ToolBarSeparatorUI";
  533. }
  534. /**
  535. * Sets the size of the separator.
  536. *
  537. * @param size the new <code>Dimension</code> of the separator
  538. */
  539. public void setSeparatorSize( Dimension size )
  540. {
  541. if (size != null) {
  542. separatorSize = size;
  543. } else {
  544. super.updateUI();
  545. }
  546. this.invalidate();
  547. }
  548. /**
  549. * Returns the size of the separator
  550. *
  551. * @return the <code>Dimension</code> object containing the separator's
  552. * size (This is a reference, NOT a copy!)
  553. */
  554. public Dimension getSeparatorSize()
  555. {
  556. return separatorSize;
  557. }
  558. /**
  559. * Returns the minimum size for the separator.
  560. *
  561. * @return the <code>Dimension</code> object containing the separator's
  562. * minimum size
  563. */
  564. public Dimension getMinimumSize()
  565. {
  566. return getPreferredSize();
  567. }
  568. /**
  569. * Returns the maximum size for the separator.
  570. *
  571. * @return the <code>Dimension</code> object containing the separator's
  572. * maximum size
  573. */
  574. public Dimension getMaximumSize()
  575. {
  576. return getPreferredSize();
  577. }
  578. /**
  579. * Returns the preferred size for the separator.
  580. *
  581. * @return the <code>Dimension</code> object containing the separator's
  582. * preferred size
  583. */
  584. public Dimension getPreferredSize()
  585. {
  586. return separatorSize.getSize();
  587. }
  588. }
  589. /**
  590. * See <code>readObject</code> and <code>writeObject</code> in
  591. * <code>JComponent</code> for more
  592. * information about serialization in Swing.
  593. */
  594. private void writeObject(ObjectOutputStream s) throws IOException {
  595. s.defaultWriteObject();
  596. if ((ui != null) && (getUIClassID().equals(uiClassID))) {
  597. ui.installUI(this);
  598. }
  599. }
  600. /**
  601. * Returns a string representation of this <code>JToolBar</code>.
  602. * This method
  603. * is intended to be used only for debugging purposes, and the
  604. * content and format of the returned string may vary between
  605. * implementations. The returned string may be empty but may not
  606. * be <code>null</code>.
  607. *
  608. * @return a string representation of this <code>JToolBar</code>.
  609. */
  610. protected String paramString() {
  611. String paintBorderString = (paintBorder ?
  612. "true" : "false");
  613. String marginString = (margin != null ?
  614. margin.toString() : "");
  615. String floatableString = (floatable ?
  616. "true" : "false");
  617. String orientationString = (orientation == HORIZONTAL ?
  618. "HORIZONTAL" : "VERTICAL");
  619. return super.paramString() +
  620. ",floatable=" + floatableString +
  621. ",margin=" + marginString +
  622. ",orientation=" + orientationString +
  623. ",paintBorder=" + paintBorderString;
  624. }
  625. /*
  626. * This <code>PropertyChangeListener</code> is used to adjust the
  627. * default layout
  628. * manger when the toolBar is given a right-to-left ComponentOrientation.
  629. * This is a hack to work around the fact that the DefaultMenuLayout
  630. * (BoxLayout) isn't aware of ComponentOrientation. When BoxLayout is
  631. * made aware of ComponentOrientation, this listener will no longer be
  632. * necessary.
  633. */
  634. private class PropertyChangeHandler implements PropertyChangeListener {
  635. public void propertyChange(PropertyChangeEvent e) {
  636. String name = e.getPropertyName();
  637. if( name.equals("componentOrientation") ) {
  638. if( SwingUtilities.isLeftToRight(JToolBar.this) ) {
  639. setLayout(new BoxLayout(JToolBar.this, BoxLayout.X_AXIS));
  640. } else {
  641. setLayout(new RightToLeftToolBarLayout());
  642. }
  643. }
  644. }
  645. }
  646. private static class RightToLeftToolBarLayout
  647. extends FlowLayout implements UIResource
  648. {
  649. private RightToLeftToolBarLayout() {
  650. super(3/*FlowLayout.LEADING*/, 0, 0);
  651. }
  652. }
  653. /////////////////
  654. // Accessibility support
  655. ////////////////
  656. /**
  657. * Gets the AccessibleContext associated with this JToolBar.
  658. * For tool bars, the AccessibleContext takes the form of an
  659. * AccessibleJToolBar.
  660. * A new AccessibleJToolBar instance is created if necessary.
  661. *
  662. * @return an AccessibleJToolBar that serves as the
  663. * AccessibleContext of this JToolBar
  664. */
  665. public AccessibleContext getAccessibleContext() {
  666. if (accessibleContext == null) {
  667. accessibleContext = new AccessibleJToolBar();
  668. }
  669. return accessibleContext;
  670. }
  671. /**
  672. * This class implements accessibility support for the
  673. * <code>JToolBar</code> class. It provides an implementation of the
  674. * Java Accessibility API appropriate to tool bar user-interface elements.
  675. */
  676. protected class AccessibleJToolBar extends AccessibleJComponent {
  677. /**
  678. * Get the state of this object.
  679. *
  680. * @return an instance of AccessibleStateSet containing the current
  681. * state set of the object
  682. * @see AccessibleState
  683. */
  684. public AccessibleStateSet getAccessibleStateSet() {
  685. AccessibleStateSet states = super.getAccessibleStateSet();
  686. // FIXME: [[[WDW - need to add orientation from BoxLayout]]]
  687. // FIXME: [[[WDW - need to do SELECTABLE if SelectionModel is added]]]
  688. return states;
  689. }
  690. /**
  691. * Get the role of this object.
  692. *
  693. * @return an instance of AccessibleRole describing the role of the object
  694. */
  695. public AccessibleRole getAccessibleRole() {
  696. return AccessibleRole.TOOL_BAR;
  697. }
  698. } // inner class AccessibleJToolBar
  699. }