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

Last change on this file since 16438 was 16438, checked in by simon04, 5 years ago

see #19251 - Java 8: use Stream

  • Property svn:eol-style set to native
File size: 3.5 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 = ((Map<?, ?>) tableField.get(appContextClass.getMethod("getAppContext").invoke(appContextClass)))
59 .entrySet().stream()
60 .filter(e -> MOUSE_GRABBER_KEY.equals(Objects.toString(e.getKey())))
61 .map(Entry::getValue)
62 .findFirst().orElse(null);
63 final ChangeListener changeListener = (ChangeListener) mouseGrabber;
64 final AWTEventListener awtEventListener = (AWTEventListener) mouseGrabber;
65 final MenuSelectionManager msm = MenuSelectionManager.defaultManager();
66 final Toolkit tk = Toolkit.getDefaultToolkit();
67 AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
68 if (b)
69 msm.removeChangeListener(changeListener);
70 else
71 msm.addChangeListener(changeListener);
72 tk.removeAWTEventListener(awtEventListener);
73 tk.addAWTEventListener(awtEventListener,
74 AWTEvent.MOUSE_EVENT_MASK |
75 AWTEvent.MOUSE_MOTION_EVENT_MASK |
76 AWTEvent.MOUSE_WHEEL_EVENT_MASK |
77 AWTEvent.WINDOW_EVENT_MASK | (b ? 0 : GRAB_EVENT_MASK));
78 return null;
79 });
80 } catch (ReflectiveOperationException | RuntimeException e) {
81 Logging.error(e);
82 }
83 }
84 super.setVisible(b);
85 }
86}
Note: See TracBrowser for help on using the repository browser.