7996e8ff8b329b9befc7de87ad48e0c3313e7b81
[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.application.Result;
6 import com.intellij.openapi.command.WriteCommandAction;
7 import com.intellij.openapi.command.undo.BasicUndoableAction;
8 import com.intellij.openapi.command.undo.UndoManager;
9 import com.intellij.openapi.command.undo.UnexpectedUndoException;
10 import com.intellij.openapi.editor.Document;
11 import com.intellij.openapi.editor.Editor;
12 import com.intellij.openapi.editor.SelectionModel;
13 import com.intellij.openapi.fileEditor.FileDocumentManager;
14 import com.intellij.openapi.project.Project;
15 import com.intellij.openapi.ui.DialogWrapper;
16 import com.intellij.psi.PsiDocumentManager;
17 import com.intellij.psi.PsiFile;
18 import com.intellij.ui.JBColor;
19 import com.intellij.util.DocumentUtil;
20 import com.jetbrains.edu.coursecreator.ui.CCCreateAnswerPlaceholderDialog;
21 import com.jetbrains.edu.learning.StudyUtils;
22 import com.jetbrains.edu.learning.core.EduAnswerPlaceholderPainter;
23 import com.jetbrains.edu.learning.core.EduNames;
24 import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder;
25 import com.jetbrains.edu.learning.courseFormat.TaskFile;
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.getAnswerPlaceholders();
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 static 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     final AnswerPlaceholder answerPlaceholder = new AnswerPlaceholder();
63
64     answerPlaceholder.setOffset(offset);
65     answerPlaceholder.setUseLength(false);
66
67     String defaultPlaceholderText = "type here";
68     answerPlaceholder.setPossibleAnswer(model.hasSelection() ? model.getSelectedText() : defaultPlaceholderText);
69
70     CCCreateAnswerPlaceholderDialog dlg = new CCCreateAnswerPlaceholderDialog(project, answerPlaceholder);
71     dlg.show();
72     if (dlg.getExitCode() != DialogWrapper.OK_EXIT_CODE) {
73       return;
74     }
75
76     if (!model.hasSelection()) {
77       DocumentUtil.writeInRunUndoTransparentAction(() -> document.insertString(offset, defaultPlaceholderText));
78     }
79
80     TaskFile taskFile = state.getTaskFile();
81     int index = taskFile.getAnswerPlaceholders().size();
82     answerPlaceholder.setIndex(index);
83     answerPlaceholder.setTaskFile(taskFile);
84     taskFile.sortAnswerPlaceholders();
85     answerPlaceholder.setPossibleAnswer(model.hasSelection() ? model.getSelectedText() : defaultPlaceholderText);
86     AddAction action = new AddAction(answerPlaceholder, taskFile, editor);
87     new WriteCommandAction(project, "Add Answer Placeholder") {
88       protected void run(@NotNull final Result result) throws Throwable {
89         action.redo();
90         UndoManager.getInstance(project).undoableActionPerformed(action);
91       }
92     }.execute();
93   }
94
95   static class AddAction extends BasicUndoableAction {
96     private final AnswerPlaceholder myPlaceholder;
97     private final TaskFile myTaskFile;
98     private final Editor myEditor;
99
100     public AddAction(AnswerPlaceholder placeholder, TaskFile taskFile, Editor editor) {
101       super(editor.getDocument());
102       myPlaceholder = placeholder;
103       myTaskFile = taskFile;
104       myEditor = editor;
105     }
106
107     @Override
108     public void undo() throws UnexpectedUndoException {
109       final List<AnswerPlaceholder> answerPlaceholders = myTaskFile.getAnswerPlaceholders();
110       if (answerPlaceholders.contains(myPlaceholder)) {
111         answerPlaceholders.remove(myPlaceholder);
112         myEditor.getMarkupModel().removeAllHighlighters();
113         StudyUtils.drawAllWindows(myEditor, myTaskFile);
114         EduAnswerPlaceholderPainter.createGuardedBlocks(myEditor, myTaskFile);
115       }
116     }
117
118     @Override
119     public void redo() throws UnexpectedUndoException {
120       myTaskFile.addAnswerPlaceholder(myPlaceholder);
121       EduAnswerPlaceholderPainter.drawAnswerPlaceholder(myEditor, myPlaceholder, JBColor.BLUE);
122       EduAnswerPlaceholderPainter.createGuardedBlocks(myEditor, myPlaceholder);
123     }
124   }
125
126   static class DeleteAction extends AddAction {
127
128     public DeleteAction(AnswerPlaceholder placeholder, TaskFile taskFile, Editor editor) {
129       super(placeholder, taskFile, editor);
130     }
131
132     @Override
133     public void undo() throws UnexpectedUndoException {
134       super.redo();
135     }
136
137     @Override
138     public void redo() throws UnexpectedUndoException {
139       super.undo();
140     }
141   }
142
143   @Override
144   protected void performAnswerPlaceholderAction(@NotNull CCState state) {
145     if (canAddPlaceholder(state)) {
146       addPlaceholder(state);
147       return;
148     }
149     if (canDeletePlaceholder(state)) {
150       deletePlaceholder(state);
151     }
152   }
153
154   private static void deletePlaceholder(@NotNull CCState state) {
155     Project project = state.getProject();
156     TaskFile taskFile = state.getTaskFile();
157     AnswerPlaceholder answerPlaceholder = state.getAnswerPlaceholder();
158     DeleteAction action = new DeleteAction(answerPlaceholder, taskFile, state.getEditor());
159     new WriteCommandAction(project, "Delete Answer Placeholder") {
160       protected void run(@NotNull final Result result) throws Throwable {
161         action.redo();
162         UndoManager.getInstance(project).undoableActionPerformed(action);
163       }
164     }.execute();
165
166   }
167
168   @Override
169   public void update(@NotNull AnActionEvent event) {
170     final Presentation presentation = event.getPresentation();
171     presentation.setEnabledAndVisible(false);
172
173     CCState state = getState(event);
174     if (state == null) {
175       return;
176     }
177
178     presentation.setVisible(true);
179     if (canAddPlaceholder(state) || canDeletePlaceholder(state)) {
180       presentation.setEnabled(true);
181       presentation.setText((state.getAnswerPlaceholder() == null ? "Add " : "Delete ") + EduNames.ANSWER_PLACEHOLDER);
182     }
183   }
184
185
186   private static boolean canAddPlaceholder(@NotNull CCState state) {
187     Editor editor = state.getEditor();
188     SelectionModel selectionModel = editor.getSelectionModel();
189     if (selectionModel.hasSelection()) {
190       int start = selectionModel.getSelectionStart();
191       int end = selectionModel.getSelectionEnd();
192       return !arePlaceholdersIntersect(state.getTaskFile(), start, end);
193     }
194     int offset = editor.getCaretModel().getOffset();
195     return state.getTaskFile().getAnswerPlaceholder(offset) == null;
196   }
197
198   private static boolean canDeletePlaceholder(@NotNull CCState state) {
199     if (state.getEditor().getSelectionModel().hasSelection()) {
200       return false;
201     }
202     return state.getAnswerPlaceholder() != null;
203   }
204 }