source: josm/trunk/src/org/openstreetmap/josm/gui/util/StayOpenPopupMenu.java@ 15492

Last change on this file since 15492 was 15492, checked in by Don-vip, 6 years ago

see #18032, see #10435 - fix mappaint popup menu disappearance on macOS

macOS triggers a spurious sun.awt.UngrabEvent that is catched by BasicPopupMenuUI.MouseGrabber
and makes the popup menu disappear. Probably related to https://bugs.openjdk.java.net/browse/JDK-8225698

  • Property svn:eol-style set to native
File size: 3.6 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.util;
3
4import java.awt.AWTEvent;
5import java.awt.Toolkit;
6import java.awt.event.AWTEventListener;
7import java.lang.reflect.Field;
8import java.security.AccessController;
9import java.security.PrivilegedAction;
10import java.util.Map;
11import java.util.Map.Entry;
12import java.util.Objects;
13
14import javax.swing.JPopupMenu;
15import javax.swing.MenuSelectionManager;
16import javax.swing.event.ChangeListener;
17
18import org.openstreetmap.josm.tools.Logging;
19import org.openstreetmap.josm.tools.PlatformManager;
20import org.openstreetmap.josm.tools.ReflectionUtils;
21
22/**
23 * A {@link JPopupMenu} that can stay open on all platforms when containing {@code StayOpen*} items.
24 * @since 15492
25 */
26public class StayOpenPopupMenu extends JPopupMenu {
27
28 private static final String MOUSE_GRABBER_KEY = "javax.swing.plaf.basic.BasicPopupMenuUI.MouseGrabber";
29
30 /**
31 * Special mask for the UngrabEvent events, in addition to the public masks defined in AWTEvent.
32 */
33 private static final int GRAB_EVENT_MASK = 0x80000000;
34
35 /**
36 * Constructs a new {@code StayOpenPopupMenu}.
37 */
38 public StayOpenPopupMenu() {
39 }
40
41 /**
42 * Constructs a new {@code StayOpenPopupMenu} with the specified title.
43 * @param label the string that a UI may use to display as a title for the popup menu.
44 */
45 public StayOpenPopupMenu(String label) {
46 super(label);
47 }
48
49 @Override
50 public void setVisible(boolean b) {
51 // macOS triggers a spurious UngrabEvent that is catched by BasicPopupMenuUI.MouseGrabber
52 // and makes the popup menu disappear. Probably related to https://bugs.openjdk.java.net/browse/JDK-8225698
53 if (PlatformManager.isPlatformOsx()) {
54 try {
55 Class<?> AppContextClass = Class.forName("sun.awt.AppContext");
56 Field tableField = AppContextClass.getDeclaredField("table");
57 ReflectionUtils.setObjectsAccessible(tableField);
58 Object mouseGrabber = null;
59 for (Entry<?, ?> e : ((Map<?, ?>)
60 tableField.get(AppContextClass.getMethod("getAppContext").invoke(AppContextClass))).entrySet()) {
61 if (MOUSE_GRABBER_KEY.equals(Objects.toString(e.getKey()))) {
62 mouseGrabber = e.getValue();
63 break;
64 }
65 }
66 final ChangeListener changeListener = (ChangeListener) mouseGrabber;
67 final AWTEventListener awtEventListener = (AWTEventListener) mouseGrabber;
68 final MenuSelectionManager msm = MenuSelectionManager.defaultManager();
69 final Toolkit tk = Toolkit.getDefaultToolkit();
70 AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
71 if (b)
72 msm.removeChangeListener(changeListener);
73 else
74 msm.addChangeListener(changeListener);
75 tk.removeAWTEventListener(awtEventListener);
76 tk.addAWTEventListener(awtEventListener,
77 AWTEvent.MOUSE_EVENT_MASK |
78 AWTEvent.MOUSE_MOTION_EVENT_MASK |
79 AWTEvent.MOUSE_WHEEL_EVENT_MASK |
80 AWTEvent.WINDOW_EVENT_MASK | (b ? 0 : GRAB_EVENT_MASK));
81 return null;
82 });
83 } catch (ReflectiveOperationException | RuntimeException e) {
84 Logging.error(e);
85 }
86 }
87 super.setVisible(b);
88 }
89}
Note: See TracBrowser for help on using the repository browser.