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
.DataManager
;
19 import com
.intellij
.openapi
.actionSystem
.*;
20 import com
.intellij
.openapi
.actionSystem
.ex
.ActionUtil
;
21 import com
.intellij
.openapi
.application
.ApplicationManager
;
22 import com
.intellij
.openapi
.diagnostic
.Logger
;
23 import com
.intellij
.openapi
.progress
.ProcessCanceledException
;
24 import com
.intellij
.openapi
.project
.DumbAware
;
25 import com
.intellij
.openapi
.project
.DumbService
;
26 import com
.intellij
.openapi
.project
.Project
;
27 import com
.intellij
.openapi
.util
.ActionCallback
;
28 import com
.intellij
.openapi
.util
.SystemInfo
;
29 import com
.intellij
.openapi
.util
.registry
.Registry
;
30 import com
.intellij
.openapi
.wm
.IdeFocusManager
;
31 import org
.jetbrains
.annotations
.NonNls
;
32 import org
.jetbrains
.annotations
.NotNull
;
36 import java
.util
.ArrayList
;
39 * @author Anton Katilin
40 * @author Vladimir Kondratyev
43 private static final Logger LOG
=Logger
.getInstance("#com.intellij.openapi.actionSystem.impl.Utils");
44 @NonNls public static final String NOTHING_HERE
= "Nothing here";
45 public static final AnAction EMPTY_MENU_FILLER
= new AnAction(NOTHING_HERE
) {
48 getTemplatePresentation().setEnabled(false);
52 public void actionPerformed(AnActionEvent e
) {
56 public void update(AnActionEvent e
) {
57 e
.getPresentation().setEnabled(false);
64 private static void handleUpdateException(AnAction action
, Presentation presentation
, Throwable exc
) {
65 String id
= ActionManager
.getInstance().getId(action
);
67 LOG
.error("update failed for AnAction with ID=" + id
, exc
);
70 LOG
.error("update failed for ActionGroup: " + action
+ "[" + presentation
.getText() + "]", exc
);
75 * @param actionManager manager
76 * @param list this list contains expanded actions.
78 public static void expandActionGroup(@NotNull ActionGroup group
,
79 ArrayList
<AnAction
> list
,
80 PresentationFactory presentationFactory
,
82 String place
, ActionManager actionManager
){
83 Presentation presentation
= presentationFactory
.getPresentation(group
);
84 AnActionEvent e
= new AnActionEvent(
92 if (!doUpdate(group
, e
, presentation
)) return;
94 if(!presentation
.isVisible()){ // don't process invisible groups
97 AnAction
[] children
=group
.getChildren(e
);
98 for (int i
= 0; i
< children
.length
; i
++) {
99 AnAction child
= children
[i
];
101 String groupId
= ActionManager
.getInstance().getId(group
);
102 LOG
.assertTrue(false, "action is null: i=" + i
+ " group=" + group
+ " group id=" + groupId
);
106 presentation
= presentationFactory
.getPresentation(child
);
107 AnActionEvent e1
= new AnActionEvent(null, context
, place
, presentation
, actionManager
, 0);
108 e1
.setInjectedContext(child
.isInInjectedContext());
109 if (!doUpdate(child
, e1
, presentation
)) continue;
110 if (!presentation
.isVisible()) { // don't create invisible items in the menu
113 if (child
instanceof ActionGroup
) {
114 ActionGroup actionGroup
= (ActionGroup
)child
;
115 if (actionGroup
.isPopup()) { // popup menu has its own presentation
116 // disable group if it contains no visible actions
117 final boolean enabled
= hasVisibleChildren(actionGroup
, presentationFactory
, context
, place
);
118 presentation
.setEnabled(enabled
);
122 expandActionGroup((ActionGroup
)child
, list
, presentationFactory
, context
, place
, actionManager
);
125 else if (child
instanceof Separator
) {
126 if (!list
.isEmpty() && !(list
.get(list
.size() - 1) instanceof Separator
)) {
136 // returns false if exception was thrown and handled
137 private static boolean doUpdate(final AnAction action
, final AnActionEvent e
, final Presentation presentation
) throws ProcessCanceledException
{
138 if (ApplicationManager
.getApplication().isDisposed()) return false;
140 long startTime
= System
.currentTimeMillis();
141 final boolean result
;
143 result
= !ActionUtil
.performDumbAwareUpdate(action
, e
, false);
145 catch (ProcessCanceledException ex
) {
148 catch (Throwable exc
) {
149 handleUpdateException(action
, presentation
, exc
);
152 long endTime
= System
.currentTimeMillis();
153 if (endTime
- startTime
> 10 && LOG
.isDebugEnabled()) {
154 LOG
.debug("Action " + action
+ ": updated in " + (endTime
-startTime
) + " ms");
159 private static boolean hasVisibleChildren(ActionGroup group
, PresentationFactory factory
, DataContext context
, String place
) {
160 AnActionEvent event
= new AnActionEvent(null, context
, place
, factory
.getPresentation(group
), ActionManager
.getInstance(), 0);
161 event
.setInjectedContext(group
.isInInjectedContext());
162 AnAction
[] children
= group
.getChildren(event
);
163 for (AnAction anAction
: children
) {
164 if (anAction
instanceof Separator
) {
167 final Project project
= PlatformDataKeys
.PROJECT
.getData(context
);
168 if (project
!= null && DumbService
.getInstance(project
).isDumb() && !(anAction
instanceof DumbAware
) && !(anAction
instanceof ActionGroup
)) {
172 LOG
.assertTrue(anAction
!= null, "Null action found in group " + group
);
174 final Presentation presentation
= factory
.getPresentation(anAction
);
175 updateGroupChild(context
, place
, anAction
, presentation
);
176 if (anAction
instanceof ActionGroup
) {
177 ActionGroup childGroup
= (ActionGroup
)anAction
;
179 // popup menu must be visible itself
180 if (childGroup
.isPopup()) {
181 if (!presentation
.isVisible()) {
186 if (hasVisibleChildren(childGroup
, factory
, context
, place
)) {
190 else if (presentation
.isVisible()) {
198 public static void updateGroupChild(DataContext context
, String place
, AnAction anAction
, final Presentation presentation
) {
199 AnActionEvent event1
= new AnActionEvent(null, context
, place
, presentation
, ActionManager
.getInstance(), 0);
200 event1
.setInjectedContext(anAction
.isInInjectedContext());
201 doUpdate(anAction
, event1
, presentation
);
205 public static void fillMenu(@NotNull final ActionGroup group
,
206 final JComponent component
, boolean enableMnemonics
, final PresentationFactory presentationFactory
, DataContext context
, final String place
, boolean isWindowMenu
){
207 final ActionCallback menuBuilt
= new ActionCallback();
209 ArrayList
<AnAction
> list
= new ArrayList
<AnAction
>();
210 expandActionGroup(group
, list
, presentationFactory
, context
, place
, ActionManager
.getInstance());
212 final boolean fixMacScreenMenu
= SystemInfo
.isMacSystemMenu
&& isWindowMenu
&& Registry
.is("actionSystem.mac.screenMenuNotUpdatedFix");
214 final ArrayList
<Component
> children
= new ArrayList
<Component
>();
216 for (int i
= 0; i
< list
.size(); i
++) {
217 AnAction action
= list
.get(i
);
218 if (action
instanceof Separator
) {
219 if (i
> 0 && i
< list
.size() - 1) {
220 component
.add(new JPopupMenu
.Separator());
223 else if (action
instanceof ActionGroup
) {
224 ActionMenu menu
= new ActionMenu(context
, place
, (ActionGroup
)action
, presentationFactory
, enableMnemonics
);
229 final ActionMenuItem each
=
230 new ActionMenuItem(action
, presentationFactory
.getPresentation(action
), place
, context
, enableMnemonics
, !fixMacScreenMenu
);
236 if (list
.isEmpty()) {
237 final ActionMenuItem each
=
238 new ActionMenuItem(EMPTY_MENU_FILLER
, presentationFactory
.getPresentation(EMPTY_MENU_FILLER
), place
, context
, enableMnemonics
,
244 if (fixMacScreenMenu
) {
245 SwingUtilities
.invokeLater(new Runnable() {
247 for (Component each
: children
) {
248 if (each
.getParent() != null && each
instanceof ActionMenuItem
) {
249 ((ActionMenuItem
)each
).prepare();
260 menuBuilt
.doWhenDone(new Runnable() {
262 if (IdeFocusManager
.getInstance(null).isFocusBeingTransferred()) {
263 IdeFocusManager
.getInstance(null).doWhenFocusSettlesDown(new Runnable() {
265 if (!component
.isShowing()) return;
267 DataContext context
= DataManager
.getInstance().getDataContext();
268 expandActionGroup(group
, new ArrayList
<AnAction
>(), presentationFactory
, context
, place
, ActionManager
.getInstance());
270 for (Component each
: children
) {
271 if (each
instanceof ActionMenuItem
) {
272 ((ActionMenuItem
)each
).updateContext(context
);
273 } else if (each
instanceof ActionMenu
) {
274 ((ActionMenu
)each
).updateContext(context
);