1 /*
2  * Copyright (c) 2011, 2018, Oracle and/or its affiliates. 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.  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 sun.lwawt.macosx;
27 
28 import java.awt.MenuItem;
29 import java.awt.MenuShortcut;
30 import java.awt.event.ActionEvent;
31 import java.awt.event.InputEvent;
32 import java.awt.event.KeyEvent;
33 import java.awt.peer.MenuItemPeer;
34 import java.util.concurrent.atomic.AtomicBoolean;
35 
36 import sun.awt.SunToolkit;
37 import sun.lwawt.LWToolkit;
38 
39 public class CMenuItem extends CMenuComponent implements MenuItemPeer {
40 
41     private final AtomicBoolean enabled = new AtomicBoolean(true);
42 
CMenuItem(MenuItem target)43     public CMenuItem(MenuItem target) {
44         super(target);
45         initialize(target);
46     }
47 
48     // This way we avoiding invocation of the setters twice
initialize(MenuItem target)49     protected void initialize(MenuItem target) {
50         if (!isSeparator()) {
51             setLabel(target.getLabel());
52             setEnabled(target.isEnabled());
53         }
54     }
55 
isSeparator()56     private boolean isSeparator() {
57         String label = ((MenuItem)getTarget()).getLabel();
58         return (label != null && label.equals("-"));
59     }
60 
61     @Override
createModel()62     long createModel() {
63         CMenuComponent parent = (CMenuComponent)LWToolkit.targetToPeer(getTarget().getParent());
64         return parent.executeGet(ptr->nativeCreate(ptr, isSeparator()));
65     }
66     @SuppressWarnings("deprecation")
setLabel(String label, char keyChar, int keyCode, int modifiers)67     public void setLabel(String label, char keyChar, int keyCode, int modifiers) {
68         int keyMask = modifiers;
69         if (keyCode == KeyEvent.VK_UNDEFINED) {
70             MenuShortcut shortcut = ((MenuItem)getTarget()).getShortcut();
71 
72             if (shortcut != null) {
73                 keyCode = shortcut.getKey();
74                 keyMask |= InputEvent.META_MASK;
75 
76                 if (shortcut.usesShiftModifier()) {
77                     keyMask |= InputEvent.SHIFT_MASK;
78                 }
79             }
80         }
81 
82         if (label == null) {
83             label = "";
84         }
85 
86         // <rdar://problem/3654824>
87         // Native code uses a keyChar of 0 to indicate that the
88         // keyCode should be used to generate the shortcut.  Translate
89         // CHAR_UNDEFINED into 0.
90         if (keyChar == KeyEvent.CHAR_UNDEFINED) {
91             keyChar = 0;
92         }
93 
94         final String finalLabel = label;
95         final char finalKeyChar = keyChar;
96         final int finalKeyCode = keyCode;
97         final int finalKeyMask = keyMask;
98         execute(ptr -> nativeSetLabel(ptr, finalLabel, finalKeyChar,
99                                       finalKeyCode, finalKeyMask));
100     }
101 
102     @Override
setLabel(String label)103     public void setLabel(String label) {
104         setLabel(label, (char)0, KeyEvent.VK_UNDEFINED, 0);
105     }
106 
107     /**
108      * This is new API that we've added to AWT menu items
109      * because AWT menu items are used for Swing screen menu bars
110      * and we want to support the NSMenuItem image apis.
111      * There isn't a need to expose this except in a instanceof because
112      * it isn't defined in the peer api.
113      */
setImage(final java.awt.Image img)114     public final void setImage(final java.awt.Image img) {
115         CImage cimg = CImage.getCreator().createFromImage(img);
116         execute(ptr -> {
117             if (cimg == null) {
118                 nativeSetImage(ptr, 0L);
119             } else {
120                 cimg.execute(imgPtr -> nativeSetImage(ptr, imgPtr));
121             }
122         });
123     }
124 
125     /**
126      * New API for tooltips
127      */
setToolTipText(final String text)128     public final void setToolTipText(final String text) {
129         execute(ptr -> nativeSetTooltip(ptr, text));
130     }
131 
isEnabled()132     public final boolean isEnabled() {
133         return enabled.get();
134     }
135 
136     @Override
setEnabled(boolean b)137     public void setEnabled(boolean b) {
138         final Object parent = LWToolkit.targetToPeer(getTarget().getParent());
139         if (parent instanceof CMenuItem) {
140             b &= ((CMenuItem) parent).isEnabled();
141         }
142         if (enabled.compareAndSet(!b, b)) {
143             final boolean finalB = b;
144             execute(ptr->nativeSetEnabled(ptr, finalB));
145         }
146     }
147 
nativeCreate(long parentMenu, boolean isSeparator)148     private native long nativeCreate(long parentMenu, boolean isSeparator);
nativeSetLabel(long modelPtr, String label, char keyChar, int keyCode, int modifiers)149     private native void nativeSetLabel(long modelPtr, String label, char keyChar, int keyCode, int modifiers);
nativeSetImage(long modelPtr, long image)150     private native void nativeSetImage(long modelPtr, long image);
nativeSetTooltip(long modelPtr, String text)151     private native void nativeSetTooltip(long modelPtr, String text);
nativeSetEnabled(long modelPtr, boolean b)152     private native void nativeSetEnabled(long modelPtr, boolean b);
153 
154     // native callbacks
handleAction(final long when, final int modifiers)155     void handleAction(final long when, final int modifiers) {
156         SunToolkit.executeOnEventHandlerThread(getTarget(), new Runnable() {
157             public void run() {
158                 final String cmd = ((MenuItem)getTarget()).getActionCommand();
159                 final ActionEvent event = new ActionEvent(getTarget(), ActionEvent.ACTION_PERFORMED, cmd, when, modifiers);
160                 SunToolkit.postEvent(SunToolkit.targetToAppContext(getTarget()), event);
161             }
162         });
163     }
164 }
165