1 // =================================
  2 // Copyright (c) 2024 Seppo Laakko
  3 // Distributed under the MIT license
  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(lineIndexcolumnIndex00removeIndent);
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(lineIndexcolumnIndexc);
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(lineIndexcolumnIndexc);
174                 if (result.Error()) return result;
175             }
176             else
177             {
178                 auto result = textView->NewLine(lineIndexcolumnIndex);
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(lineIndexcolumnIndex00false);
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(lineIndexcolumnIndexindentnumSpacesfalse);
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(lineIndexcolumnIndex);
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(lineIndexcolumnIndextext.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(lineIndexcolumnIndextext);
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(lineIndexcolumnIndexlines);
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(lineIndexcolumnIndexlines);
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(selectionselectionDatawholeLine);
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(lineIndexcolumnIndex + 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(lineIndexcolumnIndex);
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(lineIndexcolumnIndexnumSpaces);
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(lineIndexcolumnIndexnumSpaces);
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     }