ff63e3ec1d6fa5e69d419f49e175fa9d8a9d39c2
[idea/community.git] / python / educational / interactive-learning / src / com / jetbrains / edu / learning / StudyProjectComponent.java
1 package com.jetbrains.edu.learning;
2
3 import com.intellij.ide.ui.UISettings;
4 import com.intellij.notification.Notification;
5 import com.intellij.notification.NotificationType;
6 import com.intellij.openapi.actionSystem.*;
7 import com.intellij.openapi.actionSystem.ex.AnActionListener;
8 import com.intellij.openapi.application.ApplicationManager;
9 import com.intellij.openapi.components.ProjectComponent;
10 import com.intellij.openapi.diagnostic.Logger;
11 import com.intellij.openapi.editor.EditorFactory;
12 import com.intellij.openapi.fileEditor.FileEditor;
13 import com.intellij.openapi.fileEditor.FileEditorManager;
14 import com.intellij.openapi.keymap.Keymap;
15 import com.intellij.openapi.keymap.ex.KeymapManagerEx;
16 import com.intellij.openapi.module.Module;
17 import com.intellij.openapi.module.ModuleManager;
18 import com.intellij.openapi.project.DumbAwareRunnable;
19 import com.intellij.openapi.project.Project;
20 import com.intellij.openapi.startup.StartupManager;
21 import com.intellij.openapi.util.Pair;
22 import com.intellij.openapi.util.io.FileUtil;
23 import com.intellij.openapi.vfs.VirtualFile;
24 import com.intellij.openapi.vfs.VirtualFileAdapter;
25 import com.intellij.openapi.vfs.VirtualFileEvent;
26 import com.intellij.openapi.vfs.VirtualFileManager;
27 import com.intellij.openapi.wm.*;
28 import com.intellij.util.containers.hash.HashMap;
29 import com.jetbrains.edu.EduNames;
30 import com.jetbrains.edu.EduUtils;
31 import com.jetbrains.edu.courseFormat.Course;
32 import com.jetbrains.edu.courseFormat.Lesson;
33 import com.jetbrains.edu.courseFormat.Task;
34 import com.jetbrains.edu.courseFormat.TaskFile;
35 import com.jetbrains.edu.learning.actions.*;
36 import com.jetbrains.edu.learning.editor.StudyEditorFactoryListener;
37 import com.jetbrains.edu.learning.ui.StudyProgressToolWindowFactory;
38 import com.jetbrains.edu.learning.ui.StudyToolWindowFactory;
39 import org.jetbrains.annotations.NotNull;
40 import org.jetbrains.annotations.Nullable;
41
42 import javax.swing.*;
43 import java.io.File;
44 import java.io.IOException;
45 import java.util.ArrayList;
46 import java.util.List;
47 import java.util.Map;
48
49
50 public class StudyProjectComponent implements ProjectComponent {
51   private static final Logger LOG = Logger.getInstance(StudyProjectComponent.class.getName());
52   private final Project myProject;
53
54   private FileCreatedByUserListener myListener;
55
56   private Map<Keymap, List<Pair<String, String>>> myDeletedShortcuts = new HashMap<Keymap, List<Pair<String, String>>>();
57   private StudyProjectComponent(@NotNull final Project project) {
58     myProject = project;
59   }
60
61   @Override
62   public void projectOpened() {
63     final Course course = StudyTaskManager.getInstance(myProject).getCourse();
64     if (course != null && !course.isUpToDate()) {
65       course.setUpToDate(true);
66       updateCourse();
67     }
68
69     registerStudyToolwindow(course);
70     ApplicationManager.getApplication().invokeLater(new DumbAwareRunnable() {
71       @Override
72       public void run() {
73         ApplicationManager.getApplication().runWriteAction(new DumbAwareRunnable() {
74           @Override
75           public void run() {
76             if (course != null) {
77               moveFocusToEditor();
78               UISettings.getInstance().HIDE_TOOL_STRIPES = false;
79               UISettings.getInstance().fireUISettingsChanged();
80               registerShortcuts();
81             }
82           }
83         });
84       }
85     });
86   }
87
88   public void registerStudyToolwindow(@Nullable final Course course) {
89     if (course != null && "PyCharm".equals(course.getCourseType())) {
90       final ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(myProject);
91       registerToolWindows(toolWindowManager);
92       final ToolWindow studyToolWindow = toolWindowManager.getToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW);
93       final ToolWindow progressToolWindow = toolWindowManager.getToolWindow(StudyProgressToolWindowFactory.ID);
94       if (studyToolWindow != null) {
95         studyToolWindow.show(null);
96       }
97       if (progressToolWindow != null) {
98         StudyUtils.updateToolWindows(myProject);
99         progressToolWindow.show(null);
100       }
101     }
102   }
103
104   private void moveFocusToEditor() {
105     StartupManager.getInstance(myProject).runWhenProjectIsInitialized(new Runnable() {
106       @Override
107       public void run() {
108         ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.PROJECT_VIEW).show(new Runnable() {
109           @Override
110           public void run() {
111             FileEditor[] editors = FileEditorManager.getInstance(myProject).getSelectedEditors();
112             if (editors.length > 0) {
113               final JComponent focusedComponent = editors[0].getPreferredFocusedComponent();
114               if (focusedComponent != null) {
115                 ApplicationManager.getApplication().invokeLater(new Runnable() {
116                   @Override
117                   public void run() {
118                     IdeFocusManager.getInstance(myProject).requestFocus(focusedComponent, true);
119                   }
120                 });
121               }
122             }
123           }
124         });
125       }
126     });
127   }
128
129   private void registerShortcuts() {
130     addShortcut(StudyNextWindowAction.ACTION_ID, new String[]{StudyNextWindowAction.SHORTCUT, StudyNextWindowAction.SHORTCUT2});
131     addShortcut(StudyPrevWindowAction.ACTION_ID, new String[]{StudyPrevWindowAction.SHORTCUT});
132     addShortcut(StudyShowHintAction.ACTION_ID, new String[]{StudyShowHintAction.SHORTCUT});
133     addShortcut(StudyCheckAction.ACTION_ID, new String[]{StudyCheckAction.SHORTCUT});
134     addShortcut(StudyNextStudyTaskAction.ACTION_ID, new String[]{StudyNextStudyTaskAction.SHORTCUT});
135     addShortcut(StudyPreviousStudyTaskAction.ACTION_ID, new String[]{StudyPreviousStudyTaskAction.SHORTCUT});
136     addShortcut(StudyRefreshTaskFileAction.ACTION_ID, new String[]{StudyRefreshTaskFileAction.SHORTCUT});
137   }
138
139   private void registerToolWindows(@NotNull final ToolWindowManager toolWindowManager) {
140     final ToolWindow toolWindow = toolWindowManager.getToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW);
141     if (toolWindow == null) {
142       toolWindowManager.registerToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW, true, ToolWindowAnchor.RIGHT, myProject, true);
143     }
144     ToolWindow progressToolWindow = toolWindowManager.getToolWindow(StudyProgressToolWindowFactory.ID);
145     if (progressToolWindow == null) {
146       toolWindowManager.registerToolWindow(StudyProgressToolWindowFactory.ID, true, ToolWindowAnchor.LEFT, myProject, true, true);
147     }
148   }
149
150   private void updateCourse() {
151     final Course course = StudyTaskManager.getInstance(myProject).getCourse();
152     if (course == null) {
153       return;
154     }
155     final File resourceDirectory = new File(course.getCourseDirectory());
156     if (!resourceDirectory.exists()) {
157       return;
158     }
159     StudyLanguageManager manager = StudyUtils.getLanguageManager(course);
160     if (manager == null) {
161       LOG.info("Study Language Manager is null for " + course.getLanguageById().getDisplayName());
162       return;
163     }
164     final File[] files = resourceDirectory.listFiles();
165     if (files == null) return;
166     for (File file : files) {
167       String testHelper = manager.getTestHelperFileName();
168       if (file.getName().equals(testHelper)) {
169         copyFile(file, new File(myProject.getBasePath(), testHelper));
170       }
171       if (file.getName().startsWith(EduNames.LESSON)) {
172         final File[] tasks = file.listFiles();
173         if (tasks == null) continue;
174         for (File task : tasks) {
175           final File taskDescr = new File(task, EduNames.TASK_HTML);
176           String testFileName = manager.getTestFileName();
177           final File taskTests = new File(task, testFileName);
178           copyFile(taskDescr, new File(new File(new File(myProject.getBasePath(), file.getName()), task.getName()), EduNames.TASK_HTML));
179           copyFile(taskTests, new File(new File(new File(myProject.getBasePath(), file.getName()), task.getName()),
180                                        testFileName));
181         }
182       }
183     }
184
185     final Notification notification =
186       new Notification("Update.course", "Course update", "Current course is synchronized", NotificationType.INFORMATION);
187     notification.notify(myProject);
188   }
189
190   private static void copyFile(@NotNull final File from, @NotNull final File to) {
191     if (from.exists()) {
192       try {
193         FileUtil.copy(from, to);
194       }
195       catch (IOException e) {
196         LOG.warn("Failed to copy " + from.getName());
197       }
198     }
199   }
200
201   private void addShortcut(@NotNull final String actionIdString, @NotNull final String[] shortcuts) {
202     KeymapManagerEx keymapManager = KeymapManagerEx.getInstanceEx();
203     for (Keymap keymap : keymapManager.getAllKeymaps()) {
204       List<Pair<String, String>> pairs = myDeletedShortcuts.get(keymap);
205       if (pairs == null) {
206         pairs = new ArrayList<Pair<String, String>>();
207         myDeletedShortcuts.put(keymap, pairs);
208       }
209       for (String shortcutString : shortcuts) {
210         Shortcut studyActionShortcut = new KeyboardShortcut(KeyStroke.getKeyStroke(shortcutString), null);
211         String[] actionsIds = keymap.getActionIds(studyActionShortcut);
212         for (String actionId : actionsIds) {
213           pairs.add(Pair.create(actionId, shortcutString));
214           keymap.removeShortcut(actionId, studyActionShortcut);
215         }
216         keymap.addShortcut(actionIdString, studyActionShortcut);
217       }
218     }
219   }
220
221   @Override
222   public void projectClosed() {
223     final Course course = StudyTaskManager.getInstance(myProject).getCourse();
224     if (course != null) {
225       final ToolWindow toolWindow = ToolWindowManager.getInstance(myProject).getToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW);
226       if (toolWindow != null) {
227         toolWindow.getContentManager().removeAllContents(false);
228       }
229       final ToolWindow progressToolWindow = ToolWindowManager.getInstance(myProject).getToolWindow(StudyProgressToolWindowFactory.ID);
230       if (progressToolWindow != null) {
231         progressToolWindow.getContentManager().removeAllContents(false);
232       }
233       KeymapManagerEx keymapManager = KeymapManagerEx.getInstanceEx();
234       for (Keymap keymap : keymapManager.getAllKeymaps()) {
235         List<Pair<String, String>> pairs = myDeletedShortcuts.get(keymap);
236         if (pairs != null && !pairs.isEmpty()) {
237           for (Pair<String, String> actionShortcut : pairs) {
238             keymap.addShortcut(actionShortcut.first, new KeyboardShortcut(KeyStroke.getKeyStroke(actionShortcut.second), null));
239           }
240         }
241        }
242     }
243     myListener = null;
244   }
245
246   @Override
247   public void initComponent() {
248     EditorFactory.getInstance().addEditorFactoryListener(new StudyEditorFactoryListener(), myProject);
249     ActionManager.getInstance().addAnActionListener(new AnActionListener() {
250       @Override
251       public void beforeActionPerformed(AnAction action, DataContext dataContext, AnActionEvent event) {
252         AnAction[] newGroupActions = ((ActionGroup)ActionManager.getInstance().getAction("NewGroup")).getChildren(null);
253         for (AnAction newAction : newGroupActions) {
254           if (newAction == action) {
255             myListener =  new FileCreatedByUserListener();
256             VirtualFileManager.getInstance().addVirtualFileListener(myListener);
257             break;
258           }
259         }
260       }
261
262       @Override
263       public void afterActionPerformed(AnAction action, DataContext dataContext, AnActionEvent event) {
264         AnAction[] newGroupActions = ((ActionGroup)ActionManager.getInstance().getAction("NewGroup")).getChildren(null);
265         for (AnAction newAction : newGroupActions) {
266           if (newAction == action) {
267             VirtualFileManager.getInstance().removeVirtualFileListener(myListener);
268           }
269         }
270       }
271
272       @Override
273       public void beforeEditorTyping(char c, DataContext dataContext) {
274
275       }
276     });
277   }
278
279   @Override
280   public void disposeComponent() {
281   }
282
283   @NotNull
284   @Override
285   public String getComponentName() {
286     return "StudyTaskManager";
287   }
288
289   public static StudyProjectComponent getInstance(@NotNull final Project project) {
290     final Module module = ModuleManager.getInstance(project).getModules()[0];
291     return module.getComponent(StudyProjectComponent.class);
292   }
293
294   private class FileCreatedByUserListener extends VirtualFileAdapter {
295     @Override
296     public void fileCreated(@NotNull VirtualFileEvent event) {
297       if (myProject.isDisposed()) return;
298       final VirtualFile createdFile = event.getFile();
299       final VirtualFile taskDir = createdFile.getParent();
300       final Course course = StudyTaskManager.getInstance(myProject).getCourse();
301       if (taskDir != null && taskDir.getName().contains(EduNames.TASK)) {
302         int taskIndex = EduUtils.getIndex(taskDir.getName(), EduNames.TASK);
303         final VirtualFile lessonDir = taskDir.getParent();
304         if (lessonDir != null && lessonDir.getName().contains(EduNames.LESSON)) {
305           int lessonIndex = EduUtils.getIndex(lessonDir.getName(), EduNames.LESSON);
306           if (course != null) {
307             List<Lesson> lessons = course.getLessons();
308             if (StudyUtils.indexIsValid(lessonIndex, lessons)) {
309               final Lesson lesson = lessons.get(lessonIndex);
310               final List<Task> tasks = lesson.getTaskList();
311               if (StudyUtils.indexIsValid(taskIndex, tasks)) {
312                 final Task task = tasks.get(taskIndex);
313                 final TaskFile taskFile = new TaskFile();
314                 taskFile.initTaskFile(task, false);
315                 taskFile.setUserCreated(true);
316                 final String name = createdFile.getName();
317                 taskFile.name = name;
318                 task.getTaskFiles().put(name, taskFile);
319               }
320             }
321           }
322         }
323       }
324     }
325   }
326
327 }