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