2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com
.intellij
.openapi
.actionSystem
.impl
;
18 import com
.intellij
.ide
.ui
.UISettings
;
19 import com
.intellij
.openapi
.Disposable
;
20 import com
.intellij
.openapi
.actionSystem
.*;
21 import com
.intellij
.openapi
.actionSystem
.ex
.ActionManagerEx
;
22 import com
.intellij
.openapi
.actionSystem
.ex
.ActionUtil
;
23 import com
.intellij
.openapi
.keymap
.KeymapManager
;
24 import com
.intellij
.openapi
.keymap
.KeymapUtil
;
25 import com
.intellij
.openapi
.util
.Disposer
;
26 import com
.intellij
.openapi
.util
.IconLoader
;
27 import com
.intellij
.openapi
.util
.SystemInfo
;
28 import com
.intellij
.ui
.plaf
.beg
.BegMenuItemUI
;
29 import com
.intellij
.util
.ui
.EmptyIcon
;
30 import com
.intellij
.util
.ui
.UIUtil
;
31 import org
.jetbrains
.annotations
.NonNls
;
35 import java
.awt
.event
.ActionEvent
;
36 import java
.awt
.event
.ActionListener
;
37 import java
.awt
.event
.MouseEvent
;
38 import java
.beans
.PropertyChangeEvent
;
39 import java
.beans
.PropertyChangeListener
;
40 import java
.util
.HashSet
;
43 public class ActionMenuItem
extends JMenuItem
{
44 private static final Icon ourCheckedIcon
= IconLoader
.getIcon("/actions/check.png");
45 private static final Icon ourUncheckedIcon
= new EmptyIcon(18, 18);
47 private final AnAction myAction
;
48 private final Presentation myPresentation
;
49 private final String myPlace
;
50 private final DataContext myContext
;
51 private final AnActionEvent myEvent
;
52 private MenuItemSynchronizer myMenuItemSynchronizer
;
53 private boolean myEnableMnemonics
;
55 public ActionMenuItem(AnAction action
, Presentation presentation
, String place
, DataContext context
, final boolean enableMnemonics
, boolean prepareNow
) {
57 myPresentation
= presentation
;
60 myEnableMnemonics
= enableMnemonics
;
61 myEvent
= new AnActionEvent(null, context
, place
, myPresentation
, ActionManager
.getInstance(), 0);
62 addActionListener(new ActionTransmitter());
63 setBorderPainted(false);
68 setText("loading...");
72 public void prepare() {
74 installSynchronizer();
78 * We have to make this method public to allow BegMenuItemUI to invoke it.
80 public void fireActionPerformed(ActionEvent event
) {
81 super.fireActionPerformed(event
);
84 public void addNotify() {
86 installSynchronizer();
90 public void removeNotify() {
91 uninstallSynchronizer();
96 protected void paintComponent(Graphics g
) {
97 super.paintComponent(g
);
98 //g.setColor(getBackground());
99 //g.fillRect(0, 0, getWidth(), getHeight());
103 protected void paintChildren(Graphics g
) {
104 super.paintChildren(g
);
108 protected void paintBorder(Graphics g
) {
109 super.paintBorder(g
);
114 public void paint(Graphics g
) {
118 private void installSynchronizer() {
119 if (myMenuItemSynchronizer
== null) {
120 myMenuItemSynchronizer
= new MenuItemSynchronizer();
124 private void uninstallSynchronizer() {
125 if (myMenuItemSynchronizer
!= null) {
126 Disposer
.dispose(myMenuItemSynchronizer
);
127 myMenuItemSynchronizer
= null;
131 private void init() {
132 setVisible(myPresentation
.isVisible());
133 setEnabled(myPresentation
.isEnabled());
134 setMnemonic(myEnableMnemonics ? myPresentation
.getMnemonic() : 0);
135 setText(myPresentation
.getText());
136 final int mnemonicIndex
= myEnableMnemonics ? myPresentation
.getDisplayedMnemonicIndex() : -1;
138 if (getText() != null && mnemonicIndex
>= 0 && mnemonicIndex
< getText().length()) {
139 setDisplayedMnemonicIndex(mnemonicIndex
);
143 String id
= ActionManager
.getInstance().getId(myAction
);
145 Shortcut
[] shortcuts
= KeymapManager
.getInstance().getActiveKeymap().getShortcuts(id
);
146 setAcceleratorFromShortcuts(shortcuts
);
149 final ShortcutSet shortcutSet
= myAction
.getShortcutSet();
150 if (shortcutSet
!= null) {
151 setAcceleratorFromShortcuts(shortcutSet
.getShortcuts());
156 private void setAcceleratorFromShortcuts(final Shortcut
[] shortcuts
) {
157 for (Shortcut shortcut
: shortcuts
) {
158 if (shortcut
instanceof KeyboardShortcut
) {
159 setAccelerator(((KeyboardShortcut
)shortcut
).getFirstKeyStroke());
165 public void updateUI() {
166 if (UIUtil
.isStandardMenuLAF()) {
170 setUI(BegMenuItemUI
.createUI(this));
175 * Updates long description of action at the status bar.
177 public void menuSelectionChanged(boolean isIncluded
) {
178 super.menuSelectionChanged(isIncluded
);
179 ActionMenu
.showDescriptionInStatusBar(isIncluded
, this, myPresentation
.getDescription());
182 public String
getFirstShortcutText() {
183 return KeymapUtil
.getFirstKeyboardShortcutText(myAction
);
186 private final class ActionTransmitter
implements ActionListener
{
188 * @param component component
189 * @return whether the component in Swing tree or not. This method is more
190 * weak then {@link Component#isShowing() }
192 private boolean isInTree(final Component component
) {
193 if (component
instanceof Window
) {
194 return component
.isShowing();
197 Window windowAncestor
= SwingUtilities
.getWindowAncestor(component
);
198 return windowAncestor
!= null && windowAncestor
.isShowing();
202 public void actionPerformed(final ActionEvent e
) {
203 AnActionEvent event
= new AnActionEvent(
204 new MouseEvent(ActionMenuItem
.this, MouseEvent
.MOUSE_PRESSED
, 0, e
.getModifiers(), getWidth() / 2, getHeight() / 2, 1, false),
205 myContext
, myPlace
, myPresentation
, ActionManager
.getInstance(), e
.getModifiers());
206 if (ActionUtil
.lastUpdateAndCheckDumb(myAction
, event
, false)) {
207 ActionManagerEx actionManager
= ActionManagerEx
.getInstanceEx();
208 actionManager
.fireBeforeActionPerformed(myAction
, myContext
, event
);
209 Component component
= PlatformDataKeys
.CONTEXT_COMPONENT
.getData(event
.getDataContext());
210 if (component
!= null && !isInTree(component
)) {
213 myAction
.actionPerformed(event
);
214 actionManager
.queueActionPerformedEvent(myAction
, myContext
, event
);
219 private void updateIcon() {
220 if (myAction
instanceof Toggleable
&& myPresentation
.getIcon() == null) {
221 myAction
.update(myEvent
);
222 if (Boolean
.TRUE
.equals(myEvent
.getPresentation().getClientProperty(Toggleable
.SELECTED_PROPERTY
))) {
223 setIcon(ourCheckedIcon
);
224 setDisabledIcon(IconLoader
.getDisabledIcon(ourCheckedIcon
));
227 setIcon(ourUncheckedIcon
);
228 setDisabledIcon(IconLoader
.getDisabledIcon(ourUncheckedIcon
));
232 if (!SystemInfo
.isMac
|| UISettings
.getInstance().SHOW_ICONS_IN_MENUS
) {
233 Icon icon
= myPresentation
.getIcon();
235 if (myPresentation
.getDisabledIcon() != null) {
236 setDisabledIcon(myPresentation
.getDisabledIcon());
239 setDisabledIcon(IconLoader
.getDisabledIcon(icon
));
245 private final class MenuItemSynchronizer
implements PropertyChangeListener
, Disposable
{
246 @NonNls private static final String SELECTED
= "selected";
248 private final Set
<String
> mySynchronized
= new HashSet
<String
>();
250 private MenuItemSynchronizer() {
251 myPresentation
.addPropertyChangeListener(this);
254 public void dispose() {
255 myPresentation
.removePropertyChangeListener(this);
258 public void propertyChange(PropertyChangeEvent e
) {
259 boolean queueForDispose
= getParent() == null;
261 String name
= e
.getPropertyName();
262 if (mySynchronized
.contains(name
)) return;
264 mySynchronized
.add(name
);
267 if (Presentation
.PROP_VISIBLE
.equals(name
)) {
268 final boolean visible
= myPresentation
.isVisible();
269 if (!visible
&& SystemInfo
.isMacSystemMenu
&& myPlace
== ActionPlaces
.MAIN_MENU
) {
276 else if (Presentation
.PROP_ENABLED
.equals(name
)) {
277 setEnabled(myPresentation
.isEnabled());
280 else if (Presentation
.PROP_MNEMONIC_KEY
.equals(name
)) {
281 setMnemonic(myPresentation
.getMnemonic());
283 else if (Presentation
.PROP_MNEMONIC_INDEX
.equals(name
)) {
284 setDisplayedMnemonicIndex(myPresentation
.getDisplayedMnemonicIndex());
286 else if (Presentation
.PROP_TEXT
.equals(name
)) {
287 setText(myPresentation
.getText());
289 else if (Presentation
.PROP_ICON
.equals(name
) || Presentation
.PROP_DISABLED_ICON
.equals(name
)) {
292 else if (SELECTED
.equals(name
)) {
297 mySynchronized
.remove(name
);
298 if (queueForDispose
) {
299 // later since we cannot remove property listeners inside event processing
300 //noinspection SSBasedInspection
301 SwingUtilities
.invokeLater(new Runnable() {
303 if (getParent() == null) {
304 uninstallSynchronizer();