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