author | darcy |
Tue, 16 Dec 2014 10:09:01 -0800 | |
changeset 28235 | 0dfebfcb9f8a |
parent 25859 | 3317bb8137f4 |
child 30469 | bac0a7ff7e1e |
permissions | -rw-r--r-- |
12047 | 1 |
/* |
23649
f4f882f0056b
8035692: Fix serial lint warnings in mac-specific code
darcy
parents:
20840
diff
changeset
|
2 |
* Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. |
12047 | 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. Oracle designates this |
|
8 |
* particular file as subject to the "Classpath" exception as provided |
|
9 |
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
22 |
* or visit www.oracle.com if you need additional information or have any |
|
23 |
* questions. |
|
24 |
*/ |
|
25 |
||
26 |
package com.apple.laf; |
|
27 |
||
28 |
import java.awt.*; |
|
29 |
import java.awt.event.*; |
|
30 |
import java.awt.peer.MenuComponentPeer; |
|
31 |
import java.util.Hashtable; |
|
32 |
||
33 |
import javax.swing.*; |
|
34 |
||
18760
438afe2fc852
8020038: [macosx] Incorrect usage of invokeLater() and likes in callbacks called via JNI from AppKit thread
leonidr
parents:
12559
diff
changeset
|
35 |
import sun.awt.SunToolkit; |
12047 | 36 |
import sun.lwawt.LWToolkit; |
37 |
import sun.lwawt.macosx.*; |
|
38 |
||
23649
f4f882f0056b
8035692: Fix serial lint warnings in mac-specific code
darcy
parents:
20840
diff
changeset
|
39 |
@SuppressWarnings("serial") // JDK implementation class |
20840 | 40 |
final class ScreenMenu extends Menu |
41 |
implements ContainerListener, ComponentListener, |
|
42 |
ScreenMenuPropertyHandler { |
|
43 |
||
12047 | 44 |
static { |
12559
9456ceada8b1
7164376: Replace use of sun.security.action.LoadLibraryAction with System.loadLibrary
mchung
parents:
12047
diff
changeset
|
45 |
java.security.AccessController.doPrivileged( |
9456ceada8b1
7164376: Replace use of sun.security.action.LoadLibraryAction with System.loadLibrary
mchung
parents:
12047
diff
changeset
|
46 |
new java.security.PrivilegedAction<Void>() { |
9456ceada8b1
7164376: Replace use of sun.security.action.LoadLibraryAction with System.loadLibrary
mchung
parents:
12047
diff
changeset
|
47 |
public Void run() { |
9456ceada8b1
7164376: Replace use of sun.security.action.LoadLibraryAction with System.loadLibrary
mchung
parents:
12047
diff
changeset
|
48 |
System.loadLibrary("awt"); |
9456ceada8b1
7164376: Replace use of sun.security.action.LoadLibraryAction with System.loadLibrary
mchung
parents:
12047
diff
changeset
|
49 |
return null; |
9456ceada8b1
7164376: Replace use of sun.security.action.LoadLibraryAction with System.loadLibrary
mchung
parents:
12047
diff
changeset
|
50 |
} |
9456ceada8b1
7164376: Replace use of sun.security.action.LoadLibraryAction with System.loadLibrary
mchung
parents:
12047
diff
changeset
|
51 |
}); |
12047 | 52 |
} |
53 |
||
54 |
// screen menu stuff |
|
20840 | 55 |
private static native long addMenuListeners(ScreenMenu listener, long nativeMenu); |
56 |
private static native void removeMenuListeners(long modelPtr); |
|
12047 | 57 |
|
20840 | 58 |
private transient long fModelPtr; |
12047 | 59 |
|
20840 | 60 |
private final Hashtable<Component, MenuItem> fItems; |
61 |
private final JMenu fInvoker; |
|
12047 | 62 |
|
20840 | 63 |
private Component fLastMouseEventTarget; |
64 |
private Rectangle fLastTargetRect; |
|
12047 | 65 |
private volatile Rectangle[] fItemBounds; |
66 |
||
20840 | 67 |
private ScreenMenuPropertyListener fPropertyListener; |
68 |
||
12047 | 69 |
// Array of child hashes used to see if we need to recreate the Menu. |
20840 | 70 |
private int childHashArray[]; |
12047 | 71 |
|
72 |
ScreenMenu(final JMenu invoker) { |
|
73 |
super(invoker.getText()); |
|
74 |
fInvoker = invoker; |
|
75 |
||
76 |
int count = fInvoker.getMenuComponentCount(); |
|
77 |
if (count < 5) count = 5; |
|
78 |
fItems = new Hashtable<Component, MenuItem>(count); |
|
79 |
setEnabled(fInvoker.isEnabled()); |
|
80 |
updateItems(); |
|
81 |
} |
|
82 |
||
83 |
/** |
|
84 |
* Determine if we need to tear down the Menu and re-create it, since the contents may have changed in the Menu opened listener and |
|
85 |
* we do not get notified of it, because EDT is busy in our code. We only need to update if the menu contents have changed in some |
|
86 |
* way, such as the number of menu items, the text of the menuitems, icon, shortcut etc. |
|
87 |
*/ |
|
20840 | 88 |
private static boolean needsUpdate(final Component items[], final int childHashArray[]) { |
12047 | 89 |
if (items == null || childHashArray == null) { |
90 |
return true; |
|
91 |
} |
|
92 |
if (childHashArray.length != items.length) { |
|
93 |
return true; |
|
94 |
} |
|
95 |
for (int i = 0; i < items.length; i++) { |
|
96 |
final int hashCode = getHashCode(items[i]); |
|
97 |
if (hashCode != childHashArray[i]) { |
|
98 |
return true; |
|
99 |
} |
|
100 |
} |
|
101 |
return false; |
|
102 |
} |
|
103 |
||
104 |
/** |
|
105 |
* Used to recreate the AWT based Menu structure that implements the Screen Menu. |
|
106 |
* Also computes hashcode and stores them so that we can compare them later in needsUpdate. |
|
107 |
*/ |
|
20840 | 108 |
private void updateItems() { |
12047 | 109 |
final int count = fInvoker.getMenuComponentCount(); |
110 |
final Component[] items = fInvoker.getMenuComponents(); |
|
111 |
if (needsUpdate(items, childHashArray)) { |
|
112 |
removeAll(); |
|
113 |
if (count <= 0) return; |
|
114 |
||
115 |
childHashArray = new int[count]; |
|
116 |
for (int i = 0; i < count; i++) { |
|
117 |
addItem(items[i]); |
|
118 |
childHashArray[i] = getHashCode(items[i]); |
|
119 |
} |
|
120 |
} |
|
121 |
} |
|
122 |
||
123 |
/** |
|
124 |
* Callback from JavaMenuUpdater.m -- called when menu first opens |
|
125 |
*/ |
|
126 |
public void invokeOpenLater() { |
|
127 |
final JMenu invoker = fInvoker; |
|
128 |
if (invoker == null) { |
|
129 |
System.err.println("invoker is null!"); |
|
130 |
return; |
|
131 |
} |
|
132 |
||
133 |
try { |
|
134 |
LWCToolkit.invokeAndWait(new Runnable() { |
|
135 |
public void run() { |
|
136 |
invoker.setSelected(true); |
|
137 |
invoker.validate(); |
|
138 |
updateItems(); |
|
139 |
fItemBounds = new Rectangle[invoker.getMenuComponentCount()]; |
|
140 |
} |
|
18760
438afe2fc852
8020038: [macosx] Incorrect usage of invokeLater() and likes in callbacks called via JNI from AppKit thread
leonidr
parents:
12559
diff
changeset
|
141 |
}, invoker); |
12047 | 142 |
} catch (final Exception e) { |
143 |
System.err.println(e); |
|
144 |
e.printStackTrace(); |
|
145 |
} |
|
146 |
} |
|
147 |
||
148 |
/** |
|
149 |
* Callback from JavaMenuUpdater.m -- called when menu closes. |
|
150 |
*/ |
|
151 |
public void invokeMenuClosing() { |
|
152 |
final JMenu invoker = fInvoker; |
|
153 |
if (invoker == null) return; |
|
154 |
||
155 |
try { |
|
156 |
LWCToolkit.invokeAndWait(new Runnable() { |
|
157 |
public void run() { |
|
158 |
invoker.setSelected(false); |
|
20840 | 159 |
// Null out the tracking rectangles and the array. |
12047 | 160 |
if (fItemBounds != null) { |
20840 | 161 |
for (int i = 0; i < fItemBounds.length; i++) { |
162 |
fItemBounds[i] = null; |
|
163 |
} |
|
12047 | 164 |
} |
20840 | 165 |
fItemBounds = null; |
166 |
} |
|
18760
438afe2fc852
8020038: [macosx] Incorrect usage of invokeLater() and likes in callbacks called via JNI from AppKit thread
leonidr
parents:
12559
diff
changeset
|
167 |
}, invoker); |
12047 | 168 |
} catch (final Exception e) { |
169 |
e.printStackTrace(); |
|
170 |
} |
|
171 |
} |
|
172 |
||
173 |
/** |
|
174 |
* Callback from JavaMenuUpdater.m -- called when menu item is hilighted. |
|
175 |
* |
|
176 |
* @param inWhichItem The menu item selected by the user. -1 if mouse moves off the menu. |
|
177 |
* @param itemRectTop |
|
178 |
* @param itemRectLeft |
|
179 |
* @param itemRectBottom |
|
180 |
* @param itemRectRight Tracking rectangle coordinates. |
|
181 |
*/ |
|
182 |
public void handleItemTargeted(final int inWhichItem, final int itemRectTop, final int itemRectLeft, final int itemRectBottom, final int itemRectRight) { |
|
183 |
if (fItemBounds == null || inWhichItem < 0 || inWhichItem > (fItemBounds.length - 1)) return; |
|
184 |
final Rectangle itemRect = new Rectangle(itemRectLeft, itemRectTop, itemRectRight - itemRectLeft, itemRectBottom - itemRectTop); |
|
185 |
fItemBounds[inWhichItem] = itemRect; |
|
186 |
} |
|
187 |
||
188 |
/** |
|
189 |
* Callback from JavaMenuUpdater.m -- called when mouse event happens on the menu. |
|
190 |
*/ |
|
191 |
public void handleMouseEvent(final int kind, final int x, final int y, final int modifiers, final long when) { |
|
192 |
if (kind == 0) return; |
|
193 |
if (fItemBounds == null) return; |
|
194 |
||
18760
438afe2fc852
8020038: [macosx] Incorrect usage of invokeLater() and likes in callbacks called via JNI from AppKit thread
leonidr
parents:
12559
diff
changeset
|
195 |
SunToolkit.executeOnEventHandlerThread(fInvoker, new Runnable() { |
12047 | 196 |
@Override |
197 |
public void run() { |
|
198 |
Component target = null; |
|
199 |
Rectangle targetRect = null; |
|
200 |
for (int i = 0; i < fItemBounds.length; i++) { |
|
201 |
final Rectangle testRect = fItemBounds[i]; |
|
202 |
if (testRect != null) { |
|
203 |
if (testRect.contains(x, y)) { |
|
204 |
target = fInvoker.getMenuComponent(i); |
|
205 |
targetRect = testRect; |
|
206 |
break; |
|
207 |
} |
|
208 |
} |
|
209 |
} |
|
210 |
if (target == null && fLastMouseEventTarget == null) return; |
|
211 |
||
212 |
// Send a mouseExited to the previously hilited item, if it wasn't 0. |
|
213 |
if (target != fLastMouseEventTarget) { |
|
214 |
if (fLastMouseEventTarget != null) { |
|
215 |
LWToolkit.postEvent(new MouseEvent(fLastMouseEventTarget, MouseEvent.MOUSE_EXITED, when, modifiers, x - fLastTargetRect.x, y - fLastTargetRect.y, 0, false)); |
|
216 |
} |
|
217 |
// Send a mouseEntered to the current hilited item, if it wasn't 0. |
|
218 |
if (target != null) { |
|
219 |
LWToolkit.postEvent(new MouseEvent(target, MouseEvent.MOUSE_ENTERED, when, modifiers, x - targetRect.x, y - targetRect.y, 0, false)); |
|
220 |
} |
|
221 |
fLastMouseEventTarget = target; |
|
222 |
fLastTargetRect = targetRect; |
|
223 |
} |
|
224 |
// Post a mouse event to the current item. |
|
225 |
if (target == null) return; |
|
226 |
LWToolkit.postEvent(new MouseEvent(target, kind, when, modifiers, x - targetRect.x, y - targetRect.y, 0, false)); |
|
227 |
} |
|
228 |
}); |
|
229 |
} |
|
230 |
||
20840 | 231 |
@Override |
28235
0dfebfcb9f8a
8067086: Suppress mac-specific deprecation warnings in the java.desktop module
darcy
parents:
25859
diff
changeset
|
232 |
@SuppressWarnings("deprecation") |
12047 | 233 |
public void addNotify() { |
20840 | 234 |
synchronized (getTreeLock()) { |
235 |
super.addNotify(); |
|
236 |
if (fModelPtr == 0) { |
|
237 |
fInvoker.addContainerListener(this); |
|
238 |
fInvoker.addComponentListener(this); |
|
239 |
fPropertyListener = new ScreenMenuPropertyListener(this); |
|
240 |
fInvoker.addPropertyChangeListener(fPropertyListener); |
|
12047 | 241 |
|
20840 | 242 |
final Icon icon = fInvoker.getIcon(); |
243 |
if (icon != null) { |
|
244 |
setIcon(icon); |
|
245 |
} |
|
12047 | 246 |
|
20840 | 247 |
final String tooltipText = fInvoker.getToolTipText(); |
248 |
if (tooltipText != null) { |
|
249 |
setToolTipText(tooltipText); |
|
250 |
} |
|
251 |
final MenuComponentPeer peer = getPeer(); |
|
252 |
if (peer instanceof CMenu) { |
|
253 |
final CMenu menu = (CMenu) peer; |
|
254 |
final long nativeMenu = menu.getNativeMenu(); |
|
255 |
fModelPtr = addMenuListeners(this, nativeMenu); |
|
256 |
} |
|
12047 | 257 |
} |
258 |
} |
|
259 |
} |
|
260 |
||
20840 | 261 |
@Override |
12047 | 262 |
public void removeNotify() { |
20840 | 263 |
synchronized (getTreeLock()) { |
264 |
// Call super so that the NSMenu has been removed, before we release |
|
265 |
// the delegate in removeMenuListeners |
|
266 |
super.removeNotify(); |
|
267 |
fItems.clear(); |
|
268 |
if (fModelPtr != 0) { |
|
269 |
removeMenuListeners(fModelPtr); |
|
270 |
fModelPtr = 0; |
|
271 |
fInvoker.removeContainerListener(this); |
|
272 |
fInvoker.removeComponentListener(this); |
|
273 |
fInvoker.removePropertyChangeListener(fPropertyListener); |
|
274 |
} |
|
12047 | 275 |
} |
276 |
} |
|
277 |
||
278 |
/** |
|
279 |
* Invoked when a component has been added to the container. |
|
280 |
*/ |
|
20840 | 281 |
@Override |
12047 | 282 |
public void componentAdded(final ContainerEvent e) { |
283 |
addItem(e.getChild()); |
|
284 |
} |
|
285 |
||
286 |
/** |
|
287 |
* Invoked when a component has been removed from the container. |
|
288 |
*/ |
|
20840 | 289 |
@Override |
12047 | 290 |
public void componentRemoved(final ContainerEvent e) { |
291 |
final Component child = e.getChild(); |
|
292 |
final MenuItem sm = fItems.get(child); |
|
293 |
if (sm == null) return; |
|
294 |
||
20840 | 295 |
remove(sm); |
296 |
fItems.remove(sm); |
|
297 |
} |
|
12047 | 298 |
|
299 |
/** |
|
300 |
* Invoked when the component's size changes. |
|
301 |
*/ |
|
20840 | 302 |
@Override |
12047 | 303 |
public void componentResized(final ComponentEvent e) {} |
304 |
||
305 |
/** |
|
306 |
* Invoked when the component's position changes. |
|
307 |
*/ |
|
20840 | 308 |
@Override |
12047 | 309 |
public void componentMoved(final ComponentEvent e) {} |
310 |
||
311 |
/** |
|
312 |
* Invoked when the component has been made visible. |
|
313 |
* See componentHidden - we should still have a MenuItem |
|
314 |
* it just isn't inserted |
|
315 |
*/ |
|
20840 | 316 |
@Override |
12047 | 317 |
public void componentShown(final ComponentEvent e) { |
318 |
setVisible(true); |
|
319 |
} |
|
320 |
||
321 |
/** |
|
322 |
* Invoked when the component has been made invisible. |
|
323 |
* MenuComponent.setVisible does nothing, |
|
324 |
* so we remove the ScreenMenuItem from the ScreenMenu |
|
325 |
* but leave it in fItems |
|
326 |
*/ |
|
20840 | 327 |
@Override |
12047 | 328 |
public void componentHidden(final ComponentEvent e) { |
329 |
setVisible(false); |
|
330 |
} |
|
331 |
||
20840 | 332 |
private void setVisible(final boolean b) { |
12047 | 333 |
// Tell our parent to add/remove us |
334 |
final MenuContainer parent = getParent(); |
|
335 |
||
336 |
if (parent != null) { |
|
337 |
if (parent instanceof ScreenMenu) { |
|
338 |
final ScreenMenu sm = (ScreenMenu)parent; |
|
339 |
sm.setChildVisible(fInvoker, b); |
|
20840 | 340 |
} |
12047 | 341 |
} |
342 |
} |
|
343 |
||
20840 | 344 |
@Override |
12047 | 345 |
public void setChildVisible(final JMenuItem child, final boolean b) { |
346 |
fItems.remove(child); |
|
347 |
updateItems(); |
|
348 |
} |
|
349 |
||
20840 | 350 |
@Override |
12047 | 351 |
public void setAccelerator(final KeyStroke ks) {} |
352 |
||
353 |
// only check and radio items can be indeterminate |
|
20840 | 354 |
@Override |
12047 | 355 |
public void setIndeterminate(boolean indeterminate) { } |
356 |
||
20840 | 357 |
@Override |
28235
0dfebfcb9f8a
8067086: Suppress mac-specific deprecation warnings in the java.desktop module
darcy
parents:
25859
diff
changeset
|
358 |
@SuppressWarnings("deprecation") |
12047 | 359 |
public void setToolTipText(final String text) { |
360 |
final MenuComponentPeer peer = getPeer(); |
|
361 |
if (!(peer instanceof CMenuItem)) return; |
|
362 |
||
363 |
final CMenuItem cmi = (CMenuItem)peer; |
|
364 |
cmi.setToolTipText(text); |
|
365 |
} |
|
366 |
||
20840 | 367 |
@Override |
28235
0dfebfcb9f8a
8067086: Suppress mac-specific deprecation warnings in the java.desktop module
darcy
parents:
25859
diff
changeset
|
368 |
@SuppressWarnings("deprecation") |
12047 | 369 |
public void setIcon(final Icon i) { |
370 |
final MenuComponentPeer peer = getPeer(); |
|
371 |
if (!(peer instanceof CMenuItem)) return; |
|
372 |
||
373 |
final CMenuItem cmi = (CMenuItem)peer; |
|
374 |
Image img = null; |
|
375 |
||
376 |
if (i != null) { |
|
377 |
if (i.getIconWidth() > 0 && i.getIconHeight() > 0) { |
|
378 |
img = AquaIcon.getImageForIcon(i); |
|
379 |
} |
|
380 |
} |
|
381 |
cmi.setImage(img); |
|
382 |
} |
|
383 |
||
384 |
||
385 |
/** |
|
386 |
* Gets a hashCode for a JMenu or JMenuItem or subclass so that we can compare for |
|
387 |
* changes in the Menu. |
|
388 |
*/ |
|
20840 | 389 |
private static int getHashCode(final Component m) { |
12047 | 390 |
int hashCode = m.hashCode(); |
391 |
||
392 |
if (m instanceof JMenuItem) { |
|
393 |
final JMenuItem mi = (JMenuItem) m; |
|
394 |
||
395 |
final String text = mi.getText(); |
|
396 |
if (text != null) hashCode ^= text.hashCode(); |
|
397 |
||
398 |
final Icon icon = mi.getIcon(); |
|
399 |
if (icon != null) hashCode ^= icon.hashCode(); |
|
400 |
||
401 |
final Icon disabledIcon = mi.getDisabledIcon(); |
|
402 |
if (disabledIcon != null) hashCode ^= disabledIcon.hashCode(); |
|
403 |
||
404 |
final Action action = mi.getAction(); |
|
405 |
if (action != null) hashCode ^= action.hashCode(); |
|
406 |
||
407 |
final KeyStroke ks = mi.getAccelerator(); |
|
408 |
if (ks != null) hashCode ^= ks.hashCode(); |
|
409 |
||
410 |
hashCode ^= Boolean.valueOf(mi.isVisible()).hashCode(); |
|
411 |
hashCode ^= Boolean.valueOf(mi.isEnabled()).hashCode(); |
|
412 |
hashCode ^= Boolean.valueOf(mi.isSelected()).hashCode(); |
|
413 |
||
414 |
} else if (m instanceof JSeparator) { |
|
415 |
hashCode ^= "-".hashCode(); |
|
416 |
} |
|
417 |
||
418 |
return hashCode; |
|
419 |
} |
|
420 |
||
20840 | 421 |
private void addItem(final Component m) { |
12047 | 422 |
if (!m.isVisible()) return; |
423 |
MenuItem sm = fItems.get(m); |
|
424 |
||
425 |
if (sm == null) { |
|
426 |
if (m instanceof JMenu) { |
|
427 |
sm = new ScreenMenu((JMenu)m); |
|
428 |
} else if (m instanceof JCheckBoxMenuItem) { |
|
429 |
sm = new ScreenMenuItemCheckbox((JCheckBoxMenuItem)m); |
|
430 |
} else if (m instanceof JRadioButtonMenuItem) { |
|
431 |
sm = new ScreenMenuItemCheckbox((JRadioButtonMenuItem)m); |
|
432 |
} else if (m instanceof JMenuItem) { |
|
433 |
sm = new ScreenMenuItem((JMenuItem)m); |
|
434 |
} else if (m instanceof JPopupMenu.Separator || m instanceof JSeparator) { |
|
435 |
sm = new MenuItem("-"); // This is what java.awt.Menu.addSeparator does |
|
436 |
} |
|
437 |
||
438 |
// Only place the menu item in the hashtable if we just created it. |
|
439 |
if (sm != null) { |
|
440 |
fItems.put(m, sm); |
|
441 |
} |
|
442 |
} |
|
443 |
||
444 |
if (sm != null) { |
|
445 |
add(sm); |
|
446 |
} |
|
447 |
} |
|
448 |
} |