new subtask action
[idea/community.git] / python / educational-core / course-creator / src / com / jetbrains / edu / coursecreator / actions / CCAddAnswerPlaceholder.java
1 package com.jetbrains.edu.coursecreator.actions;
2
3 import com.intellij.openapi.actionSystem.AnActionEvent;
4 import com.intellij.openapi.actionSystem.Presentation;
5 import com.intellij.openapi.command.undo.BasicUndoableAction;
6 import com.intellij.openapi.command.undo.UnexpectedUndoException;
7 import com.intellij.openapi.editor.Document;
8 import com.intellij.openapi.editor.Editor;
9 import com.intellij.openapi.editor.SelectionModel;
10 import com.intellij.openapi.fileEditor.FileDocumentManager;
11 import com.intellij.openapi.project.Project;
12 import com.intellij.openapi.util.text.StringUtil;
13 import com.intellij.psi.PsiDocumentManager;
14 import com.intellij.psi.PsiFile;
15 import com.intellij.ui.JBColor;
16 import com.intellij.util.DocumentUtil;
17 import com.jetbrains.edu.learning.StudyUtils;
18 import com.jetbrains.edu.learning.core.EduAnswerPlaceholderPainter;
19 import com.jetbrains.edu.learning.core.EduNames;
20 import com.jetbrains.edu.learning.core.EduUtils;
21 import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder;
22 import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholderSubtaskInfo;
23 import com.jetbrains.edu.learning.courseFormat.TaskFile;
24 import com.jetbrains.edu.learning.ui.CCCreateAnswerPlaceholderDialog;
25 import org.jetbrains.annotations.NotNull;
26
27 import java.util.List;
28
29 public class CCAddAnswerPlaceholder extends CCAnswerPlaceholderAction {
30
31   public CCAddAnswerPlaceholder() {
32     super("Add/Delete Answer Placeholder", "Add/Delete answer placeholder");
33   }
34
35
36   private static boolean arePlaceholdersIntersect(@NotNull final TaskFile taskFile, int start, int end) {
37     List<AnswerPlaceholder> answerPlaceholders = taskFile.getAnswerPlaceholders();
38     for (AnswerPlaceholder existingAnswerPlaceholder : answerPlaceholders) {
39       int twStart = existingAnswerPlaceholder.getOffset();
40       int twEnd = existingAnswerPlaceholder.getPossibleAnswerLength() + twStart;
41       if ((start >= twStart && start < twEnd) || (end > twStart && end <= twEnd) ||
42           (twStart >= start && twStart < end) || (twEnd > start && twEnd <= end)) {
43         return true;
44       }
45     }
46     return false;
47   }
48
49   private void addPlaceholder(@NotNull CCState state) {
50     Editor editor = state.getEditor();
51     Project project = state.getProject();
52     PsiFile file = state.getFile();
53
54     final Document document = PsiDocumentManager.getInstance(project).getDocument(file);
55     if (document == null) {
56       return;
57     }
58     FileDocumentManager.getInstance().saveDocument(document);
59     final SelectionModel model = editor.getSelectionModel();
60     final int offset = model.hasSelection() ? model.getSelectionStart() : editor.getCaretModel().getOffset();
61     int stepIndex = state.getTaskFile().getTask().getActiveSubtaskIndex();
62     final AnswerPlaceholder answerPlaceholder = new AnswerPlaceholder();
63     answerPlaceholder.getSubtaskInfos().put(stepIndex, new AnswerPlaceholderSubtaskInfo());
64     TaskFile taskFile = state.getTaskFile();
65     int index = taskFile.getAnswerPlaceholders().size();
66     answerPlaceholder.setIndex(index);
67     answerPlaceholder.setTaskFile(taskFile);
68     taskFile.sortAnswerPlaceholders();
69     answerPlaceholder.setOffset(offset);
70     answerPlaceholder.setUseLength(false);
71
72     String defaultPlaceholderText = "type here";
73     CCCreateAnswerPlaceholderDialog dlg = createDialog(project, answerPlaceholder);
74     if (!dlg.showAndGet()) {
75       return;
76     }
77     String answerPlaceholderText = dlg.getTaskText();
78     answerPlaceholder.setPossibleAnswer(model.hasSelection() ? model.getSelectedText() : defaultPlaceholderText);
79     answerPlaceholder.setTaskText(StringUtil.notNullize(answerPlaceholderText));
80     answerPlaceholder.setLength(StringUtil.notNullize(answerPlaceholderText).length());
81     answerPlaceholder.setHints(dlg.getHints());
82
83     if (!model.hasSelection()) {
84       DocumentUtil.writeInRunUndoTransparentAction(() -> document.insertString(offset, defaultPlaceholderText));
85     }
86
87
88     answerPlaceholder.setPossibleAnswer(model.hasSelection() ? model.getSelectedText() : defaultPlaceholderText);
89     AddAction action = new AddAction(answerPlaceholder, taskFile, editor);
90     EduUtils.runUndoableAction(project, "Add Answer Placeholder", action);
91   }
92
93   static class AddAction extends BasicUndoableAction {
94     private final AnswerPlaceholder myPlaceholder;
95     private final TaskFile myTaskFile;
96     private final Editor myEditor;
97
98     public AddAction(AnswerPlaceholder placeholder, TaskFile taskFile, Editor editor) {
99       super(editor.getDocument());
100       myPlaceholder = placeholder;
101       myTaskFile = taskFile;
102       myEditor = editor;
103     }
104
105     @Override
106     public void undo() throws UnexpectedUndoException {
107       final List<AnswerPlaceholder> answerPlaceholders = myTaskFile.getAnswerPlaceholders();
108       if (answerPlaceholders.contains(myPlaceholder)) {
109         answerPlaceholders.remove(myPlaceholder);
110         myEditor.getMarkupModel().removeAllHighlighters();
111         StudyUtils.drawAllWindows(myEditor, myTaskFile);
112         EduAnswerPlaceholderPainter.createGuardedBlocks(myEditor, myTaskFile);
113       }
114     }
115
116     @Override
117     public void redo() throws UnexpectedUndoException {
118       myTaskFile.addAnswerPlaceholder(myPlaceholder);
119       EduAnswerPlaceholderPainter.drawAnswerPlaceholder(myEditor, myPlaceholder, JBColor.BLUE);
120       EduAnswerPlaceholderPainter.createGuardedBlocks(myEditor, myPlaceholder);
121     }
122   }
123
124   @Override
125   protected void performAnswerPlaceholderAction(@NotNull CCState state) {
126     if (canAddPlaceholder(state)) {
127       addPlaceholder(state);
128       return;
129     }
130     if (canDeletePlaceholder(state)) {
131       deletePlaceholder(state);
132     }
133   }
134
135   private static void deletePlaceholder(@NotNull CCState state) {
136     Project project = state.getProject();
137     TaskFile taskFile = state.getTaskFile();
138     AnswerPlaceholder answerPlaceholder = state.getAnswerPlaceholder();
139     EduUtils.runUndoableAction(project, "Delete Answer Placeholder", new AddAction(answerPlaceholder, taskFile, state.getEditor()) {
140       @Override
141       public void undo() throws UnexpectedUndoException {
142         super.redo();
143       }
144
145       @Override
146       public void redo() throws UnexpectedUndoException {
147         super.undo();
148       }
149     });
150   }
151
152   @Override
153   public void update(@NotNull AnActionEvent event) {
154     final Presentation presentation = event.getPresentation();
155     presentation.setEnabledAndVisible(false);
156
157     CCState state = getState(event);
158     if (state == null) {
159       return;
160     }
161
162     presentation.setVisible(true);
163     if (canAddPlaceholder(state) || canDeletePlaceholder(state)) {
164       presentation.setEnabled(true);
165       presentation.setText((state.getAnswerPlaceholder() == null ? "Add " : "Delete ") + EduNames.ANSWER_PLACEHOLDER);
166     }
167   }
168
169
170   private static boolean canAddPlaceholder(@NotNull CCState state) {
171     Editor editor = state.getEditor();
172     SelectionModel selectionModel = editor.getSelectionModel();
173     if (selectionModel.hasSelection()) {
174       int start = selectionModel.getSelectionStart();
175       int end = selectionModel.getSelectionEnd();
176       return !arePlaceholdersIntersect(state.getTaskFile(), start, end);
177     }
178     int offset = editor.getCaretModel().getOffset();
179     return state.getTaskFile().getAnswerPlaceholder(offset) == null;
180   }
181
182   private static boolean canDeletePlaceholder(@NotNull CCState state) {
183     if (state.getEditor().getSelectionModel().hasSelection()) {
184       return false;
185     }
186     return state.getAnswerPlaceholder() != null;
187   }
188
189   protected CCCreateAnswerPlaceholderDialog createDialog(Project project, AnswerPlaceholder answerPlaceholder) {
190     return new CCCreateAnswerPlaceholderDialog(project, StringUtil.notNullize(answerPlaceholder.getTaskText()), answerPlaceholder.getHints());
191   }
192 }