OSDN Git Service

Merged gcj-eclipse branch to trunk.
[pf3gnuchains/gcc-fork.git] / libjava / classpath / javax / swing / plaf / basic / BasicMenuUI.java
1 /* BasicMenuUI.java
2    Copyright (C) 2002, 2004, 2005  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 gnu.classpath.NotImplementedException;
42
43 import java.awt.Component;
44 import java.awt.Container;
45 import java.awt.Dimension;
46 import java.awt.Point;
47 import java.awt.event.ActionEvent;
48 import java.awt.event.MouseEvent;
49 import java.beans.PropertyChangeListener;
50
51 import javax.swing.AbstractAction;
52 import javax.swing.JComponent;
53 import javax.swing.JMenu;
54 import javax.swing.JMenuBar;
55 import javax.swing.JPopupMenu;
56 import javax.swing.LookAndFeel;
57 import javax.swing.MenuElement;
58 import javax.swing.MenuSelectionManager;
59 import javax.swing.Timer;
60 import javax.swing.UIDefaults;
61 import javax.swing.UIManager;
62 import javax.swing.event.ChangeEvent;
63 import javax.swing.event.ChangeListener;
64 import javax.swing.event.MenuDragMouseEvent;
65 import javax.swing.event.MenuDragMouseListener;
66 import javax.swing.event.MenuEvent;
67 import javax.swing.event.MenuKeyEvent;
68 import javax.swing.event.MenuKeyListener;
69 import javax.swing.event.MenuListener;
70 import javax.swing.event.MouseInputListener;
71 import javax.swing.plaf.ComponentUI;
72
73 /**
74  * UI Delegate for JMenu
75  */
76 public class BasicMenuUI extends BasicMenuItemUI
77 {
78   /**
79    * Selects a menu. This is used to delay menu selection.
80    */
81   class SelectMenuAction
82     extends AbstractAction
83   {
84     /**
85      * Performs the action.
86      */
87     public void actionPerformed(ActionEvent event)
88     {
89       JMenu menu = (JMenu) menuItem;
90       MenuSelectionManager defaultManager =
91         MenuSelectionManager.defaultManager();
92       MenuElement path[] = defaultManager.getSelectedPath();
93       if(path.length > 0 && path[path.length - 1] == menu)
94         {
95           MenuElement newPath[] = new MenuElement[path.length + 1];
96           System.arraycopy(path, 0, newPath, 0, path.length);
97           newPath[path.length] = menu.getPopupMenu();
98           defaultManager.setSelectedPath(newPath);
99       }
100     }
101     
102   }
103
104   protected ChangeListener changeListener;
105
106   /* MenuListener listens to MenuEvents fired by JMenu */
107   protected MenuListener menuListener;
108
109   /* PropertyChangeListner that listens to propertyChangeEvents occuring in JMenu*/
110   protected PropertyChangeListener propertyChangeListener;
111
112   /**
113    * Creates a new BasicMenuUI object.
114    */
115   public BasicMenuUI()
116   {
117     mouseInputListener = createMouseInputListener((JMenu) menuItem);
118     menuListener = createMenuListener((JMenu) menuItem);
119     propertyChangeListener = createPropertyChangeListener((JMenu) menuItem);
120   }
121
122   /**
123    * This method creates a new ChangeListener.
124    *
125    * @return A new ChangeListener.
126    */
127   protected ChangeListener createChangeListener(JComponent c)
128   {
129     return new ChangeHandler((JMenu) c, this);
130   }
131
132   /**
133    * This method creates new MenuDragMouseListener to listen to mouse dragged events
134    * occuring in the Menu
135    *
136    * @param c the menu to listen to
137    *
138    * @return The MenuDrageMouseListener
139    */
140   protected MenuDragMouseListener createMenuDragMouseListener(JComponent c)
141   {
142     return new MenuDragMouseHandler();
143   }
144
145   /**
146    * This method creates new MenuDragKeyListener to listen to key events
147    *
148    * @param c the menu to listen to
149    *
150    * @return The MenuKeyListener
151    */
152   protected MenuKeyListener createMenuKeyListener(JComponent c)
153   {
154     return new MenuKeyHandler();
155   }
156
157   /**
158    * This method creates new MenuListener to listen to menu events
159    * occuring in the Menu
160    *
161    * @param c the menu to listen to
162    *
163    * @return The MenuListener
164    */
165   protected MenuListener createMenuListener(JComponent c)
166   {
167     return new MenuHandler();
168   }
169
170   /**
171    * This method creates new MouseInputListener to listen to mouse input events
172    * occuring in the Menu
173    *
174    * @param c the menu to listen to
175    *
176    * @return The MouseInputListener
177    */
178   protected MouseInputListener createMouseInputListener(JComponent c)
179   {
180     return new MouseInputHandler();
181   }
182
183   /**
184    * This method creates newPropertyChangeListener to listen to property changes
185    * occuring in the Menu
186    *
187    * @param c the menu to listen to
188    *
189    * @return The PropertyChangeListener
190    */
191   protected PropertyChangeListener createPropertyChangeListener(JComponent c)
192   {
193     return new PropertyChangeHandler();
194   }
195
196   /**
197    * This method creates a new BasicMenuUI.
198    *
199    * @param c The JComponent to create a UI for.
200    *
201    * @return A new BasicMenuUI.
202    */
203   public static ComponentUI createUI(JComponent c)
204   {
205     return new BasicMenuUI();
206   }
207
208   /**
209    * Get the component's maximum size.
210    *
211    * @param c The JComponent for which to get maximum size
212    *
213    * @return The maximum size of the component
214    */
215   public Dimension getMaximumSize(JComponent c)
216   {
217     return c.getPreferredSize();
218   }
219
220   /**
221    * Returns the prefix for entries in the {@link UIDefaults} table.
222    *
223    * @return "Menu"
224    */
225   protected String getPropertyPrefix()
226   {
227     return "Menu";
228   }
229
230   /**
231    * Initializes any default properties that this UI has from the defaults for
232    * the Basic look and feel.
233    */
234   protected void installDefaults()
235   {
236     
237     LookAndFeel.installBorder(menuItem, "Menu.border");
238     LookAndFeel.installColorsAndFont(menuItem, "Menu.background",
239                                      "Menu.foreground", "Menu.font");
240     menuItem.setMargin(UIManager.getInsets("Menu.margin"));
241     acceleratorFont = UIManager.getFont("Menu.acceleratorFont");
242     acceleratorForeground = UIManager.getColor("Menu.acceleratorForeground");
243     acceleratorSelectionForeground = UIManager.getColor("Menu.acceleratorSelectionForeground");
244     selectionBackground = UIManager.getColor("Menu.selectionBackground");
245     selectionForeground = UIManager.getColor("Menu.selectionForeground");
246     arrowIcon = UIManager.getIcon("Menu.arrowIcon");
247     oldBorderPainted = UIManager.getBoolean("Menu.borderPainted");
248     ((JMenu) menuItem).setDelay(200);
249   }
250
251   /**
252    * Installs any keyboard actions. The list of keys that need to be bound are
253    * listed in Basic look and feel's defaults.
254    *
255    */
256   protected void installKeyboardActions()
257   {
258     super.installKeyboardActions();
259   }
260
261   /**
262    * Creates and registers all the listeners for this UI delegate.
263    */
264   protected void installListeners()
265   {
266     super.installListeners();
267     ((JMenu) menuItem).addMenuListener(menuListener);
268   }
269
270   protected void setupPostTimer(JMenu menu)
271   {
272     Timer timer = new Timer(menu.getDelay(), new SelectMenuAction());
273     timer.setRepeats(false);
274     timer.start();
275   }
276
277   /**
278    * This method uninstalls the defaults and sets any objects created during
279    * install to null
280    */
281   protected void uninstallDefaults()
282   {
283     menuItem.setBackground(null);
284     menuItem.setBorder(null);
285     menuItem.setFont(null);
286     menuItem.setForeground(null);
287     menuItem.setMargin(null);
288     acceleratorFont = null;
289     acceleratorForeground = null;
290     acceleratorSelectionForeground = null;
291     selectionBackground = null;
292     selectionForeground = null;
293     arrowIcon = null;
294   }
295
296   /**
297    * Uninstalls any keyboard actions. The list of keys used  are listed in
298    * Basic look and feel's defaults.
299    */
300   protected void uninstallKeyboardActions()
301   {
302     super.installKeyboardActions();
303   }
304
305   /**
306    * Unregisters all the listeners that this UI delegate was using. In
307    * addition, it will also null any listeners that it was using.
308    */
309   protected void uninstallListeners()
310   {
311     super.uninstallListeners();
312     ((JMenu) menuItem).removeMenuListener(menuListener);
313   }
314
315   /**
316    * This class is used by menus to handle mouse events occuring in the
317    * menu.
318    */
319   protected class MouseInputHandler implements MouseInputListener
320   {
321     public void mouseClicked(MouseEvent e)
322     {
323       // Nothing to do here.
324     }
325
326     public void mouseDragged(MouseEvent e)
327     {
328       MenuSelectionManager manager = MenuSelectionManager.defaultManager();
329       manager.processMouseEvent(e);
330     }
331
332     private boolean popupVisible()
333     {
334       JMenuBar mb = (JMenuBar) ((JMenu) menuItem).getParent();
335       // check if mb.isSelected because if no menus are selected
336       // we don't have to look through the list for popup menus
337       if (!mb.isSelected())
338         return false;
339       for (int i = 0; i < mb.getMenuCount(); i++)
340       {
341          JMenu m = mb.getMenu(i);
342         if (m != null && m.isPopupMenuVisible())
343           return true;
344       }
345       return false;
346     }
347
348     public void mouseEntered(MouseEvent e)
349     {
350       JMenu menu = (JMenu) menuItem;
351       if (menu.isEnabled())
352         {
353           MenuSelectionManager manager =
354             MenuSelectionManager.defaultManager();
355           MenuElement[] selectedPath = manager.getSelectedPath();
356           if (! menu.isTopLevelMenu())
357             {
358               // Open the menu immediately or delayed, depending on the
359               // delay value.
360               if(! (selectedPath.length > 0
361                   && selectedPath[selectedPath.length - 1] == menu.getPopupMenu()))
362                 {
363                   if(menu.getDelay() == 0)
364                     {
365                       MenuElement[] path = getPath();
366                       MenuElement[] newPath = new MenuElement[path.length + 1];
367                       System.arraycopy(path, 0, newPath, 0, path.length);
368                       newPath[path.length] = menu.getPopupMenu();
369                       manager.setSelectedPath(newPath);
370                     }
371                   else
372                     {
373                       manager.setSelectedPath(getPath());
374                       setupPostTimer(menu);
375                     }
376                 }
377             }
378           else
379             {
380               if(selectedPath.length > 0
381                   && selectedPath[0] == menu.getParent())
382                 {
383                   MenuElement[] newPath = new MenuElement[3];
384                   newPath[0] = (MenuElement) menu.getParent();
385                   newPath[1] = menu;
386                   newPath[2] = menu.getPopupMenu();
387                   manager.setSelectedPath(newPath);
388                 }
389             }
390         }
391     }
392
393     public void mouseExited(MouseEvent e)
394     {
395       MenuSelectionManager manager = MenuSelectionManager.defaultManager();
396       manager.processMouseEvent(e);
397     }
398
399     public void mouseMoved(MouseEvent e)
400     {
401       // Nothing to do here.
402     }
403
404     public void mousePressed(MouseEvent e)
405     {
406       MenuSelectionManager manager = MenuSelectionManager.defaultManager();
407       JMenu menu = (JMenu) menuItem;
408       if (menu.isEnabled())
409         {
410           // Open up the menu immediately if it's a toplevel menu.
411           // But not yet the popup, which might be opened delayed, see below.
412           if (menu.isTopLevelMenu())
413             {
414               if (menu.isSelected())
415                 manager.clearSelectedPath();
416               else
417                 {
418                   Container cnt = menu.getParent();
419                   if (cnt != null && cnt instanceof JMenuBar)
420                     {
421                       MenuElement[] me = new MenuElement[2];
422                       me[0] = (MenuElement) cnt;
423                       me[1] = menu;
424                       manager.setSelectedPath(me);
425                    }
426                 }
427             }
428
429           // Open the menu's popup. Either do that immediately if delay == 0,
430           // or delayed when delay > 0.
431           MenuElement[] selectedPath = manager.getSelectedPath();
432           if (selectedPath.length > 0
433               && selectedPath[selectedPath.length - 1] != menu.getPopupMenu())
434             {
435               if(menu.isTopLevelMenu() || menu.getDelay() == 0)
436                 {
437                   MenuElement[] newPath =
438                     new MenuElement[selectedPath.length + 1];
439                   System.arraycopy(selectedPath, 0, newPath, 0,
440                                    selectedPath.length);
441                   newPath[selectedPath.length] = menu.getPopupMenu();
442                   manager.setSelectedPath(newPath);
443                 }
444               else
445                 {
446                   setupPostTimer(menu);
447                 }
448             }
449
450         }
451     }
452
453     public void mouseReleased(MouseEvent e)
454     {
455       MenuSelectionManager manager = MenuSelectionManager.defaultManager();
456       manager.processMouseEvent(e);
457     }
458   }
459
460   /**
461    * This class handles MenuEvents fired by the JMenu
462    */
463   private class MenuHandler implements MenuListener
464   {
465     /**
466      * This method is called when menu is cancelled. The menu is cancelled
467      * when its popup menu is closed without selection. It clears selected index
468      * in the selectionModel of the menu parent.
469      *
470      * @param e The MenuEvent.
471      */
472     public void menuCanceled(MenuEvent e)
473     {
474       menuDeselected(e);
475     }
476
477     /**
478      * This method is called when menu is deselected. It clears selected index
479      * in the selectionModel of the menu parent.
480      *
481      * @param e The MenuEvent.
482      */
483     public void menuDeselected(MenuEvent e)
484     {
485       JMenu menu = (JMenu) menuItem;
486       if (menu.getParent() != null)
487         {
488           if (menu.isTopLevelMenu())
489             ((JMenuBar) menu.getParent()).getSelectionModel().clearSelection();
490           else
491             ((JPopupMenu) menu.getParent()).getSelectionModel().clearSelection();
492         }
493     }
494
495     /**
496      * This method is called when menu is selected.  It sets selected index
497      * in the selectionModel of the menu parent.
498      *
499      * @param e The MenuEvent.
500      */
501     public void menuSelected(MenuEvent e)
502     {
503       JMenu menu = (JMenu) menuItem;
504       if (menu.isTopLevelMenu())
505         ((JMenuBar) menu.getParent()).setSelected(menu);
506       else
507         ((JPopupMenu) menu.getParent()).setSelected(menu);
508     }
509   }
510
511   /**
512    * Obsolete as of JDK1.4.
513    */
514   public class ChangeHandler implements ChangeListener
515   {
516     /**
517      * Not used.
518      */
519     public boolean isSelected;
520
521     /**
522      * Not used.
523      */
524     public JMenu menu;
525
526     /**
527      * Not used.
528      */
529     public BasicMenuUI ui;
530
531     /**
532      * Not used.
533      */
534     public Component wasFocused;
535
536     /**
537      * Not used.
538      */
539     public ChangeHandler(JMenu m, BasicMenuUI ui)
540     {
541       menu = m;
542       this.ui = ui;
543     }
544
545     /**
546      * Not used.
547      */
548     public void stateChanged(ChangeEvent e)
549     {
550       // Not used.
551     }
552   }
553
554   /**
555    * This class handles mouse dragged events occuring in the menu.
556    */
557   private class MenuDragMouseHandler implements MenuDragMouseListener
558   {
559     /**
560      * This method is invoked when mouse is dragged over the menu item.
561      *
562      * @param e The MenuDragMouseEvent
563      */
564     public void menuDragMouseDragged(MenuDragMouseEvent e)
565     {
566       if (menuItem.isEnabled())
567         {
568           MenuSelectionManager manager = e.getMenuSelectionManager();
569           MenuElement path[] = e.getPath();
570
571           Point p = e.getPoint();
572           if(p.x >= 0 && p.x < menuItem.getWidth()
573               && p.y >= 0 && p.y < menuItem.getHeight())
574             {
575               JMenu menu = (JMenu) menuItem;
576               MenuElement[] selectedPath = manager.getSelectedPath();
577               if(! (selectedPath.length > 0
578                   && selectedPath[selectedPath.length-1]
579                                   == menu.getPopupMenu()))
580                 {
581                   if(menu.isTopLevelMenu() || menu.getDelay() == 0
582                      || e.getID() == MouseEvent.MOUSE_DRAGGED)
583                     {
584                       MenuElement[] newPath = new MenuElement[path.length + 1];
585                       System.arraycopy(path, 0, newPath, 0, path.length);
586                       newPath[path.length] = menu.getPopupMenu();
587                       manager.setSelectedPath(newPath);
588                     }
589                   else
590                     {
591                       manager.setSelectedPath(path);
592                       setupPostTimer(menu);
593                     }
594                 }
595             }
596           else if (e.getID() == MouseEvent.MOUSE_RELEASED)
597             {
598               Component comp = manager.componentForPoint(e.getComponent(),
599                                                          e.getPoint());
600               if (comp == null)
601                 manager.clearSelectedPath();
602             }
603         }
604     }
605
606     /**
607      * This method is invoked when mouse enters the menu item while it is
608      * being dragged.
609      *
610      * @param e The MenuDragMouseEvent
611      */
612     public void menuDragMouseEntered(MenuDragMouseEvent e)
613     {
614       // Nothing to do here.
615     }
616
617     /**
618      * This method is invoked when mouse exits the menu item while
619      * it is being dragged
620      *
621      * @param e The MenuDragMouseEvent
622      */
623     public void menuDragMouseExited(MenuDragMouseEvent e)
624     {
625       // Nothing to do here.
626     }
627
628     /**
629      * This method is invoked when mouse was dragged and released
630      * inside the menu item.
631      *
632      * @param e The MenuDragMouseEvent
633      */
634     public void menuDragMouseReleased(MenuDragMouseEvent e)
635     {
636       // Nothing to do here.
637     }
638   }
639
640   /**
641    * This class handles key events occuring when menu item is visible on the
642    * screen.
643    */
644   private class MenuKeyHandler implements MenuKeyListener
645   {
646     /**
647      * This method is invoked when key has been pressed
648      *
649      * @param e A {@link MenuKeyEvent}.
650      */
651     public void menuKeyPressed(MenuKeyEvent e)
652     {
653       // Nothing to do here.
654     }
655
656     /**
657      * This method is invoked when key has been pressed
658      *
659      * @param e A {@link MenuKeyEvent}.
660      */
661     public void menuKeyReleased(MenuKeyEvent e)
662     {
663       // Nothing to do here.
664     }
665
666     /**
667      * This method is invoked when key has been typed
668      * It handles the mnemonic key for the menu item.
669      *
670      * @param e A {@link MenuKeyEvent}.
671      */
672     public void menuKeyTyped(MenuKeyEvent e)
673     throws NotImplementedException
674     {
675       // TODO: What should be done here, if anything?
676     }
677   }
678 }