001    /* BasicToolBarUI.java --
002       Copyright (C) 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.plaf.basic;
040    
041    import java.awt.BorderLayout;
042    import java.awt.Color;
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.Point;
049    import java.awt.Rectangle;
050    import java.awt.Window;
051    import java.awt.event.ActionEvent;
052    import java.awt.event.ContainerEvent;
053    import java.awt.event.ContainerListener;
054    import java.awt.event.FocusEvent;
055    import java.awt.event.FocusListener;
056    import java.awt.event.MouseEvent;
057    import java.awt.event.WindowAdapter;
058    import java.awt.event.WindowEvent;
059    import java.awt.event.WindowListener;
060    import java.beans.PropertyChangeEvent;
061    import java.beans.PropertyChangeListener;
062    import java.util.Hashtable;
063    
064    import javax.swing.AbstractAction;
065    import javax.swing.AbstractButton;
066    import javax.swing.Action;
067    import javax.swing.ActionMap;
068    import javax.swing.InputMap;
069    import javax.swing.JButton;
070    import javax.swing.JComponent;
071    import javax.swing.JDialog;
072    import javax.swing.JFrame;
073    import javax.swing.JToolBar;
074    import javax.swing.KeyStroke;
075    import javax.swing.LookAndFeel;
076    import javax.swing.RootPaneContainer;
077    import javax.swing.SwingConstants;
078    import javax.swing.SwingUtilities;
079    import javax.swing.UIManager;
080    import javax.swing.border.Border;
081    import javax.swing.border.CompoundBorder;
082    import javax.swing.event.MouseInputListener;
083    import javax.swing.plaf.ActionMapUIResource;
084    import javax.swing.plaf.ComponentUI;
085    import javax.swing.plaf.ToolBarUI;
086    import javax.swing.plaf.UIResource;
087    import javax.swing.plaf.basic.BasicBorders.ButtonBorder;
088    
089    /**
090     * This is the Basic Look and Feel UI class for JToolBar.
091     */
092    public class BasicToolBarUI extends ToolBarUI implements SwingConstants
093    {
094    
095      /**
096       * Implements the keyboard actions for JToolBar.
097       */
098      static class ToolBarAction
099        extends AbstractAction
100      {
101        /**
102         * Performs the action.
103         */
104        public void actionPerformed(ActionEvent event)
105        {
106          Object cmd = getValue("__command__");
107          JToolBar toolBar = (JToolBar) event.getSource();
108          BasicToolBarUI ui = (BasicToolBarUI) toolBar.getUI();
109    
110          if (cmd.equals("navigateRight"))
111            ui.navigateFocusedComp(EAST);
112          else if (cmd.equals("navigateLeft"))
113              ui.navigateFocusedComp(WEST);
114          else if (cmd.equals("navigateUp"))
115              ui.navigateFocusedComp(NORTH);
116          else if (cmd.equals("navigateDown"))
117            ui.navigateFocusedComp(SOUTH);
118          else
119            assert false : "Shouldn't reach here";
120        }
121      }
122    
123      /** Static owner of all DragWindows.
124       * This is package-private to avoid an accessor method.  */
125      static JFrame owner = new JFrame();
126    
127      /** The border used when the JToolBar is in nonrollover mode. */
128      private static Border nonRolloverBorder;
129    
130      /** The border used when the JToolBar is in rollover mode. */
131      private static Border rolloverBorder;
132    
133      /** The last known BorderLayout constraint before floating. */
134      protected String constraintBeforeFloating;
135    
136      /** The last known orientation of the JToolBar before floating.
137       * This is package-private to avoid an accessor method.  */
138      int lastGoodOrientation;
139    
140      /** The color of the border when it is dockable. */
141      protected Color dockingBorderColor;
142    
143      /** The background color of the JToolBar when it is dockable. */
144      protected Color dockingColor;
145    
146      /** The docking listener responsible for mouse events on the JToolBar. */
147      protected MouseInputListener dockingListener;
148    
149      /** The window used for dragging the JToolBar. */
150      protected BasicToolBarUI.DragWindow dragWindow;
151    
152      /** The color of the border when it is not dockable. */
153      protected Color floatingBorderColor;
154    
155      /** The background color of the JToolBar when it is not dockable. */
156      protected Color floatingColor;
157    
158      /** The index of the focused component. */
159      protected int focusedCompIndex;
160    
161      /** The PropertyChangeListener for the JToolBar. */
162      protected PropertyChangeListener propertyListener;
163    
164      /** The JToolBar this UI delegate is responsible for. */
165      protected JToolBar toolBar;
166    
167      /** The Container listener for the JToolBar. */
168      protected ContainerListener toolBarContListener;
169    
170      /** The Focus listener for the JToolBar. */
171      protected FocusListener toolBarFocusListener;
172    
173      /**
174       * @deprecated since JDK1.3.
175       */
176      protected KeyStroke leftKey;
177    
178      /**
179       * @deprecated since JDK1.3.
180       */
181      protected KeyStroke rightKey;
182    
183      /**
184       * @deprecated since JDK1.3.
185       */
186      protected KeyStroke upKey;
187    
188      /**
189       * @deprecated since JDK1.3.
190       */
191      protected KeyStroke downKey;
192    
193      /**
194       * The floating window that is responsible for holding the JToolBar when it
195       * is dragged outside of its original parent.
196       */
197      private transient Window floatFrame;
198    
199      /** The original parent of the JToolBar.
200       * This is package-private to avoid an accessor method.  */
201      transient Container origParent;
202    
203      /** A hashtable of components and their original borders.
204       * This is package-private to avoid an accessor method.  */
205      transient Hashtable borders;
206    
207      /** A window listener for the floatable frame. */
208      private transient WindowListener windowListener;
209    
210      /** A set of cached bounds of the JToolBar.
211       * This is package-private to avoid an accessor method.  */
212      transient Dimension cachedBounds;
213    
214      /** The cached orientation of the JToolBar.
215       * This is package-private to avoid an accessor method.  */
216      transient int cachedOrientation;
217    
218      /**
219       * This method creates a new <code>BasicToolBarUI</code> object for the given JToolBar.
220       */
221      public BasicToolBarUI()
222      {
223        // Do nothing here.
224      }
225    
226      /**
227       * This method returns whether the JToolBar can dock at the given position.
228       *
229       * @param c The component to try to dock in.
230       * @param p The position of the mouse cursor relative to the given
231       *        component.
232       *
233       * @return Whether the JToolBar can dock.
234       */
235      public boolean canDock(Component c, Point p)
236      {
237        return areaOfClick(c, p) != -1;
238      }
239    
240      /**
241       * This helper method returns the position of the JToolBar if it can dock.
242       *
243       * @param c The component to try to dock in.
244       * @param p The position of the mouse cursor relative to the given
245       *        component.
246       *
247       * @return One of the SwingConstants directions or -1 if the JToolBar can't
248       *         dock.
249       */
250      private int areaOfClick(Component c, Point p)
251      {
252        // Has to dock in immediate parent, not eventual root container.
253        Rectangle pBounds = c.getBounds();
254    
255        // XXX: In Sun's implementation, the space the toolbar has to dock is dependent on the size it had last.
256        Dimension d = toolBar.getSize();
257        int limit = Math.min(d.width, d.height);
258    
259        // The order of checking is 1. top 2. bottom 3. left 4. right
260        if (! pBounds.contains(p))
261          return -1;
262    
263        if (p.y < limit)
264          return SwingConstants.NORTH;
265    
266        if (p.y > (pBounds.height - limit))
267          return SwingConstants.SOUTH;
268    
269        if (p.x < limit)
270          return SwingConstants.WEST;
271    
272        if (p.x > (pBounds.width - limit))
273          return SwingConstants.EAST;
274    
275        return -1;
276      }
277    
278      /**
279       * This method creates a new DockingListener for the JToolBar.
280       *
281       * @return A new DockingListener for the JToolBar.
282       */
283      protected MouseInputListener createDockingListener()
284      {
285        return new DockingListener(toolBar);
286      }
287    
288      /**
289       * This method creates a new DragWindow for the given JToolBar.
290       *
291       * @param toolbar The JToolBar to create a DragWindow for.
292       *
293       * @return A new DragWindow.
294       */
295      protected BasicToolBarUI.DragWindow createDragWindow(JToolBar toolbar)
296      {
297        return new DragWindow();
298      }
299    
300      /**
301       * This method creates a new floating frame for the JToolBar. By default,
302       * this UI uses createFloatingWindow instead. This method of creating a
303       * floating frame is deprecated.
304       *
305       * @param toolbar The JToolBar to create a floating frame for.
306       *
307       * @return A new floating frame.
308       */
309      protected JFrame createFloatingFrame(JToolBar toolbar)
310      {
311        // FIXME: Though deprecated, this should still work.
312        return null;
313      }
314    
315      /**
316       * This method creates a new floating window for the JToolBar. This is the
317       * method used by default to create a floating container for the JToolBar.
318       *
319       * @param toolbar The JToolBar to create a floating window for.
320       *
321       * @return A new floating window.
322       */
323      protected RootPaneContainer createFloatingWindow(JToolBar toolbar)
324      {
325        // This one is used by default though.
326        return new ToolBarDialog();
327      }
328    
329      /**
330       * This method creates a new WindowListener for the JToolBar.
331       *
332       * @return A new WindowListener.
333       */
334      protected WindowListener createFrameListener()
335      {
336        return new FrameListener();
337      }
338    
339      /**
340       * This method creates a new nonRolloverBorder for JButtons when the
341       * JToolBar's rollover property is set to false.
342       *
343       * @return A new NonRolloverBorder.
344       */
345      protected Border createNonRolloverBorder()
346      {
347        Border b = UIManager.getBorder("ToolBar.nonrolloverBorder");
348        
349        if (b == null)
350          {
351            b = new CompoundBorder(
352                new ButtonBorder(UIManager.getColor("Button.shadow"),
353                                 UIManager.getColor("Button.darkShadow"),
354                                 UIManager.getColor("Button.light"),
355                                 UIManager.getColor("Button.highlight")),
356                BasicBorders.getMarginBorder());
357          }
358        
359        return b;  }
360    
361      /**
362       * This method creates a new PropertyChangeListener for the JToolBar.
363       *
364       * @return A new PropertyChangeListener.
365       */
366      protected PropertyChangeListener createPropertyListener()
367      {
368        return new PropertyListener();
369      }
370    
371      /**
372       * This method creates a new rollover border for JButtons when the
373       * JToolBar's rollover property is set to true.
374       *
375       * @return A new rollover border.
376       */
377      protected Border createRolloverBorder()
378      {
379        Border b = UIManager.getBorder("ToolBar.rolloverBorder");
380        
381        if (b == null)
382          {
383            b = new CompoundBorder(
384                new ButtonBorder(UIManager.getColor("Button.shadow"),
385                                 UIManager.getColor("Button.darkShadow"),
386                                 UIManager.getColor("Button.light"),
387                                 UIManager.getColor("Button.highlight")),
388                BasicBorders.getMarginBorder());
389          }
390        
391        return b;
392      }
393    
394      /**
395       * This method creates a new Container listener for the JToolBar.
396       *
397       * @return A new Container listener.
398       */
399      protected ContainerListener createToolBarContListener()
400      {
401        return new ToolBarContListener();
402      }
403    
404      /**
405       * This method creates a new FocusListener for the JToolBar.
406       *
407       * @return A new FocusListener for the JToolBar.
408       */
409      protected FocusListener createToolBarFocusListener()
410      {
411        return new ToolBarFocusListener();
412      }
413    
414      /**
415       * This method creates a new UI delegate for the given JComponent.
416       *
417       * @param c The JComponent to create a UI delegate for.
418       *
419       * @return A new UI delegate.
420       */
421      public static ComponentUI createUI(JComponent c)
422      {
423        return new BasicToolBarUI();
424      }
425    
426      /**
427       * This method is called to drag the DragWindow around when the JToolBar is
428       * being dragged around.
429       *
430       * @param position The mouse cursor coordinates relative to the JToolBar.
431       * @param origin The screen position of the JToolBar.
432       */
433      protected void dragTo(Point position, Point origin)
434      {
435        int loc = areaOfClick(origParent,
436                              SwingUtilities.convertPoint(toolBar, position,
437                                                          origParent));
438    
439        if (loc != -1)
440          {
441            dragWindow.setBorderColor(dockingBorderColor);
442            dragWindow.setBackground(dockingColor);
443          }
444        else
445          {
446            dragWindow.setBorderColor(floatingBorderColor);
447            dragWindow.setBackground(floatingColor);
448          }
449    
450        int w = 0;
451        int h = 0;
452    
453        boolean tmp = (loc == SwingConstants.NORTH)
454                      || (loc == SwingConstants.SOUTH) || (loc == -1);
455    
456        cachedOrientation = toolBar.getOrientation();
457        cachedBounds = toolBar.getSize();
458        if (((cachedOrientation == SwingConstants.HORIZONTAL) && tmp)
459            || ((cachedOrientation == VERTICAL) && ! tmp))
460          {
461            w = cachedBounds.width;
462            h = cachedBounds.height;
463          }
464        else
465          {
466            w = cachedBounds.height;
467            h = cachedBounds.width;
468          }
469    
470        Point p = dragWindow.getOffset();
471        Insets insets = toolBar.getInsets();
472    
473        dragWindow.setBounds((origin.x + position.x) - p.x
474                             - ((insets.left + insets.right) / 2),
475                             (origin.y + position.y) - p.y
476                             - ((insets.top + insets.bottom) / 2), w, h);
477    
478        if (! dragWindow.isVisible())
479          dragWindow.show();
480      }
481    
482      /**
483       * This method is used at the end of a drag session to place the frame in
484       * either its original parent as a docked JToolBar or in its floating
485       * frame.
486       *
487       * @param position The position of the mouse cursor relative to the
488       *        JToolBar.
489       * @param origin The screen position of the JToolBar before the drag session
490       *        started.
491       */
492      protected void floatAt(Point position, Point origin)
493      {
494        Point p = new Point(position);
495        int aoc = areaOfClick(origParent,
496                              SwingUtilities.convertPoint(toolBar, p, origParent));
497    
498        Container oldParent = toolBar.getParent();
499    
500        oldParent.remove(toolBar);
501        oldParent.doLayout();
502        oldParent.repaint();
503    
504        Container newParent;
505    
506        if (aoc == -1)
507          newParent = ((RootPaneContainer) floatFrame).getContentPane();
508        else
509          {
510            floatFrame.hide();
511            newParent = origParent;
512          }
513    
514        String constraint;
515        switch (aoc)
516          {
517          case SwingConstants.EAST:
518            constraint = BorderLayout.EAST;
519            break;
520          case SwingConstants.NORTH:
521            constraint = BorderLayout.NORTH;
522            break;
523          case SwingConstants.SOUTH:
524            constraint = BorderLayout.SOUTH;
525            break;
526          case SwingConstants.WEST:
527            constraint = BorderLayout.WEST;
528            break;
529          default:
530            constraint = BorderLayout.CENTER;
531            break;
532          }
533    
534        int newOrientation = SwingConstants.HORIZONTAL;
535        if ((aoc != -1)
536            && ((aoc == SwingConstants.EAST) || (aoc == SwingConstants.WEST)))
537          newOrientation = SwingConstants.VERTICAL;
538    
539        if (aoc != -1)
540          {
541            constraintBeforeFloating = constraint;
542            lastGoodOrientation = newOrientation;
543          }
544    
545        newParent.add(toolBar, constraint);
546    
547        setFloating(aoc == -1, null);
548        toolBar.setOrientation(newOrientation);
549    
550        Insets insets = floatFrame.getInsets();
551        Dimension dims = toolBar.getPreferredSize();
552        p = dragWindow.getOffset();
553        setFloatingLocation((position.x + origin.x) - p.x
554                            - ((insets.left + insets.right) / 2),
555                            (position.y + origin.y) - p.y
556                            - ((insets.top + insets.bottom) / 2));
557    
558        if (aoc == -1)
559          {
560            floatFrame.pack();
561            floatFrame.setSize(dims.width + insets.left + insets.right,
562                               dims.height + insets.top + insets.bottom);
563            floatFrame.show();
564          }
565    
566        newParent.invalidate();
567        newParent.validate();
568        newParent.repaint();
569      }
570    
571      /**
572       * This method returns the docking color.
573       *
574       * @return The docking color.
575       */
576      public Color getDockingColor()
577      {
578        return dockingColor;
579      }
580    
581      /**
582       * This method returns the Color which is displayed when over a floating
583       * area.
584       *
585       * @return The color which is displayed when over a floating area.
586       */
587      public Color getFloatingColor()
588      {
589        return floatingColor;
590      }
591    
592      /**
593       * This method returns the maximum size of the given JComponent for this UI.
594       *
595       * @param c The JComponent to find the maximum size for.
596       *
597       * @return The maximum size for this UI.
598       */
599      public Dimension getMaximumSize(JComponent c)
600      {
601        return getPreferredSize(c);
602      }
603    
604      /**
605       * This method returns the minimum size of the given JComponent for this UI.
606       *
607       * @param c The JComponent to find a minimum size for.
608       *
609       * @return The minimum size for this UI.
610       */
611      public Dimension getMinimumSize(JComponent c)
612      {
613        return getPreferredSize(c);
614      }
615    
616      /**
617       * This method installs the needed components for the JToolBar.
618       */
619      protected void installComponents()
620      {
621        floatFrame = (Window) createFloatingWindow(toolBar);
622    
623        dragWindow = createDragWindow(toolBar);
624    
625        nonRolloverBorder = createNonRolloverBorder();
626        rolloverBorder = createRolloverBorder();
627    
628        borders = new Hashtable();
629        setRolloverBorders(toolBar.isRollover());
630    
631        fillHashtable();
632      }
633    
634      /**
635       * This method installs the defaults as specified by the look and feel.
636       */
637      protected void installDefaults()
638      {
639        LookAndFeel.installBorder(toolBar, "ToolBar.border");
640        LookAndFeel.installColorsAndFont(toolBar, "ToolBar.background",
641                                         "ToolBar.foreground", "ToolBar.font");
642    
643        dockingBorderColor = UIManager.getColor("ToolBar.dockingForeground");
644        dockingColor = UIManager.getColor("ToolBar.dockingBackground");
645    
646        floatingBorderColor = UIManager.getColor("ToolBar.floatingForeground");
647        floatingColor = UIManager.getColor("ToolBar.floatingBackground");
648      }
649    
650      /**
651       * This method installs the keyboard actions for the JToolBar as specified
652       * by the look and feel.
653       */
654      protected void installKeyboardActions()
655      {
656        // Install the input map.
657        InputMap inputMap =
658          (InputMap) SharedUIDefaults.get("ToolBar.ancestorInputMap");
659        SwingUtilities.replaceUIInputMap(toolBar,
660                                     JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
661                                     inputMap);
662    
663        // FIXME: The JDK uses a LazyActionMap for parentActionMap
664        SwingUtilities.replaceUIActionMap(toolBar, getActionMap());
665      }
666    
667      /**
668       * Fetches the action map from  the UI defaults, or create a new one
669       * if the action map hasn't been initialized.
670       *
671       * @return the action map
672       */
673      private ActionMap getActionMap()
674      {
675        ActionMap am = (ActionMap) UIManager.get("ToolBar.actionMap");
676        if (am == null)
677          {
678            am = createDefaultActions();
679            UIManager.getLookAndFeelDefaults().put("ToolBar.actionMap", am);
680          }
681        return am;
682      }
683    
684      private ActionMap createDefaultActions()
685      {
686        ActionMapUIResource am = new ActionMapUIResource();
687        Action action = new ToolBarAction();
688    
689        am.put("navigateLeft", action);
690        am.put("navigateRight", action);
691        am.put("navigateUp", action);
692        am.put("navigateDown", action);
693    
694        return am;
695      }
696    
697      /**
698       * This method installs listeners for the JToolBar.
699       */
700      protected void installListeners()
701      {
702        dockingListener = createDockingListener();
703        toolBar.addMouseListener(dockingListener);
704        toolBar.addMouseMotionListener(dockingListener);
705    
706        propertyListener = createPropertyListener();
707        toolBar.addPropertyChangeListener(propertyListener);
708    
709        toolBarContListener = createToolBarContListener();
710        toolBar.addContainerListener(toolBarContListener);
711    
712        windowListener = createFrameListener();
713        floatFrame.addWindowListener(windowListener);
714    
715        toolBarFocusListener = createToolBarFocusListener();
716        if (toolBarFocusListener != null)
717          {
718            int count = toolBar.getComponentCount();
719            for (int i = 0; i < count; i++)
720              toolBar.getComponent(i).addFocusListener(toolBarFocusListener);
721          }
722      }
723    
724      /**
725       * This method installs non rollover borders for each component inside the
726       * given JComponent.
727       *
728       * @param c The JComponent whose children need to have non rollover borders
729       *        installed.
730       */
731      protected void installNonRolloverBorders(JComponent c)
732      {
733        Component[] components = toolBar.getComponents();
734    
735        for (int i = 0; i < components.length; i++)
736          setBorderToNonRollover(components[i]);
737      }
738    
739      /**
740       * This method installs normal (or their original) borders for each
741       * component inside the given JComponent.
742       *
743       * @param c The JComponent whose children need to have their original
744       *        borders installed.
745       */
746      protected void installNormalBorders(JComponent c)
747      {
748        Component[] components = toolBar.getComponents();
749    
750        for (int i = 0; i < components.length; i++)
751          setBorderToNormal(components[i]);
752      }
753    
754      /**
755       * This method install rollover borders for each component inside the given
756       * JComponent.
757       *
758       * @param c The JComponent whose children need to have rollover borders
759       *        installed.
760       */
761      protected void installRolloverBorders(JComponent c)
762      {
763        Component[] components = toolBar.getComponents();
764    
765        for (int i = 0; i < components.length; i++)
766          setBorderToRollover(components[i]);
767      }
768    
769      /**
770       * This method fills the borders hashtable with a list of components that
771       * are JButtons and their borders.
772       */
773      private void fillHashtable()
774      {
775        Component[] c = toolBar.getComponents();
776    
777        for (int i = 0; i < c.length; i++)
778          {
779            if (c[i] instanceof JButton)
780              {
781                // Don't really care about anything other than JButtons
782                JButton b = (JButton) c[i];
783    
784                if (b.getBorder() != null)
785                  borders.put(b, b.getBorder());
786              }
787          }
788      }
789    
790      /**
791       * This method installs the UI for the given JComponent.
792       *
793       * @param c The JComponent to install a UI for.
794       */
795      public void installUI(JComponent c)
796      {
797        super.installUI(c);
798    
799        if (c instanceof JToolBar)
800          {
801            toolBar = (JToolBar) c;
802        installDefaults();
803        installComponents();
804            installListeners();
805            installKeyboardActions();
806          }
807      }
808    
809      /**
810       * This method returns whether the JToolBar is floating.
811       *
812       * @return Whether the JToolBar is floating.
813       */
814      public boolean isFloating()
815      {
816        return floatFrame.isVisible();
817      }
818    
819      /**
820       * This method returns whether rollover borders have been set.
821       *
822       * @return Whether rollover borders have been set.
823       */
824      public boolean isRolloverBorders()
825      {
826        return toolBar.isRollover();
827      }
828    
829      /**
830       * This method navigates in the given direction giving focus to the next
831       * component in the given direction.
832       *
833       * @param direction The direction to give focus to.
834       */
835      protected void navigateFocusedComp(int direction)
836      {
837        int count = toolBar.getComponentCount();
838        switch (direction)
839        {
840          case EAST:
841          case SOUTH:
842            if (focusedCompIndex >= 0 && focusedCompIndex < count)
843              {
844                int i = focusedCompIndex + 1;
845                boolean focusRequested = false;
846                // Find component to focus and request focus on it.
847                while (i != focusedCompIndex && ! focusRequested)
848                  {
849                    if (i >= count)
850                      i = 0;
851                    Component comp = toolBar.getComponentAtIndex(i++);
852                    if (comp != null && comp.isFocusable()
853                        && comp.isEnabled())
854                      {
855                        comp.requestFocus();
856                        focusRequested = true;
857                      }
858                  }
859              }
860            break;
861          case WEST:
862          case NORTH:
863            if (focusedCompIndex >= 0 && focusedCompIndex < count)
864              {
865                int i = focusedCompIndex - 1;
866                boolean focusRequested = false;
867                // Find component to focus and request focus on it.
868                while (i != focusedCompIndex && ! focusRequested)
869                  {
870                    if (i < 0)
871                      i = count - 1;
872                    Component comp = toolBar.getComponentAtIndex(i--);
873                    if (comp != null && comp.isFocusable()
874                        && comp.isEnabled())
875                      {
876                        comp.requestFocus();
877                        focusRequested = true;
878                      }
879                  }
880              }
881            break;
882          default:
883            break;
884        }
885      }
886    
887      /**
888       * This method sets the border of the given component to a non rollover
889       * border.
890       *
891       * @param c The Component whose border needs to be set.
892       */
893      protected void setBorderToNonRollover(Component c)
894      {
895        if (c instanceof AbstractButton)
896          {
897            AbstractButton b = (AbstractButton) c;
898            b.setRolloverEnabled(false);
899    
900            // Save old border in hashtable.
901            if (b.getBorder() != null)
902              borders.put(b, b.getBorder());
903            
904            b.setBorder(nonRolloverBorder);
905          }
906      }
907    
908      /**
909       * This method sets the border of the given component to its original value.
910       *
911       * @param c The Component whose border needs to be set.
912       */
913      protected void setBorderToNormal(Component c)
914      {
915        if (c instanceof AbstractButton)
916          {
917            AbstractButton b = (AbstractButton) c;
918            b.setRolloverEnabled(true);
919            b.setBorder((Border) borders.remove(b));
920          }
921      }
922    
923      /**
924       * This method sets the border of the given component to a rollover border.
925       *
926       * @param c The Component whose border needs to be set.
927       */
928      protected void setBorderToRollover(Component c)
929      {
930        if (c instanceof AbstractButton)
931          {
932            AbstractButton b = (AbstractButton) c;
933            b.setRolloverEnabled(false);
934            
935            // Save old border in hashtable.
936            if (b.getBorder() != null)
937              borders.put(b, b.getBorder());
938            
939            b.setBorder(rolloverBorder);
940          }
941      }
942    
943      /**
944       * This method sets the docking color.
945       *
946       * @param c The docking color.
947       */
948      public void setDockingColor(Color c)
949      {
950        dockingColor = c;
951      }
952    
953      /**
954       * This method sets the floating property for the JToolBar.
955       *
956       * @param b Whether the JToolBar is floating.
957       * @param p FIXME
958       */
959      public void setFloating(boolean b, Point p)
960      {
961        // FIXME: use p for something. It's not location
962        // since we already have setFloatingLocation.
963        floatFrame.setVisible(b);
964      }
965    
966      /**
967       * This method sets the color displayed when the JToolBar is not in a
968       * dockable area.
969       *
970       * @param c The floating color.
971       */
972      public void setFloatingColor(Color c)
973      {
974        floatingColor = c;
975      }
976    
977      /**
978       * This method sets the floating location of the JToolBar.
979       *
980       * @param x The x coordinate for the floating frame.
981       * @param y The y coordinate for the floating frame.
982       */
983      public void setFloatingLocation(int x, int y)
984      {
985        // x,y are the coordinates of the new JFrame created to store the toolbar
986        // XXX: The floating location is bogus is not floating.
987        floatFrame.setLocation(x, y);
988        floatFrame.invalidate();
989        floatFrame.validate();
990        floatFrame.repaint();
991      }
992    
993      /**
994       * This is a convenience method for changing the orientation of the
995       * JToolBar.
996       *
997       * @param orientation The new orientation.
998       */
999      public void setOrientation(int orientation)
1000      {
1001        toolBar.setOrientation(orientation);
1002      }
1003    
1004      /**
1005       * This method changes the child components to have rollover borders if the
1006       * given parameter is true. Otherwise, the components are set to have non
1007       * rollover borders.
1008       *
1009       * @param rollover Whether the children will have rollover borders.
1010       */
1011      public void setRolloverBorders(boolean rollover)
1012      {
1013        if (rollover)
1014          installRolloverBorders(toolBar);
1015        else
1016          installNonRolloverBorders(toolBar);
1017      }
1018    
1019      /**
1020       * This method uninstall UI installed components from the JToolBar.
1021       */
1022      protected void uninstallComponents()
1023      {
1024        installNormalBorders(toolBar);
1025        borders = null;
1026        cachedBounds = null;
1027    
1028        floatFrame = null;
1029        dragWindow = null;
1030      }
1031    
1032      /**
1033       * This method removes the defaults installed by the Look and Feel.
1034       */
1035      protected void uninstallDefaults()
1036      {
1037        toolBar.setBackground(null);
1038        toolBar.setForeground(null);
1039        toolBar.setFont(null);
1040    
1041        dockingBorderColor = null;
1042        dockingColor = null;
1043        floatingBorderColor = null;
1044        floatingColor = null;
1045      }
1046    
1047      /**
1048       * This method uninstalls keyboard actions installed by the UI.
1049       */
1050      protected void uninstallKeyboardActions()
1051      {
1052        SwingUtilities.replaceUIInputMap(toolBar, JComponent.
1053                                         WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null);
1054        SwingUtilities.replaceUIActionMap(toolBar, null);
1055      }
1056    
1057      /**
1058       * This method uninstalls listeners installed by the UI.
1059       */
1060      protected void uninstallListeners()
1061      {
1062        if (toolBarFocusListener != null)
1063          {
1064            int count = toolBar.getComponentCount();
1065            for (int i = 0; i < count; i++)
1066              toolBar.getComponent(i).removeFocusListener(toolBarFocusListener);
1067            toolBarFocusListener = null;
1068          }
1069    
1070        floatFrame.removeWindowListener(windowListener);
1071        windowListener = null;
1072    
1073        toolBar.removeContainerListener(toolBarContListener);
1074        toolBarContListener = null;
1075    
1076        toolBar.removeMouseMotionListener(dockingListener);
1077        toolBar.removeMouseListener(dockingListener);
1078        dockingListener = null;
1079      }
1080    
1081      /**
1082       * This method uninstalls the UI.
1083       *
1084       * @param c The JComponent that is having this UI removed.
1085       */
1086      public void uninstallUI(JComponent c)
1087      {
1088        uninstallKeyboardActions();
1089        uninstallListeners();
1090        uninstallComponents();
1091        uninstallDefaults();
1092        toolBar = null;
1093      }
1094    
1095      /**
1096       * This is the MouseHandler class that allows the user to drag the JToolBar
1097       * in and out of the parent and dock it if it can.
1098       */
1099      public class DockingListener implements MouseInputListener
1100      {
1101        /** Whether the JToolBar is being dragged. */
1102        protected boolean isDragging;
1103    
1104        /**
1105         * The origin point. This point is saved from the beginning press and is
1106         * used until the end of the drag session.
1107         */
1108        protected Point origin;
1109    
1110        /** The JToolBar being dragged. */
1111        protected JToolBar toolBar;
1112    
1113        /**
1114         * Creates a new DockingListener object.
1115         *
1116         * @param t The JToolBar this DockingListener is being used for.
1117         */
1118        public DockingListener(JToolBar t)
1119        {
1120          toolBar = t;
1121        }
1122    
1123        /**
1124         * This method is called when the mouse is clicked.
1125         *
1126         * @param e The MouseEvent.
1127         */
1128        public void mouseClicked(MouseEvent e)
1129        {
1130          // Nothing to do here.
1131        }
1132    
1133        /**
1134         * This method is called when the mouse is dragged. It delegates the drag
1135         * painting to the dragTo method.
1136         *
1137         * @param e The MouseEvent.
1138         */
1139        public void mouseDragged(MouseEvent e)
1140        {
1141          if (isDragging)
1142            dragTo(e.getPoint(), origin);
1143        }
1144    
1145        /**
1146         * This method is called when the mouse enters the JToolBar.
1147         *
1148         * @param e The MouseEvent.
1149         */
1150        public void mouseEntered(MouseEvent e)
1151        {
1152          // Nothing to do here.
1153        }
1154    
1155        /**
1156         * This method is called when the mouse exits the JToolBar.
1157         *
1158         * @param e The MouseEvent.
1159         */
1160        public void mouseExited(MouseEvent e)
1161        {
1162          // Nothing to do here.
1163        }
1164    
1165        /**
1166         * This method is called when the mouse is moved in the JToolBar.
1167         *
1168         * @param e The MouseEvent.
1169         */
1170        public void mouseMoved(MouseEvent e)
1171        {
1172          // Nothing to do here.
1173        }
1174    
1175        /**
1176         * This method is called when the mouse is pressed in the JToolBar. If the
1177         * press doesn't occur in a place where it causes the JToolBar to be
1178         * dragged, it returns. Otherwise, it starts a drag session.
1179         *
1180         * @param e The MouseEvent.
1181         */
1182        public void mousePressed(MouseEvent e)
1183        {
1184          if (! toolBar.isFloatable())
1185            return;
1186    
1187          Point ssd = e.getPoint();
1188          Insets insets = toolBar.getInsets();
1189    
1190          // Verify that this click occurs in the top inset.
1191          if (toolBar.getOrientation() == SwingConstants.HORIZONTAL)
1192            {
1193              if (e.getX() > insets.left)
1194                return;
1195            }
1196          else
1197            {
1198              if (e.getY() > insets.top)
1199                return;
1200            }
1201    
1202          origin = new Point(0, 0);
1203          if (toolBar.isShowing())
1204            SwingUtilities.convertPointToScreen(ssd, toolBar);
1205    
1206          if (! (SwingUtilities.getAncestorOfClass(Window.class, toolBar) instanceof UIResource))
1207            // Need to know who keeps the toolBar if it gets dragged back into it.
1208            origParent = toolBar.getParent();
1209          
1210          if (toolBar.isShowing())
1211            SwingUtilities.convertPointToScreen(origin, toolBar);
1212    
1213          isDragging = true;
1214    
1215          if (dragWindow != null)
1216            dragWindow.setOffset(new Point(cachedBounds.width / 2, 
1217                cachedBounds.height / 2));
1218    
1219          dragTo(e.getPoint(), origin);
1220        }
1221    
1222        /**
1223         * This method is called when the mouse is released from the JToolBar.
1224         *
1225         * @param e The MouseEvent.
1226         */
1227        public void mouseReleased(MouseEvent e)
1228        {
1229          if (! isDragging || ! toolBar.isFloatable())
1230            return;
1231    
1232          isDragging = false;
1233          floatAt(e.getPoint(), origin);
1234          dragWindow.hide();
1235        }
1236      }
1237    
1238      /**
1239       * This is the window that appears when the JToolBar is being dragged
1240       * around.
1241       */
1242      protected class DragWindow extends Window
1243      {
1244        /**
1245         * The current border color. It changes depending on whether the JToolBar
1246         * is over a place that allows it to dock.
1247         */
1248        private Color borderColor;
1249    
1250        /** The between the mouse and the top left corner of the window. */
1251        private Point offset;
1252    
1253        /**
1254         * Creates a new DragWindow object.
1255         * This is package-private to avoid an accessor method.
1256         */
1257        DragWindow()
1258        {
1259          super(owner);
1260        }
1261    
1262        /**
1263         * The color that the border should be.
1264         *
1265         * @return The border color.
1266         */
1267        public Color getBorderColor()
1268        {
1269          if (borderColor == null)
1270            return Color.BLACK;
1271    
1272          return borderColor;
1273        }
1274    
1275        /**
1276         * This method returns the insets for the DragWindow.
1277         *
1278         * @return The insets for the DragWindow.
1279         */
1280        public Insets getInsets()
1281        {
1282          // This window has no decorations, so insets are empty.
1283          return new Insets(0, 0, 0, 0);
1284        }
1285    
1286        /**
1287         * This method returns the mouse offset from the top left corner of the
1288         * DragWindow.
1289         *
1290         * @return The mouse offset.
1291         */
1292        public Point getOffset()
1293        {
1294          return offset;
1295        }
1296    
1297        /**
1298         * This method paints the DragWindow.
1299         *
1300         * @param g The Graphics object to paint with.
1301         */
1302        public void paint(Graphics g)
1303        {
1304          //  No visiting children necessary.
1305          Color saved = g.getColor();
1306          Rectangle b = getBounds();
1307    
1308          g.setColor(getBorderColor());
1309          g.drawRect(0, 0, b.width - 1, b.height - 1);
1310    
1311          g.setColor(saved);
1312        }
1313    
1314        /**
1315         * This method changes the border color.
1316         *
1317         * @param c The new border color.
1318         */
1319        public void setBorderColor(Color c)
1320        {
1321          borderColor = c;
1322        }
1323    
1324        /**
1325         * This method changes the mouse offset.
1326         *
1327         * @param p The new mouse offset.
1328         */
1329        public void setOffset(Point p)
1330        {
1331          offset = p;
1332        }
1333    
1334        /**
1335         * Sets the orientation of the toolbar and the
1336         * drag window.
1337         *
1338         * @param o - the new orientation of the toolbar and drag
1339         * window.
1340         */
1341        public void setOrientation(int o)
1342        {
1343          toolBar.setOrientation(o);
1344          if (dragWindow != null) 
1345            dragWindow.setOrientation(o);
1346        }
1347      }
1348    
1349      /**
1350       * This helper class listens for Window events from the floatable window and
1351       * if it is closed, returns the JToolBar to the last known good location.
1352       */
1353      protected class FrameListener extends WindowAdapter
1354      {
1355        /**
1356         * This method is called when the floating window is closed.
1357         *
1358         * @param e The WindowEvent.
1359         */
1360        public void windowClosing(WindowEvent e)
1361        {
1362          Container parent = toolBar.getParent();
1363          parent.remove(toolBar);
1364    
1365          if (origParent != null)
1366            {
1367              origParent.add(toolBar,
1368                             (constraintBeforeFloating != null)
1369                             ? constraintBeforeFloating : BorderLayout.NORTH);
1370              toolBar.setOrientation(lastGoodOrientation);
1371            }
1372    
1373          origParent.invalidate();
1374          origParent.validate();
1375          origParent.repaint();
1376        }
1377      }
1378    
1379      /**
1380       * This helper class listens for PropertyChangeEvents from the JToolBar.
1381       */
1382      protected class PropertyListener implements PropertyChangeListener
1383      {
1384        /**
1385         * This method is called when a property from the JToolBar is changed.
1386         *
1387         * @param e The PropertyChangeEvent.
1388         */
1389        public void propertyChange(PropertyChangeEvent e)
1390        {
1391          // FIXME: need name properties so can change floatFrame title.
1392          if (e.getPropertyName().equals("rollover") && toolBar != null)
1393            setRolloverBorders(toolBar.isRollover());
1394        }
1395      }
1396    
1397      /**
1398       * This helper class listens for components added to and removed from the
1399       * JToolBar.
1400       */
1401      protected class ToolBarContListener implements ContainerListener
1402      {
1403        /**
1404         * This method is responsible for setting rollover or non rollover for new
1405         * buttons added to the JToolBar.
1406         *
1407         * @param e The ContainerEvent.
1408         */
1409        public void componentAdded(ContainerEvent e)
1410        {
1411          if (e.getChild() instanceof JButton)
1412            {
1413              JButton b = (JButton) e.getChild();
1414    
1415              if (b.getBorder() != null)
1416                borders.put(b, b.getBorder());
1417            }
1418    
1419          if (isRolloverBorders())
1420            setBorderToRollover(e.getChild());
1421          else
1422            setBorderToNonRollover(e.getChild());
1423    
1424          cachedBounds = toolBar.getPreferredSize();
1425          cachedOrientation = toolBar.getOrientation();
1426    
1427          Component c = e.getChild();
1428          if (toolBarFocusListener != null)
1429            c.addFocusListener(toolBarFocusListener);
1430        }
1431    
1432        /**
1433         * This method is responsible for giving the child components their
1434         * original borders when they are removed.
1435         *
1436         * @param e The ContainerEvent.
1437         */
1438        public void componentRemoved(ContainerEvent e)
1439        {
1440          setBorderToNormal(e.getChild());
1441          cachedBounds = toolBar.getPreferredSize();
1442          cachedOrientation = toolBar.getOrientation();
1443    
1444          Component c = e.getChild();
1445          if (toolBarFocusListener != null)
1446            c.removeFocusListener(toolBarFocusListener);
1447        }
1448      }
1449    
1450      /**
1451       * This is the floating window that is returned when getFloatingWindow is
1452       * called.
1453       */
1454      private class ToolBarDialog extends JDialog implements UIResource
1455      {
1456        /**
1457         * Creates a new ToolBarDialog object with the name given by the JToolBar.
1458         */
1459        public ToolBarDialog()
1460        {
1461          super();
1462          setName((toolBar.getName() != null) ? toolBar.getName() : "");
1463        }
1464      }
1465    
1466      /**
1467       * DOCUMENT ME!
1468       */
1469      protected class ToolBarFocusListener implements FocusListener
1470      {
1471        /**
1472         * Creates a new ToolBarFocusListener object.
1473         */
1474        protected ToolBarFocusListener()
1475        {
1476          // Nothing to do here.
1477        }
1478    
1479        /**
1480         * Receives notification when the toolbar or one of it's component
1481         * receives the keyboard input focus.
1482         * 
1483         * @param e the focus event
1484         */
1485        public void focusGained(FocusEvent e)
1486        {
1487          Component c = e.getComponent();
1488          focusedCompIndex = toolBar.getComponentIndex(c);
1489        }
1490    
1491        /**
1492         * Receives notification when the toolbar or one of it's component
1493         * looses the keyboard input focus.
1494         * 
1495         * @param e the focus event
1496         */
1497        public void focusLost(FocusEvent e)
1498        {
1499          // Do nothing here.
1500        }
1501      }
1502    
1503      /**
1504       * This helper class acts as the border for the JToolBar.
1505       */
1506      private static class ToolBarBorder implements Border
1507      {
1508        /** The size of the larger, draggable side of the border. */
1509        private static final int offset = 10;
1510    
1511        /** The other sides. */
1512        private static final int regular = 2;
1513    
1514        /**
1515         * This method returns the border insets for the JToolBar.
1516         *
1517         * @param c The Component to find insets for.
1518         *
1519         * @return The border insets.
1520         */
1521        public Insets getBorderInsets(Component c)
1522        {
1523          if (c instanceof JToolBar)
1524            {
1525              JToolBar tb = (JToolBar) c;
1526              int orientation = tb.getOrientation();
1527    
1528              if (! tb.isFloatable())
1529                return new Insets(regular, regular, regular, regular);
1530              else if (orientation == SwingConstants.HORIZONTAL)
1531                return new Insets(regular, offset, regular, regular);
1532              else
1533                return new Insets(offset, regular, regular, regular);
1534            }
1535    
1536          return new Insets(0, 0, 0, 0);
1537        }
1538    
1539        /**
1540         * This method returns whether the border is opaque.
1541         *
1542         * @return Whether the border is opaque.
1543         */
1544        public boolean isBorderOpaque()
1545        {
1546          return false;
1547        }
1548    
1549        /**
1550         * This method paints the ribbed area of the border.
1551         *
1552         * @param g The Graphics object to paint with.
1553         * @param x The x coordinate of the area.
1554         * @param y The y coordinate of the area.
1555         * @param w The width of the area.
1556         * @param h The height of the area.
1557         * @param size The size of the bump.
1558         * @param c The color of the bumps.
1559         */
1560        private void paintBumps(Graphics g, int x, int y, int w, int h, int size,
1561                                Color c)
1562        {
1563          Color saved = g.getColor();
1564          g.setColor(c);
1565    
1566          int hgap = 2 * size;
1567          int vgap = 4 * size;
1568          int count = 0;
1569    
1570          for (int i = x; i < (w + x); i += hgap)
1571            for (int j = ((count++ % 2) == 0) ? y : (y + (2 * size)); j < (h + y);
1572                 j += vgap)
1573              g.fillRect(i, j, size, size);
1574    
1575          g.setColor(saved);
1576        }
1577    
1578        /**
1579         * This method paints the border around the given Component.
1580         *
1581         * @param c The Component whose border is being painted.
1582         * @param g The Graphics object to paint with.
1583         * @param x The x coordinate of the component.
1584         * @param y The y coordinate of the component.
1585         * @param width The width of the component.
1586         * @param height The height of the component.
1587         */
1588        public void paintBorder(Component c, Graphics g, int x, int y, int width,
1589                                int height)
1590        {
1591          if (c instanceof JToolBar)
1592            {
1593              JToolBar tb = (JToolBar) c;
1594    
1595              int orientation = tb.getOrientation();
1596    
1597              if (orientation == SwingConstants.HORIZONTAL)
1598                {
1599                  paintBumps(g, x, y, offset, height, 1, Color.WHITE);
1600                  paintBumps(g, x + 1, y + 1, offset - 1, height - 1, 1, Color.GRAY);
1601                }
1602              else
1603                {
1604                  paintBumps(g, x, y, width, offset, 1, Color.WHITE);
1605                  paintBumps(g, x + 1, y + 1, width - 1, offset - 1, 1, Color.GRAY);
1606                }
1607            }
1608        }
1609      }
1610    }