Merge branch 'liana/simplified_debugger'
[idea/community.git] / python / edu / src / com / jetbrains / python / edu / PyCharmEduInitialConfigurator.java
1 /*
2  * Copyright 2000-2013 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 package com.jetbrains.python.edu;
17
18 import com.google.common.collect.Sets;
19 import com.intellij.codeInsight.CodeInsightSettings;
20 import com.intellij.codeInsight.intention.IntentionActionBean;
21 import com.intellij.codeInsight.intention.IntentionManager;
22 import com.intellij.execution.Executor;
23 import com.intellij.execution.ExecutorRegistryImpl;
24 import com.intellij.execution.executors.DefaultDebugExecutor;
25 import com.intellij.ide.AppLifecycleListener;
26 import com.intellij.ide.GeneralSettings;
27 import com.intellij.ide.SelectInTarget;
28 import com.intellij.ide.projectView.impl.AbstractProjectViewPane;
29 import com.intellij.ide.scopeView.ScopeViewPane;
30 import com.intellij.ide.ui.UISettings;
31 import com.intellij.ide.ui.customization.ActionUrl;
32 import com.intellij.ide.ui.customization.CustomActionsSchema;
33 import com.intellij.ide.ui.customization.CustomizationUtil;
34 import com.intellij.ide.util.PropertiesComponent;
35 import com.intellij.ide.util.TipAndTrickBean;
36 import com.intellij.notification.EventLog;
37 import com.intellij.openapi.actionSystem.ActionManager;
38 import com.intellij.openapi.actionSystem.AnAction;
39 import com.intellij.openapi.actionSystem.DefaultActionGroup;
40 import com.intellij.openapi.application.ApplicationManager;
41 import com.intellij.openapi.editor.colors.EditorColorsManager;
42 import com.intellij.openapi.editor.colors.EditorColorsScheme;
43 import com.intellij.openapi.editor.ex.EditorSettingsExternalizable;
44 import com.intellij.openapi.extensions.Extensions;
45 import com.intellij.openapi.extensions.ExtensionsArea;
46 import com.intellij.openapi.fileChooser.impl.FileChooserUtil;
47 import com.intellij.openapi.fileTypes.FileTypeManager;
48 import com.intellij.openapi.keymap.Keymap;
49 import com.intellij.openapi.keymap.ex.KeymapManagerEx;
50 import com.intellij.openapi.keymap.impl.KeymapImpl;
51 import com.intellij.openapi.keymap.impl.ui.Group;
52 import com.intellij.openapi.project.DumbAwareRunnable;
53 import com.intellij.openapi.project.Project;
54 import com.intellij.openapi.project.ProjectManager;
55 import com.intellij.openapi.project.ProjectManagerAdapter;
56 import com.intellij.openapi.project.ex.ProjectManagerEx;
57 import com.intellij.openapi.startup.StartupManager;
58 import com.intellij.openapi.util.Disposer;
59 import com.intellij.openapi.util.registry.Registry;
60 import com.intellij.openapi.vfs.VfsUtil;
61 import com.intellij.openapi.wm.*;
62 import com.intellij.platform.DirectoryProjectConfigurator;
63 import com.intellij.platform.PlatformProjectViewOpener;
64 import com.intellij.psi.codeStyle.CodeStyleSettings;
65 import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
66 import com.intellij.ui.treeStructure.Tree;
67 import com.intellij.util.containers.ContainerUtil;
68 import com.intellij.util.messages.MessageBus;
69 import com.intellij.util.ui.tree.TreeUtil;
70 import com.jetbrains.python.PythonLanguage;
71 import com.jetbrains.python.codeInsight.PyCodeInsightSettings;
72 import org.jetbrains.annotations.NonNls;
73 import org.jetbrains.annotations.NotNull;
74 import org.jetbrains.annotations.Nullable;
75
76 import javax.swing.*;
77 import javax.swing.tree.DefaultMutableTreeNode;
78 import javax.swing.tree.DefaultTreeModel;
79 import javax.swing.tree.TreeNode;
80 import javax.swing.tree.TreePath;
81 import java.util.HashSet;
82 import java.util.Set;
83
84 /**
85  * @author traff
86  */
87 @SuppressWarnings({"UtilityClassWithoutPrivateConstructor", "UtilityClassWithPublicConstructor"})
88 public class PyCharmEduInitialConfigurator {
89   @NonNls private static final String DISPLAYED_PROPERTY = "PyCharmEDU.initialConfigurationShown";
90
91   @NonNls private static final String CONFIGURED = "PyCharmEDU.InitialConfiguration";
92   @NonNls private static final String CONFIGURED_V1 = "PyCharmEDU.InitialConfiguration.V1";
93
94   private static final Set<String> UNRELATED_TIPS = Sets.newHashSet("LiveTemplatesDjango.html", "TerminalOpen.html",
95                                                                     "Terminal.html", "ConfiguringTerminal.html");
96   private static final Set<String> HIDDEN_ACTIONS = ContainerUtil.newHashSet("CopyAsPlainText", "CopyAsRichText", "EditorPasteSimple",
97                                                                              "Folding", "Generate", "CompareClipboardWithSelection",
98                                                                              "ChangeFileEncodingAction", "CloseAllUnmodifiedEditors",
99                                                                              "CloseAllUnpinnedEditors", "CloseAllEditorsButActive",
100                                                                              "CopyReference", "MoveTabRight", "MoveTabDown", "External Tools",
101                                                                              "MoveEditorToOppositeTabGroup", "OpenEditorInOppositeTabGroup",
102                                                                              "ChangeSplitOrientation", "PinActiveTab", "Tabs Placement",
103                                                                              "TabsAlphabeticalMode", "AddNewTabToTheEndMode", "NextTab",
104                                                                              "PreviousTab", "Add to Favorites", "Add All To Favorites",
105                                                                              "ValidateXml", "NewHtmlFile", "CleanPyc", "Images.ShowThumbnails",
106                                                                              "CompareFileWithEditor", "SynchronizeCurrentFile",
107                                                                              "Mark Directory As", "CompareTwoFiles", "ShowFilePath",
108                                                                              "ChangesView.ApplyPatch", "TemplateProjectProperties",
109                                                                              "ExportToHTML", "SaveAll", "Export/Import Actions",
110                                                                              "Synchronize", "Line Separators", "ToggleReadOnlyAttribute",
111                                                                              "Macros", "EditorToggleCase", "EditorJoinLines", "FillParagraph",
112                                                                              "Convert Indents", "TemplateParametersNavigation", "EscapeEntities",
113                                                                              "QuickDefinition", "ExpressionTypeInfo", "EditorContextInfo",
114                                                                              "ShowErrorDescription", "RecentChanges", "CompareActions",
115                                                                              "GotoCustomRegion", "JumpToLastChange", "JumpToNextChange",
116                                                                              "SelectIn", "GotoTypeDeclaration", "QuickChangeScheme",
117                                                                              "GotoTest", "GotoRelated", "Hierarchy Actions", "Bookmarks",
118                                                                              "Goto Error/Bookmark Actions", "GoToEditPointGroup",
119                                                                              "Change Navigation Actions", "Method Navigation Actions",
120                                                                              "EvaluateExpression", "Pause", "ViewBreakpoints",
121                                                                              "XDebugger.MuteBreakpoints");
122
123   public static class First {
124
125     public First() {
126       patchRootAreaExtensions();
127     }
128   }
129
130   /**
131    * @noinspection UnusedParameters
132    */
133   public PyCharmEduInitialConfigurator(MessageBus bus,
134                                        CodeInsightSettings codeInsightSettings,
135                                        final PropertiesComponent propertiesComponent,
136                                        FileTypeManager fileTypeManager,
137                                        final ProjectManagerEx projectManager) {
138     final UISettings uiSettings = UISettings.getInstance();
139     if (!propertiesComponent.getBoolean(CONFIGURED_V1, false)) {
140       patchMainMenu();
141       uiSettings.SHOW_NAVIGATION_BAR = false;
142       propertiesComponent.setValue(CONFIGURED_V1, "true");
143     }
144
145     if (!propertiesComponent.getBoolean(CONFIGURED, false)) {
146       propertiesComponent.setValue(CONFIGURED, "true");
147       propertiesComponent.setValue("toolwindow.stripes.buttons.info.shown", "true");
148
149       uiSettings.HIDE_TOOL_STRIPES = false;
150       uiSettings.SHOW_MEMORY_INDICATOR = false;
151       uiSettings.SHOW_DIRECTORY_FOR_NON_UNIQUE_FILENAMES = true;
152       uiSettings.SHOW_MAIN_TOOLBAR = false;
153
154       codeInsightSettings.REFORMAT_ON_PASTE = CodeInsightSettings.NO_REFORMAT;
155
156       Registry.get("ide.new.settings.dialog").setValue(true);
157
158       GeneralSettings.getInstance().setShowTipsOnStartup(false);
159
160       EditorSettingsExternalizable.getInstance().setVirtualSpace(false);
161       EditorSettingsExternalizable.getInstance().getOptions().ARE_LINE_NUMBERS_SHOWN = true;
162       final CodeStyleSettings settings = CodeStyleSettingsManager.getInstance().getCurrentSettings();
163       settings.ALIGN_MULTILINE_PARAMETERS_IN_CALLS = true;
164       settings.getCommonSettings(PythonLanguage.getInstance()).ALIGN_MULTILINE_PARAMETERS_IN_CALLS = true;
165       uiSettings.SHOW_DIRECTORY_FOR_NON_UNIQUE_FILENAMES = true;
166       uiSettings.SHOW_MEMORY_INDICATOR = false;
167       final String ignoredFilesList = fileTypeManager.getIgnoredFilesList();
168       ApplicationManager.getApplication().invokeLater(new Runnable() {
169         @Override
170         public void run() {
171           ApplicationManager.getApplication().runWriteAction(new Runnable() {
172             @Override
173             public void run() {
174               FileTypeManager.getInstance().setIgnoredFilesList(ignoredFilesList + ";*$py.class");
175             }
176           });
177         }
178       });
179       PyCodeInsightSettings.getInstance().SHOW_IMPORT_POPUP = false;
180     }
181     final EditorColorsScheme editorColorsScheme = EditorColorsManager.getInstance().getScheme(EditorColorsScheme.DEFAULT_SCHEME_NAME);
182     editorColorsScheme.setEditorFontSize(14);
183
184     if (!propertiesComponent.isValueSet(DISPLAYED_PROPERTY)) {
185
186       bus.connect().subscribe(AppLifecycleListener.TOPIC, new AppLifecycleListener.Adapter() {
187         @Override
188         public void welcomeScreenDisplayed() {
189
190           ApplicationManager.getApplication().invokeLater(new Runnable() {
191             @Override
192             public void run() {
193               if (!propertiesComponent.isValueSet(DISPLAYED_PROPERTY)) {
194                 GeneralSettings.getInstance().setShowTipsOnStartup(false);
195                 propertiesComponent.setValue(DISPLAYED_PROPERTY, "true");
196
197                 patchKeymap();
198               }
199             }
200           });
201         }
202       });
203     }
204
205     bus.connect().subscribe(ProjectManager.TOPIC, new ProjectManagerAdapter() {
206       @Override
207       public void projectOpened(final Project project) {
208         if (project.isDefault()) return;
209         if (FileChooserUtil.getLastOpenedFile(project) == null) {
210           FileChooserUtil.setLastOpenedFile(project, VfsUtil.getUserHomeDir());
211         }
212
213         patchProjectAreaExtensions(project);
214
215         StartupManager.getInstance(project).runWhenProjectIsInitialized(new DumbAwareRunnable() {
216           @Override
217           public void run() {
218             if (project.isDisposed()) return;
219
220             ToolWindowManager.getInstance(project).invokeLater(new Runnable() {
221               int count = 0;
222
223               public void run() {
224                 if (project.isDisposed()) return;
225                 if (count++ < 3) { // we need to call this after ToolWindowManagerImpl.registerToolWindowsFromBeans
226                   ToolWindowManager.getInstance(project).invokeLater(this);
227                   return;
228                 }
229                 ToolWindow toolWindow = ToolWindowManager.getInstance(project).getToolWindow("Project");
230                 if (toolWindow.getType() != ToolWindowType.SLIDING) {
231                   toolWindow.activate(null);
232                 }
233               }
234             });
235           }
236         });
237       }
238     });
239   }
240
241   private static void patchMainMenu() {
242     final CustomActionsSchema schema = new CustomActionsSchema();
243
244     final JTree actionsTree = new Tree();
245     Group rootGroup = new Group("root", null, null);
246     final DefaultMutableTreeNode root = new DefaultMutableTreeNode(rootGroup);
247     DefaultTreeModel model = new DefaultTreeModel(root);
248     actionsTree.setModel(model);
249
250     schema.fillActionGroups(root);
251     for (int i = 0; i < root.getChildCount(); i++) {
252       final DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)root.getChildAt(i);
253       if ("Main menu".equals(getItemId(treeNode))) {
254         hideActionFromMainMenu(root, schema, treeNode);
255       }
256       hideActions(schema, root, treeNode, HIDDEN_ACTIONS);
257     }
258     CustomActionsSchema.getInstance().copyFrom(schema);
259   }
260
261   private static void hideActionFromMainMenu(@NotNull final DefaultMutableTreeNode root,
262                                              @NotNull final CustomActionsSchema schema, DefaultMutableTreeNode mainMenu){
263     final HashSet<String> menuItems = ContainerUtil.newHashSet("Tools", "VCS", "Refactor", "Code", "Window", "Run");
264     hideActions(schema, root, mainMenu, menuItems);
265   }
266
267   private static void hideActions(@NotNull CustomActionsSchema schema, @NotNull DefaultMutableTreeNode root,
268                                   @NotNull final TreeNode actionGroup, Set<String> items) {
269     for(int i = 0; i < actionGroup.getChildCount(); i++){
270       final DefaultMutableTreeNode child = (DefaultMutableTreeNode)actionGroup.getChildAt(i);
271       final int childCount = child.getChildCount();
272       final String childId = getItemId(child);
273       if (childId != null && items.contains(childId)){
274         final TreePath treePath = TreeUtil.getPath(root, child);
275         final ActionUrl url = CustomizationUtil.getActionUrl(treePath, ActionUrl.DELETED);
276         schema.addAction(url);
277       }
278       else if (childCount > 0) {
279         hideActions(schema, child, child, items);
280       }
281     }
282   }
283
284   @Nullable
285   private static String getItemId(@NotNull final DefaultMutableTreeNode child) {
286     final Object userObject = child.getUserObject();
287     if (userObject instanceof String) return (String)userObject;
288     return userObject instanceof Group ? ((Group)userObject).getName() : null;
289   }
290
291   private static void patchRootAreaExtensions() {
292     ExtensionsArea rootArea = Extensions.getArea(null);
293
294     for (ToolWindowEP ep : Extensions.getExtensions(ToolWindowEP.EP_NAME)) {
295       if (ToolWindowId.FAVORITES_VIEW.equals(ep.id) || ToolWindowId.TODO_VIEW.equals(ep.id) || EventLog.LOG_TOOL_WINDOW_ID.equals(ep.id)
296           || ToolWindowId.STRUCTURE_VIEW.equals(ep.id)) {
297         rootArea.getExtensionPoint(ToolWindowEP.EP_NAME).unregisterExtension(ep);
298       }
299     }
300
301     for (DirectoryProjectConfigurator ep : Extensions.getExtensions(DirectoryProjectConfigurator.EP_NAME)) {
302       if (ep instanceof PlatformProjectViewOpener) {
303         rootArea.getExtensionPoint(DirectoryProjectConfigurator.EP_NAME).unregisterExtension(ep);
304       }
305     }
306
307     // unregister unrelated tips
308     for (TipAndTrickBean tip : Extensions.getExtensions(TipAndTrickBean.EP_NAME)) {
309       if (UNRELATED_TIPS.contains(tip.fileName)) {
310         rootArea.getExtensionPoint(TipAndTrickBean.EP_NAME).unregisterExtension(tip);
311       }
312     }
313
314     for (IntentionActionBean ep : Extensions.getExtensions(IntentionManager.EP_INTENTION_ACTIONS)) {
315       if ("org.intellij.lang.regexp.intention.CheckRegExpIntentionAction".equals(ep.className)) {
316         rootArea.getExtensionPoint(IntentionManager.EP_INTENTION_ACTIONS).unregisterExtension(ep);
317       }
318     }
319   }
320
321   private static void patchProjectAreaExtensions(@NotNull final Project project) {
322     Executor debugExecutor = DefaultDebugExecutor.getDebugExecutorInstance();
323     unregisterAction(debugExecutor.getId(), ExecutorRegistryImpl.RUNNERS_GROUP);
324     unregisterAction(debugExecutor.getContextActionId(), ExecutorRegistryImpl.RUN_CONTEXT_GROUP);
325
326     ExtensionsArea projectArea = Extensions.getArea(project);
327
328     for (SelectInTarget target : Extensions.getExtensions(SelectInTarget.EP_NAME, project)) {
329       if (ToolWindowId.FAVORITES_VIEW.equals(target.getToolWindowId()) ||
330           ToolWindowId.STRUCTURE_VIEW.equals(target.getToolWindowId())) {
331         projectArea.getExtensionPoint(SelectInTarget.EP_NAME).unregisterExtension(target);
332       }
333     }
334
335     for (AbstractProjectViewPane pane : Extensions.getExtensions(AbstractProjectViewPane.EP_NAME, project)) {
336       if (pane.getId().equals(ScopeViewPane.ID)) {
337         Disposer.dispose(pane);
338         projectArea.getExtensionPoint(AbstractProjectViewPane.EP_NAME).unregisterExtension(pane);
339       }
340     }
341   }
342
343   private static void unregisterAction(String actionId, String groupId) {
344     ActionManager actionManager = ActionManager.getInstance();
345     AnAction action = actionManager.getAction(actionId);
346     if (action != null) {
347       AnAction actionGroup = actionManager.getAction(groupId);
348       if (actionGroup != null && actionGroup instanceof DefaultActionGroup) {
349         ((DefaultActionGroup)actionGroup).remove(action);
350         actionManager.unregisterAction(actionId);
351       }
352     }
353   }
354
355   private static void patchKeymap() {
356     Set<String> droppedActions = ContainerUtil.newHashSet(
357       "AddToFavoritesPopup",
358       "DatabaseView.ImportDataSources",
359       "CompileDirty", "Compile",
360       // hidden
361       "AddNewFavoritesList", "EditFavorites", "RenameFavoritesList", "RemoveFavoritesList");
362     KeymapManagerEx keymapManager = KeymapManagerEx.getInstanceEx();
363
364
365     for (Keymap keymap : keymapManager.getAllKeymaps()) {
366       if (keymap.canModify()) continue;
367
368       KeymapImpl keymapImpl = (KeymapImpl)keymap;
369
370       for (String id : keymapImpl.getOwnActionIds()) {
371         if (droppedActions.contains(id)) keymapImpl.clearOwnActionsId(id);
372       }
373     }
374   }
375
376 }