3fe08af26bef69f21abbb1c03c0f783c22f4b1b1
[fedora-idea.git] / platform / platform-impl / src / com / intellij / openapi / actionSystem / impl / ActionMenuItem.java
blob3fe08af26bef69f21abbb1c03c0f783c22f4b1b1
1 /*
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;
33 import javax.swing.*;
34 import java.awt.*;
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;
41 import java.util.Set;
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) {
56 myAction = action;
57 myPresentation = presentation;
58 myPlace = place;
59 myContext = context;
60 myEnableMnemonics = enableMnemonics;
61 myEvent = new AnActionEvent(null, context, place, myPresentation, ActionManager.getInstance(), 0);
62 addActionListener(new ActionTransmitter());
63 setBorderPainted(false);
65 if (prepareNow) {
66 init();
67 } else {
68 setText("loading...");
72 public void prepare() {
73 init();
74 installSynchronizer();
77 /**
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() {
85 super.addNotify();
86 installSynchronizer();
87 init();
90 public void removeNotify() {
91 uninstallSynchronizer();
92 super.removeNotify();
95 @Override
96 protected void paintComponent(Graphics g) {
97 super.paintComponent(g);
98 //g.setColor(getBackground());
99 //g.fillRect(0, 0, getWidth(), getHeight());
102 @Override
103 protected void paintChildren(Graphics g) {
104 super.paintChildren(g);
107 @Override
108 protected void paintBorder(Graphics g) {
109 super.paintBorder(g);
113 @Override
114 public void paint(Graphics g) {
115 super.paint(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);
142 updateIcon();
143 String id = ActionManager.getInstance().getId(myAction);
144 if (id != null) {
145 Shortcut[] shortcuts = KeymapManager.getInstance().getActiveKeymap().getShortcuts(id);
146 setAcceleratorFromShortcuts(shortcuts);
148 else {
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());
160 break;
165 public void updateUI() {
166 if (UIUtil.isStandardMenuLAF()) {
167 super.updateUI();
169 else {
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();
196 else {
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)) {
211 return;
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));
226 else {
227 setIcon(ourUncheckedIcon);
228 setDisabledIcon(IconLoader.getDisabledIcon(ourUncheckedIcon));
231 else {
232 if (!SystemInfo.isMac || UISettings.getInstance().SHOW_ICONS_IN_MENUS) {
233 Icon icon = myPresentation.getIcon();
234 setIcon(icon);
235 if (myPresentation.getDisabledIcon() != null) {
236 setDisabledIcon(myPresentation.getDisabledIcon());
238 else {
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);
266 try {
267 if (Presentation.PROP_VISIBLE.equals(name)) {
268 final boolean visible = myPresentation.isVisible();
269 if (!visible && SystemInfo.isMacSystemMenu && myPlace == ActionPlaces.MAIN_MENU) {
270 setEnabled(false);
272 else {
273 setVisible(visible);
276 else if (Presentation.PROP_ENABLED.equals(name)) {
277 setEnabled(myPresentation.isEnabled());
278 updateIcon();
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)) {
290 updateIcon();
292 else if (SELECTED.equals(name)) {
293 updateIcon();
296 finally {
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() {
302 public void run() {
303 if (getParent() == null) {
304 uninstallSynchronizer();