1 /* BasicToolBarUI.java --
2    Copyright (C) 2004, 2005, 2006  Free Software Foundation, Inc.
3 
4 This file is part of GNU Classpath.
5 
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10 
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20 
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25 
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37 
38 
39 package javax.swing.plaf.basic;
40 
41 import java.awt.BorderLayout;
42 import java.awt.Color;
43 import java.awt.Component;
44 import java.awt.Container;
45 import java.awt.Dimension;
46 import java.awt.Graphics;
47 import java.awt.Insets;
48 import java.awt.Point;
49 import java.awt.Rectangle;
50 import java.awt.Window;
51 import java.awt.event.ActionEvent;
52 import java.awt.event.ContainerEvent;
53 import java.awt.event.ContainerListener;
54 import java.awt.event.FocusEvent;
55 import java.awt.event.FocusListener;
56 import java.awt.event.MouseEvent;
57 import java.awt.event.WindowAdapter;
58 import java.awt.event.WindowEvent;
59 import java.awt.event.WindowListener;
60 import java.beans.PropertyChangeEvent;
61 import java.beans.PropertyChangeListener;
62 import java.util.Hashtable;
63 
64 import javax.swing.AbstractAction;
65 import javax.swing.AbstractButton;
66 import javax.swing.Action;
67 import javax.swing.ActionMap;
68 import javax.swing.InputMap;
69 import javax.swing.JButton;
70 import javax.swing.JComponent;
71 import javax.swing.JDialog;
72 import javax.swing.JFrame;
73 import javax.swing.JToolBar;
74 import javax.swing.KeyStroke;
75 import javax.swing.LookAndFeel;
76 import javax.swing.RootPaneContainer;
77 import javax.swing.SwingConstants;
78 import javax.swing.SwingUtilities;
79 import javax.swing.UIManager;
80 import javax.swing.border.Border;
81 import javax.swing.border.CompoundBorder;
82 import javax.swing.event.MouseInputListener;
83 import javax.swing.plaf.ActionMapUIResource;
84 import javax.swing.plaf.ComponentUI;
85 import javax.swing.plaf.ToolBarUI;
86 import javax.swing.plaf.UIResource;
87 import javax.swing.plaf.basic.BasicBorders.ButtonBorder;
88 
89 /**
90  * This is the Basic Look and Feel UI class for JToolBar.
91  */
92 public class BasicToolBarUI extends ToolBarUI implements SwingConstants
93 {
94 
95   /**
96    * Implements the keyboard actions for JToolBar.
97    */
98   static class ToolBarAction
99     extends AbstractAction
100   {
101     /**
102      * Performs the action.
103      */
actionPerformed(ActionEvent event)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    */
BasicToolBarUI()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    */
canDock(Component c, Point p)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    */
areaOfClick(Component c, Point p)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    */
createDockingListener()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    */
createDragWindow(JToolBar toolbar)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    */
createFloatingFrame(JToolBar toolbar)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    */
createFloatingWindow(JToolBar toolbar)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    */
createFrameListener()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    */
createNonRolloverBorder()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    */
createPropertyListener()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    */
createRolloverBorder()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    */
createToolBarContListener()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    */
createToolBarFocusListener()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    */
createUI(JComponent c)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    */
dragTo(Point position, Point origin)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    */
floatAt(Point position, Point origin)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    */
getDockingColor()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    */
getFloatingColor()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    */
getMaximumSize(JComponent c)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    */
getMinimumSize(JComponent c)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    */
installComponents()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    */
installDefaults()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    */
installKeyboardActions()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    */
getActionMap()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 
createDefaultActions()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    */
installListeners()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    */
installNonRolloverBorders(JComponent c)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    */
installNormalBorders(JComponent c)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    */
installRolloverBorders(JComponent c)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    */
fillHashtable()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    */
installUI(JComponent c)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    */
isFloating()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    */
isRolloverBorders()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    */
navigateFocusedComp(int direction)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    */
setBorderToNonRollover(Component c)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    */
setBorderToNormal(Component c)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    */
setBorderToRollover(Component c)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    */
setDockingColor(Color c)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    */
setFloating(boolean b, Point p)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    */
setFloatingColor(Color c)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    */
setFloatingLocation(int x, int y)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    */
setOrientation(int orientation)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    */
setRolloverBorders(boolean rollover)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    */
uninstallComponents()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    */
uninstallDefaults()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    */
uninstallKeyboardActions()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    */
uninstallListeners()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    */
uninstallUI(JComponent c)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      */
DockingListener(JToolBar t)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      */
mouseClicked(MouseEvent e)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      */
mouseDragged(MouseEvent e)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      */
mouseEntered(MouseEvent e)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      */
mouseExited(MouseEvent e)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      */
mouseMoved(MouseEvent e)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      */
mousePressed(MouseEvent e)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      */
mouseReleased(MouseEvent e)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      */
DragWindow()1257     DragWindow()
1258     {
1259       super(owner);
1260     }
1261 
1262     /**
1263      * The color that the border should be.
1264      *
1265      * @return The border color.
1266      */
getBorderColor()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      */
getInsets()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      */
getOffset()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      */
paint(Graphics g)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      */
setBorderColor(Color c)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      */
setOffset(Point p)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      */
setOrientation(int o)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      */
windowClosing(WindowEvent e)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      */
propertyChange(PropertyChangeEvent e)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      */
componentAdded(ContainerEvent e)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      */
componentRemoved(ContainerEvent e)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      */
ToolBarDialog()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      */
ToolBarFocusListener()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      */
focusGained(FocusEvent e)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      */
focusLost(FocusEvent e)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      */
getBorderInsets(Component c)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      */
isBorderOpaque()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      */
paintBumps(Graphics g, int x, int y, int w, int h, int size, Color c)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      */
paintBorder(Component c, Graphics g, int x, int y, int width, int height)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 }
1611