b4e8d16905f055a753d842e30221e8f567efd967
[idea/community.git] / python / educational-core / student / src / com / jetbrains / edu / learning / actions / StudyTaskNavigationAction.java
1 package com.jetbrains.edu.learning.actions;
2
3 import com.intellij.ide.projectView.ProjectView;
4 import com.intellij.openapi.actionSystem.AnActionEvent;
5 import com.intellij.openapi.fileEditor.FileEditorManager;
6 import com.intellij.openapi.project.DumbAwareAction;
7 import com.intellij.openapi.project.Project;
8 import com.intellij.openapi.ui.MessageType;
9 import com.intellij.openapi.ui.popup.Balloon;
10 import com.intellij.openapi.ui.popup.BalloonBuilder;
11 import com.intellij.openapi.ui.popup.JBPopupFactory;
12 import com.intellij.openapi.vfs.VirtualFile;
13 import com.intellij.openapi.wm.ToolWindow;
14 import com.intellij.openapi.wm.ToolWindowId;
15 import com.intellij.openapi.wm.ToolWindowManager;
16 import com.intellij.util.ui.tree.TreeUtil;
17 import com.jetbrains.edu.EduNames;
18 import com.jetbrains.edu.courseFormat.Task;
19 import com.jetbrains.edu.courseFormat.TaskFile;
20 import com.jetbrains.edu.learning.StudyState;
21 import com.jetbrains.edu.learning.StudyUtils;
22 import com.jetbrains.edu.learning.editor.StudyEditor;
23 import org.jetbrains.annotations.NotNull;
24 import org.jetbrains.annotations.Nullable;
25
26 import javax.swing.*;
27 import javax.swing.tree.TreePath;
28 import java.util.Map;
29
30
31 abstract public class StudyTaskNavigationAction extends DumbAwareAction {
32   public StudyTaskNavigationAction(@Nullable String text, @Nullable String description, @Nullable Icon icon) {
33     super(text, description, icon);
34   }
35
36   public void navigateTask(@NotNull final Project project) {
37     StudyEditor studyEditor = StudyUtils.getSelectedStudyEditor(project);
38     StudyState studyState = new StudyState(studyEditor);
39     if (!studyState.isValid()) {
40       return;
41     }
42     Task nextTask = getTargetTask(studyState.getTask());
43     if (nextTask == null) {
44       BalloonBuilder balloonBuilder =
45         JBPopupFactory.getInstance().createHtmlTextBalloonBuilder(getNavigationFinishedMessage(), MessageType.INFO, null);
46       Balloon balloon = balloonBuilder.createBalloon();
47       assert studyEditor != null;
48       balloon.show(StudyUtils.computeLocation(studyEditor.getEditor()), Balloon.Position.above);
49       return;
50     }
51     for (VirtualFile file : FileEditorManager.getInstance(project).getOpenFiles()) {
52       FileEditorManager.getInstance(project).closeFile(file);
53     }
54     int nextTaskIndex = nextTask.getIndex();
55     int lessonIndex = nextTask.getLesson().getIndex();
56     Map<String, TaskFile> nextTaskFiles = nextTask.getTaskFiles();
57     if (nextTaskFiles.isEmpty()) {
58       return;
59     }
60     VirtualFile projectDir = project.getBaseDir();
61     String lessonDirName = EduNames.LESSON + String.valueOf(lessonIndex);
62     if (projectDir == null) {
63       return;
64     }
65     VirtualFile lessonDir = projectDir.findChild(lessonDirName);
66     if (lessonDir == null) {
67       return;
68     }
69     String taskDirName = EduNames.TASK + String.valueOf(nextTaskIndex);
70     VirtualFile taskDir = lessonDir.findChild(taskDirName);
71     if (taskDir == null) {
72       return;
73     }
74
75     VirtualFile shouldBeActive = getFileToActivate(project, nextTaskFiles, taskDir);
76     JTree tree = ProjectView.getInstance(project).getCurrentProjectViewPane().getTree();
77     TreePath path = TreeUtil.getFirstNodePath(tree);
78     tree.collapsePath(path);
79     if (shouldBeActive != null) {
80       ProjectView.getInstance(project).select(shouldBeActive, shouldBeActive, false);
81       FileEditorManager.getInstance(project).openFile(shouldBeActive, true);
82     }
83     ToolWindow runToolWindow = ToolWindowManager.getInstance(project).getToolWindow(ToolWindowId.RUN);
84     if (runToolWindow != null) {
85       runToolWindow.hide(null);
86     }
87   }
88
89   @Nullable
90   protected VirtualFile getFileToActivate(@NotNull Project project, Map<String, TaskFile> nextTaskFiles, VirtualFile taskDir) {
91     VirtualFile shouldBeActive = null;
92     for (Map.Entry<String, TaskFile> entry : nextTaskFiles.entrySet()) {
93       String name = entry.getKey();
94       TaskFile taskFile = entry.getValue();
95       VirtualFile srcDir = taskDir.findChild("src");
96       VirtualFile vf = srcDir == null ? taskDir.findChild(name) : srcDir.findChild(name);
97       if (vf != null) {
98         FileEditorManager.getInstance(project).openFile(vf, true);
99         if (!taskFile.getAnswerPlaceholders().isEmpty()) {
100           shouldBeActive = vf;
101         }
102       }
103     }
104     return shouldBeActive != null ? shouldBeActive : getFirstTaskFile(taskDir, project);
105   }
106
107   @Nullable
108   private static VirtualFile getFirstTaskFile(@NotNull final VirtualFile taskDir, @NotNull final Project project) {
109     for (VirtualFile virtualFile : taskDir.getChildren()) {
110       if (StudyUtils.getTaskFile(project, virtualFile) != null) {
111         return virtualFile;
112       }
113     }
114     return null;
115   }
116
117   @Override
118   public void actionPerformed(AnActionEvent e) {
119     Project project = e.getProject();
120     if (project == null) {
121       return;
122     }
123     navigateTask(project);
124   }
125
126   protected abstract String getNavigationFinishedMessage();
127
128   protected abstract Task getTargetTask(@NotNull final Task sourceTask);
129
130   @Override
131   public void update(AnActionEvent e) {
132     StudyUtils.updateAction(e);
133   }
134 }