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.EditorSettings;
25 import com.intellij.openapi.editor.colors.EditorColorsManager;
26 import com.intellij.openapi.editor.ex.EditorEx;
27 import com.intellij.openapi.extensions.Extensions;
28 import com.intellij.openapi.fileEditor.FileDocumentManager;
29 import com.intellij.openapi.fileEditor.FileEditorManagerListener;
30 import com.intellij.openapi.project.Project;
31 import com.intellij.openapi.ui.SimpleToolWindowPanel;
32 import com.intellij.openapi.util.Disposer;
33 import com.intellij.openapi.vfs.VirtualFile;
34 import com.intellij.ui.JBCardLayout;
35 import com.intellij.ui.JBColor;
36 import com.intellij.ui.OnePixelSplitter;
37 import com.intellij.util.ui.JBUI;
38 import com.jetbrains.edu.learning.*;
39 import com.jetbrains.edu.learning.core.EduNames;
40 import com.jetbrains.edu.learning.courseFormat.*;
41 import com.jetbrains.edu.learning.stepic.StepicAdaptiveReactionsPanel;
42 import org.jetbrains.annotations.NotNull;
45 import javax.swing.border.EmptyBorder;
47 import java.util.List;
50 public abstract class StudyToolWindow extends SimpleToolWindowPanel implements DataProvider, Disposable {
51 private static final Logger LOG = Logger.getInstance(StudyToolWindow.class);
52 private static final String TASK_INFO_ID = "taskInfo";
53 private static final String EMPTY_TASK_TEXT = "Please, open any task to see task description";
55 private final JBCardLayout myCardLayout;
56 private final JPanel myContentPanel;
57 private final OnePixelSplitter mySplitPane;
58 private JLabel myStatisticLabel;
59 private StudyProgressBar myStudyProgressBar;
61 public StudyToolWindow() {
63 myCardLayout = new JBCardLayout();
64 myContentPanel = new JPanel(myCardLayout);
65 mySplitPane = new OnePixelSplitter(myVertical = true);
68 public void init(@NotNull final Project project, final boolean isToolwindow) {
69 String taskText = StudyUtils.getTaskText(project);
70 if (taskText == null) return;
72 final DefaultActionGroup group = getActionGroup(project);
73 setActionToolbar(group);
75 final JPanel panel = new JPanel(new BorderLayout());
76 final Course course = StudyTaskManager.getInstance(project).getCourse();
77 if (isToolwindow && course != null && course.isAdaptive()) {
78 panel.add(new StepicAdaptiveReactionsPanel(project), BorderLayout.NORTH);
81 JComponent taskInfoPanel = createTaskInfoPanel(project);
82 panel.add(taskInfoPanel, BorderLayout.CENTER);
84 final JPanel courseProgress = createCourseProgress(project);
85 if (isToolwindow && course != null && !course.isAdaptive() && EduNames.STUDY.equals(course.getCourseMode())) {
86 panel.add(courseProgress, BorderLayout.SOUTH);
89 myContentPanel.add(TASK_INFO_ID, panel);
90 mySplitPane.setFirstComponent(myContentPanel);
91 addAdditionalPanels(project);
92 myCardLayout.show(myContentPanel, TASK_INFO_ID);
94 setContent(mySplitPane);
97 StudyPluginConfigurator configurator = StudyUtils.getConfigurator(project);
98 if (configurator != null) {
99 final FileEditorManagerListener listener = configurator.getFileEditorManagerListener(project, this);
100 project.getMessageBus().connect().subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, listener);
103 if (StudyTaskManager.getInstance(project).isTurnEditingMode() ||
104 StudyTaskManager.getInstance(project).getToolWindowMode() == StudyToolWindowMode.EDITING) {
105 TaskFile file = StudyUtils.getSelectedTaskFile(project);
107 VirtualFile taskDir = file.getTask().getTaskDir(project);
108 setTaskText(taskText, taskDir, project);
112 setTaskText(taskText, null, project);
117 public void setTopComponent(@NotNull final JComponent component) {
118 mySplitPane.setFirstComponent(component);
121 public void setDefaultTopComponent() {
122 mySplitPane.setFirstComponent(myContentPanel);
125 public void setActionToolbar(DefaultActionGroup group) {
126 JPanel toolbarPanel = createToolbarPanel(group);
127 setToolbar(toolbarPanel);
130 private void addAdditionalPanels(Project project) {
131 StudyPluginConfigurator configurator = StudyUtils.getConfigurator(project);
132 if (configurator != null) {
133 Map<String, JPanel> panels = configurator.getAdditionalPanels(project);
134 for (Map.Entry<String, JPanel> entry : panels.entrySet()) {
135 myContentPanel.add(entry.getKey(), entry.getValue());
141 public void dispose() {
144 //used in checkiO plugin.
145 @SuppressWarnings("unused")
146 public void showPanelById(@NotNull final String panelId) {
147 myCardLayout.swipe(myContentPanel, panelId, JBCardLayout.SwipeDirection.AUTO);
150 //used in checkiO plugin.
151 @SuppressWarnings("unused")
152 public void setBottomComponent(JComponent component) {
153 mySplitPane.setSecondComponent(component);
156 //used in checkiO plugin.
157 @SuppressWarnings("unused")
158 public JComponent getBottomComponent() {
159 return mySplitPane.getSecondComponent();
162 //used in checkiO plugin.
163 @SuppressWarnings("unused")
164 public void setTopComponentPrefferedSize(@NotNull final Dimension dimension) {
165 myContentPanel.setPreferredSize(dimension);
168 //used in checkiO plugin.
169 @SuppressWarnings("unused")
170 public JPanel getContentPanel() {
171 return myContentPanel;
175 public abstract JComponent createTaskInfoPanel(Project project);
177 public static JPanel createToolbarPanel(ActionGroup group) {
178 final ActionToolbar actionToolBar = ActionManager.getInstance().createActionToolbar("Study", group, true);
179 return JBUI.Panels.simplePanel(actionToolBar.getComponent());
182 public static DefaultActionGroup getActionGroup(@NotNull final Project project) {
183 DefaultActionGroup group = new DefaultActionGroup();
184 Course course = StudyTaskManager.getInstance(project).getCourse();
185 if (course == null) {
186 LOG.warn("Course is null");
189 StudyPluginConfigurator configurator = StudyUtils.getConfigurator(project);
190 if (configurator != null) {
191 group.addAll(configurator.getActionGroup(project));
192 addAdditionalActions(group);
196 LOG.warn("No configurator is provided for plugin");
197 return StudyBasePluginConfigurator.getDefaultActionGroup();
201 private static void addAdditionalActions(DefaultActionGroup group) {
202 StudyActionsProvider[] providers = Extensions.getExtensions(StudyActionsProvider.EP_NAME);
203 for (StudyActionsProvider provider : providers) {
204 group.addAll(provider.getActions());
208 public void setTaskText(String text, VirtualFile taskDirectory, Project project) {
209 if (!EMPTY_TASK_TEXT.equals(text) && StudyTaskManager.getInstance(project).isTurnEditingMode()) {
210 if (taskDirectory == null) {
211 LOG.info("Failed to enter editing mode for StudyToolWindow");
214 VirtualFile taskTextFile = StudyUtils.findTaskDescriptionVirtualFile(project, taskDirectory);
215 enterEditingMode(taskTextFile, project);
216 StudyTaskManager.getInstance(project).setTurnEditingMode(false);
218 if (taskDirectory != null && StudyTaskManager.getInstance(project).getToolWindowMode() == StudyToolWindowMode.EDITING) {
219 VirtualFile taskTextFile = StudyUtils.findTaskDescriptionVirtualFile(project, taskDirectory);
220 enterEditingMode(taskTextFile, project);
227 protected abstract void setText(@NotNull String text);
229 public void setEmptyText(@NotNull Project project) {
230 if (StudyTaskManager.getInstance(project).getToolWindowMode() == StudyToolWindowMode.EDITING) {
231 mySplitPane.setFirstComponent(myContentPanel);
232 StudyTaskManager.getInstance(project).setTurnEditingMode(true);
234 setTaskText(EMPTY_TASK_TEXT, null, project);
237 public enum StudyToolWindowMode {
242 public void enterEditingMode(VirtualFile taskFile, Project project) {
243 final EditorFactory factory = EditorFactory.getInstance();
244 Document document = FileDocumentManager.getInstance().getDocument(taskFile);
245 if (document == null) {
248 WebBrowserManager.getInstance().setShowBrowserHover(false);
249 final EditorEx createdEditor = (EditorEx)factory.createEditor(document, project, taskFile, false);
250 Disposer.register(project, new Disposable() {
251 public void dispose() {
252 factory.releaseEditor(createdEditor);
255 JComponent editorComponent = createdEditor.getComponent();
256 editorComponent.setBorder(new EmptyBorder(10, 20, 0, 10));
257 editorComponent.setBackground(EditorColorsManager.getInstance().getGlobalScheme().getDefaultBackground());
258 EditorSettings editorSettings = createdEditor.getSettings();
259 editorSettings.setLineMarkerAreaShown(false);
260 editorSettings.setLineNumbersShown(false);
261 editorSettings.setFoldingOutlineShown(false);
262 mySplitPane.setFirstComponent(editorComponent);
263 mySplitPane.repaint();
265 StudyTaskManager.getInstance(project).setToolWindowMode(StudyToolWindowMode.EDITING);
269 public void leaveEditingMode(Project project) {
270 WebBrowserManager.getInstance().setShowBrowserHover(true);
271 mySplitPane.setFirstComponent(myContentPanel);
272 StudyTaskManager.getInstance(project).setToolWindowMode(StudyToolWindowMode.TEXT);
273 StudyUtils.updateToolWindows(project);
276 private JPanel createCourseProgress(@NotNull final Project project) {
277 JPanel contentPanel = new JPanel();
278 contentPanel.setBackground(JBColor.WHITE);
279 contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.PAGE_AXIS));
280 contentPanel.add(Box.createRigidArea(new Dimension(10, 0)));
281 contentPanel.add(Box.createRigidArea(new Dimension(0, 10)));
282 myStudyProgressBar = new StudyProgressBar(0, 20, 10);
284 myStatisticLabel = new JLabel("", SwingConstants.LEFT);
285 contentPanel.add(myStatisticLabel);
286 contentPanel.add(myStudyProgressBar);
288 contentPanel.setPreferredSize(new Dimension(100, 60));
289 contentPanel.setMinimumSize(new Dimension(300, 40));
290 updateCourseProgress(project);
294 public void updateCourseProgress(@NotNull final Project project) {
295 final Course course = StudyTaskManager.getInstance(project).getCourse();
296 if (course != null) {
299 List<Lesson> lessons = course.getLessons();
300 for (Lesson lesson : lessons) {
301 if (lesson.getName().equals(EduNames.PYCHARM_ADDITIONAL)) continue;
302 taskNum += lesson.getTaskList().size();
303 taskSolved += getSolvedTasks(lesson);
305 String completedTasks = String.format("%d of %d tasks completed", taskSolved, taskNum);
306 double percent = (taskSolved * 100.0) / taskNum;
308 myStatisticLabel.setText(completedTasks);
309 myStudyProgressBar.setFraction(percent / 100);
313 private static int getSolvedTasks(@NotNull final Lesson lesson) {
315 for (Task task : lesson.getTaskList()) {
316 if (task.getStatus() == StudyStatus.Solved) {