Dead
[official-gcc.git] / gomp-20050608-branch / libjava / classpath / javax / swing / plaf / basic / BasicPopupMenuUI.java
blobe15a17bab289240edd09ed6aee02bc9ea6705afc
1 /* BasicPopupMenuUI.java
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)
9 any later version.
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
19 02110-1301 USA.
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.
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. */
38 package javax.swing.plaf.basic;
40 import java.awt.AWTEvent;
41 import java.awt.Component;
42 import java.awt.Container;
43 import java.awt.Cursor;
44 import java.awt.Dimension;
45 import java.awt.Point;
46 import java.awt.event.ComponentEvent;
47 import java.awt.event.ComponentListener;
48 import java.awt.event.MouseEvent;
50 import javax.swing.BoxLayout;
51 import javax.swing.JComponent;
52 import javax.swing.JLayeredPane;
53 import javax.swing.JMenu;
54 import javax.swing.JMenuItem;
55 import javax.swing.JPopupMenu;
56 import javax.swing.LookAndFeel;
57 import javax.swing.MenuElement;
58 import javax.swing.MenuSelectionManager;
59 import javax.swing.RootPaneContainer;
60 import javax.swing.SwingUtilities;
61 import javax.swing.event.MouseInputListener;
62 import javax.swing.event.PopupMenuEvent;
63 import javax.swing.event.PopupMenuListener;
64 import javax.swing.plaf.ComponentUI;
65 import javax.swing.plaf.PopupMenuUI;
68 /**
69 * UI Delegate for JPopupMenu
71 public class BasicPopupMenuUI extends PopupMenuUI
73 /* popupMenu for which this UI delegate is for*/
74 protected JPopupMenu popupMenu;
76 /* MouseInputListener listens to mouse events. Package private for inner classes. */
77 static transient MouseInputListener mouseInputListener;
79 /* PopupMenuListener listens to popup menu events fired by JPopupMenu*/
80 private transient PopupMenuListener popupMenuListener;
82 /* ComponentListener listening to popupMenu's invoker.
83 * This is package-private to avoid an accessor method. */
84 TopWindowListener topWindowListener;
86 /**
87 * Creates a new BasicPopupMenuUI object.
89 public BasicPopupMenuUI()
91 popupMenuListener = new PopupMenuHandler();
92 topWindowListener = new TopWindowListener();
95 /**
96 * Factory method to create a BasicPopupMenuUI for the given {@link
97 * JComponent}, which should be a {@link JMenuItem}.
99 * @param x The {@link JComponent} a UI is being created for.
101 * @return A BasicPopupMenuUI for the {@link JComponent}.
103 public static ComponentUI createUI(JComponent x)
105 return new BasicPopupMenuUI();
109 * Installs and initializes all fields for this UI delegate. Any properties
110 * of the UI that need to be initialized and/or set to defaults will be
111 * done now. It will also install any listeners necessary.
113 * @param c The {@link JComponent} that is having this UI installed.
115 public void installUI(JComponent c)
117 super.installUI(c);
118 popupMenu = (JPopupMenu) c;
119 popupMenu.setLayout(new DefaultMenuLayout(popupMenu, BoxLayout.Y_AXIS));
120 popupMenu.setBorderPainted(true);
121 JPopupMenu.setDefaultLightWeightPopupEnabled(true);
123 installDefaults();
124 installListeners();
128 * This method installs the defaults that are defined in the Basic look
129 * and feel for this {@link JPopupMenu}.
131 public void installDefaults()
133 LookAndFeel.installColorsAndFont(popupMenu, "PopupMenu.background",
134 "PopupMenu.foreground", "PopupMenu.font");
135 LookAndFeel.installBorder(popupMenu, "PopupMenu.border");
136 popupMenu.setOpaque(true);
140 * This method installs the listeners for the {@link JMenuItem}.
142 protected void installListeners()
144 popupMenu.addPopupMenuListener(popupMenuListener);
148 * This method installs the keyboard actions for this {@link JPopupMenu}.
150 protected void installKeyboardActions()
152 // FIXME: Need to implement
156 * Performs the opposite of installUI. Any properties or resources that need
157 * to be cleaned up will be done now. It will also uninstall any listeners
158 * it has. In addition, any properties of this UI will be nulled.
160 * @param c The {@link JComponent} that is having this UI uninstalled.
162 public void uninstallUI(JComponent c)
164 uninstallListeners();
165 uninstallDefaults();
166 popupMenu = null;
170 * This method uninstalls the defaults and sets any objects created during
171 * install to null
173 protected void uninstallDefaults()
175 popupMenu.setBackground(null);
176 popupMenu.setBorder(null);
177 popupMenu.setFont(null);
178 popupMenu.setForeground(null);
182 * Unregisters all the listeners that this UI delegate was using.
184 protected void uninstallListeners()
186 popupMenu.removePopupMenuListener(popupMenuListener);
190 * Uninstalls any keyboard actions.
192 protected void uninstallKeyboardActions()
194 // FIXME: Need to implement
198 * This method returns the minimum size of the JPopupMenu.
200 * @param c The JComponent to find a size for.
202 * @return The minimum size.
204 public Dimension getMinimumSize(JComponent c)
206 return null;
210 * This method returns the preferred size of the JPopupMenu.
212 * @param c The JComponent to find a size for.
214 * @return The preferred size.
216 public Dimension getPreferredSize(JComponent c)
218 return null;
222 * This method returns the minimum size of the JPopupMenu.
224 * @param c The JComponent to find a size for.
226 * @return The minimum size.
228 public Dimension getMaximumSize(JComponent c)
230 return null;
234 * Return true if given mouse event is a platform popup trigger, and false
235 * otherwise
237 * @param e MouseEvent that is to be checked for popup trigger event
239 * @return true if given mouse event is a platform popup trigger, and false
240 * otherwise
242 public boolean isPopupTrigger(MouseEvent e)
244 return false;
248 * This listener handles PopupMenuEvents fired by JPopupMenu
250 private class PopupMenuHandler implements PopupMenuListener
253 * This method is invoked when JPopupMenu is cancelled.
255 * @param event the PopupMenuEvent
257 public void popupMenuCanceled(PopupMenuEvent event)
259 MenuSelectionManager manager = MenuSelectionManager.defaultManager();
260 manager.clearSelectedPath();
264 * This method is invoked when JPopupMenu becomes invisible
266 * @param event the PopupMenuEvent
268 public void popupMenuWillBecomeInvisible(PopupMenuEvent event)
270 // remove listener that listens to component events fired
271 // by the top - level window that this popup belongs to.
272 Component invoker = popupMenu.getInvoker();
274 RootPaneContainer rootContainer = (RootPaneContainer) SwingUtilities
275 .getRoot(invoker);
276 if (rootContainer != null)
278 ((Container) rootContainer).removeComponentListener(topWindowListener);
280 // If this popup menu is the last popup menu visible on the screen,
281 // then
282 // stop interrupting mouse events in the glass pane before hiding this
283 // last popup menu.
284 boolean topLevelMenu = (popupMenu.getInvoker() instanceof JMenu)
285 && ((JMenu) popupMenu.getInvoker()).isTopLevelMenu();
287 if (topLevelMenu || !(popupMenu.getInvoker() instanceof MenuElement))
289 // set glass pane not to interrupt mouse events and remove
290 // mouseInputListener
291 Container glassPane = (Container) rootContainer.getGlassPane();
292 glassPane.setVisible(false);
293 glassPane.removeMouseListener(mouseInputListener);
294 mouseInputListener = null;
300 * This method is invoked when JPopupMenu becomes visible
302 * @param event the PopupMenuEvent
304 public void popupMenuWillBecomeVisible(PopupMenuEvent event)
306 // Adds topWindowListener to top-level window to listener to
307 // ComponentEvents fired by it. We need to cancel this popup menu
308 // if topWindow to which this popup belongs was resized or moved.
309 Component invoker = popupMenu.getInvoker();
310 RootPaneContainer rootContainer = (RootPaneContainer) SwingUtilities
311 .getRoot(invoker);
312 ((Container) rootContainer).addComponentListener(topWindowListener);
314 // Set the glass pane to interrupt all mouse events originating in root
315 // container
316 if (mouseInputListener == null)
318 Container glassPane = (Container) rootContainer.getGlassPane();
319 glassPane.setVisible(true);
320 mouseInputListener = new MouseInputHandler(rootContainer);
321 glassPane.addMouseListener(mouseInputListener);
322 glassPane.addMouseMotionListener(mouseInputListener);
325 // if this popup menu is a free floating popup menu,
326 // then by default its first element should be always selected when
327 // this popup menu becomes visible.
328 MenuSelectionManager manager = MenuSelectionManager.defaultManager();
330 if (manager.getSelectedPath().length == 0)
332 // Set selected path to point to the first item in the popup menu
333 MenuElement[] path = new MenuElement[2];
334 path[0] = popupMenu;
335 Component[] comps = popupMenu.getComponents();
336 if (comps.length != 0 && comps[0] instanceof MenuElement)
338 path[1] = (MenuElement) comps[0];
339 manager.setSelectedPath(path);
346 * ComponentListener that listens to Component Events fired by the top -
347 * level window to which popup menu belongs. If top-level window was
348 * resized, moved or hidded then popup menu will be hidded and selected
349 * path of current menu hierarchy will be set to null.
351 private class TopWindowListener implements ComponentListener
354 * This method is invoked when top-level window is resized. This method
355 * closes current menu hierarchy.
357 * @param e The ComponentEvent
359 public void componentResized(ComponentEvent e)
361 MenuSelectionManager manager = MenuSelectionManager.defaultManager();
362 manager.clearSelectedPath();
366 * This method is invoked when top-level window is moved. This method
367 * closes current menu hierarchy.
369 * @param e The ComponentEvent
371 public void componentMoved(ComponentEvent e)
373 MenuSelectionManager manager = MenuSelectionManager.defaultManager();
374 manager.clearSelectedPath();
378 * This method is invoked when top-level window is shown This method
379 * does nothing by default.
381 * @param e The ComponentEvent
383 public void componentShown(ComponentEvent e)
385 MenuSelectionManager manager = MenuSelectionManager.defaultManager();
386 manager.clearSelectedPath();
390 * This method is invoked when top-level window is hidden This method
391 * closes current menu hierarchy.
393 * @param e The ComponentEvent
395 public void componentHidden(ComponentEvent e)
397 MenuSelectionManager manager = MenuSelectionManager.defaultManager();
398 manager.clearSelectedPath();
403 * MouseInputHandler listens to all mouse events originated in the root
404 * container. This class is responsible for closing menu hierarchy when the
405 * user presses mouse over any component that do not belong to the current
406 * menu hierarchy. This is acomplished by interrupting all mouse event in
407 * the glass pane and checking if other component was pressed while menu
408 * was open, before redestributing events further to intended components
410 private class MouseInputHandler implements MouseInputListener
412 private JLayeredPane layeredPane;
413 private Container glassPane;
414 private Cursor nativeCursor;
415 private transient Component mouseEventTarget;
416 private transient Component pressedComponent;
417 private transient Component lastComponentEntered;
418 private transient Component tempComponent;
419 private transient int pressCount;
422 * Creates a new MouseInputHandler object.
424 * @param c the top most root container
426 public MouseInputHandler(RootPaneContainer c)
428 layeredPane = c.getLayeredPane();
429 glassPane = (Container) c.getGlassPane();
433 * Handles mouse clicked event
435 * @param e Mouse event
437 public void mouseClicked(MouseEvent e)
439 handleEvent(e);
443 * Handles mouseDragged event
445 * @param e MouseEvent
447 public void mouseDragged(MouseEvent e)
449 handleEvent(e);
453 * Handles mouseEntered event
455 * @param e MouseEvent
457 public void mouseEntered(MouseEvent e)
459 handleEvent(e);
463 * Handles mouseExited event
465 * @param e MouseEvent
467 public void mouseExited(MouseEvent e)
469 handleEvent(e);
473 * Handles mouse moved event
475 * @param e MouseEvent
477 public void mouseMoved(MouseEvent e)
479 handleEvent(e);
483 * Handles mouse pressed event
485 * @param e MouseEvent
487 public void mousePressed(MouseEvent e)
489 handleEvent(e);
493 * Handles mouse released event
495 * @param e MouseEvent
497 public void mouseReleased(MouseEvent e)
499 handleEvent(e);
503 * This method determines component that was intended to received mouse
504 * event, before it was interrupted within the glass pane. This method
505 * also redispatches mouse entered and mouse exited events to the
506 * appropriate components. This code is slightly modified code from
507 * Container.LightweightDispatcher class, which is private inside
508 * Container class and cannot be used here.
510 public void acquireComponentForMouseEvent(MouseEvent me)
512 int x = me.getX();
513 int y = me.getY();
515 // Find the candidate which should receive this event.
516 Component parent = layeredPane;
517 Component candidate = null;
518 Point p = me.getPoint();
519 while ((candidate == null) && (parent != null))
521 p = SwingUtilities.convertPoint(glassPane, p.x, p.y, parent);
522 candidate = SwingUtilities.getDeepestComponentAt(parent, p.x, p.y);
524 if (candidate == null)
526 p = SwingUtilities.convertPoint(parent, p.x, p.y,
527 parent.getParent());
528 parent = parent.getParent();
532 // If the only candidate we found was the native container itself,
533 // don't dispatch any event at all. We only care about the lightweight
534 // children here.
535 if (candidate == layeredPane)
536 candidate = null;
538 // If our candidate is new, inform the old target we're leaving.
539 if ((lastComponentEntered != null) && lastComponentEntered.isShowing()
540 && (lastComponentEntered != candidate))
542 // Old candidate could have been removed from
543 // the layeredPane so we check first.
544 if (SwingUtilities.isDescendingFrom(lastComponentEntered, layeredPane))
546 Point tp = SwingUtilities.convertPoint(layeredPane, x, y,
547 lastComponentEntered);
548 MouseEvent exited = new MouseEvent(lastComponentEntered,
549 MouseEvent.MOUSE_EXITED,
550 me.getWhen(),
551 me.getModifiersEx(), tp.x,
552 tp.y, me.getClickCount(),
553 me.isPopupTrigger(),
554 me.getButton());
556 tempComponent = lastComponentEntered;
557 lastComponentEntered = null;
558 tempComponent.dispatchEvent(exited);
561 lastComponentEntered = null;
564 // If we have a candidate, maybe enter it.
565 if (candidate != null)
567 mouseEventTarget = candidate;
569 if (candidate.isLightweight() && candidate.isShowing()
570 && (candidate != layeredPane)
571 && (candidate != lastComponentEntered))
573 lastComponentEntered = mouseEventTarget;
575 Point cp = SwingUtilities.convertPoint(layeredPane, x, y,
576 lastComponentEntered);
577 MouseEvent entered = new MouseEvent(lastComponentEntered,
578 MouseEvent.MOUSE_ENTERED,
579 me.getWhen(),
580 me.getModifiersEx(), cp.x,
581 cp.y, me.getClickCount(),
582 me.isPopupTrigger(),
583 me.getButton());
584 lastComponentEntered.dispatchEvent(entered);
588 if ((me.getID() == MouseEvent.MOUSE_RELEASED)
589 || ((me.getID() == MouseEvent.MOUSE_PRESSED) && (pressCount > 0))
590 || (me.getID() == MouseEvent.MOUSE_DRAGGED))
592 // If any of the following events occur while a button is held down,
593 // they should be dispatched to the same component to which the
594 // original MOUSE_PRESSED event was dispatched:
595 // - MOUSE_RELEASED
596 // - MOUSE_PRESSED: another button pressed while the first is held down
597 // - MOUSE_DRAGGED
598 if (SwingUtilities.isDescendingFrom(pressedComponent, layeredPane))
599 mouseEventTarget = pressedComponent;
600 else if (me.getID() == MouseEvent.MOUSE_CLICKED)
602 // Don't dispatch CLICKED events whose target is not the same as the
603 // target for the original PRESSED event.
604 if (candidate != pressedComponent)
605 mouseEventTarget = null;
606 else if (pressCount == 0)
607 pressedComponent = null;
613 * This method handles mouse events interrupted by glassPane. It
614 * redispatches the mouse events appropriately to the intended components.
615 * The code in this method is also taken from
616 * Container.LightweightDispatcher class. The code is slightly modified
617 * to handle the case when mouse is released over non-menu component. In
618 * this case this method closes current menu hierarchy before
619 * redispatching the event further.
621 public void handleEvent(AWTEvent e)
623 if (e instanceof MouseEvent)
625 MouseEvent me = (MouseEvent) e;
627 acquireComponentForMouseEvent(me);
629 // Avoid dispatching ENTERED and EXITED events twice.
630 if (mouseEventTarget != null && mouseEventTarget.isShowing()
631 && (e.getID() != MouseEvent.MOUSE_ENTERED)
632 && (e.getID() != MouseEvent.MOUSE_EXITED))
634 MouseEvent newEvt = SwingUtilities.convertMouseEvent(glassPane,
636 mouseEventTarget);
638 mouseEventTarget.dispatchEvent(newEvt);
640 // If mouse was clicked over the component that is not part
641 // of menu hierarchy,then must close the menu hierarchy */
642 if (e.getID() == MouseEvent.MOUSE_RELEASED)
644 boolean partOfMenuHierarchy = false;
645 MenuSelectionManager manager = MenuSelectionManager
646 .defaultManager();
648 partOfMenuHierarchy = manager.isComponentPartOfCurrentMenu(mouseEventTarget);
650 if (! partOfMenuHierarchy)
651 manager.clearSelectedPath();
654 switch (e.getID())
656 case MouseEvent.MOUSE_PRESSED:
657 if (pressCount++ == 0)
658 pressedComponent = mouseEventTarget;
659 break;
660 case MouseEvent.MOUSE_RELEASED:
661 // Clear our memory of the original PRESSED event, only if
662 // we're not expecting a CLICKED event after this. If
663 // there is a CLICKED event after this, it will do clean up.
664 if ((--pressCount == 0)
665 && (mouseEventTarget != pressedComponent))
666 pressedComponent = null;
667 break;