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 gnu.java.lang.CPStringBuilder;
042    
043    import java.awt.Component;
044    import java.awt.Container;
045    import java.awt.Dimension;
046    import java.awt.Graphics;
047    import java.awt.Insets;
048    import java.awt.LayoutManager;
049    import java.beans.PropertyChangeListener;
050    
051    import javax.accessibility.Accessible;
052    import javax.accessibility.AccessibleContext;
053    import javax.accessibility.AccessibleRole;
054    import javax.accessibility.AccessibleStateSet;
055    import 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     */
067    public 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    }