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