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