1 package com.jetbrains.edu.learning;
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.keymap.Keymap;
13 import com.intellij.openapi.keymap.ex.KeymapManagerEx;
14 import com.intellij.openapi.module.Module;
15 import com.intellij.openapi.module.ModuleManager;
16 import com.intellij.openapi.project.DumbAwareRunnable;
17 import com.intellij.openapi.project.Project;
18 import com.intellij.openapi.util.Pair;
19 import com.intellij.openapi.util.io.FileUtil;
20 import com.intellij.openapi.vfs.VirtualFile;
21 import com.intellij.openapi.vfs.VirtualFileAdapter;
22 import com.intellij.openapi.vfs.VirtualFileEvent;
23 import com.intellij.openapi.vfs.VirtualFileManager;
24 import com.intellij.openapi.wm.ToolWindow;
25 import com.intellij.openapi.wm.ToolWindowAnchor;
26 import com.intellij.openapi.wm.ToolWindowManager;
27 import com.intellij.util.containers.hash.HashMap;
28 import com.jetbrains.edu.EduNames;
29 import com.jetbrains.edu.EduUtils;
30 import com.jetbrains.edu.courseFormat.Course;
31 import com.jetbrains.edu.courseFormat.Lesson;
32 import com.jetbrains.edu.courseFormat.Task;
33 import com.jetbrains.edu.courseFormat.TaskFile;
34 import com.jetbrains.edu.learning.actions.*;
35 import com.jetbrains.edu.learning.editor.StudyEditorFactoryListener;
36 import com.jetbrains.edu.learning.ui.StudyProgressToolWindowFactory;
37 import com.jetbrains.edu.learning.ui.StudyToolWindowFactory;
38 import javafx.application.Platform;
39 import org.jetbrains.annotations.NotNull;
40 import org.jetbrains.annotations.Nullable;
44 import java.io.IOException;
45 import java.util.ArrayList;
46 import java.util.List;
50 public class StudyProjectComponent implements ProjectComponent {
51 private static final Logger LOG = Logger.getInstance(StudyProjectComponent.class.getName());
52 private final Project myProject;
53 private FileCreatedByUserListener myListener;
54 // Shows could we use JavaFX Task Description panel or should use Swing
55 private boolean useJavaFx = true;
56 private Map<Keymap, List<Pair<String, String>>> myDeletedShortcuts = new HashMap<Keymap, List<Pair<String, String>>>();
57 private StudyProjectComponent(@NotNull final Project project) {
62 public void projectOpened() {
63 final Course course = StudyTaskManager.getInstance(myProject).getCourse();
64 // Check if user has javafx lib in his JDK. Now bundled JDK doesn't have this lib inside.
66 Platform.setImplicitExit(false);
68 catch (NoClassDefFoundError e) {
72 if (course != null && !course.isUpToDate()) {
73 course.setUpToDate(true);
77 registerStudyToolWindow(course);
78 ApplicationManager.getApplication().invokeLater(new DumbAwareRunnable() {
81 ApplicationManager.getApplication().runWriteAction(new DumbAwareRunnable() {
85 UISettings.getInstance().HIDE_TOOL_STRIPES = false;
86 UISettings.getInstance().fireUISettingsChanged();
95 public void registerStudyToolWindow(@Nullable final Course course) {
96 if (course != null && "PyCharm".equals(course.getCourseType())) {
97 final ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(myProject);
98 registerToolWindows(toolWindowManager);
99 final ToolWindow studyToolWindow = toolWindowManager.getToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW);
100 final ToolWindow progressToolWindow = toolWindowManager.getToolWindow(StudyProgressToolWindowFactory.ID);
101 if (studyToolWindow != null) {
102 studyToolWindow.show(null);
104 if (progressToolWindow != null) {
105 StudyUtils.initToolWindows(myProject);
106 progressToolWindow.show(null);
111 private void registerShortcuts() {
112 addShortcut(StudyNextWindowAction.ACTION_ID, new String[]{StudyNextWindowAction.SHORTCUT, StudyNextWindowAction.SHORTCUT2});
113 addShortcut(StudyPrevWindowAction.ACTION_ID, new String[]{StudyPrevWindowAction.SHORTCUT});
114 addShortcut(StudyShowHintAction.ACTION_ID, new String[]{StudyShowHintAction.SHORTCUT});
115 addShortcut(StudyCheckAction.ACTION_ID, new String[]{StudyCheckAction.SHORTCUT});
116 addShortcut(StudyNextStudyTaskAction.ACTION_ID, new String[]{StudyNextStudyTaskAction.SHORTCUT});
117 addShortcut(StudyPreviousStudyTaskAction.ACTION_ID, new String[]{StudyPreviousStudyTaskAction.SHORTCUT});
118 addShortcut(StudyRefreshTaskFileAction.ACTION_ID, new String[]{StudyRefreshTaskFileAction.SHORTCUT});
121 private void registerToolWindows(@NotNull final ToolWindowManager toolWindowManager) {
122 final ToolWindow toolWindow = toolWindowManager.getToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW);
123 if (toolWindow == null) {
124 toolWindowManager.registerToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW, true, ToolWindowAnchor.RIGHT, myProject, true);
126 ToolWindow progressToolWindow = toolWindowManager.getToolWindow(StudyProgressToolWindowFactory.ID);
127 if (progressToolWindow == null) {
128 toolWindowManager.registerToolWindow(StudyProgressToolWindowFactory.ID, true, ToolWindowAnchor.LEFT, myProject, true, true);
132 private void updateCourse() {
133 final Course course = StudyTaskManager.getInstance(myProject).getCourse();
134 if (course == null) {
137 final File resourceDirectory = new File(course.getCourseDirectory());
138 if (!resourceDirectory.exists()) {
141 StudyLanguageManager manager = StudyUtils.getLanguageManager(course);
142 if (manager == null) {
143 LOG.info("Study Language Manager is null for " + course.getLanguageById().getDisplayName());
146 final File[] files = resourceDirectory.listFiles();
147 if (files == null) return;
148 for (File file : files) {
149 String testHelper = manager.getTestHelperFileName();
150 if (file.getName().equals(testHelper)) {
151 copyFile(file, new File(myProject.getBasePath(), testHelper));
153 if (file.getName().startsWith(EduNames.LESSON)) {
154 final File[] tasks = file.listFiles();
155 if (tasks == null) continue;
156 for (File task : tasks) {
157 final File taskDescr = new File(task, EduNames.TASK_HTML);
158 String testFileName = manager.getTestFileName();
159 final File taskTests = new File(task, testFileName);
160 copyFile(taskDescr, new File(new File(new File(myProject.getBasePath(), file.getName()), task.getName()), EduNames.TASK_HTML));
161 copyFile(taskTests, new File(new File(new File(myProject.getBasePath(), file.getName()), task.getName()),
167 final Notification notification =
168 new Notification("Update.course", "Course update", "Current course is synchronized", NotificationType.INFORMATION);
169 notification.notify(myProject);
172 private static void copyFile(@NotNull final File from, @NotNull final File to) {
175 FileUtil.copy(from, to);
177 catch (IOException e) {
178 LOG.warn("Failed to copy " + from.getName());
183 private void addShortcut(@NotNull final String actionIdString, @NotNull final String[] shortcuts) {
184 KeymapManagerEx keymapManager = KeymapManagerEx.getInstanceEx();
185 for (Keymap keymap : keymapManager.getAllKeymaps()) {
186 List<Pair<String, String>> pairs = myDeletedShortcuts.get(keymap);
188 pairs = new ArrayList<Pair<String, String>>();
189 myDeletedShortcuts.put(keymap, pairs);
191 for (String shortcutString : shortcuts) {
192 Shortcut studyActionShortcut = new KeyboardShortcut(KeyStroke.getKeyStroke(shortcutString), null);
193 String[] actionsIds = keymap.getActionIds(studyActionShortcut);
194 for (String actionId : actionsIds) {
195 pairs.add(Pair.create(actionId, shortcutString));
196 keymap.removeShortcut(actionId, studyActionShortcut);
198 keymap.addShortcut(actionIdString, studyActionShortcut);
204 public void projectClosed() {
205 final Course course = StudyTaskManager.getInstance(myProject).getCourse();
206 if (course != null) {
207 final ToolWindow toolWindow = ToolWindowManager.getInstance(myProject).getToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW);
208 if (toolWindow != null) {
209 toolWindow.getContentManager().removeAllContents(false);
211 final ToolWindow progressToolWindow = ToolWindowManager.getInstance(myProject).getToolWindow(StudyProgressToolWindowFactory.ID);
212 if (progressToolWindow != null) {
213 progressToolWindow.getContentManager().removeAllContents(false);
215 KeymapManagerEx keymapManager = KeymapManagerEx.getInstanceEx();
216 for (Keymap keymap : keymapManager.getAllKeymaps()) {
217 List<Pair<String, String>> pairs = myDeletedShortcuts.get(keymap);
218 if (pairs != null && !pairs.isEmpty()) {
219 for (Pair<String, String> actionShortcut : pairs) {
220 keymap.addShortcut(actionShortcut.first, new KeyboardShortcut(KeyStroke.getKeyStroke(actionShortcut.second), null));
229 public void initComponent() {
230 EditorFactory.getInstance().addEditorFactoryListener(new StudyEditorFactoryListener(), myProject);
231 ActionManager.getInstance().addAnActionListener(new AnActionListener() {
233 public void beforeActionPerformed(AnAction action, DataContext dataContext, AnActionEvent event) {
234 AnAction[] newGroupActions = ((ActionGroup)ActionManager.getInstance().getAction("NewGroup")).getChildren(null);
235 for (AnAction newAction : newGroupActions) {
236 if (newAction == action) {
237 myListener = new FileCreatedByUserListener();
238 VirtualFileManager.getInstance().addVirtualFileListener(myListener);
245 public void afterActionPerformed(AnAction action, DataContext dataContext, AnActionEvent event) {
246 AnAction[] newGroupActions = ((ActionGroup)ActionManager.getInstance().getAction("NewGroup")).getChildren(null);
247 for (AnAction newAction : newGroupActions) {
248 if (newAction == action) {
249 VirtualFileManager.getInstance().removeVirtualFileListener(myListener);
255 public void beforeEditorTyping(char c, DataContext dataContext) {
262 public void disposeComponent() {
267 public String getComponentName() {
268 return "StudyTaskManager";
271 public static StudyProjectComponent getInstance(@NotNull final Project project) {
272 final Module module = ModuleManager.getInstance(project).getModules()[0];
273 return module.getComponent(StudyProjectComponent.class);
276 public boolean useJavaFx() {
280 private class FileCreatedByUserListener extends VirtualFileAdapter {
282 public void fileCreated(@NotNull VirtualFileEvent event) {
283 if (myProject.isDisposed()) return;
284 final VirtualFile createdFile = event.getFile();
285 final VirtualFile taskDir = createdFile.getParent();
286 final Course course = StudyTaskManager.getInstance(myProject).getCourse();
287 if (taskDir != null && taskDir.getName().contains(EduNames.TASK)) {
288 int taskIndex = EduUtils.getIndex(taskDir.getName(), EduNames.TASK);
289 final VirtualFile lessonDir = taskDir.getParent();
290 if (lessonDir != null && lessonDir.getName().contains(EduNames.LESSON)) {
291 int lessonIndex = EduUtils.getIndex(lessonDir.getName(), EduNames.LESSON);
292 if (course != null) {
293 List<Lesson> lessons = course.getLessons();
294 if (StudyUtils.indexIsValid(lessonIndex, lessons)) {
295 final Lesson lesson = lessons.get(lessonIndex);
296 final List<Task> tasks = lesson.getTaskList();
297 if (StudyUtils.indexIsValid(taskIndex, tasks)) {
298 final Task task = tasks.get(taskIndex);
299 final TaskFile taskFile = new TaskFile();
300 taskFile.initTaskFile(task, false);
301 taskFile.setUserCreated(true);
302 final String name = createdFile.getName();
303 taskFile.name = name;
304 task.getTaskFiles().put(name, taskFile);