2ad1d79faf1fb5776b10d5163992b878dbf8a6ee
[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.core.EduUtils;
20 import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder;
21 import com.jetbrains.edu.learning.courseFormat.TaskFile;
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/Delete 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.getAnswerPlaceholders();
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 static void addPlaceholder(@NotNull CCState state) {
47     Editor editor = state.getEditor();
48     Project project = state.getProject();
49     PsiFile file = state.getFile();
50
51     final Document document = PsiDocumentManager.getInstance(project).getDocument(file);
52     if (document == null) {
53       return;
54     }
55
56     final SelectionModel model = editor.getSelectionModel();
57     final int offset = model.getSelectionStart();
58     final AnswerPlaceholder answerPlaceholder = new AnswerPlaceholder();
59
60     answerPlaceholder.setOffset(offset);
61     answerPlaceholder.setUseLength(false);
62
63     String selectedText = model.getSelectedText();
64     answerPlaceholder.setPossibleAnswer(selectedText);
65
66     CCCreateAnswerPlaceholderDialog dlg = new CCCreateAnswerPlaceholderDialog(project, answerPlaceholder);
67     dlg.show();
68     if (dlg.getExitCode() != DialogWrapper.OK_EXIT_CODE) {
69       return;
70     }
71
72     TaskFile taskFile = state.getTaskFile();
73     int index = taskFile.getAnswerPlaceholders().size() + 1;
74     answerPlaceholder.setIndex(index);
75     taskFile.addAnswerPlaceholder(answerPlaceholder);
76     answerPlaceholder.setTaskFile(taskFile);
77     taskFile.sortAnswerPlaceholders();
78
79
80     computeInitialState(project, file, taskFile, document);
81
82     EduAnswerPlaceholderPainter.drawAnswerPlaceholder(editor, answerPlaceholder, JBColor.BLUE);
83     EduAnswerPlaceholderPainter.createGuardedBlocks(editor, answerPlaceholder);
84   }
85
86   private static void computeInitialState(Project project, PsiFile file, TaskFile taskFile, Document document) {
87     Document patternDocument = StudyUtils.getPatternDocument(taskFile, file.getName());
88     if (patternDocument == null) {
89       return;
90     }
91     DocumentUtil.writeInRunUndoTransparentAction(() -> {
92       patternDocument.replaceString(0, patternDocument.getTextLength(), document.getCharsSequence());
93       FileDocumentManager.getInstance().saveDocument(patternDocument);
94     });
95     TaskFile target = new TaskFile();
96     TaskFile.copy(taskFile, target);
97     List<AnswerPlaceholder> placeholders = target.getAnswerPlaceholders();
98     for (AnswerPlaceholder placeholder : placeholders) {
99       placeholder.setUseLength(false);
100     }
101     EduUtils.createStudentDocument(project, target, file.getVirtualFile(), patternDocument);
102
103     for (int i = 0; i < placeholders.size(); i++) {
104       AnswerPlaceholder fromPlaceholder = placeholders.get(i);
105       taskFile.getAnswerPlaceholders().get(i).setInitialState(new AnswerPlaceholder.MyInitialState(fromPlaceholder.getOffset(), fromPlaceholder.getLength()));
106     }
107   }
108
109   @Override
110   protected void performAnswerPlaceholderAction(@NotNull CCState state) {
111     if (canAddPlaceholder(state)) {
112       addPlaceholder(state);
113       return;
114     }
115     if (canDeletePlaceholder(state)) {
116       deletePlaceholder(state);
117     }
118   }
119
120   private static void deletePlaceholder(@NotNull CCState state) {
121     Project project = state.getProject();
122     PsiFile psiFile = state.getFile();
123     final Document document = PsiDocumentManager.getInstance(project).getDocument(psiFile);
124     if (document == null) return;
125     TaskFile taskFile = state.getTaskFile();
126     AnswerPlaceholder answerPlaceholder = state.getAnswerPlaceholder();
127     final List<AnswerPlaceholder> answerPlaceholders = taskFile.getAnswerPlaceholders();
128     if (answerPlaceholders.contains(answerPlaceholder)) {
129       answerPlaceholders.remove(answerPlaceholder);
130       final Editor editor = state.getEditor();
131       editor.getMarkupModel().removeAllHighlighters();
132       StudyUtils.drawAllWindows(editor, taskFile);
133       EduAnswerPlaceholderPainter.createGuardedBlocks(editor, taskFile);
134     }
135   }
136
137   @Override
138   public void update(@NotNull AnActionEvent event) {
139     final Presentation presentation = event.getPresentation();
140     presentation.setEnabledAndVisible(false);
141
142     CCState state = getState(event);
143     if (state == null) {
144       return;
145     }
146
147     presentation.setVisible(true);
148     if (canAddPlaceholder(state) || canDeletePlaceholder(state)) {
149       presentation.setEnabled(true);
150       presentation.setText((state.getAnswerPlaceholder() == null ? "Add " : "Delete ") + EduNames.PLACEHOLDER);
151     }
152   }
153
154
155   private static boolean canAddPlaceholder(@NotNull CCState state) {
156     Editor editor = state.getEditor();
157     SelectionModel selectionModel = editor.getSelectionModel();
158     if (!selectionModel.hasSelection()) {
159       return false;
160     }
161     int start = selectionModel.getSelectionStart();
162     int end = selectionModel.getSelectionEnd();
163     return !arePlaceholdersIntersect(state.getTaskFile(), start, end);
164   }
165
166   private static boolean canDeletePlaceholder(@NotNull CCState state) {
167     if (state.getEditor().getSelectionModel().hasSelection()) {
168       return false;
169     }
170     return state.getAnswerPlaceholder() != null;
171   }
172 }