blob: b1cdd7db6afd114d0b82f894ada5156747509c0d [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package javax.swing;
27
28import java.awt.*;
29import java.awt.event.*;
30import java.io.IOException;
31import java.io.ObjectInputStream;
32import java.io.ObjectOutputStream;
33import java.io.Serializable;
34import java.beans.*;
35
36import java.util.Locale;
37import java.util.Vector;
38import java.util.Hashtable;
39import javax.accessibility.*;
40import javax.swing.plaf.PopupMenuUI;
41import javax.swing.plaf.ComponentUI;
42import javax.swing.plaf.basic.BasicComboPopup;
43import javax.swing.event.*;
44
45import java.applet.Applet;
46
47/**
48 * An implementation of a popup menu -- a small window that pops up
49 * and displays a series of choices. A <code>JPopupMenu</code> is used for the
50 * menu that appears when the user selects an item on the menu bar.
51 * It is also used for "pull-right" menu that appears when the
52 * selects a menu item that activates it. Finally, a <code>JPopupMenu</code>
53 * can also be used anywhere else you want a menu to appear. For
54 * example, when the user right-clicks in a specified area.
55 * <p>
56 * For information and examples of using popup menus, see
57 * <a
58 href="http://java.sun.com/docs/books/tutorial/uiswing/components/menu.html">How to Use Menus</a>
59 * in <em>The Java Tutorial.</em>
60 * <p>
61 * <strong>Warning:</strong> Swing is not thread safe. For more
62 * information see <a
63 * href="package-summary.html#threading">Swing's Threading
64 * Policy</a>.
65 * <p>
66 * <strong>Warning:</strong>
67 * Serialized objects of this class will not be compatible with
68 * future Swing releases. The current serialization support is
69 * appropriate for short term storage or RMI between applications running
70 * the same version of Swing. As of 1.4, support for long term storage
71 * of all JavaBeans<sup><font size="-2">TM</font></sup>
72 * has been added to the <code>java.beans</code> package.
73 * Please see {@link java.beans.XMLEncoder}.
74 *
75 * @beaninfo
76 * attribute: isContainer false
77 * description: A small window that pops up and displays a series of choices.
78 *
79 * @author Georges Saab
80 * @author David Karlton
81 * @author Arnaud Weber
82 */
83public class JPopupMenu extends JComponent implements Accessible,MenuElement {
84
85 /**
86 * @see #getUIClassID
87 * @see #readObject
88 */
89 private static final String uiClassID = "PopupMenuUI";
90
91 /**
92 * Key used in AppContext to determine if light way popups are the default.
93 */
94 private static final Object defaultLWPopupEnabledKey =
95 new StringBuffer("JPopupMenu.defaultLWPopupEnabledKey");
96
97 /** Bug#4425878-Property javax.swing.adjustPopupLocationToFit introduced */
98 static boolean popupPostionFixDisabled = false;
99
100 static {
101 popupPostionFixDisabled = java.security.AccessController.doPrivileged(
102 new sun.security.action.GetPropertyAction(
103 "javax.swing.adjustPopupLocationToFit","")).equals("false");
104
105 }
106
107 transient Component invoker;
108 transient Popup popup;
109 transient Frame frame;
110 private int desiredLocationX,desiredLocationY;
111
112 private String label = null;
113 private boolean paintBorder = true;
114 private Insets margin = null;
115
116 /**
117 * Used to indicate if lightweight popups should be used.
118 */
119 private boolean lightWeightPopup = true;
120
121 /*
122 * Model for the selected subcontrol.
123 */
124 private SingleSelectionModel selectionModel;
125
126 /* Lock object used in place of class object for synchronization.
127 * (4187686)
128 */
129 private static final Object classLock = new Object();
130
131 /* diagnostic aids -- should be false for production builds. */
132 private static final boolean TRACE = false; // trace creates and disposes
133 private static final boolean VERBOSE = false; // show reuse hits/misses
134 private static final boolean DEBUG = false; // show bad params, misc.
135
136 /**
137 * Sets the default value of the <code>lightWeightPopupEnabled</code>
138 * property.
139 *
140 * @param aFlag <code>true</code> if popups can be lightweight,
141 * otherwise <code>false</code>
142 * @see #getDefaultLightWeightPopupEnabled
143 * @see #setLightWeightPopupEnabled
144 */
145 public static void setDefaultLightWeightPopupEnabled(boolean aFlag) {
146 SwingUtilities.appContextPut(defaultLWPopupEnabledKey,
147 Boolean.valueOf(aFlag));
148 }
149
150 /**
151 * Gets the <code>defaultLightWeightPopupEnabled</code> property,
152 * which by default is <code>true</code>.
153 *
154 * @return the value of the <code>defaultLightWeightPopupEnabled</code>
155 * property
156 *
157 * @see #setDefaultLightWeightPopupEnabled
158 */
159 public static boolean getDefaultLightWeightPopupEnabled() {
160 Boolean b = (Boolean)
161 SwingUtilities.appContextGet(defaultLWPopupEnabledKey);
162 if (b == null) {
163 SwingUtilities.appContextPut(defaultLWPopupEnabledKey,
164 Boolean.TRUE);
165 return true;
166 }
167 return b.booleanValue();
168 }
169
170 /**
171 * Constructs a <code>JPopupMenu</code> without an "invoker".
172 */
173 public JPopupMenu() {
174 this(null);
175 }
176
177 /**
178 * Constructs a <code>JPopupMenu</code> with the specified title.
179 *
180 * @param label the string that a UI may use to display as a title
181 * for the popup menu.
182 */
183 public JPopupMenu(String label) {
184 this.label = label;
185 lightWeightPopup = getDefaultLightWeightPopupEnabled();
186 setSelectionModel(new DefaultSingleSelectionModel());
187 enableEvents(AWTEvent.MOUSE_EVENT_MASK);
188 setFocusTraversalKeysEnabled(false);
189 updateUI();
190 }
191
192
193
194 /**
195 * Returns the look and feel (L&F) object that renders this component.
196 *
197 * @return the <code>PopupMenuUI</code> object that renders this component
198 */
199 public PopupMenuUI getUI() {
200 return (PopupMenuUI)ui;
201 }
202
203 /**
204 * Sets the L&F object that renders this component.
205 *
206 * @param ui the new <code>PopupMenuUI</code> L&F object
207 * @see UIDefaults#getUI
208 * @beaninfo
209 * bound: true
210 * hidden: true
211 * attribute: visualUpdate true
212 * description: The UI object that implements the Component's LookAndFeel.
213 */
214 public void setUI(PopupMenuUI ui) {
215 super.setUI(ui);
216 }
217
218 /**
219 * Resets the UI property to a value from the current look and feel.
220 *
221 * @see JComponent#updateUI
222 */
223 public void updateUI() {
224 setUI((PopupMenuUI)UIManager.getUI(this));
225 }
226
227
228 /**
229 * Returns the name of the L&F class that renders this component.
230 *
231 * @return the string "PopupMenuUI"
232 * @see JComponent#getUIClassID
233 * @see UIDefaults#getUI
234 */
235 public String getUIClassID() {
236 return uiClassID;
237 }
238
239 protected void processFocusEvent(FocusEvent evt) {
240 super.processFocusEvent(evt);
241 }
242
243 /**
244 * Processes key stroke events such as mnemonics and accelerators.
245 *
246 * @param evt the key event to be processed
247 */
248 protected void processKeyEvent(KeyEvent evt) {
249 MenuSelectionManager.defaultManager().processKeyEvent(evt);
250 if (evt.isConsumed()) {
251 return;
252 }
253 super.processKeyEvent(evt);
254 }
255
256
257 /**
258 * Returns the model object that handles single selections.
259 *
260 * @return the <code>selectionModel</code> property
261 * @see SingleSelectionModel
262 */
263 public SingleSelectionModel getSelectionModel() {
264 return selectionModel;
265 }
266
267 /**
268 * Sets the model object to handle single selections.
269 *
270 * @param model the new <code>SingleSelectionModel</code>
271 * @see SingleSelectionModel
272 * @beaninfo
273 * description: The selection model for the popup menu
274 * expert: true
275 */
276 public void setSelectionModel(SingleSelectionModel model) {
277 selectionModel = model;
278 }
279
280 /**
281 * Appends the specified menu item to the end of this menu.
282 *
283 * @param menuItem the <code>JMenuItem</code> to add
284 * @return the <code>JMenuItem</code> added
285 */
286 public JMenuItem add(JMenuItem menuItem) {
287 super.add(menuItem);
288 return menuItem;
289 }
290
291 /**
292 * Creates a new menu item with the specified text and appends
293 * it to the end of this menu.
294 *
295 * @param s the string for the menu item to be added
296 */
297 public JMenuItem add(String s) {
298 return add(new JMenuItem(s));
299 }
300
301 /**
302 * Appends a new menu item to the end of the menu which
303 * dispatches the specified <code>Action</code> object.
304 *
305 * @param a the <code>Action</code> to add to the menu
306 * @return the new menu item
307 * @see Action
308 */
309 public JMenuItem add(Action a) {
310 JMenuItem mi = createActionComponent(a);
311 mi.setAction(a);
312 add(mi);
313 return mi;
314 }
315
316 /**
317 * Returns an point which has been adjusted to take into account of the
318 * desktop bounds, taskbar and multi-monitor configuration.
319 * <p>
320 * This adustment may be cancelled by invoking the application with
321 * -Djavax.swing.adjustPopupLocationToFit=false
322 */
323 Point adjustPopupLocationToFitScreen(int xposition, int yposition) {
324 Point p = new Point(xposition, yposition);
325
326 if(popupPostionFixDisabled == true || GraphicsEnvironment.isHeadless())
327 return p;
328
329 Toolkit toolkit = Toolkit.getDefaultToolkit();
330 Rectangle screenBounds;
331 GraphicsConfiguration gc = null;
332 // Try to find GraphicsConfiguration, that includes mouse
333 // pointer position
334 GraphicsEnvironment ge =
335 GraphicsEnvironment.getLocalGraphicsEnvironment();
336 GraphicsDevice[] gd = ge.getScreenDevices();
337 for(int i = 0; i < gd.length; i++) {
338 if(gd[i].getType() == GraphicsDevice.TYPE_RASTER_SCREEN) {
339 GraphicsConfiguration dgc =
340 gd[i].getDefaultConfiguration();
341 if(dgc.getBounds().contains(p)) {
342 gc = dgc;
343 break;
344 }
345 }
346 }
347
348 // If not found and we have invoker, ask invoker about his gc
349 if(gc == null && getInvoker() != null) {
350 gc = getInvoker().getGraphicsConfiguration();
351 }
352
353 if(gc != null) {
354 // If we have GraphicsConfiguration use it to get
355 // screen bounds
356 screenBounds = gc.getBounds();
357 } else {
358 // If we don't have GraphicsConfiguration use primary screen
359 screenBounds = new Rectangle(toolkit.getScreenSize());
360 }
361
362 Dimension size;
363
364 size = JPopupMenu.this.getPreferredSize();
365
366 // Use long variables to prevent overflow
367 long pw = (long) p.x + (long) size.width;
368 long ph = (long) p.y + (long) size.height;
369
370 if( pw > screenBounds.x + screenBounds.width )
371 p.x = screenBounds.x + screenBounds.width - size.width;
372
373 if( ph > screenBounds.y + screenBounds.height)
374 p.y = screenBounds.y + screenBounds.height - size.height;
375
376 /* Change is made to the desired (X,Y) values, when the
377 PopupMenu is too tall OR too wide for the screen
378 */
379 if( p.x < screenBounds.x )
380 p.x = screenBounds.x ;
381 if( p.y < screenBounds.y )
382 p.y = screenBounds.y;
383
384 return p;
385 }
386
387
388 /**
389 * Factory method which creates the <code>JMenuItem</code> for
390 * <code>Actions</code> added to the <code>JPopupMenu</code>.
391 *
392 * @param a the <code>Action</code> for the menu item to be added
393 * @return the new menu item
394 * @see Action
395 *
396 * @since 1.3
397 */
398 protected JMenuItem createActionComponent(Action a) {
399 JMenuItem mi = new JMenuItem() {
400 protected PropertyChangeListener createActionPropertyChangeListener(Action a) {
401 PropertyChangeListener pcl = createActionChangeListener(this);
402 if (pcl == null) {
403 pcl = super.createActionPropertyChangeListener(a);
404 }
405 return pcl;
406 }
407 };
408 mi.setHorizontalTextPosition(JButton.TRAILING);
409 mi.setVerticalTextPosition(JButton.CENTER);
410 return mi;
411 }
412
413 /**
414 * Returns a properly configured <code>PropertyChangeListener</code>
415 * which updates the control as changes to the <code>Action</code> occur.
416 */
417 protected PropertyChangeListener createActionChangeListener(JMenuItem b) {
418 return b.createActionPropertyChangeListener0(b.getAction());
419 }
420
421 /**
422 * Removes the component at the specified index from this popup menu.
423 *
424 * @param pos the position of the item to be removed
425 * @exception IllegalArgumentException if the value of
426 * <code>pos</code> < 0, or if the value of
427 * <code>pos</code> is greater than the
428 * number of items
429 */
430 public void remove(int pos) {
431 if (pos < 0) {
432 throw new IllegalArgumentException("index less than zero.");
433 }
434 if (pos > getComponentCount() -1) {
435 throw new IllegalArgumentException("index greater than the number of items.");
436 }
437 super.remove(pos);
438 }
439
440 /**
441 * Sets the value of the <code>lightWeightPopupEnabled</code> property,
442 * which by default is <code>true</code>.
443 * By default, when a look and feel displays a popup,
444 * it can choose to
445 * use a lightweight (all-Java) popup.
446 * Lightweight popup windows are more efficient than heavyweight
447 * (native peer) windows,
448 * but lightweight and heavyweight components do not mix well in a GUI.
449 * If your application mixes lightweight and heavyweight components,
450 * you should disable lightweight popups.
451 * Some look and feels might always use heavyweight popups,
452 * no matter what the value of this property.
453 *
454 * @param aFlag <code>false</code> to disable lightweight popups
455 * @beaninfo
456 * description: Determines whether lightweight popups are used when possible
457 * expert: true
458 *
459 * @see #isLightWeightPopupEnabled
460 */
461 public void setLightWeightPopupEnabled(boolean aFlag) {
462 // NOTE: this use to set the flag on a shared JPopupMenu, which meant
463 // this effected ALL JPopupMenus.
464 lightWeightPopup = aFlag;
465 }
466
467 /**
468 * Gets the <code>lightWeightPopupEnabled</code> property.
469 *
470 * @return the value of the <code>lightWeightPopupEnabled</code> property
471 * @see #setLightWeightPopupEnabled
472 */
473 public boolean isLightWeightPopupEnabled() {
474 return lightWeightPopup;
475 }
476
477 /**
478 * Returns the popup menu's label
479 *
480 * @return a string containing the popup menu's label
481 * @see #setLabel
482 */
483 public String getLabel() {
484 return label;
485 }
486
487 /**
488 * Sets the popup menu's label. Different look and feels may choose
489 * to display or not display this.
490 *
491 * @param label a string specifying the label for the popup menu
492 *
493 * @see #setLabel
494 * @beaninfo
495 * description: The label for the popup menu.
496 * bound: true
497 */
498 public void setLabel(String label) {
499 String oldValue = this.label;
500 this.label = label;
501 firePropertyChange("label", oldValue, label);
502 if (accessibleContext != null) {
503 accessibleContext.firePropertyChange(
504 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
505 oldValue, label);
506 }
507 invalidate();
508 repaint();
509 }
510
511 /**
512 * Appends a new separator at the end of the menu.
513 */
514 public void addSeparator() {
515 add( new JPopupMenu.Separator() );
516 }
517
518 /**
519 * Inserts a menu item for the specified <code>Action</code> object at
520 * a given position.
521 *
522 * @param a the <code>Action</code> object to insert
523 * @param index specifies the position at which to insert the
524 * <code>Action</code>, where 0 is the first
525 * @exception IllegalArgumentException if <code>index</code> < 0
526 * @see Action
527 */
528 public void insert(Action a, int index) {
529 JMenuItem mi = createActionComponent(a);
530 mi.setAction(a);
531 insert(mi, index);
532 }
533
534 /**
535 * Inserts the specified component into the menu at a given
536 * position.
537 *
538 * @param component the <code>Component</code> to insert
539 * @param index specifies the position at which
540 * to insert the component, where 0 is the first
541 * @exception IllegalArgumentException if <code>index</code> < 0
542 */
543 public void insert(Component component, int index) {
544 if (index < 0) {
545 throw new IllegalArgumentException("index less than zero.");
546 }
547
548 int nitems = getComponentCount();
549 // PENDING(ges): Why not use an array?
550 Vector tempItems = new Vector();
551
552 /* Remove the item at index, nitems-index times
553 storing them in a temporary vector in the
554 order they appear on the menu.
555 */
556 for (int i = index ; i < nitems; i++) {
557 tempItems.addElement(getComponent(index));
558 remove(index);
559 }
560
561 add(component);
562
563 /* Add the removed items back to the menu, they are
564 already in the correct order in the temp vector.
565 */
566 for (int i = 0; i < tempItems.size() ; i++) {
567 add((Component)tempItems.elementAt(i));
568 }
569 }
570
571 /**
572 * Adds a <code>PopupMenu</code> listener.
573 *
574 * @param l the <code>PopupMenuListener</code> to add
575 */
576 public void addPopupMenuListener(PopupMenuListener l) {
577 listenerList.add(PopupMenuListener.class,l);
578 }
579
580 /**
581 * Removes a <code>PopupMenu</code> listener.
582 *
583 * @param l the <code>PopupMenuListener</code> to remove
584 */
585 public void removePopupMenuListener(PopupMenuListener l) {
586 listenerList.remove(PopupMenuListener.class,l);
587 }
588
589 /**
590 * Returns an array of all the <code>PopupMenuListener</code>s added
591 * to this JMenuItem with addPopupMenuListener().
592 *
593 * @return all of the <code>PopupMenuListener</code>s added or an empty
594 * array if no listeners have been added
595 * @since 1.4
596 */
597 public PopupMenuListener[] getPopupMenuListeners() {
598 return (PopupMenuListener[])listenerList.getListeners(
599 PopupMenuListener.class);
600 }
601
602 /**
603 * Adds a <code>MenuKeyListener</code> to the popup menu.
604 *
605 * @param l the <code>MenuKeyListener</code> to be added
606 * @since 1.5
607 */
608 public void addMenuKeyListener(MenuKeyListener l) {
609 listenerList.add(MenuKeyListener.class, l);
610 }
611
612 /**
613 * Removes a <code>MenuKeyListener</code> from the popup menu.
614 *
615 * @param l the <code>MenuKeyListener</code> to be removed
616 * @since 1.5
617 */
618 public void removeMenuKeyListener(MenuKeyListener l) {
619 listenerList.remove(MenuKeyListener.class, l);
620 }
621
622 /**
623 * Returns an array of all the <code>MenuKeyListener</code>s added
624 * to this JPopupMenu with addMenuKeyListener().
625 *
626 * @return all of the <code>MenuKeyListener</code>s added or an empty
627 * array if no listeners have been added
628 * @since 1.5
629 */
630 public MenuKeyListener[] getMenuKeyListeners() {
631 return (MenuKeyListener[])listenerList.getListeners(
632 MenuKeyListener.class);
633 }
634
635 /**
636 * Notifies <code>PopupMenuListener</code>s that this popup menu will
637 * become visible.
638 */
639 protected void firePopupMenuWillBecomeVisible() {
640 Object[] listeners = listenerList.getListenerList();
641 PopupMenuEvent e=null;
642 for (int i = listeners.length-2; i>=0; i-=2) {
643 if (listeners[i]==PopupMenuListener.class) {
644 if (e == null)
645 e = new PopupMenuEvent(this);
646 ((PopupMenuListener)listeners[i+1]).popupMenuWillBecomeVisible(e);
647 }
648 }
649 }
650
651 /**
652 * Notifies <code>PopupMenuListener</code>s that this popup menu will
653 * become invisible.
654 */
655 protected void firePopupMenuWillBecomeInvisible() {
656 Object[] listeners = listenerList.getListenerList();
657 PopupMenuEvent e=null;
658 for (int i = listeners.length-2; i>=0; i-=2) {
659 if (listeners[i]==PopupMenuListener.class) {
660 if (e == null)
661 e = new PopupMenuEvent(this);
662 ((PopupMenuListener)listeners[i+1]).popupMenuWillBecomeInvisible(e);
663 }
664 }
665 }
666
667 /**
668 * Notifies <code>PopupMenuListeners</code> that this popup menu is
669 * cancelled.
670 */
671 protected void firePopupMenuCanceled() {
672 Object[] listeners = listenerList.getListenerList();
673 PopupMenuEvent e=null;
674 for (int i = listeners.length-2; i>=0; i-=2) {
675 if (listeners[i]==PopupMenuListener.class) {
676 if (e == null)
677 e = new PopupMenuEvent(this);
678 ((PopupMenuListener)listeners[i+1]).popupMenuCanceled(e);
679 }
680 }
681 }
682
683 /**
684 * Always returns true since popups, by definition, should always
685 * be on top of all other windows.
686 * @return true
687 */
688 // package private
689 boolean alwaysOnTop() {
690 return true;
691 }
692
693 /**
694 * Lays out the container so that it uses the minimum space
695 * needed to display its contents.
696 */
697 public void pack() {
698 if(popup != null) {
699 Dimension pref = getPreferredSize();
700
701 if (pref == null || pref.width != getWidth() ||
702 pref.height != getHeight()) {
703 popup = getPopup();
704 } else {
705 validate();
706 }
707 }
708 }
709
710 /**
711 * Sets the visibility of the popup menu.
712 *
713 * @param b true to make the popup visible, or false to
714 * hide it
715 * @beaninfo
716 * bound: true
717 * description: Makes the popup visible
718 */
719 public void setVisible(boolean b) {
720 if (DEBUG) {
721 System.out.println("JPopupMenu.setVisible " + b);
722 }
723
724 // Is it a no-op?
725 if (b == isVisible())
726 return;
727
728 // if closing, first close all Submenus
729 if (b == false) {
730
731 // 4234793: This is a workaround because JPopupMenu.firePopupMenuCanceled is
732 // a protected method and cannot be called from BasicPopupMenuUI directly
733 // The real solution could be to make
734 // firePopupMenuCanceled public and call it directly.
735 Boolean doCanceled = (Boolean)getClientProperty("JPopupMenu.firePopupMenuCanceled");
736 if (doCanceled != null && doCanceled == Boolean.TRUE) {
737 putClientProperty("JPopupMenu.firePopupMenuCanceled", Boolean.FALSE);
738 firePopupMenuCanceled();
739 }
740 getSelectionModel().clearSelection();
741
742 } else {
743 // This is a popup menu with MenuElement children,
744 // set selection path before popping up!
745 if (isPopupMenu()) {
746 MenuElement me[] = new MenuElement[1];
747 me[0] = (MenuElement) this;
748 MenuSelectionManager.defaultManager().setSelectedPath(me);
749 }
750 }
751
752 if(b) {
753 firePopupMenuWillBecomeVisible();
754 popup = getPopup();
755 firePropertyChange("visible", Boolean.FALSE, Boolean.TRUE);
756
757
758 } else if(popup != null) {
759 firePopupMenuWillBecomeInvisible();
760 popup.hide();
761 popup = null;
762 firePropertyChange("visible", Boolean.TRUE, Boolean.FALSE);
763 // 4694797: When popup menu is made invisible, selected path
764 // should be cleared
765 if (isPopupMenu()) {
766 MenuSelectionManager.defaultManager().clearSelectedPath();
767 }
768 }
769 }
770
771 /**
772 * Returns a <code>Popup</code> instance from the
773 * <code>PopupMenuUI</code> that has had <code>show</code> invoked on
774 * it. If the current <code>popup</code> is non-null,
775 * this will invoke <code>dispose</code> of it, and then
776 * <code>show</code> the new one.
777 * <p>
778 * This does NOT fire any events, it is up the caller to dispatch
779 * the necessary events.
780 */
781 private Popup getPopup() {
782 Popup oldPopup = popup;
783
784 if (oldPopup != null) {
785 oldPopup.hide();
786 }
787 PopupFactory popupFactory = PopupFactory.getSharedInstance();
788
789 if (isLightWeightPopupEnabled()) {
790 popupFactory.setPopupType(PopupFactory.LIGHT_WEIGHT_POPUP);
791 }
792 else {
793 popupFactory.setPopupType(PopupFactory.MEDIUM_WEIGHT_POPUP);
794 }
795
796 // adjust the location of the popup
797 Point p = adjustPopupLocationToFitScreen(desiredLocationX,desiredLocationY);
798 desiredLocationX = p.x;
799 desiredLocationY = p.y;
800
801 Popup newPopup = getUI().getPopup(this, desiredLocationX,
802 desiredLocationY);
803
804 popupFactory.setPopupType(PopupFactory.LIGHT_WEIGHT_POPUP);
805 newPopup.show();
806 return newPopup;
807 }
808
809 /**
810 * Returns true if the popup menu is visible (currently
811 * being displayed).
812 */
813 public boolean isVisible() {
814 if(popup != null)
815 return true;
816 else
817 return false;
818 }
819
820 /**
821 * Sets the location of the upper left corner of the
822 * popup menu using x, y coordinates.
823 *
824 * @param x the x coordinate of the popup's new position
825 * in the screen's coordinate space
826 * @param y the y coordinate of the popup's new position
827 * in the screen's coordinate space
828 * @beaninfo
829 * description: The location of the popup menu.
830 */
831 public void setLocation(int x, int y) {
832 int oldX = desiredLocationX;
833 int oldY = desiredLocationY;
834
835 desiredLocationX = x;
836 desiredLocationY = y;
837 if(popup != null && (x != oldX || y != oldY)) {
838 popup = getPopup();
839 }
840 }
841
842 /**
843 * Returns true if the popup menu is a standalone popup menu
844 * rather than the submenu of a <code>JMenu</code>.
845 *
846 * @return true if this menu is a standalone popup menu, otherwise false
847 */
848 private boolean isPopupMenu() {
849 return ((invoker != null) && !(invoker instanceof JMenu));
850 }
851
852 /**
853 * Returns the component which is the 'invoker' of this
854 * popup menu.
855 *
856 * @return the <code>Component</code> in which the popup menu is displayed
857 */
858 public Component getInvoker() {
859 return this.invoker;
860 }
861
862 /**
863 * Sets the invoker of this popup menu -- the component in which
864 * the popup menu menu is to be displayed.
865 *
866 * @param invoker the <code>Component</code> in which the popup
867 * menu is displayed
868 * @beaninfo
869 * description: The invoking component for the popup menu
870 * expert: true
871 */
872 public void setInvoker(Component invoker) {
873 Component oldInvoker = this.invoker;
874 this.invoker = invoker;
875 if ((oldInvoker != this.invoker) && (ui != null)) {
876 ui.uninstallUI(this);
877 ui.installUI(this);
878 }
879 invalidate();
880 }
881
882 /**
883 * Displays the popup menu at the position x,y in the coordinate
884 * space of the component invoker.
885 *
886 * @param invoker the component in whose space the popup menu is to appear
887 * @param x the x coordinate in invoker's coordinate space at which
888 * the popup menu is to be displayed
889 * @param y the y coordinate in invoker's coordinate space at which
890 * the popup menu is to be displayed
891 */
892 public void show(Component invoker, int x, int y) {
893 if (DEBUG) {
894 System.out.println("in JPopupMenu.show " );
895 }
896 setInvoker(invoker);
897 Frame newFrame = getFrame(invoker);
898 if (newFrame != frame) {
899 // Use the invoker's frame so that events
900 // are propagated properly
901 if (newFrame!=null) {
902 this.frame = newFrame;
903 if(popup != null) {
904 setVisible(false);
905 }
906 }
907 }
908 Point invokerOrigin;
909 if (invoker != null) {
910 invokerOrigin = invoker.getLocationOnScreen();
911
912 // To avoid integer overflow
913 long lx, ly;
914 lx = ((long) invokerOrigin.x) +
915 ((long) x);
916 ly = ((long) invokerOrigin.y) +
917 ((long) y);
918 if(lx > Integer.MAX_VALUE) lx = Integer.MAX_VALUE;
919 if(lx < Integer.MIN_VALUE) lx = Integer.MIN_VALUE;
920 if(ly > Integer.MAX_VALUE) ly = Integer.MAX_VALUE;
921 if(ly < Integer.MIN_VALUE) ly = Integer.MIN_VALUE;
922
923 setLocation((int) lx, (int) ly);
924 } else {
925 setLocation(x, y);
926 }
927 setVisible(true);
928 }
929
930 /**
931 * Returns the popup menu which is at the root of the menu system
932 * for this popup menu.
933 *
934 * @return the topmost grandparent <code>JPopupMenu</code>
935 */
936 JPopupMenu getRootPopupMenu() {
937 JPopupMenu mp = this;
938 while((mp!=null) && (mp.isPopupMenu()!=true) &&
939 (mp.getInvoker() != null) &&
940 (mp.getInvoker().getParent() != null) &&
941 (mp.getInvoker().getParent() instanceof JPopupMenu)
942 ) {
943 mp = (JPopupMenu) mp.getInvoker().getParent();
944 }
945 return mp;
946 }
947
948 /**
949 * Returns the component at the specified index.
950 *
951 * @param i the index of the component, where 0 is the first
952 * @return the <code>Component</code> at that index
953 * @deprecated replaced by {@link java.awt.Container#getComponent(int)}
954 */
955 @Deprecated
956 public Component getComponentAtIndex(int i) {
957 return getComponent(i);
958 }
959
960 /**
961 * Returns the index of the specified component.
962 *
963 * @param c the <code>Component</code> to find
964 * @return the index of the component, where 0 is the first;
965 * or -1 if the component is not found
966 */
967 public int getComponentIndex(Component c) {
968 int ncomponents = this.getComponentCount();
969 Component[] component = this.getComponents();
970 for (int i = 0 ; i < ncomponents ; i++) {
971 Component comp = component[i];
972 if (comp == c)
973 return i;
974 }
975 return -1;
976 }
977
978 /**
979 * Sets the size of the Popup window using a <code>Dimension</code> object.
980 * This is equivalent to <code>setPreferredSize(d)</code>.
981 *
982 * @param d the <code>Dimension</code> specifying the new size
983 * of this component.
984 * @beaninfo
985 * description: The size of the popup menu
986 */
987 public void setPopupSize(Dimension d) {
988 Dimension oldSize = getPreferredSize();
989
990 setPreferredSize(d);
991 if (popup != null) {
992 Dimension newSize = getPreferredSize();
993
994 if (!oldSize.equals(newSize)) {
995 popup = getPopup();
996 }
997 }
998 }
999
1000 /**
1001 * Sets the size of the Popup window to the specified width and
1002 * height. This is equivalent to
1003 * <code>setPreferredSize(new Dimension(width, height))</code>.
1004 *
1005 * @param width the new width of the Popup in pixels
1006 * @param height the new height of the Popup in pixels
1007 * @beaninfo
1008 * description: The size of the popup menu
1009 */
1010 public void setPopupSize(int width, int height) {
1011 setPopupSize(new Dimension(width, height));
1012 }
1013
1014 /**
1015 * Sets the currently selected component, This will result
1016 * in a change to the selection model.
1017 *
1018 * @param sel the <code>Component</code> to select
1019 * @beaninfo
1020 * description: The selected component on the popup menu
1021 * expert: true
1022 * hidden: true
1023 */
1024 public void setSelected(Component sel) {
1025 SingleSelectionModel model = getSelectionModel();
1026 int index = getComponentIndex(sel);
1027 model.setSelectedIndex(index);
1028 }
1029
1030 /**
1031 * Checks whether the border should be painted.
1032 *
1033 * @return true if the border is painted, false otherwise
1034 * @see #setBorderPainted
1035 */
1036 public boolean isBorderPainted() {
1037 return paintBorder;
1038 }
1039
1040 /**
1041 * Sets whether the border should be painted.
1042 *
1043 * @param b if true, the border is painted.
1044 * @see #isBorderPainted
1045 * @beaninfo
1046 * description: Is the border of the popup menu painted
1047 */
1048 public void setBorderPainted(boolean b) {
1049 paintBorder = b;
1050 repaint();
1051 }
1052
1053 /**
1054 * Paints the popup menu's border if the <code>borderPainted</code>
1055 * property is <code>true</code>.
1056 * @param g the <code>Graphics</code> object
1057 *
1058 * @see JComponent#paint
1059 * @see JComponent#setBorder
1060 */
1061 protected void paintBorder(Graphics g) {
1062 if (isBorderPainted()) {
1063 super.paintBorder(g);
1064 }
1065 }
1066
1067 /**
1068 * Returns the margin, in pixels, between the popup menu's border and
1069 * its containees.
1070 *
1071 * @return an <code>Insets</code> object containing the margin values.
1072 */
1073 public Insets getMargin() {
1074 if(margin == null) {
1075 return new Insets(0,0,0,0);
1076 } else {
1077 return margin;
1078 }
1079 }
1080
1081
1082 /**
1083 * Examines the list of menu items to determine whether
1084 * <code>popup</code> is a popup menu.
1085 *
1086 * @param popup a <code>JPopupMenu</code>
1087 * @return true if <code>popup</code>
1088 */
1089 boolean isSubPopupMenu(JPopupMenu popup) {
1090 int ncomponents = this.getComponentCount();
1091 Component[] component = this.getComponents();
1092 for (int i = 0 ; i < ncomponents ; i++) {
1093 Component comp = component[i];
1094 if (comp instanceof JMenu) {
1095 JMenu menu = (JMenu)comp;
1096 JPopupMenu subPopup = menu.getPopupMenu();
1097 if (subPopup == popup)
1098 return true;
1099 if (subPopup.isSubPopupMenu(popup))
1100 return true;
1101 }
1102 }
1103 return false;
1104 }
1105
1106
1107 private static Frame getFrame(Component c) {
1108 Component w = c;
1109
1110 while(!(w instanceof Frame) && (w!=null)) {
1111 w = w.getParent();
1112 }
1113 return (Frame)w;
1114 }
1115
1116
1117 /**
1118 * Returns a string representation of this <code>JPopupMenu</code>.
1119 * This method
1120 * is intended to be used only for debugging purposes, and the
1121 * content and format of the returned string may vary between
1122 * implementations. The returned string may be empty but may not
1123 * be <code>null</code>.
1124 *
1125 * @return a string representation of this <code>JPopupMenu</code>.
1126 */
1127 protected String paramString() {
1128 String labelString = (label != null ?
1129 label : "");
1130 String paintBorderString = (paintBorder ?
1131 "true" : "false");
1132 String marginString = (margin != null ?
1133 margin.toString() : "");
1134 String lightWeightPopupEnabledString = (isLightWeightPopupEnabled() ?
1135 "true" : "false");
1136 return super.paramString() +
1137 ",desiredLocationX=" + desiredLocationX +
1138 ",desiredLocationY=" + desiredLocationY +
1139 ",label=" + labelString +
1140 ",lightWeightPopupEnabled=" + lightWeightPopupEnabledString +
1141 ",margin=" + marginString +
1142 ",paintBorder=" + paintBorderString;
1143 }
1144
1145/////////////////
1146// Accessibility support
1147////////////////
1148
1149 /**
1150 * Gets the AccessibleContext associated with this JPopupMenu.
1151 * For JPopupMenus, the AccessibleContext takes the form of an
1152 * AccessibleJPopupMenu.
1153 * A new AccessibleJPopupMenu instance is created if necessary.
1154 *
1155 * @return an AccessibleJPopupMenu that serves as the
1156 * AccessibleContext of this JPopupMenu
1157 */
1158 public AccessibleContext getAccessibleContext() {
1159 if (accessibleContext == null) {
1160 accessibleContext = new AccessibleJPopupMenu();
1161 }
1162 return accessibleContext;
1163 }
1164
1165 /**
1166 * This class implements accessibility support for the
1167 * <code>JPopupMenu</code> class. It provides an implementation of the
1168 * Java Accessibility API appropriate to popup menu user-interface
1169 * elements.
1170 */
1171 protected class AccessibleJPopupMenu extends AccessibleJComponent
1172 implements PropertyChangeListener {
1173
1174 /**
1175 * AccessibleJPopupMenu constructor
1176 *
1177 * @since 1.5
1178 */
1179 protected AccessibleJPopupMenu() {
1180 JPopupMenu.this.addPropertyChangeListener(this);
1181 }
1182
1183 /**
1184 * Get the role of this object.
1185 *
1186 * @return an instance of AccessibleRole describing the role of
1187 * the object
1188 */
1189 public AccessibleRole getAccessibleRole() {
1190 return AccessibleRole.POPUP_MENU;
1191 }
1192
1193 /**
1194 * This method gets called when a bound property is changed.
1195 * @param e A <code>PropertyChangeEvent</code> object describing
1196 * the event source and the property that has changed. Must not be null.
1197 *
1198 * @throws NullPointerException if the parameter is null.
1199 * @since 1.5
1200 */
1201 public void propertyChange(PropertyChangeEvent e) {
1202 String propertyName = e.getPropertyName();
1203 if (propertyName == "visible") {
1204 if (e.getOldValue() == Boolean.FALSE &&
1205 e.getNewValue() == Boolean.TRUE) {
1206 handlePopupIsVisibleEvent(true);
1207
1208 } else if (e.getOldValue() == Boolean.TRUE &&
1209 e.getNewValue() == Boolean.FALSE) {
1210 handlePopupIsVisibleEvent(false);
1211 }
1212 }
1213 }
1214
1215 /*
1216 * Handles popup "visible" PropertyChangeEvent
1217 */
1218 private void handlePopupIsVisibleEvent(boolean visible) {
1219 if (visible) {
1220 // notify listeners that the popup became visible
1221 firePropertyChange(ACCESSIBLE_STATE_PROPERTY,
1222 null, AccessibleState.VISIBLE);
1223 // notify listeners that a popup list item is selected
1224 fireActiveDescendant();
1225 } else {
1226 // notify listeners that the popup became hidden
1227 firePropertyChange(ACCESSIBLE_STATE_PROPERTY,
1228 AccessibleState.VISIBLE, null);
1229 }
1230 }
1231
1232 /*
1233 * Fires AccessibleActiveDescendant PropertyChangeEvent to notify listeners
1234 * on the popup menu invoker that a popup list item has been selected
1235 */
1236 private void fireActiveDescendant() {
1237 if (JPopupMenu.this instanceof BasicComboPopup) {
1238 // get the popup list
1239 JList popupList = ((BasicComboPopup)JPopupMenu.this).getList();
1240 if (popupList == null) {
1241 return;
1242 }
1243
1244 // get the first selected item
1245 AccessibleContext ac = popupList.getAccessibleContext();
1246 AccessibleSelection selection = ac.getAccessibleSelection();
1247 if (selection == null) {
1248 return;
1249 }
1250 Accessible a = selection.getAccessibleSelection(0);
1251 if (a == null) {
1252 return;
1253 }
1254 AccessibleContext selectedItem = a.getAccessibleContext();
1255
1256 // fire the event with the popup invoker as the source.
1257 if (selectedItem != null && invoker != null) {
1258 AccessibleContext invokerContext = invoker.getAccessibleContext();
1259 if (invokerContext != null) {
1260 // Check invokerContext because Component.getAccessibleContext
1261 // returns null. Classes that extend Component are responsible
1262 // for returning a non-null AccessibleContext.
1263 invokerContext.firePropertyChange(
1264 ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY,
1265 null, selectedItem);
1266 }
1267 }
1268 }
1269 }
1270 } // inner class AccessibleJPopupMenu
1271
1272
1273////////////
1274// Serialization support.
1275////////////
1276 private void writeObject(ObjectOutputStream s) throws IOException {
1277 Vector values = new Vector();
1278
1279 s.defaultWriteObject();
1280 // Save the invoker, if its Serializable.
1281 if(invoker != null && invoker instanceof Serializable) {
1282 values.addElement("invoker");
1283 values.addElement(invoker);
1284 }
1285 // Save the popup, if its Serializable.
1286 if(popup != null && popup instanceof Serializable) {
1287 values.addElement("popup");
1288 values.addElement(popup);
1289 }
1290 s.writeObject(values);
1291
1292 if (getUIClassID().equals(uiClassID)) {
1293 byte count = JComponent.getWriteObjCounter(this);
1294 JComponent.setWriteObjCounter(this, --count);
1295 if (count == 0 && ui != null) {
1296 ui.installUI(this);
1297 }
1298 }
1299 }
1300
1301 // implements javax.swing.MenuElement
1302 private void readObject(ObjectInputStream s)
1303 throws IOException, ClassNotFoundException {
1304 s.defaultReadObject();
1305
1306 Vector values = (Vector)s.readObject();
1307 int indexCounter = 0;
1308 int maxCounter = values.size();
1309
1310 if(indexCounter < maxCounter && values.elementAt(indexCounter).
1311 equals("invoker")) {
1312 invoker = (Component)values.elementAt(++indexCounter);
1313 indexCounter++;
1314 }
1315 if(indexCounter < maxCounter && values.elementAt(indexCounter).
1316 equals("popup")) {
1317 popup = (Popup)values.elementAt(++indexCounter);
1318 indexCounter++;
1319 }
1320 }
1321
1322
1323 /**
1324 * This method is required to conform to the
1325 * <code>MenuElement</code> interface, but it not implemented.
1326 * @see MenuElement#processMouseEvent(MouseEvent, MenuElement[], MenuSelectionManager)
1327 */
1328 public void processMouseEvent(MouseEvent event,MenuElement path[],MenuSelectionManager manager) {}
1329
1330 /**
1331 * Processes a key event forwarded from the
1332 * <code>MenuSelectionManager</code> and changes the menu selection,
1333 * if necessary, by using <code>MenuSelectionManager</code>'s API.
1334 * <p>
1335 * Note: you do not have to forward the event to sub-components.
1336 * This is done automatically by the <code>MenuSelectionManager</code>.
1337 *
1338 * @param e a <code>KeyEvent</code>
1339 * @param path the <code>MenuElement</code> path array
1340 * @param manager the <code>MenuSelectionManager</code>
1341 */
1342 public void processKeyEvent(KeyEvent e, MenuElement path[],
1343 MenuSelectionManager manager) {
1344 MenuKeyEvent mke = new MenuKeyEvent(e.getComponent(), e.getID(),
1345 e.getWhen(), e.getModifiers(),
1346 e.getKeyCode(), e.getKeyChar(),
1347 path, manager);
1348 processMenuKeyEvent(mke);
1349
1350 if (mke.isConsumed()) {
1351 e.consume();
1352 }
1353 }
1354
1355 /**
1356 * Handles a keystroke in a menu.
1357 *
1358 * @param e a <code>MenuKeyEvent</code> object
1359 * @since 1.5
1360 */
1361 private void processMenuKeyEvent(MenuKeyEvent e) {
1362 switch (e.getID()) {
1363 case KeyEvent.KEY_PRESSED:
1364 fireMenuKeyPressed(e); break;
1365 case KeyEvent.KEY_RELEASED:
1366 fireMenuKeyReleased(e); break;
1367 case KeyEvent.KEY_TYPED:
1368 fireMenuKeyTyped(e); break;
1369 default:
1370 break;
1371 }
1372 }
1373
1374 /**
1375 * Notifies all listeners that have registered interest for
1376 * notification on this event type.
1377 *
1378 * @param event a <code>MenuKeyEvent</code>
1379 * @see EventListenerList
1380 */
1381 private void fireMenuKeyPressed(MenuKeyEvent event) {
1382 Object[] listeners = listenerList.getListenerList();
1383 for (int i = listeners.length-2; i>=0; i-=2) {
1384 if (listeners[i]==MenuKeyListener.class) {
1385 ((MenuKeyListener)listeners[i+1]).menuKeyPressed(event);
1386 }
1387 }
1388 }
1389
1390 /**
1391 * Notifies all listeners that have registered interest for
1392 * notification on this event type.
1393 *
1394 * @param event a <code>MenuKeyEvent</code>
1395 * @see EventListenerList
1396 */
1397 private void fireMenuKeyReleased(MenuKeyEvent event) {
1398 Object[] listeners = listenerList.getListenerList();
1399 for (int i = listeners.length-2; i>=0; i-=2) {
1400 if (listeners[i]==MenuKeyListener.class) {
1401 ((MenuKeyListener)listeners[i+1]).menuKeyReleased(event);
1402 }
1403 }
1404 }
1405
1406 /**
1407 * Notifies all listeners that have registered interest for
1408 * notification on this event type.
1409 *
1410 * @param event a <code>MenuKeyEvent</code>
1411 * @see EventListenerList
1412 */
1413 private void fireMenuKeyTyped(MenuKeyEvent event) {
1414 Object[] listeners = listenerList.getListenerList();
1415 for (int i = listeners.length-2; i>=0; i-=2) {
1416 if (listeners[i]==MenuKeyListener.class) {
1417 ((MenuKeyListener)listeners[i+1]).menuKeyTyped(event);
1418 }
1419 }
1420 }
1421
1422 /**
1423 * Messaged when the menubar selection changes to activate or
1424 * deactivate this menu. This implements the
1425 * <code>javax.swing.MenuElement</code> interface.
1426 * Overrides <code>MenuElement.menuSelectionChanged</code>.
1427 *
1428 * @param isIncluded true if this menu is active, false if
1429 * it is not
1430 * @see MenuElement#menuSelectionChanged(boolean)
1431 */
1432 public void menuSelectionChanged(boolean isIncluded) {
1433 if (DEBUG) {
1434 System.out.println("In JPopupMenu.menuSelectionChanged " + isIncluded);
1435 }
1436 if(invoker instanceof JMenu) {
1437 JMenu m = (JMenu) invoker;
1438 if(isIncluded)
1439 m.setPopupMenuVisible(true);
1440 else
1441 m.setPopupMenuVisible(false);
1442 }
1443 if (isPopupMenu() && !isIncluded)
1444 setVisible(false);
1445 }
1446
1447 /**
1448 * Returns an array of <code>MenuElement</code>s containing the submenu
1449 * for this menu component. It will only return items conforming to
1450 * the <code>JMenuElement</code> interface.
1451 * If popup menu is <code>null</code> returns
1452 * an empty array. This method is required to conform to the
1453 * <code>MenuElement</code> interface.
1454 *
1455 * @return an array of <code>MenuElement</code> objects
1456 * @see MenuElement#getSubElements
1457 */
1458 public MenuElement[] getSubElements() {
1459 MenuElement result[];
1460 Vector tmp = new Vector();
1461 int c = getComponentCount();
1462 int i;
1463 Component m;
1464
1465 for(i=0 ; i < c ; i++) {
1466 m = getComponent(i);
1467 if(m instanceof MenuElement)
1468 tmp.addElement(m);
1469 }
1470
1471 result = new MenuElement[tmp.size()];
1472 for(i=0,c=tmp.size() ; i < c ; i++)
1473 result[i] = (MenuElement) tmp.elementAt(i);
1474 return result;
1475 }
1476
1477 /**
1478 * Returns this <code>JPopupMenu</code> component.
1479 * @return this <code>JPopupMenu</code> object
1480 * @see MenuElement#getComponent
1481 */
1482 public Component getComponent() {
1483 return this;
1484 }
1485
1486
1487 /**
1488 * A popup menu-specific separator.
1489 */
1490 static public class Separator extends JSeparator
1491 {
1492 public Separator( )
1493 {
1494 super( JSeparator.HORIZONTAL );
1495 }
1496
1497 /**
1498 * Returns the name of the L&F class that renders this component.
1499 *
1500 * @return the string "PopupMenuSeparatorUI"
1501 * @see JComponent#getUIClassID
1502 * @see UIDefaults#getUI
1503 */
1504 public String getUIClassID()
1505 {
1506 return "PopupMenuSeparatorUI";
1507
1508 }
1509 }
1510
1511 /**
1512 * Returns true if the <code>MouseEvent</code> is considered a popup trigger
1513 * by the <code>JPopupMenu</code>'s currently installed UI.
1514 *
1515 * @return true if the mouse event is a popup trigger
1516 * @since 1.3
1517 */
1518 public boolean isPopupTrigger(MouseEvent e) {
1519 return getUI().isPopupTrigger(e);
1520 }
1521}