c206f6679bb2d7faffed5038727e9caf3bb0d32f
[idea/community.git] / python / educational-core / student / src / com / jetbrains / edu / learning / ui / StudyToolWindow.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 package com.jetbrains.edu.learning.ui;
17
18 import com.intellij.ide.browsers.WebBrowserManager;
19 import com.intellij.openapi.Disposable;
20 import com.intellij.openapi.actionSystem.*;
21 import com.intellij.openapi.diagnostic.Logger;
22 import com.intellij.openapi.editor.Document;
23 import com.intellij.openapi.editor.EditorFactory;
24 import com.intellij.openapi.editor.ex.EditorEx;
25 import com.intellij.openapi.extensions.Extensions;
26 import com.intellij.openapi.fileEditor.FileDocumentManager;
27 import com.intellij.openapi.fileEditor.FileEditorManagerListener;
28 import com.intellij.openapi.project.Project;
29 import com.intellij.openapi.ui.SimpleToolWindowPanel;
30 import com.intellij.openapi.util.Disposer;
31 import com.intellij.openapi.vfs.VirtualFile;
32 import com.intellij.ui.JBCardLayout;
33 import com.intellij.ui.JBColor;
34 import com.intellij.ui.OnePixelSplitter;
35 import com.intellij.util.ui.JBUI;
36 import com.jetbrains.edu.learning.*;
37 import com.jetbrains.edu.learning.core.EduNames;
38 import com.jetbrains.edu.learning.courseFormat.*;
39 import com.jetbrains.edu.learning.stepic.StepicAdaptiveReactionsPanel;
40 import org.jetbrains.annotations.NotNull;
41
42 import javax.swing.*;
43 import java.awt.*;
44 import java.util.List;
45 import java.util.Map;
46
47 public abstract class StudyToolWindow extends SimpleToolWindowPanel implements DataProvider, Disposable {
48   private static final Logger LOG = Logger.getInstance(StudyToolWindow.class);
49   private static final String TASK_INFO_ID = "taskInfo";
50   private static final String EMPTY_TASK_TEXT = "Please, open any task to see task description";
51
52   private final JBCardLayout myCardLayout;
53   private final JPanel myContentPanel;
54   private final OnePixelSplitter mySplitPane;
55   private JLabel myStatisticLabel;
56   private StudyProgressBar myStudyProgressBar;
57
58   public StudyToolWindow() {
59     super(true, true);
60     myCardLayout = new JBCardLayout();
61     myContentPanel = new JPanel(myCardLayout);
62     mySplitPane = new OnePixelSplitter(myVertical = true);
63   }
64
65   public void init(@NotNull final Project project, final boolean isToolwindow) {
66     String taskText = StudyUtils.getTaskText(project);
67     if (taskText == null) return;
68
69     final DefaultActionGroup group = getActionGroup(project);
70     setActionToolbar(group);
71
72     final JPanel panel = new JPanel(new BorderLayout());
73     final Course course = StudyTaskManager.getInstance(project).getCourse();
74     if (isToolwindow && course != null && course.isAdaptive()) {
75       panel.add(new StepicAdaptiveReactionsPanel(project), BorderLayout.NORTH);
76     }
77     
78     JComponent taskInfoPanel = createTaskInfoPanel(project);
79     panel.add(taskInfoPanel, BorderLayout.CENTER);
80     
81     final JPanel courseProgress = createCourseProgress(project);
82     if (isToolwindow && course != null && !course.isAdaptive() && EduNames.STUDY.equals(course.getCourseMode())) {
83       panel.add(courseProgress, BorderLayout.SOUTH);
84     }
85
86     myContentPanel.add(TASK_INFO_ID, panel);
87     mySplitPane.setFirstComponent(myContentPanel);
88     addAdditionalPanels(project);
89     myCardLayout.show(myContentPanel, TASK_INFO_ID);
90
91     setContent(mySplitPane);
92     
93     if (isToolwindow) {
94       StudyPluginConfigurator configurator = StudyUtils.getConfigurator(project);
95       if (configurator != null) {
96         final FileEditorManagerListener listener = configurator.getFileEditorManagerListener(project, this);
97         project.getMessageBus().connect().subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, listener);
98       }
99
100       if (StudyTaskManager.getInstance(project).isTurnEditingMode() ||
101           StudyTaskManager.getInstance(project).getToolWindowMode() == StudyToolWindowMode.EDITING) {
102         TaskFile file = StudyUtils.getSelectedTaskFile(project);
103         if (file != null) {
104           VirtualFile taskDir = file.getTask().getTaskDir(project);
105           setTaskText(taskText, taskDir, project);
106         }
107       }
108       else {
109         setTaskText(taskText, null, project);
110       }
111     }
112   }
113   
114   public void setTopComponent(@NotNull final JComponent component) {
115     mySplitPane.setFirstComponent(component);
116   }
117   
118   public void setDefaultTopComponent() {
119     mySplitPane.setFirstComponent(myContentPanel);
120   }
121
122   public void setActionToolbar(DefaultActionGroup group) {
123     JPanel toolbarPanel = createToolbarPanel(group);
124     setToolbar(toolbarPanel);
125   }
126
127   private void addAdditionalPanels(Project project) {
128     StudyPluginConfigurator configurator = StudyUtils.getConfigurator(project);
129     if (configurator != null) {
130       Map<String, JPanel> panels = configurator.getAdditionalPanels(project);
131       for (Map.Entry<String, JPanel> entry : panels.entrySet()) {
132         myContentPanel.add(entry.getKey(), entry.getValue());
133       }
134     }
135   }
136
137
138   public void dispose() {
139   }
140
141   //used in checkiO plugin.
142   @SuppressWarnings("unused")
143   public void showPanelById(@NotNull final String panelId) {
144     myCardLayout.swipe(myContentPanel, panelId, JBCardLayout.SwipeDirection.AUTO);
145   }
146
147   //used in checkiO plugin.
148   @SuppressWarnings("unused")
149   public void setBottomComponent(JComponent component) {
150     mySplitPane.setSecondComponent(component);
151   }
152
153   //used in checkiO plugin.
154   @SuppressWarnings("unused")
155   public JComponent getBottomComponent() {
156     return mySplitPane.getSecondComponent();
157   }
158
159   //used in checkiO plugin.
160   @SuppressWarnings("unused")
161   public void setTopComponentPrefferedSize(@NotNull final Dimension dimension) {
162     myContentPanel.setPreferredSize(dimension);
163   }
164
165   //used in checkiO plugin.
166   @SuppressWarnings("unused")
167   public JPanel getContentPanel() {
168     return myContentPanel;
169   }
170
171
172   public abstract JComponent createTaskInfoPanel(Project project);
173
174   public static JPanel createToolbarPanel(ActionGroup group) {
175     final ActionToolbar actionToolBar = ActionManager.getInstance().createActionToolbar("Study", group, true);
176     return JBUI.Panels.simplePanel(actionToolBar.getComponent());
177   }
178
179   public static DefaultActionGroup getActionGroup(@NotNull final Project project) {
180     DefaultActionGroup group = new DefaultActionGroup();
181     Course course = StudyTaskManager.getInstance(project).getCourse();
182     if (course == null) {
183       LOG.warn("Course is null");
184       return group;
185     }
186     StudyPluginConfigurator configurator = StudyUtils.getConfigurator(project);
187     if (configurator != null) {
188       group.addAll(configurator.getActionGroup(project));
189       addAdditionalActions(group);
190       return group;
191     }
192     else {
193       LOG.warn("No configurator is provided for plugin");
194       return StudyBasePluginConfigurator.getDefaultActionGroup();
195     }
196   }
197
198   private static void addAdditionalActions(DefaultActionGroup group) {
199     StudyActionsProvider[] providers = Extensions.getExtensions(StudyActionsProvider.EP_NAME);
200     for (StudyActionsProvider provider : providers) {
201       group.addAll(provider.getActions());
202     }
203   }
204
205   public void setTaskText(String text, VirtualFile taskDirectory, Project project) {
206     if (!EMPTY_TASK_TEXT.equals(text) && StudyTaskManager.getInstance(project).isTurnEditingMode()) {
207       if (taskDirectory == null) {
208         LOG.info("Failed to enter editing mode for StudyToolWindow");
209         return;
210       }
211       VirtualFile taskTextFile = StudyUtils.findTaskDescriptionVirtualFile(project, taskDirectory);
212       enterEditingMode(taskTextFile, project);
213       StudyTaskManager.getInstance(project).setTurnEditingMode(false);
214     }
215     if (taskDirectory != null && StudyTaskManager.getInstance(project).getToolWindowMode() == StudyToolWindowMode.EDITING) {
216       VirtualFile taskTextFile = StudyUtils.findTaskDescriptionVirtualFile(project, taskDirectory);
217       enterEditingMode(taskTextFile, project);
218     }
219     else {
220       setText(text);
221     }
222   }
223
224   protected abstract void setText(@NotNull String text);
225
226   public void setEmptyText(@NotNull Project project) {
227     if (StudyTaskManager.getInstance(project).getToolWindowMode() == StudyToolWindowMode.EDITING) {
228       mySplitPane.setFirstComponent(myContentPanel);
229       StudyTaskManager.getInstance(project).setTurnEditingMode(true);
230     }
231     setTaskText(EMPTY_TASK_TEXT, null, project);
232   }
233
234   public enum StudyToolWindowMode {
235     TEXT, EDITING
236   }
237
238
239   public void enterEditingMode(VirtualFile taskFile, Project project) {
240     final EditorFactory factory = EditorFactory.getInstance();
241     Document document = FileDocumentManager.getInstance().getDocument(taskFile);
242     if (document == null) {
243       return;
244     }
245     WebBrowserManager.getInstance().setShowBrowserHover(false);
246     final EditorEx createdEditor = (EditorEx)factory.createEditor(document, project, taskFile, false);
247     Disposer.register(project, new Disposable() {
248       public void dispose() {
249         factory.releaseEditor(createdEditor);
250       }
251     });
252     JComponent editorComponent = createdEditor.getComponent();
253     mySplitPane.setFirstComponent(editorComponent);
254     mySplitPane.repaint();
255
256     StudyTaskManager.getInstance(project).setToolWindowMode(StudyToolWindowMode.EDITING);
257   }
258
259
260   public void leaveEditingMode(Project project) {
261     WebBrowserManager.getInstance().setShowBrowserHover(true);
262     mySplitPane.setFirstComponent(myContentPanel);
263     StudyTaskManager.getInstance(project).setToolWindowMode(StudyToolWindowMode.TEXT);
264     StudyUtils.updateToolWindows(project);
265   }
266
267   private JPanel createCourseProgress(@NotNull final Project project) {
268     JPanel contentPanel = new JPanel();
269     contentPanel.setBackground(JBColor.WHITE);
270     contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.PAGE_AXIS));
271     contentPanel.add(Box.createRigidArea(new Dimension(10, 0)));
272     contentPanel.add(Box.createRigidArea(new Dimension(0, 10)));
273     myStudyProgressBar = new StudyProgressBar(0, 20, 10);
274
275     myStatisticLabel = new JLabel("", SwingConstants.LEFT);
276     contentPanel.add(myStatisticLabel);
277     contentPanel.add(myStudyProgressBar);
278
279     contentPanel.setPreferredSize(new Dimension(100, 60));
280     contentPanel.setMinimumSize(new Dimension(300, 40));
281     updateCourseProgress(project);
282     return contentPanel;
283   }
284
285   public void updateCourseProgress(@NotNull final Project project) {
286     final Course course = StudyTaskManager.getInstance(project).getCourse();
287     if (course != null) {
288       int taskNum = 0;
289       int taskSolved = 0;
290       List<Lesson> lessons = course.getLessons();
291       for (Lesson lesson : lessons) {
292         if (lesson.getName().equals(EduNames.PYCHARM_ADDITIONAL)) continue;
293         taskNum += lesson.getTaskList().size();
294         taskSolved += getSolvedTasks(lesson);
295       }
296       String completedTasks = String.format("%d of %d tasks completed", taskSolved, taskNum);
297       double percent = (taskSolved * 100.0) / taskNum;
298
299       myStatisticLabel.setText(completedTasks);
300       myStudyProgressBar.setFraction(percent / 100);
301     }
302   }
303
304   private static int getSolvedTasks(@NotNull final Lesson lesson) {
305     int solved = 0;
306     for (Task task : lesson.getTaskList()) {
307       if (task.getStatus() == StudyStatus.Solved) {
308         solved += 1;
309       }
310     }
311     return solved;
312   }
313 }