001/* JToolBar.java --
002   Copyright (C) 2002, 2004, 2005, 2006,  Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038
039package javax.swing;
040
041import gnu.java.lang.CPStringBuilder;
042
043import java.awt.Component;
044import java.awt.Container;
045import java.awt.Dimension;
046import java.awt.Graphics;
047import java.awt.Insets;
048import java.awt.LayoutManager;
049import java.beans.PropertyChangeListener;
050
051import javax.accessibility.Accessible;
052import javax.accessibility.AccessibleContext;
053import javax.accessibility.AccessibleRole;
054import javax.accessibility.AccessibleStateSet;
055import javax.swing.plaf.ToolBarUI;
056
057/**
058 * JToolBar is a component that provides a toolbar to Swing programs. Users
059 * can add buttons (or actions that will be represented by JButtons) as well
060 * as other components to the JToolBar. JToolBars can be dragged in and out
061 * of their parent components. If the JToolBar is dragged out of the parent,
062 * then it will be displayed in its own RootPaneContainer. For dragging to
063 * work properly, JToolBars need to be placed in a Container that has a
064 * BorderLayout. That parent Container cannot have components in the NORTH,
065 * EAST, SOUTH,  or WEST components (that is not the JToolBar).
066 */
067public class JToolBar extends JComponent implements SwingConstants, Accessible
068{
069  /**
070   * Provides the accessibility features for the <code>JToolBar</code>
071   * component.
072   */
073  protected class AccessibleJToolBar extends AccessibleJComponent
074  {
075    private static final long serialVersionUID = -5516888265903814215L;
076
077    /**
078     * Creates a new <code>AccessibleJToolBar</code> instance.
079     */
080    protected AccessibleJToolBar()
081    {
082      // Nothing to do here.
083    }
084
085    /**
086     * Returns a set containing the current state of the {@link JToolBar}
087     * component.  The current implementation simply calls the superclass.
088     *
089     * @return The accessible state set.
090     */
091    public AccessibleStateSet getAccessibleStateSet()
092    {
093      // running tests against the reference implementation, I was unable
094      // to find any state information that is set specifically by the
095      // tool bar...
096      return super.getAccessibleStateSet();
097    }
098
099    /**
100     * Returns the accessible role for the <code>JToolBar</code> component.
101     *
102     * @return {@link AccessibleRole#TOOL_BAR}.
103     */
104    public AccessibleRole getAccessibleRole()
105    {
106      return AccessibleRole.TOOL_BAR;
107    }
108  }
109
110  /**
111   * This is the private JToolBar layout manager.
112   */
113  private class DefaultToolBarLayout implements LayoutManager
114  {
115    /**
116     * This method is called when a new component is added to the container.
117     *
118     * @param name The name of the component added.
119     * @param comp The component that was added.
120     */
121    public void addLayoutComponent(String name, Component comp)
122    {
123      // Do nothing.
124    }
125
126    /**
127     * This method is called to lay out the given container  to position and
128     * size the child components.
129     *
130     * @param c The container to lay out.
131     *
132     * @throws Error DOCUMENT ME!
133     */
134    public void layoutContainer(Container c)
135    {
136      if (! (c instanceof JToolBar))
137        throw new Error("DefaultToolBarLayout can only be used on JToolBars.");
138      Insets insets = getInsets();
139      Insets margin = getMargin();
140      int middle;
141      if (margin != null)
142        {
143          insets.left += margin.left;
144          insets.top += margin.top;
145          insets.bottom += margin.bottom;
146          insets.right += margin.right;
147        }
148      Component[] components = c.getComponents();
149      Dimension tdims = c.getSize();
150      int start = 0;
151      Dimension pref;
152
153      if (getOrientation() == SwingUtilities.HORIZONTAL)
154        {
155          start += insets.left;
156          for (int i = 0; i < components.length; i++)
157            {
158              if (components[i] != null && components[i].isVisible())
159                {
160                  pref = components[i].getPreferredSize();
161                  if (pref != null)
162                    {
163                      middle = (tdims.height - pref.height) / 2;
164                      components[i].setBounds(start, middle, pref.width,
165                                              pref.height);
166                      start += pref.width;
167                    }
168                }
169            }
170        }
171      else
172        {
173          start += insets.top;
174          for (int i = 0; i < components.length; i++)
175            {
176              if (components[i] != null && components[i].isVisible())
177                {
178                  pref = components[i].getPreferredSize();
179                  if (pref != null)
180                    {
181                      middle = (tdims.width - pref.width) / 2;
182                      components[i].setBounds(middle, start, pref.width,
183                                              pref.height);
184                      start += pref.height;
185                    }
186                }
187            }
188        }
189    }
190
191    /**
192     * This method returns the minimum size of the given container given the
193     * child components.
194     *
195     * @param parent The container to measure.
196     *
197     * @return The minimum size of the given container.
198     */
199    public Dimension minimumLayoutSize(Container parent)
200    {
201      return preferredLayoutSize(parent);
202    }
203
204    /**
205     * This method returns the preferred size of the given container given the
206     * child components.
207     *
208     * @param parent The container to measure.
209     *
210     * @return The preferred size of the given container.
211     */
212    public Dimension preferredLayoutSize(Container parent)
213    {
214      int orientation = getOrientation();
215      Component[] components = getComponents();
216
217      int limit = 0;
218      int total = 0;
219      Dimension dims;
220
221      int w = 0;
222      int h = 0;
223
224      if (orientation == SwingConstants.HORIZONTAL)
225        {
226          for (int i = 0; i < components.length; i++)
227            {
228              dims = components[i].getPreferredSize();
229              if (dims != null)
230                {
231                  if (dims.height > limit)
232                    limit = dims.height;
233                  total += dims.width;
234                }
235            }
236          w = total;
237          h = limit;
238        }
239      else
240        {
241          for (int i = 0; i < components.length; i++)
242            {
243              dims = components[i].getPreferredSize();
244              if (dims != null)
245                {
246                  if (dims.width > limit)
247                    limit = dims.width;
248                  total += dims.height;
249                }
250            }
251          w = limit;
252          h = total;
253        }
254
255      Insets insets = getInsets();
256      w += insets.left + insets.right;
257      h += insets.top + insets.bottom;
258
259      Insets margin = getMargin();
260      if (margin != null)
261        {
262          w += margin.left + margin.right;
263          h += margin.top + margin.bottom;
264        }
265
266      return new Dimension(w, h);
267    }
268
269    /**
270     * This method is called when the given component  is removed from the
271     * container.
272     *
273     * @param comp The component removed.
274     */
275    public void removeLayoutComponent(Component comp)
276    {
277      // Do nothing.
278    }
279  }
280
281  /**
282   * This is an extension of JSeparator used in toolbars. Unlike JSeparator,
283   * nothing is painted for this Separator, it is only blank space that
284   * separates components.
285   */
286  public static class Separator extends JSeparator
287  {
288    /** DOCUMENT ME! */
289    private static final long serialVersionUID = -1656745644823105219L;
290
291    /**
292     * Creates a new Separator object.
293     */
294    public Separator()
295    {
296      super();
297    } // Separator()
298
299    /**
300     * Creates a new Separator object with the given size.
301     *
302     * @param size The size of the separator.
303     */
304    public Separator(Dimension size)
305    {
306      setPreferredSize(size);
307    } // Separator()
308
309    /**
310     * This method returns the String ID of the UI class of  Separator.
311     *
312     * @return The UI class' String ID.
313     */
314    public String getUIClassID()
315    {
316      return "ToolBarSeparatorUI";
317    } // getUIClassID()
318
319    /**
320     * This method returns the preferred size of the Separator.
321     *
322     * @return The preferred size of the Separator.
323     */
324    public Dimension getPreferredSize()
325    {
326      return super.getPreferredSize();
327    } // getPreferredSize()
328
329    /**
330     * This method returns the maximum size of the Separator.
331     *
332     * @return The maximum size of the Separator.
333     */
334    public Dimension getMaximumSize()
335    {
336      return super.getPreferredSize();
337    } // getMaximumSize()
338
339    /**
340     * This method returns the minimum size of the Separator.
341     *
342     * @return The minimum size of the Separator.
343     */
344    public Dimension getMinimumSize()
345    {
346      return super.getPreferredSize();
347    } // getMinimumSize()
348
349    /**
350     * This method returns the size of the Separator.
351     *
352     * @return The size of the Separator.
353     */
354    public Dimension getSeparatorSize()
355    {
356      return super.getPreferredSize();
357    } // getSeparatorSize()
358
359    /**
360     * This method sets the size of the Separator.
361     *
362     * @param size The new size of the Separator.
363     */
364    public void setSeparatorSize(Dimension size)
365    {
366      setPreferredSize(size);
367    } // setSeparatorSize()
368  } // Separator
369
370  /** DOCUMENT ME! */
371  private static final long serialVersionUID = -1269915519555129643L;
372
373  /** Whether the JToolBar paints its border. */
374  private transient boolean paintBorder = true;
375
376  /** The extra insets around the JToolBar. */
377  private transient Insets margin;
378
379  /** Whether the JToolBar can float (and be dragged around). */
380  private transient boolean floatable = true;
381
382  /** Whether the buttons will have rollover borders. */
383  private transient boolean rollover;
384
385  /** The orientation of the JToolBar. */
386  private int orientation = HORIZONTAL;
387
388  /**
389   * This method creates a new JToolBar object with horizontal orientation
390   * and no name.
391   */
392  public JToolBar()
393  {
394    this(null, HORIZONTAL);
395  } // JToolBar()
396
397  /**
398   * This method creates a new JToolBar with the given orientation and  no
399   * name.
400   *
401   * @param orientation JToolBar orientation (HORIZONTAL or VERTICAL)
402   */
403  public JToolBar(int orientation)
404  {
405    this(null, orientation);
406  } // JToolBar()
407
408  /**
409   * This method creates a new JToolBar object with the given name and
410   * horizontal orientation.
411   *
412   * @param name Name assigned to undocked tool bar.
413   */
414  public JToolBar(String name)
415  {
416    this(name, HORIZONTAL);
417  } // JToolBar()
418
419  /**
420   * This method creates a new JToolBar object with the given name and
421   * orientation.
422   *
423   * @param name Name assigned to undocked tool bar.
424   * @param orientation JToolBar orientation (HORIZONTAL or VERTICAL)
425   */
426  public JToolBar(String name, int orientation)
427  {
428    setName(name);
429    setOrientation(orientation);
430    setLayout(new DefaultToolBarLayout());
431    revalidate();
432    setOpaque(true);
433    updateUI();
434  }
435
436  /**
437   * This method adds a new JButton that performs the given Action to the
438   * JToolBar.
439   *
440   * @param action The Action to add to the JToolBar.
441   *
442   * @return The JButton that wraps the Action.
443   */
444  public JButton add(Action action)
445  {
446    JButton b = createActionComponent(action);
447    add(b);
448    return b;
449  } // add()
450
451  /**
452   * This method paints the border if the borderPainted property is true.
453   *
454   * @param graphics The graphics object to paint with.
455   */
456  protected void paintBorder(Graphics graphics)
457  {
458    if (paintBorder && isFloatable())
459      super.paintBorder(graphics);
460  } // paintBorder()
461
462  /**
463   * This method returns the UI class used to paint this JToolBar.
464   *
465   * @return The UI class for this JToolBar.
466   */
467  public ToolBarUI getUI()
468  {
469    return (ToolBarUI) ui;
470  } // getUI()
471
472  /**
473   * This method sets the UI used with the JToolBar.
474   *
475   * @param ui The UI used with the JToolBar.
476   */
477  public void setUI(ToolBarUI ui)
478  {
479    super.setUI(ui);
480  } // setUI()
481
482  /**
483   * This method resets the UI used to the Look and Feel defaults.
484   */
485  public void updateUI()
486  {
487    setUI((ToolBarUI) UIManager.getUI(this));
488  }
489
490  /**
491   * This method returns the String identifier for the UI class to the used
492   * with the JToolBar.
493   *
494   * @return The String identifier for the UI class.
495   */
496  public String getUIClassID()
497  {
498    return "ToolBarUI";
499  } // getUIClassID()
500
501  /**
502   * This method sets the rollover property for the JToolBar. In rollover
503   * mode, JButtons inside the JToolBar will only display their borders when
504   * the mouse is moving over them.
505   *
506   * @param b The new rollover property.
507   */
508  public void setRollover(boolean b)
509  {
510    if (b != rollover)
511      {
512        rollover = b;
513        firePropertyChange("rollover", ! rollover, rollover);
514        revalidate();
515        repaint();
516      }
517  }
518
519  /**
520   * This method returns the rollover property.
521   *
522   * @return The rollover property.
523   */
524  public boolean isRollover()
525  {
526    return rollover;
527  }
528
529  /**
530   * This method returns the index of the given component.
531   *
532   * @param component The component to find.
533   *
534   * @return The index of the given component.
535   */
536  public int getComponentIndex(Component component)
537  {
538    Component[] components = getComponents();
539    if (components == null)
540      return -1;
541
542    for (int i = 0; i < components.length; i++)
543      if (components[i] == component)
544        return i;
545
546    return -1;
547  } // getComponentIndex()
548
549  /**
550   * This method returns the component at the given index.
551   *
552   * @param index The index of the component.
553   *
554   * @return The component at the given index.
555   */
556  public Component getComponentAtIndex(int index)
557  {
558    return getComponent(index);
559  } // getComponentAtIndex()
560
561  /**
562   * This method returns the margin property.
563   *
564   * @return The margin property.
565   */
566  public Insets getMargin()
567  {
568    return margin;
569  } // getMargin()
570
571  /**
572   * This method sets the margin property. The margin property determines the
573   * extra space between the children components of the JToolBar and the
574   * border.
575   *
576   * @param margin The margin property.
577   */
578  public void setMargin(Insets margin)
579  {
580    if ((this.margin != null && margin == null)
581        || (this.margin == null && margin != null)
582        || (margin != null && this.margin != null
583        && (margin.left != this.margin.left
584        || margin.right != this.margin.right || margin.top != this.margin.top
585        || margin.bottom != this.margin.bottom)))
586      {
587        Insets oldMargin = this.margin;
588        this.margin = margin;
589        firePropertyChange("margin", oldMargin, this.margin);
590        revalidate();
591        repaint();
592      }
593  } // setMargin()
594
595  /**
596   * This method returns the borderPainted property.
597   *
598   * @return The borderPainted property.
599   */
600  public boolean isBorderPainted()
601  {
602    return paintBorder;
603  } // isBorderPainted()
604
605  /**
606   * This method sets the borderPainted property. If set to false, the border
607   * will not be painted.
608   *
609   * @param painted Whether the border will be painted.
610   */
611  public void setBorderPainted(boolean painted)
612  {
613    if (painted != paintBorder)
614      {
615        paintBorder = painted;
616        firePropertyChange("borderPainted", ! paintBorder,
617                           paintBorder);
618        repaint();
619      }
620  } // setBorderPainted()
621
622  /**
623   * This method returns the floatable property.
624   *
625   * @return The floatable property.
626   */
627  public boolean isFloatable()
628  {
629    return floatable;
630  } // isFloatable()
631
632  /**
633   * This method sets the floatable property. If set to false, the JToolBar
634   * cannot be dragged.
635   *
636   * @param floatable Whether the JToolBar can be dragged.
637   */
638  public void setFloatable(boolean floatable)
639  {
640    if (floatable != this.floatable)
641      {
642        this.floatable = floatable;
643        firePropertyChange("floatable", ! floatable, floatable);
644      }
645  } // setFloatable()
646
647  /**
648   * This method returns the orientation of the JToolBar.
649   *
650   * @return The orientation of the JToolBar.
651   */
652  public int getOrientation()
653  {
654    return orientation;
655  } // getOrientation()
656
657  /**
658   * This method sets the layout manager to be used with the JToolBar.
659   *
660   * @param mgr The Layout Manager used with the JToolBar.
661   */
662  public void setLayout(LayoutManager mgr)
663  {
664    super.setLayout(mgr);
665    revalidate();
666    repaint();
667  } // setLayout()
668
669  /**
670   * This method sets the orientation property for JToolBar.
671   *
672   * @param orientation The new orientation for JToolBar.
673   *
674   * @throws IllegalArgumentException If the orientation is not HORIZONTAL or
675   *         VERTICAL.
676   */
677  public void setOrientation(int orientation)
678  {
679    if (orientation != HORIZONTAL && orientation != VERTICAL)
680      throw new IllegalArgumentException(orientation
681                                         + " is not a legal orientation");
682    if (orientation != this.orientation)
683      {
684        int oldOrientation = this.orientation;
685        this.orientation = orientation;
686        firePropertyChange("orientation", oldOrientation, this.orientation);
687        revalidate();
688        repaint();
689      }
690  } // setOrientation()
691
692  /**
693   * This method adds a Separator of default size to the JToolBar.
694   */
695  public void addSeparator()
696  {
697    add(new Separator());
698  } // addSeparator()
699
700  /**
701   * This method adds a Separator with the given size to the JToolBar.
702   *
703   * @param size The size of the Separator.
704   */
705  public void addSeparator(Dimension size)
706  {
707    add(new Separator(size));
708  } // addSeparator()
709
710  /**
711   * This method is used to create JButtons which can be added to the JToolBar
712   * for the given action.
713   *
714   * @param action The action to create a JButton for.
715   *
716   * @return The JButton created from the action.
717   */
718  protected JButton createActionComponent(Action action)
719  {
720    return new JButton(action);
721  } // createActionComponent()
722
723  /**
724   * This method creates a pre-configured PropertyChangeListener which updates
725   * the control as changes are made to the Action. However, this is no
726   * longer the recommended way of adding Actions to Containers. As such,
727   * this method returns null.
728   *
729   * @param button The JButton to configure a PropertyChangeListener for.
730   *
731   * @return null.
732   */
733  protected PropertyChangeListener createActionChangeListener(JButton button)
734  {
735    // XXX: As specified, this returns null. But seems kind of strange, usually deprecated methods don't just return null, verify!
736    return null;
737  } // createActionChangeListener()
738
739  /**
740   * This method overrides Container's addImpl method. If a JButton is added,
741   * it is disabled.
742   *
743   * @param component The Component to add.
744   * @param constraints The Constraints placed on the component.
745   * @param index The index to place the Component at.
746   */
747  protected void addImpl(Component component, Object constraints, int index)
748  {
749    // XXX: Sun says disable button but test cases show otherwise.
750    super.addImpl(component, constraints, index);
751
752    // if we added a Swing Button then adjust this a little
753    if (component instanceof AbstractButton)
754      {
755        AbstractButton b = (AbstractButton) component;
756        b.setRolloverEnabled(rollover);
757      }
758
759  } // addImpl()
760
761  /**
762   * Returns a string describing the attributes for the <code>JToolBar</code>
763   * component, for use in debugging.  The return value is guaranteed to be
764   * non-<code>null</code>, but the format of the string may vary between
765   * implementations.
766   *
767   * @return A string describing the attributes of the <code>JToolBar</code>.
768   */
769  protected String paramString()
770  {
771    CPStringBuilder sb = new CPStringBuilder(super.paramString());
772    sb.append(",floatable=").append(floatable);
773    sb.append(",margin=");
774    if (margin != null)
775      sb.append(margin);
776    sb.append(",orientation=");
777    if (orientation == HORIZONTAL)
778      sb.append("HORIZONTAL");
779    else
780      sb.append(VERTICAL);
781    sb.append(",paintBorder=").append(paintBorder);
782    return sb.toString();
783  }
784
785  /**
786   * Returns the object that provides accessibility features for this
787   * <code>JToolBar</code> component.
788   *
789   * @return The accessible context (an instance of {@link AccessibleJToolBar}).
790   */
791  public AccessibleContext getAccessibleContext()
792  {
793    if (accessibleContext == null)
794      accessibleContext = new AccessibleJToolBar();
795
796    return accessibleContext;
797  }
798}