755ef0ee12ea62ee70d69c1f01400eac2b9c1b3c
[idea/community.git] / platform / lang-impl / src / com / intellij / ide / actions / GotoActionAction.java
1 /*
2  * Copyright 2000-2015 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package com.intellij.ide.actions;
18
19 import com.intellij.featureStatistics.FeatureUsageTracker;
20 import com.intellij.ide.DataManager;
21 import com.intellij.ide.ui.search.BooleanOptionDescription;
22 import com.intellij.ide.ui.search.OptionDescription;
23 import com.intellij.ide.util.gotoByName.ChooseByNamePopup;
24 import com.intellij.ide.util.gotoByName.GotoActionItemProvider;
25 import com.intellij.ide.util.gotoByName.GotoActionModel;
26 import com.intellij.openapi.Disposable;
27 import com.intellij.openapi.actionSystem.*;
28 import com.intellij.openapi.actionSystem.ex.ActionUtil;
29 import com.intellij.openapi.application.ApplicationManager;
30 import com.intellij.openapi.application.ModalityState;
31 import com.intellij.openapi.editor.Editor;
32 import com.intellij.openapi.keymap.KeymapUtil;
33 import com.intellij.openapi.progress.util.ProgressWindow;
34 import com.intellij.openapi.project.DumbAware;
35 import com.intellij.openapi.project.Project;
36 import com.intellij.openapi.ui.popup.JBPopupFactory;
37 import com.intellij.openapi.ui.popup.ListPopup;
38 import com.intellij.openapi.util.Disposer;
39 import com.intellij.openapi.util.Pair;
40 import com.intellij.psi.PsiFile;
41 import org.jetbrains.annotations.NotNull;
42 import org.jetbrains.annotations.Nullable;
43
44 import javax.swing.*;
45 import javax.swing.event.ListSelectionEvent;
46 import javax.swing.event.ListSelectionListener;
47 import java.awt.*;
48 import java.awt.event.InputEvent;
49 import java.awt.event.KeyEvent;
50 import java.awt.event.MouseAdapter;
51 import java.awt.event.MouseEvent;
52 import java.util.Set;
53
54 public class GotoActionAction extends GotoActionBase implements DumbAware {
55
56   @Override
57   public void gotoActionPerformed(@NotNull final AnActionEvent e) {
58     final Project project = e.getData(CommonDataKeys.PROJECT);
59     final Component component = e.getData(PlatformDataKeys.CONTEXT_COMPONENT);
60     Editor editor = e.getData(CommonDataKeys.EDITOR);
61     PsiFile file = e.getData(CommonDataKeys.PSI_FILE);
62
63     FeatureUsageTracker.getInstance().triggerFeatureUsed("navigation.popup.action");
64     GotoActionModel model = new GotoActionModel(project, component, editor, file);
65     GotoActionCallback<Object> callback = new GotoActionCallback<Object>() {
66       @Override
67       public void elementChosen(@NotNull ChooseByNamePopup popup, @NotNull Object element) {
68         String enteredText = popup.getTrimmedText();
69         openOptionOrPerformAction(((GotoActionModel.MatchedValue)element).value, enteredText, project, component, e);
70       }
71     };
72
73     Pair<String, Integer> start = getInitialText(false, e);
74     showNavigationPopup(callback, null, createPopup(project, model, start.first, start.second, component, e), false);
75   }
76
77   @Nullable
78   private static ChooseByNamePopup createPopup(@Nullable Project project,
79                                                @NotNull final GotoActionModel model,
80                                                String initialText,
81                                                int initialIndex,
82                                                final Component component, 
83                                                final AnActionEvent e) {
84     ChooseByNamePopup oldPopup = project == null ? null : project.getUserData(ChooseByNamePopup.CHOOSE_BY_NAME_POPUP_IN_PROJECT_KEY);
85     if (oldPopup != null) {
86       oldPopup.close(false);
87     }
88     final ChooseByNamePopup popup = new ChooseByNamePopup(project, model, new GotoActionItemProvider(model), oldPopup, initialText, false, initialIndex) {
89       @Override
90       protected void initUI(Callback callback, ModalityState modalityState, boolean allowMultipleSelection) {
91         super.initUI(callback, modalityState, allowMultipleSelection);
92         myList.addListSelectionListener(new ListSelectionListener() {
93           @Override
94           public void valueChanged(ListSelectionEvent e) {
95             Object value = myList.getSelectedValue();
96             String text = getText(value);
97             if (text != null && myDropdownPopup != null) {
98               myDropdownPopup.setAdText(text, SwingConstants.LEFT);
99             }
100           }
101
102           @Nullable
103           private String getText(@Nullable Object o) {
104             if (o instanceof GotoActionModel.MatchedValue) {
105               GotoActionModel.MatchedValue mv = (GotoActionModel.MatchedValue)o;
106               if (mv.value instanceof BooleanOptionDescription ||
107                   mv.value instanceof GotoActionModel.ActionWrapper && ((GotoActionModel.ActionWrapper)mv.value).getAction() instanceof ToggleAction) {
108                 return "Press " + KeymapUtil.getKeystrokeText(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0)) + " to toggle option";
109               }
110             }
111             return getAdText();
112           }
113         });
114       }
115
116       @NotNull
117       @Override
118       protected Set<Object> filter(@NotNull Set<Object> elements) {
119         return super.filter(model.sort(elements));
120       }
121
122       @Override
123       protected boolean closeForbidden(boolean ok) {
124         if (!ok) return false;
125         Object element = getChosenElement();
126         return element instanceof GotoActionModel.MatchedValue && processOptionInplace(((GotoActionModel.MatchedValue)element).value, this, component, e)
127                || super.closeForbidden(true);
128       }
129     };
130
131     ApplicationManager.getApplication().getMessageBus().connect().subscribe(ProgressWindow.TOPIC, new ProgressWindow.Listener() {
132       @Override
133       public void created(ProgressWindow pw) {
134         Disposer.register(pw, new Disposable() {
135           @Override
136           public void dispose() {
137             if (!popup.checkDisposed()) {
138               popup.repaintList();
139             }
140           }
141         });
142       }
143     });
144
145     if (project != null) {
146       project.putUserData(ChooseByNamePopup.CHOOSE_BY_NAME_POPUP_IN_PROJECT_KEY, popup);
147     }
148     popup.addMouseClickListener(new MouseAdapter() {
149       @Override
150       public void mouseClicked(@NotNull MouseEvent me) {
151         Object element = popup.getSelectionByPoint(me.getPoint());
152         if (element instanceof GotoActionModel.MatchedValue) {
153           if (processOptionInplace(((GotoActionModel.MatchedValue)element).value, popup, component, e)) {
154             me.consume();
155           }
156         }
157       }
158     });
159     return popup;
160   }
161
162   private static boolean processOptionInplace(Object value, ChooseByNamePopup popup, Component component, AnActionEvent e) {
163     if (value instanceof BooleanOptionDescription) {
164       BooleanOptionDescription option = (BooleanOptionDescription)value;
165       option.setOptionState(!option.isOptionEnabled());
166       repaint(popup);
167       return true;
168     }
169     else if (value instanceof GotoActionModel.ActionWrapper) {
170       AnAction action = ((GotoActionModel.ActionWrapper)value).getAction();
171       if (action instanceof ToggleAction) {
172         performAction(action, component, e);
173         repaint(popup);
174         return true;
175       }
176     }
177     return false;
178   }
179
180   private static void repaint(@Nullable ChooseByNamePopup popup) {
181     if (popup != null) {
182       popup.repaintList();
183     }
184   }
185
186   public static void openOptionOrPerformAction(@NotNull Object element,
187                                                final String enteredText,
188                                                final Project project,
189                                                Component component,
190                                                @Nullable AnActionEvent e) {
191     if (element instanceof OptionDescription) {
192       final String configurableId = ((OptionDescription)element).getConfigurableId();
193       ApplicationManager.getApplication().invokeLater(new Runnable() {
194         @Override
195         public void run() {
196           ShowSettingsUtilImpl.showSettingsDialog(project, configurableId, enteredText);
197         }
198       });
199     }
200     else {
201       performAction(element, component, e);
202     }
203   }
204
205   public static void performAction(Object element, @Nullable final Component component, @Nullable final AnActionEvent e) {
206     // element could be AnAction (SearchEverywhere)
207     final AnAction action = element instanceof AnAction ? (AnAction)element : ((GotoActionModel.ActionWrapper)element).getAction();
208     if (action != null) {
209       ApplicationManager.getApplication().invokeLater(new Runnable() {
210         @Override
211         public void run() {
212           if (component == null) return;
213           DataManager instance = DataManager.getInstance();
214           DataContext context = instance != null ? instance.getDataContext(component) : DataContext.EMPTY_CONTEXT;
215           InputEvent inputEvent = e == null ? null : e.getInputEvent();
216           AnActionEvent event = AnActionEvent.createFromAnAction(action, inputEvent, ActionPlaces.ACTION_SEARCH, context);
217
218           if (ActionUtil.lastUpdateAndCheckDumb(action, event, false)) {
219             if (action instanceof ActionGroup && ((ActionGroup)action).getChildren(event).length > 0) {
220               ListPopup popup = JBPopupFactory.getInstance().createActionGroupPopup(
221                 event.getPresentation().getText(), (ActionGroup)action, context, JBPopupFactory.ActionSelectionAid.SPEEDSEARCH, false);
222               Window window = SwingUtilities.getWindowAncestor(component);
223               if (window != null) {
224                 popup.showInCenterOf(window);
225               }
226               else {
227                 popup.showInFocusCenter();
228               }
229             } 
230             else {
231               ActionUtil.performActionDumbAware(action, event);
232             }
233           }
234         }
235       }, ModalityState.NON_MODAL);
236     }
237   }
238
239   @Override
240   protected boolean requiresProject() {
241     return false;
242   }
243 }