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