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