2 * Copyright 2000-2015 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com.jetbrains.edu.learning.ui;
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;
44 import java.util.List;
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";
52 private final JBCardLayout myCardLayout;
53 private final JPanel myContentPanel;
54 private final OnePixelSplitter mySplitPane;
55 private JLabel myStatisticLabel;
56 private StudyProgressBar myStudyProgressBar;
58 public StudyToolWindow() {
60 myCardLayout = new JBCardLayout();
61 myContentPanel = new JPanel(myCardLayout);
62 mySplitPane = new OnePixelSplitter(myVertical = true);
65 public void init(@NotNull final Project project, final boolean isToolwindow) {
66 String taskText = StudyUtils.getTaskText(project);
67 if (taskText == null) return;
69 final DefaultActionGroup group = getActionGroup(project);
70 setActionToolbar(group);
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);
78 JComponent taskInfoPanel = createTaskInfoPanel(project);
79 panel.add(taskInfoPanel, BorderLayout.CENTER);
81 final JPanel courseProgress = createCourseProgress(project);
82 if (isToolwindow && course != null && !course.isAdaptive() && EduNames.STUDY.equals(course.getCourseMode())) {
83 panel.add(courseProgress, BorderLayout.SOUTH);
86 myContentPanel.add(TASK_INFO_ID, panel);
87 mySplitPane.setFirstComponent(myContentPanel);
88 addAdditionalPanels(project);
89 myCardLayout.show(myContentPanel, TASK_INFO_ID);
91 setContent(mySplitPane);
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);
100 if (StudyTaskManager.getInstance(project).isTurnEditingMode() ||
101 StudyTaskManager.getInstance(project).getToolWindowMode() == StudyToolWindowMode.EDITING) {
102 TaskFile file = StudyUtils.getSelectedTaskFile(project);
104 VirtualFile taskDir = file.getTask().getTaskDir(project);
105 setTaskText(taskText, taskDir, project);
109 setTaskText(taskText, null, project);
114 public void setTopComponent(@NotNull final JComponent component) {
115 mySplitPane.setFirstComponent(component);
118 public void setDefaultTopComponent() {
119 mySplitPane.setFirstComponent(myContentPanel);
122 public void setActionToolbar(DefaultActionGroup group) {
123 JPanel toolbarPanel = createToolbarPanel(group);
124 setToolbar(toolbarPanel);
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());
138 public void dispose() {
141 //used in checkiO plugin.
142 @SuppressWarnings("unused")
143 public void showPanelById(@NotNull final String panelId) {
144 myCardLayout.swipe(myContentPanel, panelId, JBCardLayout.SwipeDirection.AUTO);
147 //used in checkiO plugin.
148 @SuppressWarnings("unused")
149 public void setBottomComponent(JComponent component) {
150 mySplitPane.setSecondComponent(component);
153 //used in checkiO plugin.
154 @SuppressWarnings("unused")
155 public JComponent getBottomComponent() {
156 return mySplitPane.getSecondComponent();
159 //used in checkiO plugin.
160 @SuppressWarnings("unused")
161 public void setTopComponentPrefferedSize(@NotNull final Dimension dimension) {
162 myContentPanel.setPreferredSize(dimension);
165 //used in checkiO plugin.
166 @SuppressWarnings("unused")
167 public JPanel getContentPanel() {
168 return myContentPanel;
172 public abstract JComponent createTaskInfoPanel(Project project);
174 public static JPanel createToolbarPanel(ActionGroup group) {
175 final ActionToolbar actionToolBar = ActionManager.getInstance().createActionToolbar("Study", group, true);
176 return JBUI.Panels.simplePanel(actionToolBar.getComponent());
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");
186 StudyPluginConfigurator configurator = StudyUtils.getConfigurator(project);
187 if (configurator != null) {
188 group.addAll(configurator.getActionGroup(project));
189 addAdditionalActions(group);
193 LOG.warn("No configurator is provided for plugin");
194 return StudyBasePluginConfigurator.getDefaultActionGroup();
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());
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");
211 VirtualFile taskTextFile = StudyUtils.findTaskDescriptionVirtualFile(project, taskDirectory);
212 enterEditingMode(taskTextFile, project);
213 StudyTaskManager.getInstance(project).setTurnEditingMode(false);
215 if (taskDirectory != null && StudyTaskManager.getInstance(project).getToolWindowMode() == StudyToolWindowMode.EDITING) {
216 VirtualFile taskTextFile = StudyUtils.findTaskDescriptionVirtualFile(project, taskDirectory);
217 enterEditingMode(taskTextFile, project);
224 protected abstract void setText(@NotNull String text);
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);
231 setTaskText(EMPTY_TASK_TEXT, null, project);
234 public enum StudyToolWindowMode {
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) {
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);
252 JComponent editorComponent = createdEditor.getComponent();
253 mySplitPane.setFirstComponent(editorComponent);
254 mySplitPane.repaint();
256 StudyTaskManager.getInstance(project).setToolWindowMode(StudyToolWindowMode.EDITING);
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);
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);
275 myStatisticLabel = new JLabel("", SwingConstants.LEFT);
276 contentPanel.add(myStatisticLabel);
277 contentPanel.add(myStudyProgressBar);
279 contentPanel.setPreferredSize(new Dimension(100, 60));
280 contentPanel.setMinimumSize(new Dimension(300, 40));
281 updateCourseProgress(project);
285 public void updateCourseProgress(@NotNull final Project project) {
286 final Course course = StudyTaskManager.getInstance(project).getCourse();
287 if (course != null) {
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);
296 String completedTasks = String.format("%d of %d tasks completed", taskSolved, taskNum);
297 double percent = (taskSolved * 100.0) / taskNum;
299 myStatisticLabel.setText(completedTasks);
300 myStudyProgressBar.setFraction(percent / 100);
304 private static int getSolvedTasks(@NotNull final Lesson lesson) {
306 for (Task task : lesson.getTaskList()) {
307 if (task.getStatus() == StudyStatus.Solved) {