0be876ae46e19d5c8496708df3556fa3e8df70d1
[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.editor.Document;
6 import com.intellij.openapi.editor.Editor;
7 import com.intellij.openapi.editor.SelectionModel;
8 import com.intellij.openapi.fileEditor.FileDocumentManager;
9 import com.intellij.openapi.project.Project;
10 import com.intellij.openapi.ui.DialogWrapper;
11 import com.intellij.psi.PsiDocumentManager;
12 import com.intellij.psi.PsiFile;
13 import com.intellij.ui.JBColor;
14 import com.intellij.util.DocumentUtil;
15 import com.jetbrains.edu.coursecreator.ui.CCCreateAnswerPlaceholderDialog;
16 import com.jetbrains.edu.learning.StudyUtils;
17 import com.jetbrains.edu.learning.core.EduAnswerPlaceholderPainter;
18 import com.jetbrains.edu.learning.core.EduNames;
19 import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder;
20 import com.jetbrains.edu.learning.courseFormat.TaskFile;
21 import org.jetbrains.annotations.NotNull;
22
23 import java.util.List;
24
25 public class CCAddAnswerPlaceholder extends CCAnswerPlaceholderAction {
26
27   public CCAddAnswerPlaceholder() {
28     super("Add/Delete Answer Placeholder", "Add/Delete answer placeholder");
29   }
30
31
32   private static boolean arePlaceholdersIntersect(@NotNull final TaskFile taskFile, int start, int end) {
33     List<AnswerPlaceholder> answerPlaceholders = taskFile.getAnswerPlaceholders();
34     for (AnswerPlaceholder existingAnswerPlaceholder : answerPlaceholders) {
35       int twStart = existingAnswerPlaceholder.getOffset();
36       int twEnd = existingAnswerPlaceholder.getPossibleAnswerLength() + twStart;
37       if ((start >= twStart && start < twEnd) || (end > twStart && end <= twEnd) ||
38           (twStart >= start && twStart < end) || (twEnd > start && twEnd <= end)) {
39         return true;
40       }
41     }
42     return false;
43   }
44
45   private static void addPlaceholder(@NotNull CCState state) {
46     Editor editor = state.getEditor();
47     Project project = state.getProject();
48     PsiFile file = state.getFile();
49
50     final Document document = PsiDocumentManager.getInstance(project).getDocument(file);
51     if (document == null) {
52       return;
53     }
54     FileDocumentManager.getInstance().saveDocument(document);
55     final SelectionModel model = editor.getSelectionModel();
56     final int offset = model.hasSelection() ? model.getSelectionStart() : editor.getCaretModel().getOffset();
57     final AnswerPlaceholder answerPlaceholder = new AnswerPlaceholder();
58
59     answerPlaceholder.setOffset(offset);
60     answerPlaceholder.setUseLength(false);
61
62     String defaultPlaceholderText = "type here";
63     answerPlaceholder.setPossibleAnswer(model.hasSelection() ? model.getSelectedText() : defaultPlaceholderText);
64
65     CCCreateAnswerPlaceholderDialog dlg = new CCCreateAnswerPlaceholderDialog(project, answerPlaceholder);
66     dlg.show();
67     if (dlg.getExitCode() != DialogWrapper.OK_EXIT_CODE) {
68       return;
69     }
70
71     if (!model.hasSelection()) {
72       DocumentUtil.writeInRunUndoTransparentAction(() -> document.insertString(offset, defaultPlaceholderText));
73     }
74
75     TaskFile taskFile = state.getTaskFile();
76     int index = taskFile.getAnswerPlaceholders().size();
77     answerPlaceholder.setIndex(index);
78     taskFile.addAnswerPlaceholder(answerPlaceholder);
79     answerPlaceholder.setTaskFile(taskFile);
80     taskFile.sortAnswerPlaceholders();
81
82     answerPlaceholder.setPossibleAnswer(model.hasSelection() ? model.getSelectedText() : defaultPlaceholderText);
83     EduAnswerPlaceholderPainter.drawAnswerPlaceholder(editor, answerPlaceholder, JBColor.BLUE);
84     EduAnswerPlaceholderPainter.createGuardedBlocks(editor, answerPlaceholder);
85   }
86
87   @Override
88   protected void performAnswerPlaceholderAction(@NotNull CCState state) {
89     if (canAddPlaceholder(state)) {
90       addPlaceholder(state);
91       return;
92     }
93     if (canDeletePlaceholder(state)) {
94       deletePlaceholder(state);
95     }
96   }
97
98   private static void deletePlaceholder(@NotNull CCState state) {
99     Project project = state.getProject();
100     PsiFile psiFile = state.getFile();
101     final Document document = PsiDocumentManager.getInstance(project).getDocument(psiFile);
102     if (document == null) return;
103     TaskFile taskFile = state.getTaskFile();
104     AnswerPlaceholder answerPlaceholder = state.getAnswerPlaceholder();
105     final List<AnswerPlaceholder> answerPlaceholders = taskFile.getAnswerPlaceholders();
106     if (answerPlaceholders.contains(answerPlaceholder)) {
107       answerPlaceholders.remove(answerPlaceholder);
108       final Editor editor = state.getEditor();
109       editor.getMarkupModel().removeAllHighlighters();
110       StudyUtils.drawAllWindows(editor, taskFile);
111       EduAnswerPlaceholderPainter.createGuardedBlocks(editor, taskFile);
112     }
113   }
114
115   @Override
116   public void update(@NotNull AnActionEvent event) {
117     final Presentation presentation = event.getPresentation();
118     presentation.setEnabledAndVisible(false);
119
120     CCState state = getState(event);
121     if (state == null) {
122       return;
123     }
124
125     presentation.setVisible(true);
126     if (canAddPlaceholder(state) || canDeletePlaceholder(state)) {
127       presentation.setEnabled(true);
128       presentation.setText((state.getAnswerPlaceholder() == null ? "Add " : "Delete ") + EduNames.ANSWER_PLACEHOLDER);
129     }
130   }
131
132
133   private static boolean canAddPlaceholder(@NotNull CCState state) {
134     Editor editor = state.getEditor();
135     SelectionModel selectionModel = editor.getSelectionModel();
136     if (selectionModel.hasSelection()) {
137       int start = selectionModel.getSelectionStart();
138       int end = selectionModel.getSelectionEnd();
139       return !arePlaceholdersIntersect(state.getTaskFile(), start, end);
140     }
141     int offset = editor.getCaretModel().getOffset();
142     return state.getTaskFile().getAnswerPlaceholder(offset) == null;
143   }
144
145   private static boolean canDeletePlaceholder(@NotNull CCState state) {
146     if (state.getEditor().getSelectionModel().hasSelection()) {
147       return false;
148     }
149     return state.getAnswerPlaceholder() != null;
150   }
151 }