1
2
3
4
5
6 using System;
7 using System.Collections;
8
9 namespace System.Windows
10 {
11 public abstract class EditCommand
12 {
13 public virtual default ~EditCommand();
14 [nodiscard]
15 public abstract Result<bool> Undo(TextView* textView);
16 [nodiscard]
17 public abstract Result<bool> Redo(TextView* textView);
18 }
19
20 public class EditCommandList
21 {
22 public explicit EditCommandList(TextView* textView_) : textView(textView_), currentIndex(0), inGroup(false)
23 {
24 }
25 [nodiscard]
26 public Result<bool> SetMenuItems(MenuItem* undoMenuItem_, MenuItem* redoMenuItem_)
27 {
28 undoMenuItem = undoMenuItem_;
29 redoMenuItem = redoMenuItem_;
30 return UpdateMenuItems();
31 }
32 public bool CanUndo() const
33 {
34 return currentIndex > 0;
35 }
36 public bool CanRedo() const
37 {
38 return currentIndex < commands.Count();
39 }
40 [nodiscard]
41 public Result<bool> UpdateMenuItems()
42 {
43 if (CanUndo())
44 {
45 auto result = undoMenuItem->Enable();
46 if (result.Error()) return result;
47 }
48 else
49 {
50 auto result = undoMenuItem->Disable();
51 if (result.Error()) return result;
52 }
53 if (CanRedo())
54 {
55 auto result = redoMenuItem->Enable();
56 if (result.Error()) return result;
57 }
58 else
59 {
60 auto result = redoMenuItem->Disable();
61 if (result.Error()) return result;
62 }
63 return Result<bool>(true);
64 }
65 [nodiscard]
66 public Result<bool> AddCommand(EditCommand* command)
67 {
68 if (inGroup)
69 {
70 groupCommand->AddCommand(command);
71 }
72 else
73 {
74 if (currentIndex >= commands.Count())
75 {
76 commands.Add(UniquePtr<EditCommand>(command));
77 currentIndex = cast<int>(commands.Count());
78 }
79 else
80 {
81 commands[currentIndex].Reset(command);
82 ++currentIndex;
83 }
84 commands.Resize(currentIndex);
85 auto result = UpdateMenuItems();
86 if (result.Error()) return result;
87 }
88 return Result<bool>(true);
89 }
90 [nodiscard]
91 public Result<bool> Undo()
92 {
93 if (CanUndo())
94 {
95 --currentIndex;
96 EditCommand* command = commands[currentIndex].Get();
97 auto result = command->Undo(textView);
98 if (result.Error()) return result;
99 }
100 auto result = UpdateMenuItems();
101 if (result.Error()) return result;
102 return Result<bool>(true);
103 }
104 [nodiscard]
105 public Result<bool> Redo()
106 {
107 if (CanRedo())
108 {
109 EditCommand* command = commands[currentIndex].Get();
110 auto result = command->Redo(textView);
111 if (result.Error()) return result;
112 ++currentIndex;
113 }
114 auto result = UpdateMenuItems();
115 if (result.Error()) return result;
116 return Result<bool>(true);
117 }
118 public void BeginGroup()
119 {
120 groupCommand.Reset(new GroupCommand());
121 inGroup = true;
122 }
123 [nodiscard]
124 public Result<bool> EndGroup()
125 {
126 inGroup = false;
127 return AddCommand(groupCommand.Release());
128 }
129 private TextView* textView;
130 private int currentIndex;
131 private List<UniquePtr<EditCommand>> commands;
132 private MenuItem* undoMenuItem;
133 private MenuItem* redoMenuItem;
134 private UniquePtr<GroupCommand> groupCommand;
135 private bool inGroup;
136 }
137
138 public class InsertCharCommand : EditCommand
139 {
140 public InsertCharCommand(int lineIndex_, int columnIndex_, uchar c_, bool removeIndent_) : lineIndex(lineIndex_), columnIndex(columnIndex_), c(c_), removeIndent(removeIndent_)
141 {
142 }
143 [nodiscard]
144 public override Result<bool> Undo(TextView* textView)
145 {
146 auto result = textView->DeleteChar(lineIndex, columnIndex, 0, 0, removeIndent);
147 if (result.Error()) return result;
148 return Result<bool>(true);
149 }
150 [nodiscard]
151 public override Result<bool> Redo(TextView* textView)
152 {
153 auto result = textView->InsertChar(lineIndex, columnIndex, c);
154 if (result.Error()) return result;
155 return Result<bool>(true);
156 }
157 private int lineIndex;
158 private int columnIndex;
159 private uchar c;
160 private bool removeIndent;
161 }
162
163 public class DeleteCharCommand : EditCommand
164 {
165 public DeleteCharCommand(int lineIndex_, int columnIndex_, uchar c_) : lineIndex(lineIndex_), columnIndex(columnIndex_), c(c_)
166 {
167 }
168 [nodiscard]
169 public override Result<bool> Undo(TextView* textView)
170 {
171 if (c != '\0')
172 {
173 auto result = textView->InsertChar(lineIndex, columnIndex, c);
174 if (result.Error()) return result;
175 }
176 else
177 {
178 auto result = textView->NewLine(lineIndex, columnIndex);
179 if (result.Error()) return result;
180 }
181 return Result<bool>(true);
182 }
183 [nodiscard]
184 public override Result<bool> Redo(TextView* textView)
185 {
186 auto result = textView->DeleteChar(lineIndex, columnIndex, 0, 0, false);
187 if (result.Error()) return result;
188 return Result<bool>(true);
189 }
190 private int lineIndex;
191 private int columnIndex;
192 private uchar c;
193 }
194
195 public class NewLineCommand : EditCommand
196 {
197 public NewLineCommand(int lineIndex_, int columnIndex_, int indent_, int numSpaces_) :
198 lineIndex(lineIndex_), columnIndex(columnIndex_), indent(indent_), numSpaces(numSpaces_)
199 {
200 }
201 [nodiscard]
202 public override Result<bool> Undo(TextView* textView)
203 {
204 auto result = textView->DeleteChar(lineIndex, columnIndex, indent, numSpaces, false);
205 if (result.Error()) return result;
206 return Result<bool>(true);
207 }
208 [nodiscard]
209 public override Result<bool> Redo(TextView* textView)
210 {
211 auto result = textView->NewLine(lineIndex, columnIndex);
212 if (result.Error()) return result;
213 return Result<bool>(true);
214 }
215 private int lineIndex;
216 private int columnIndex;
217 private int indent;
218 private int numSpaces;
219 }
220
221 public class InsertIntoLineCommand : EditCommand
222 {
223 public InsertIntoLineCommand(int lineIndex_, int columnIndex_, const ustring& text_) :
224 lineIndex(lineIndex_), columnIndex(columnIndex_), text(text_)
225 {
226 }
227 [nodiscard]
228 public override Result<bool> Undo(TextView* textView)
229 {
230 auto result = textView->RemoveFromLine(lineIndex, columnIndex, text.Length());
231 if (result.Error()) return result;
232 return Result<bool>(true);
233 }
234 [nodiscard]
235 public override Result<bool> Redo(TextView* textView)
236 {
237 auto result = textView->InsertIntoLine(lineIndex, columnIndex, text);
238 if (result.Error()) return result;
239 return Result<bool>(true);
240 }
241 private int lineIndex;
242 private int columnIndex;
243 private ustring text;
244 }
245
246 public class InsertLinesCommand : EditCommand
247 {
248 public InsertLinesCommand(int lineIndex_, int columnIndex_, const List<ustring>& lines_) :
249 lineIndex(lineIndex_), columnIndex(columnIndex_), lines(lines_)
250 {
251 }
252 [nodiscard]
253 public override Result<bool> Undo(TextView* textView)
254 {
255 auto result = textView->DeleteLines(lineIndex, columnIndex, lines);
256 if (result.Error()) return result;
257 return Result<bool>(true);
258 }
259 [nodiscard]
260 public override Result<bool> Redo(TextView* textView)
261 {
262 auto result = textView->InsertLines(lineIndex, columnIndex, lines);
263 if (result.Error()) return result;
264 return Result<bool>(true);
265 }
266 private int lineIndex;
267 private int columnIndex;
268 private List<ustring> lines;
269 }
270
271 public class RemoveSelectionCommand : EditCommand
272 {
273 public RemoveSelectionCommand(const Selection& selection_, const SelectionData& selectionData_, bool wholeLine_) :
274 selection(selection_), selectionData(selectionData_), wholeLine(wholeLine_)
275 {
276 }
277 [nodiscard]
278 public override Result<bool> Undo(TextView* textView)
279 {
280 auto result = textView->InsertSelection(selection, selectionData, wholeLine);
281 if (result.Error()) return result;
282 return Result<bool>(true);
283 }
284 [nodiscard]
285 public override Result<bool> Redo(TextView* textView)
286 {
287 auto result = textView->SetSelection(selection);
288 if (result.Error()) return result;
289 result = textView->RemoveSelection();
290 if (result.Error()) return result;
291 return Result<bool>(true);
292 }
293 private Selection selection;
294 private SelectionData selectionData;
295 private bool wholeLine;
296 }
297
298 public class TabCommand : EditCommand
299 {
300 public TabCommand(int lineIndex_, int columnIndex_) : lineIndex(lineIndex_), columnIndex(columnIndex_)
301 {
302 }
303 [nodiscard]
304 public override Result<bool> Undo(TextView* textView)
305 {
306 auto result = textView->Backtab(lineIndex, columnIndex + textView->IndentSize());
307 if (result.Error()) return result;
308 return Result<bool>(true);
309 }
310 [nodiscard]
311 public override Result<bool> Redo(TextView* textView)
312 {
313 auto result = textView->Tab(lineIndex, columnIndex);
314 if (result.Error()) return result;
315 return Result<bool>(true);
316 }
317 private int lineIndex;
318 private int columnIndex;
319 }
320
321 public class BacktabCommand : EditCommand
322 {
323 public BacktabCommand(int lineIndex_, int columnIndex_, int numSpaces_) : lineIndex(lineIndex_), columnIndex(columnIndex_), numSpaces(numSpaces_)
324 {
325 }
326 [nodiscard]
327 public override Result<bool> Undo(TextView* textView)
328 {
329 auto result = textView->AddSpaces(lineIndex, columnIndex, numSpaces);
330 if (result.Error()) return result;
331 return Result<bool>(true);
332 }
333 public override Result<bool> Redo(TextView* textView)
334 {
335 auto result = textView->RemoveSpaces(lineIndex, columnIndex, numSpaces);
336 if (result.Error()) return result;
337 return Result<bool>(true);
338 }
339 private int lineIndex;
340 private int columnIndex;
341 private int numSpaces;
342 }
343
344 public class IndentSelectionCommand : EditCommand
345 {
346 public IndentSelectionCommand(const Selection& selection_) : selection(selection_)
347 {
348 }
349 [nodiscard]
350 public override Result<bool> Undo(TextView* textView)
351 {
352 auto result = textView->SetSelection(selection);
353 if (result.Error()) return result;
354 result = textView->UnindentSelection();
355 if (result.Error()) return result;
356 return Result<bool>(true);
357 }
358 [nodiscard]
359 public override Result<bool> Redo(TextView* textView)
360 {
361 auto result = textView->SetSelection(selection);
362 if (result.Error()) return result;
363 result = textView->IndentSelection();
364 if (result.Error()) return result;
365 return Result<bool>(true);
366 }
367 private Selection selection;
368 }
369
370 public class UnindentSelectionCommand : EditCommand
371 {
372 public UnindentSelectionCommand(const Selection& selection_) : selection(selection_)
373 {
374 }
375 [nodiscard]
376 public override Result<bool> Undo(TextView* textView)
377 {
378 auto result = textView->SetSelection(selection);
379 if (result.Error()) return result;
380 result = textView->IndentSelection();
381 if (result.Error()) return result;
382 return Result<bool>(true);
383 }
384 [nodiscard]
385 public override Result<bool> Redo(TextView* textView)
386 {
387 auto result = textView->SetSelection(selection);
388 if (result.Error()) return result;
389 result = textView->UnindentSelection();
390 if (result.Error()) return result;
391 return Result<bool>(true);
392 }
393 private Selection selection;
394 }
395
396 public class GroupCommand : EditCommand
397 {
398 public GroupCommand()
399 {
400 }
401 public void AddCommand(EditCommand* command)
402 {
403 commands.Add(UniquePtr<EditCommand>(command));
404 }
405 [nodiscard]
406 public override Result<bool> Undo(TextView* textView)
407 {
408 long n = commands.Count();
409 for (long i = n - 1; i >= 0; --i;)
410 {
411 EditCommand* command = commands[i].Get();
412 auto result = command->Undo(textView);
413 if (result.Error()) return result;
414 }
415 return Result<bool>(true);
416 }
417 [nodiscard]
418 public override Result<bool> Redo(TextView* textView)
419 {
420 long n = commands.Count();
421 for (long i = 0; i < n; ++i;)
422 {
423 EditCommand* command = commands[i].Get();
424 auto result = command->Redo(textView);
425 if (result.Error()) return result;
426 }
427 return Result<bool>(true);
428 }
429 private List<UniquePtr<EditCommand>> commands;
430 }