1 // =================================
  2 // Copyright (c) 2021 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         public abstract nothrow void Undo(TextView* textView);
 15         public abstract nothrow void Redo(TextView* textView);
 16     }
 17 
 18     public class EditCommandList
 19     {
 20         public nothrow EditCommandList(TextView* textView_) : textView(textView_)currentIndex(0)inGroup(false)
 21         {
 22         }
 23         public nothrow void SetMenuItems(MenuItem* undoMenuItem_MenuItem* redoMenuItem_)
 24         {
 25             undoMenuItem = undoMenuItem_;
 26             redoMenuItem = redoMenuItem_;
 27             UpdateMenuItems();
 28         }
 29         public nothrow bool CanUndo() const
 30         {
 31             return currentIndex > 0;
 32         }
 33         public nothrow bool CanRedo() const
 34         {
 35             return currentIndex < commands.Count();
 36         }
 37         public nothrow void UpdateMenuItems()
 38         {
 39             if (CanUndo())
 40             {
 41                 undoMenuItem->Enable();
 42             }
 43             else
 44             {
 45                 undoMenuItem->Disable();
 46             }
 47             if (CanRedo())
 48             {
 49                 redoMenuItem->Enable();
 50             }
 51             else
 52             {
 53                 redoMenuItem->Disable();
 54             }
 55         }
 56         public nothrow void AddCommand(EditCommand* command)
 57         {
 58             if (inGroup)
 59             {
 60                 groupCommand->AddCommand(command);
 61             }
 62             else
 63             {
 64                 if (currentIndex >= commands.Count())
 65                 {
 66                     commands.Add(UniquePtr<EditCommand>(command));
 67                     currentIndex = cast<int>(commands.Count());
 68                 }
 69                 else
 70                 {
 71                     commands[currentIndex].Reset(command);
 72                     ++currentIndex;
 73                 }
 74                 commands.Resize(currentIndex);
 75                 UpdateMenuItems();
 76             }
 77         }
 78         public nothrow void Undo()
 79         {
 80             if (CanUndo())
 81             {
 82                 --currentIndex;
 83                 EditCommand* command = commands[currentIndex].Get();
 84                 command->Undo(textView);
 85             }
 86             UpdateMenuItems();
 87         }
 88         public nothrow void Redo()
 89         {
 90             if (CanRedo())
 91             {
 92                 EditCommand* command = commands[currentIndex].Get();
 93                 command->Redo(textView);
 94                 ++currentIndex;
 95             }
 96             UpdateMenuItems();
 97         }
 98         public nothrow void BeginGroup()
 99         {
100             groupCommand.Reset(new GroupCommand());
101             inGroup = true;
102         }
103         public nothrow void EndGroup()
104         {
105             inGroup = false;
106             AddCommand(groupCommand.Release());
107         }
108         private TextView* textView;
109         private int currentIndex;
110         private List<UniquePtr<EditCommand>> commands;
111         private MenuItem* undoMenuItem;
112         private MenuItem* redoMenuItem;
113         private UniquePtr<GroupCommand> groupCommand;
114         private bool inGroup;
115     }
116 
117     public class InsertCharCommand : EditCommand
118     {
119         public nothrow InsertCharCommand(int lineIndex_int columnIndex_uchar c_bool removeIndent_) : lineIndex(lineIndex_)columnIndex(columnIndex_)c(c_)removeIndent(removeIndent_)
120         {
121         }
122         public override nothrow void Undo(TextView* textView)
123         {
124             textView->DeleteChar(lineIndexcolumnIndex00removeIndent);
125         }
126         public override nothrow void Redo(TextView* textView)
127         {
128             textView->InsertChar(lineIndexcolumnIndexc);
129         }
130         private int lineIndex;
131         private int columnIndex;
132         private uchar c;
133         private bool removeIndent;
134     }
135 
136     public class DeleteCharCommand : EditCommand
137     {
138         public nothrow DeleteCharCommand(int lineIndex_int columnIndex_uchar c_) : lineIndex(lineIndex_)columnIndex(columnIndex_)c(c_)
139         {
140         }
141         public override nothrow void Undo(TextView* textView)
142         {
143             if (c != '\0')
144             {
145                 textView->InsertChar(lineIndexcolumnIndexc);
146             }
147             else
148             {
149                 textView->NewLine(lineIndexcolumnIndex);
150             }
151         }
152         public override nothrow void Redo(TextView* textView)
153         {
154             textView->DeleteChar(lineIndexcolumnIndex00false);
155         }
156         private int lineIndex;
157         private int columnIndex;
158         private uchar c;
159     }
160 
161     public class NewLineCommand : EditCommand
162     {
163         public nothrow NewLineCommand(int lineIndex_int columnIndex_int indent_int numSpaces_) : 
164             lineIndex(lineIndex_)columnIndex(columnIndex_)indent(indent_)numSpaces(numSpaces_)
165         {
166         }
167         public override nothrow void Undo(TextView* textView)
168         {
169             textView->DeleteChar(lineIndexcolumnIndexindentnumSpacesfalse);
170         }
171         public override nothrow void Redo(TextView* textView)
172         {
173             textView->NewLine(lineIndexcolumnIndex);
174         }
175         private int lineIndex;
176         private int columnIndex;
177         private int indent;
178         private int numSpaces;
179     }
180 
181     public class InsertIntoLineCommand : EditCommand
182     {
183         public nothrow InsertIntoLineCommand(int lineIndex_int columnIndex_const ustring& text_) : 
184             lineIndex(lineIndex_)columnIndex(columnIndex_)text(text_)
185         {
186         }
187         public override nothrow void Undo(TextView* textView)
188         {
189             textView->RemoveFromLine(lineIndexcolumnIndextext.Length());
190         }
191         public override nothrow void Redo(TextView* textView)
192         {
193             textView->InsertIntoLine(lineIndexcolumnIndextext);
194         }
195         private int lineIndex;
196         private int columnIndex;
197         private ustring text;
198     }
199     
200     public class InsertLinesCommand : EditCommand
201     {
202         public nothrow InsertLinesCommand(int lineIndex_int columnIndex_const List<ustring>& lines_) : 
203             lineIndex(lineIndex_)columnIndex(columnIndex_)lines(lines_)
204         {
205         }
206         public override nothrow void Undo(TextView* textView)
207         {
208             textView->DeleteLines(lineIndexcolumnIndexlines);
209         }
210         public override nothrow void Redo(TextView* textView)
211         {
212             textView->InsertLines(lineIndexcolumnIndexlines);
213         }
214         private int lineIndex;
215         private int columnIndex;
216         private List<ustring> lines;
217     }
218 
219     public class RemoveSelectionCommand : EditCommand
220     {
221         public nothrow RemoveSelectionCommand(const Selection& selection_const SelectionData& selectionData_bool wholeLine_) : 
222             selection(selection_)selectionData(selectionData_)wholeLine(wholeLine_)
223         {
224         }
225         public override nothrow void Undo(TextView* textView)
226         {
227             textView->InsertSelection(selectionselectionDatawholeLine);
228         }
229         public override nothrow void Redo(TextView* textView)
230         {
231             textView->SetSelection(selection);
232             textView->RemoveSelection();
233         }
234         private Selection selection;
235         private SelectionData selectionData;
236         private bool wholeLine;
237     }
238 
239     public class TabCommand : EditCommand
240     {
241         public nothrow TabCommand(int lineIndex_int columnIndex_) : lineIndex(lineIndex_)columnIndex(columnIndex_)
242         {
243         }
244         public override nothrow void Undo(TextView* textView)
245         {
246             textView->Backtab(lineIndexcolumnIndex + textView->IndentSize());
247         }
248         public override nothrow void Redo(TextView* textView)
249         {
250             textView->Tab(lineIndexcolumnIndex);
251         }
252         private int lineIndex;
253         private int columnIndex;
254     }
255 
256     public class BacktabCommand : EditCommand
257     {
258         public nothrow BacktabCommand(int lineIndex_int columnIndex_int numSpaces_) : lineIndex(lineIndex_)columnIndex(columnIndex_)numSpaces(numSpaces_)
259         {
260         }
261         public override nothrow void Undo(TextView* textView)
262         {
263             textView->AddSpaces(lineIndexcolumnIndexnumSpaces);
264         }
265         public override nothrow void Redo(TextView* textView)
266         {
267             textView->RemoveSpaces(lineIndexcolumnIndexnumSpaces);
268         }
269         private int lineIndex;
270         private int columnIndex;
271         private int numSpaces;
272     }
273 
274     public class IndentSelectionCommand : EditCommand
275     {
276         public nothrow IndentSelectionCommand(const Selection& selection_) : selection(selection_)
277         {
278         }
279         public override nothrow void Undo(TextView* textView)
280         {
281             textView->SetSelection(selection);
282             textView->UnindentSelection();
283         }
284         public override nothrow void Redo(TextView* textView)
285         {
286             textView->SetSelection(selection);
287             textView->IndentSelection();
288         }
289         private Selection selection;
290     }
291 
292     public class UnindentSelectionCommand : EditCommand
293     {
294         public UnindentSelectionCommand(const Selection& selection_) : selection(selection_)
295         {
296         }
297         public override nothrow void Undo(TextView* textView)
298         {
299             textView->SetSelection(selection);
300             textView->IndentSelection();
301         }
302         public override nothrow void Redo(TextView* textView)
303         {
304             textView->SetSelection(selection);
305             textView->UnindentSelection();
306         }
307         private Selection selection;
308     }
309 
310     public class GroupCommand : EditCommand
311     {
312         public GroupCommand()
313         {
314         }
315         public nothrow void AddCommand(EditCommand* command)
316         {
317             commands.Add(UniquePtr<EditCommand>(command));
318         }
319         public override nothrow void Undo(TextView* textView)
320         {
321             long n = commands.Count();
322             for (long i = n - 1; i >= 0; --i;)
323             {
324                 EditCommand* command = commands[i].Get();
325                 command->Undo(textView);
326             }
327         }
328         public override nothrow void Redo(TextView* textView)
329         {
330             long n = commands.Count();
331             for (long i = 0; i < n; ++i;)
332             {
333                 EditCommand* command = commands[i].Get();
334                 command->Redo(textView);
335             }
336         }
337         private List<UniquePtr<EditCommand>> commands;
338     }
339 }