fix preview for placeholders invisible on prev subtask
[idea/community.git] / python / educational-core / student / src / com / jetbrains / edu / learning / courseFormat / AnswerPlaceholder.java
1 package com.jetbrains.edu.learning.courseFormat;
2
3 import com.google.gson.annotations.Expose;
4 import com.google.gson.annotations.SerializedName;
5 import com.intellij.util.containers.ContainerUtil;
6 import com.intellij.util.containers.hash.HashMap;
7 import com.intellij.util.xmlb.annotations.Transient;
8 import org.jetbrains.annotations.NotNull;
9
10 import java.util.Collections;
11 import java.util.List;
12 import java.util.Map;
13
14 /**
15  * Implementation of windows which user should type in
16  */
17
18 public class AnswerPlaceholder {
19
20   @SerializedName("offset")
21   @Expose private int myOffset = -1;
22
23   @Expose private int length = -1;
24
25   private int myIndex = -1;
26   private MyInitialState myInitialState;
27   private StudyStatus myStatus = StudyStatus.Unchecked;
28   private boolean mySelected = false;
29   private boolean myUseLength = true;
30
31   @Transient private TaskFile myTaskFile;
32
33   @Expose private Map<Integer, AnswerPlaceholderSubtaskInfo> mySubtaskInfos = new HashMap<>();
34   public AnswerPlaceholder() {
35   }
36
37   public void initAnswerPlaceholder(final TaskFile file, boolean isRestarted) {
38     if (!isRestarted) {
39       setInitialState(new MyInitialState(myOffset, length));
40       myStatus = file.getTask().getStatus();
41     }
42
43     setTaskFile(file);
44   }
45
46   public int getIndex() {
47     return myIndex;
48   }
49
50   public void setIndex(int index) {
51     myIndex = index;
52   }
53
54   /**
55    * in actions {@link AnswerPlaceholder#getRealLength()} should be used
56    */
57   public int getLength() {
58     return length;
59   }
60
61   public void setLength(int length) {
62     this.length = length;
63   }
64
65   @Transient
66   public String getPossibleAnswer() {
67     return getActiveSubtaskInfo().getPossibleAnswer();
68   }
69
70   @Transient
71   public void setPossibleAnswer(String possibleAnswer) {
72     getActiveSubtaskInfo().setPossibleAnswer(possibleAnswer);
73   }
74
75   public MyInitialState getInitialState() {
76     return myInitialState;
77   }
78
79   public void setInitialState(MyInitialState initialState) {
80     myInitialState = initialState;
81   }
82
83   @Transient
84   public String getTaskText() {
85     return getActiveSubtaskInfo().getPlaceholderText();
86   }
87
88   @Transient
89   public void setTaskText(String taskText) {
90     getActiveSubtaskInfo().setPlaceholderText(taskText);
91   }
92
93   @Transient
94   public TaskFile getTaskFile() {
95     return myTaskFile;
96   }
97
98   @Transient
99   public void setTaskFile(TaskFile taskFile) {
100     myTaskFile = taskFile;
101   }
102
103   public int getPossibleAnswerLength() {
104     return getPossibleAnswer().length();
105   }
106
107   /**
108    * Returns window to its initial state
109    */
110   public void reset() {
111     myOffset = myInitialState.getOffset();
112     length = myInitialState.getLength();
113   }
114
115   public StudyStatus getStatus() {
116     return myStatus;
117   }
118
119   public void setStatus(StudyStatus status) {
120     myStatus = status;
121   }
122
123   public boolean getSelected() {
124     return mySelected;
125   }
126
127   public void setSelected(boolean selected) {
128     mySelected = selected;
129   }
130
131   public void init() {
132     setInitialState(new MyInitialState(myOffset, getTaskText().length()));
133   }
134
135   public boolean getUseLength() {
136     return myUseLength;
137   }
138
139   /**
140    * @return length or possible answer length
141    */
142   public int getRealLength() {
143     return myUseLength ? getLength() : getVisibleLength(getActiveSubtaskIndex());
144   }
145
146   public void setUseLength(boolean useLength) {
147     myUseLength = useLength;
148   }
149
150   public int getOffset() {
151     return myOffset;
152   }
153
154   public void setOffset(int offset) {
155     myOffset = offset;
156   }
157
158   @Transient
159   public List<String> getHints() {
160     return getActiveSubtaskInfo().getHints();
161   }
162
163   @Transient
164   public void setHints(@NotNull final List<String> hints) {
165    getActiveSubtaskInfo().setHints(hints);
166   }
167
168   public void addHint(@NotNull final String text) {
169     getActiveSubtaskInfo().addHint(text);
170   }
171
172   public void removeHint(int i) {
173     getActiveSubtaskInfo().removeHint(i);
174   }
175
176   public Map<Integer, AnswerPlaceholderSubtaskInfo> getSubtaskInfos() {
177     return mySubtaskInfos;
178   }
179
180   public void setSubtaskInfos(Map<Integer, AnswerPlaceholderSubtaskInfo> subtaskInfos) {
181     mySubtaskInfos = subtaskInfos;
182   }
183
184   public boolean isActive() {
185     return getActiveSubtaskInfo() != null;
186   }
187
188   public static class MyInitialState {
189     private int length = -1;
190     private int offset = -1;
191
192     public MyInitialState() {
193     }
194
195     public MyInitialState(int initialOffset, int length) {
196       this.offset = initialOffset;
197       this.length = length;
198     }
199
200     public int getLength() {
201       return length;
202     }
203
204     public void setLength(int length) {
205       this.length = length;
206     }
207
208     public int getOffset() {
209       return offset;
210     }
211
212     public void setOffset(int offset) {
213       this.offset = offset;
214     }
215   }
216
217   public AnswerPlaceholderSubtaskInfo getActiveSubtaskInfo() {
218     return mySubtaskInfos.get(getActiveSubtaskIndex());
219   }
220
221   public int getActiveSubtaskIndex() {
222     if (myTaskFile == null || myTaskFile.getTask() == null) {
223       return 0;
224     }
225     return myTaskFile.getTask().getActiveSubtaskIndex();
226   }
227
228   public int getVisibleLength(int subtaskIndex) {
229     int minIndex = Collections.min(mySubtaskInfos.keySet());
230     AnswerPlaceholderSubtaskInfo minInfo = mySubtaskInfos.get(minIndex);
231     if (minIndex == subtaskIndex) {
232       return getUseLength() ? length : minInfo.getPossibleAnswer().length();
233     }
234     if (minIndex > subtaskIndex) {
235       return minInfo.isNeedInsertText() ? 0 : minInfo.getPlaceholderText().length();
236     }
237     int maxIndex = Collections.max(ContainerUtil.filter(mySubtaskInfos.keySet(), i -> i < subtaskIndex));
238     return getUseLength() ? length : mySubtaskInfos.get(maxIndex).getPossibleAnswer().length();
239   }
240 }