0643eaa8cf0cb66d6dd1bf5d7ddf9455968ce22f
[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(project, 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       AnswerPlaceholder.MyInitialState state = fromPlaceholder.getInitialState();
106       taskFile.getAnswerPlaceholders().get(i).setInitialState(new AnswerPlaceholder.MyInitialState(state.getOffset(), state.getLength()));
107     }
108   }
109
110   @Override
111   protected void performAnswerPlaceholderAction(@NotNull CCState state) {
112     if (canAddPlaceholder(state)) {
113       addPlaceholder(state);
114       return;
115     }
116     if (canDeletePlaceholder(state)) {
117       deletePlaceholder(state);
118     }
119   }
120
121   private static void deletePlaceholder(@NotNull CCState state) {
122     Project project = state.getProject();
123     PsiFile psiFile = state.getFile();
124     final Document document = PsiDocumentManager.getInstance(project).getDocument(psiFile);
125     if (document == null) return;
126     TaskFile taskFile = state.getTaskFile();
127     AnswerPlaceholder answerPlaceholder = state.getAnswerPlaceholder();
128     final List<AnswerPlaceholder> answerPlaceholders = taskFile.getAnswerPlaceholders();
129     if (answerPlaceholders.contains(answerPlaceholder)) {
130       answerPlaceholders.remove(answerPlaceholder);
131       final Editor editor = state.getEditor();
132       editor.getMarkupModel().removeAllHighlighters();
133       StudyUtils.drawAllWindows(editor, taskFile);
134       EduAnswerPlaceholderPainter.createGuardedBlocks(editor, taskFile);
135     }
136   }
137
138   @Override
139   public void update(@NotNull AnActionEvent event) {
140     final Presentation presentation = event.getPresentation();
141     presentation.setEnabledAndVisible(false);
142
143     CCState state = getState(event);
144     if (state == null) {
145       return;
146     }
147
148     presentation.setVisible(true);
149     if (canAddPlaceholder(state) || canDeletePlaceholder(state)) {
150       presentation.setEnabled(true);
151       presentation.setText((state.getAnswerPlaceholder() == null ? "Add " : "Delete ") + EduNames.PLACEHOLDER);
152     }
153   }
154
155
156   private static boolean canAddPlaceholder(@NotNull CCState state) {
157     Editor editor = state.getEditor();
158     SelectionModel selectionModel = editor.getSelectionModel();
159     if (!selectionModel.hasSelection()) {
160       return false;
161     }
162     int start = selectionModel.getSelectionStart();
163     int end = selectionModel.getSelectionEnd();
164     return !arePlaceholdersIntersect(state.getTaskFile(), start, end);
165   }
166
167   private static boolean canDeletePlaceholder(@NotNull CCState state) {
168     if (state.getEditor().getSelectionModel().hasSelection()) {
169       return false;
170     }
171     return state.getAnswerPlaceholder() != null;
172   }
173 }