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