2 Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
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)
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.
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
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
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. */
39 package javax.swing.plaf.basic;
41 import gnu.classpath.NotImplementedException;
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;
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;
74 * UI Delegate for JMenu
76 public class BasicMenuUI extends BasicMenuItemUI
79 * Selects a menu. This is used to delay menu selection.
81 class SelectMenuAction
82 extends AbstractAction
85 * Performs the action.
87 public void actionPerformed(ActionEvent event)
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)
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);
104 protected ChangeListener changeListener;
106 /* MenuListener listens to MenuEvents fired by JMenu */
107 protected MenuListener menuListener;
109 /* PropertyChangeListner that listens to propertyChangeEvents occuring in JMenu*/
110 protected PropertyChangeListener propertyChangeListener;
113 * Creates a new BasicMenuUI object.
117 mouseInputListener = createMouseInputListener((JMenu) menuItem);
118 menuListener = createMenuListener((JMenu) menuItem);
119 propertyChangeListener = createPropertyChangeListener((JMenu) menuItem);
123 * This method creates a new ChangeListener.
125 * @return A new ChangeListener.
127 protected ChangeListener createChangeListener(JComponent c)
129 return new ChangeHandler((JMenu) c, this);
133 * This method creates new MenuDragMouseListener to listen to mouse dragged events
134 * occuring in the Menu
136 * @param c the menu to listen to
138 * @return The MenuDrageMouseListener
140 protected MenuDragMouseListener createMenuDragMouseListener(JComponent c)
142 return new MenuDragMouseHandler();
146 * This method creates new MenuDragKeyListener to listen to key events
148 * @param c the menu to listen to
150 * @return The MenuKeyListener
152 protected MenuKeyListener createMenuKeyListener(JComponent c)
154 return new MenuKeyHandler();
158 * This method creates new MenuListener to listen to menu events
159 * occuring in the Menu
161 * @param c the menu to listen to
163 * @return The MenuListener
165 protected MenuListener createMenuListener(JComponent c)
167 return new MenuHandler();
171 * This method creates new MouseInputListener to listen to mouse input events
172 * occuring in the Menu
174 * @param c the menu to listen to
176 * @return The MouseInputListener
178 protected MouseInputListener createMouseInputListener(JComponent c)
180 return new MouseInputHandler();
184 * This method creates newPropertyChangeListener to listen to property changes
185 * occuring in the Menu
187 * @param c the menu to listen to
189 * @return The PropertyChangeListener
191 protected PropertyChangeListener createPropertyChangeListener(JComponent c)
193 return new PropertyChangeHandler();
197 * This method creates a new BasicMenuUI.
199 * @param c The JComponent to create a UI for.
201 * @return A new BasicMenuUI.
203 public static ComponentUI createUI(JComponent c)
205 return new BasicMenuUI();
209 * Get the component's maximum size.
211 * @param c The JComponent for which to get maximum size
213 * @return The maximum size of the component
215 public Dimension getMaximumSize(JComponent c)
217 return c.getPreferredSize();
221 * Returns the prefix for entries in the {@link UIDefaults} table.
225 protected String getPropertyPrefix()
231 * Initializes any default properties that this UI has from the defaults for
232 * the Basic look and feel.
234 protected void installDefaults()
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);
252 * Installs any keyboard actions. The list of keys that need to be bound are
253 * listed in Basic look and feel's defaults.
256 protected void installKeyboardActions()
258 super.installKeyboardActions();
262 * Creates and registers all the listeners for this UI delegate.
264 protected void installListeners()
266 super.installListeners();
267 ((JMenu) menuItem).addMenuListener(menuListener);
270 protected void setupPostTimer(JMenu menu)
272 Timer timer = new Timer(menu.getDelay(), new SelectMenuAction());
273 timer.setRepeats(false);
278 * This method uninstalls the defaults and sets any objects created during
281 protected void uninstallDefaults()
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;
297 * Uninstalls any keyboard actions. The list of keys used are listed in
298 * Basic look and feel's defaults.
300 protected void uninstallKeyboardActions()
302 super.installKeyboardActions();
306 * Unregisters all the listeners that this UI delegate was using. In
307 * addition, it will also null any listeners that it was using.
309 protected void uninstallListeners()
311 super.uninstallListeners();
312 ((JMenu) menuItem).removeMenuListener(menuListener);
316 * This class is used by menus to handle mouse events occuring in the
319 protected class MouseInputHandler implements MouseInputListener
321 public void mouseClicked(MouseEvent e)
323 // Nothing to do here.
326 public void mouseDragged(MouseEvent e)
328 MenuSelectionManager manager = MenuSelectionManager.defaultManager();
329 manager.processMouseEvent(e);
332 private boolean popupVisible()
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())
339 for (int i = 0; i < mb.getMenuCount(); i++)
341 JMenu m = mb.getMenu(i);
342 if (m != null && m.isPopupMenuVisible())
348 public void mouseEntered(MouseEvent e)
350 JMenu menu = (JMenu) menuItem;
351 if (menu.isEnabled())
353 MenuSelectionManager manager =
354 MenuSelectionManager.defaultManager();
355 MenuElement[] selectedPath = manager.getSelectedPath();
356 if (! menu.isTopLevelMenu())
358 // Open the menu immediately or delayed, depending on the
360 if(! (selectedPath.length > 0
361 && selectedPath[selectedPath.length - 1] == menu.getPopupMenu()))
363 if(menu.getDelay() == 0)
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);
373 manager.setSelectedPath(getPath());
374 setupPostTimer(menu);
380 if(selectedPath.length > 0
381 && selectedPath[0] == menu.getParent())
383 MenuElement[] newPath = new MenuElement[3];
384 newPath[0] = (MenuElement) menu.getParent();
386 newPath[2] = menu.getPopupMenu();
387 manager.setSelectedPath(newPath);
393 public void mouseExited(MouseEvent e)
395 MenuSelectionManager manager = MenuSelectionManager.defaultManager();
396 manager.processMouseEvent(e);
399 public void mouseMoved(MouseEvent e)
401 // Nothing to do here.
404 public void mousePressed(MouseEvent e)
406 MenuSelectionManager manager = MenuSelectionManager.defaultManager();
407 JMenu menu = (JMenu) menuItem;
408 if (menu.isEnabled())
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())
414 if (menu.isSelected())
415 manager.clearSelectedPath();
418 Container cnt = menu.getParent();
419 if (cnt != null && cnt instanceof JMenuBar)
421 MenuElement[] me = new MenuElement[2];
422 me[0] = (MenuElement) cnt;
424 manager.setSelectedPath(me);
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())
435 if(menu.isTopLevelMenu() || menu.getDelay() == 0)
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);
446 setupPostTimer(menu);
453 public void mouseReleased(MouseEvent e)
455 MenuSelectionManager manager = MenuSelectionManager.defaultManager();
456 manager.processMouseEvent(e);
461 * This class handles MenuEvents fired by the JMenu
463 private class MenuHandler implements MenuListener
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.
470 * @param e The MenuEvent.
472 public void menuCanceled(MenuEvent e)
478 * This method is called when menu is deselected. It clears selected index
479 * in the selectionModel of the menu parent.
481 * @param e The MenuEvent.
483 public void menuDeselected(MenuEvent e)
485 JMenu menu = (JMenu) menuItem;
486 if (menu.getParent() != null)
488 if (menu.isTopLevelMenu())
489 ((JMenuBar) menu.getParent()).getSelectionModel().clearSelection();
491 ((JPopupMenu) menu.getParent()).getSelectionModel().clearSelection();
496 * This method is called when menu is selected. It sets selected index
497 * in the selectionModel of the menu parent.
499 * @param e The MenuEvent.
501 public void menuSelected(MenuEvent e)
503 JMenu menu = (JMenu) menuItem;
504 if (menu.isTopLevelMenu())
505 ((JMenuBar) menu.getParent()).setSelected(menu);
507 ((JPopupMenu) menu.getParent()).setSelected(menu);
512 * Obsolete as of JDK1.4.
514 public class ChangeHandler implements ChangeListener
519 public boolean isSelected;
529 public BasicMenuUI ui;
534 public Component wasFocused;
539 public ChangeHandler(JMenu m, BasicMenuUI ui)
548 public void stateChanged(ChangeEvent e)
555 * This class handles mouse dragged events occuring in the menu.
557 private class MenuDragMouseHandler implements MenuDragMouseListener
560 * This method is invoked when mouse is dragged over the menu item.
562 * @param e The MenuDragMouseEvent
564 public void menuDragMouseDragged(MenuDragMouseEvent e)
566 if (menuItem.isEnabled())
568 MenuSelectionManager manager = e.getMenuSelectionManager();
569 MenuElement path[] = e.getPath();
571 Point p = e.getPoint();
572 if(p.x >= 0 && p.x < menuItem.getWidth()
573 && p.y >= 0 && p.y < menuItem.getHeight())
575 JMenu menu = (JMenu) menuItem;
576 MenuElement[] selectedPath = manager.getSelectedPath();
577 if(! (selectedPath.length > 0
578 && selectedPath[selectedPath.length-1]
579 == menu.getPopupMenu()))
581 if(menu.isTopLevelMenu() || menu.getDelay() == 0
582 || e.getID() == MouseEvent.MOUSE_DRAGGED)
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);
591 manager.setSelectedPath(path);
592 setupPostTimer(menu);
596 else if (e.getID() == MouseEvent.MOUSE_RELEASED)
598 Component comp = manager.componentForPoint(e.getComponent(),
601 manager.clearSelectedPath();
607 * This method is invoked when mouse enters the menu item while it is
610 * @param e The MenuDragMouseEvent
612 public void menuDragMouseEntered(MenuDragMouseEvent e)
614 // Nothing to do here.
618 * This method is invoked when mouse exits the menu item while
619 * it is being dragged
621 * @param e The MenuDragMouseEvent
623 public void menuDragMouseExited(MenuDragMouseEvent e)
625 // Nothing to do here.
629 * This method is invoked when mouse was dragged and released
630 * inside the menu item.
632 * @param e The MenuDragMouseEvent
634 public void menuDragMouseReleased(MenuDragMouseEvent e)
636 // Nothing to do here.
641 * This class handles key events occuring when menu item is visible on the
644 private class MenuKeyHandler implements MenuKeyListener
647 * This method is invoked when key has been pressed
649 * @param e A {@link MenuKeyEvent}.
651 public void menuKeyPressed(MenuKeyEvent e)
653 // Nothing to do here.
657 * This method is invoked when key has been pressed
659 * @param e A {@link MenuKeyEvent}.
661 public void menuKeyReleased(MenuKeyEvent e)
663 // Nothing to do here.
667 * This method is invoked when key has been typed
668 * It handles the mnemonic key for the menu item.
670 * @param e A {@link MenuKeyEvent}.
672 public void menuKeyTyped(MenuKeyEvent e)
673 throws NotImplementedException
675 // TODO: What should be done here, if anything?