Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Command.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#ifndef _Stroika_Framework_Led_Command_h_
5#define _Stroika_Framework_Led_Command_h_ 1
6
7#include "Stroika/Frameworks/StroikaPreComp.h"
8
10#include "Stroika/Frameworks/Led/Support.h"
11
12/*
13@MODULE: Command
14@DESCRIPTION:
15 <p>Module for supporting UNDO. @'Command' objects encapsulate a sequence of operations the user has
16 done, and the information needed to undo/redo them. These objects are organized, and kept track of by
17 a @'CommandHandler' object. Two sorts of @'CommandHandler's are provided, @'SingleUndoCommandHandler', and
18 @'MultiLevelUndoCommandHandler' - depending on whether you want to support multilevel undo or not.</p>
19 */
20
21namespace Stroika::Frameworks::Led {
22
24
25 class TextInteractor;
26
27#if qStroika_Frameworks_Led_SupportGDI
28 /*
29 @CLASS: Command
30 @DESCRIPTION: <p>An abstraction for a user action, which is undoable. These objects, when created,
31 are posted to a @'CommandHandler' via @'CommandHandler::Post' ().</p>
32 <p>It is only legal to call Do when Not GetDone(), and only first time and before any of Do/Undo/Redo
33 called and can only call Undo when GetDone() is true, and only call ReDo when GetDone () is false.</p>
34 <p>Must call inherited Command::Do() etc methods to get fDone flag set properly.</p>
35 */
36 class Command {
37 public:
38 Command (bool done = false);
39 virtual ~Command () = default;
40
41 public:
42 virtual void Do (TextInteractor& interactor);
43 virtual void UnDo (TextInteractor& interactor);
44 virtual void ReDo (TextInteractor& interactor);
45
46 public:
47 virtual bool UpdateSimpleTextInsert (size_t insertAt, Led_tChar c);
48
49 public:
50 virtual const SDKChar* GetName () const;
51
52 public:
53 nonvirtual bool GetDone () const; // true if DONE or REDONE
54
55 private:
56 bool fDone;
57 };
58
59 /*
60 @CLASS: CommandHandler
61 @DESCRIPTION: <p>This is the guy you post new @'Command's to, and ask for the text of the
62 undo message, etc. You call undo/redo on him to get actual undo/redo etc to
63 happen. It is responsable for committing commands, and disposing of them.
64 Typically, you would have one of this in your TextInteractor to handle undo.</p>
65 */
66 class CommandHandler {
67 public:
68 CommandHandler () = default;
69
70 public:
71 /*
72 @METHOD: CommandHandler::Post
73 @DESCRIPTION: <p>Notify the CommandHandler of a newly created (and done) @'Command'. This command object is now
74 owned by the CommandHandler. And it will call delete on it, when it is through with the object. The lifetime
75 of this object is totally under the control of the CommandHandler. It can be deleted at any time. No assumptions should
76 be made about this.</p>
77 */
78 virtual void Post (Command* newCommand) = 0;
79 /*
80 @METHOD: CommandHandler::PostUpdateSimpleTextInsert
81 @DESCRIPTION: <p>(prelim docs)Part of optimized undo-info for strings of basic text inserts. Returns true if OK.</p>
82 */
83 virtual bool PostUpdateSimpleTextInsert (size_t insertAt, Led_tChar c);
84 /*
85 @METHOD: CommandHandler::BreakInGroupedCommands
86 @DESCRIPTION: <p>Some commands are plausibly chunked together. These are mostly tying, insert-character commands.
87 But, in principle, we could bundle together other commands as well. Call this method to signify that previous
88 commands should <em>not</em> be chunked together with commads about to be posted (@'CommandHandler::Post').</p>
89 */
90 virtual void BreakInGroupedCommands () = 0;
91 /*
92 @METHOD: CommandHandler::BreakInGroupedCommandsIfDifferentCommand
93 @DESCRIPTION: <p>Like @'CommandHandler::BreakInGroupedCommands', except that it only calls BreakInGroupedCommands
94 if the given command name is different than the previous command posted (@'CommandHandler::Post').</p>
95 */
96 virtual void BreakInGroupedCommandsIfDifferentCommand (const SDKString& cmdName) = 0;
97 /*
98 @METHOD: CommandHandler::DoUndo
99 @DESCRIPTION: <p>Perform a single undo action. This will undo all the command objects in the current group of commands
100 (or all, depending on the subtype of this CommandHandler; see @'SingleUndoCommandHandler' and @'MultiLevelUndoCommandHandler'
101 for details).</p>
102 */
103 virtual void DoUndo (TextInteractor& interactor) = 0;
104 /*
105 @METHOD: CommandHandler::DoRedo
106 @DESCRIPTION: <p>Redo the last undone action. See @'CommandHandler::DoUndo' () for more details.</p>
107 */
108 virtual void DoRedo (TextInteractor& interactor) = 0;
109 /*
110 @METHOD: CommandHandler::Commit
111 @DESCRIPTION: <p>Commit all currently done actions. This means no further undo will be possible (deletes the
112 currently done/redone objects). This can be called when low on memory, to retrive some. Or when some
113 logical user-action happens which would prevent further undos (save?).</p>
114 */
115 virtual void Commit () = 0; // Commit all commands currently owned - something has happened to invalidate them...
116 /*
117 @METHOD: CommandHandler::CanUndo
118 @DESCRIPTION: <p>Are there commands posted which can be undone?</p>
119 */
120 virtual bool CanUndo () = 0;
121 /*
122 @METHOD: CommandHandler::CanRedo
123 @DESCRIPTION: <p>Are there commands which have been undone, which can now be redone?</p>
124 */
125 virtual bool CanRedo () = 0;
126 /*
127 @METHOD: CommandHandler::GetUndoRedoWhatMessageText
128 @DESCRIPTION: <p>Retreives the text associated with each command to denote in a UI what command is being undone,
129 or redone.</p>
130 */
131 virtual size_t GetUndoRedoWhatMessageText (char* buf, size_t bufSize); // return nbytes - not NUL term string...
132 virtual const SDKChar* GetUndoCmdName () = 0; // don't free result - we keep
133 virtual const SDKChar* GetRedoCmdName () = 0; // don't free result - we keep
134 };
135
136 /*
137 @CLASS: SingleUndoCommandHandler
138 @BASES: @'CommandHandler'
139 @DESCRIPTION:
140 <p>CommandHandler which only allows for undo the last command.</p>
141 */
142 class SingleUndoCommandHandler : public CommandHandler {
143 public:
144 SingleUndoCommandHandler ();
145
146 public:
147 virtual void Post (Command* newCommand) override;
148 virtual bool PostUpdateSimpleTextInsert (size_t insertAt, Led_tChar c) override;
149 virtual void BreakInGroupedCommands () override;
150 virtual void BreakInGroupedCommandsIfDifferentCommand (const SDKString& cmdName) override;
151 virtual void DoUndo (TextInteractor& interactor) override;
152 virtual void DoRedo (TextInteractor& interactor) override;
153 virtual void Commit () override;
154 virtual bool CanUndo () override;
155 virtual bool CanRedo () override;
156 virtual const SDKChar* GetUndoCmdName () override;
157 virtual const SDKChar* GetRedoCmdName () override;
158
159 virtual bool GetDone () const;
160
161 // only support one cmd
162 private:
163 Command* fLastCmd;
164#if qStroika_Foundation_Debug_AssertionsChecked
165 bool fDoingCommands; // avoid common bug of posting new commands during execution of a command
166#endif
167 };
168
169 /*
170 @CLASS: MultiLevelUndoCommandHandler
171 @BASES: @'CommandHandler'
172 @DESCRIPTION:
173 <p>CommandHandler which allows for undo the last command several commands. The number of undo levels
174 is specified in a constructor argument.</p>
175 */
176 class MultiLevelUndoCommandHandler : public CommandHandler {
177 public:
178 MultiLevelUndoCommandHandler (size_t maxUndoLevels, size_t maxCmdsPerLevel = 100);
179 virtual ~MultiLevelUndoCommandHandler ();
180
181 public:
182 virtual void Post (Command* newCommand) override;
183 virtual bool PostUpdateSimpleTextInsert (size_t insertAt, Led_tChar c) override;
184 virtual void BreakInGroupedCommands () override;
185 virtual void BreakInGroupedCommandsIfDifferentCommand (const SDKString& cmdName) override;
186 virtual void DoUndo (TextInteractor& interactor) override;
187 virtual void DoRedo (TextInteractor& interactor) override;
188 virtual void Commit () override; // Commit all commands currently owned - something has happened to invalidate them...
189 virtual bool CanUndo () override;
190 virtual bool CanRedo () override;
191 virtual const SDKChar* GetUndoCmdName () override;
192 virtual const SDKChar* GetRedoCmdName () override;
193
194 public:
195 nonvirtual size_t GetMaxUnDoLevels ();
196 nonvirtual void SetMaxUnDoLevels (size_t maxUndoLevels);
197
198 private:
199 size_t fMaxUndoLevels;
200 size_t fMaxCmdsPerLevel;
201 size_t fUndoCursor;
202 vector<Command*> fCommands;
203 unsigned fCommandGroupCount;
204 unsigned fUndoneGroupCount;
205
206#if qStroika_Foundation_Debug_AssertionsChecked
207 bool fDoingCommands; // avoid common bug of posting new commands during execution of a command
208#endif
209
210 private:
211 nonvirtual bool GetLastCmdRangeBefore (size_t* startIdx, size_t* endIdx) const; // return true if any cmds in range...
212 nonvirtual bool GetLastCmdRangeAfter (size_t* startIdx, size_t* endIdx) const; // return true if any cmds in range...
213 nonvirtual void Commit_After (size_t after);
214 nonvirtual void Commit_Before (size_t before);
215 };
216
217 /*
218 @CLASS: SnoopingCommandHandler
219 @BASES: @'CommandHandler'
220 @DESCRIPTION:
221 <p>Useful for things like recording keyboard macros. Simply install this as your command handler,
222 and hand it as argument the OLD one. Later, when you are done recording, re-install the old
223 and extract the accumulated commands from this one.</p>
224
225 <p>Basicily all this does is delegate to the given REAL command handler, and call the Snoop ()
226 method YOU provide in your subclass. There - you can extract what information you like from
227 the argument Command object.</p>
228 */
229 class SnoopingCommandHandler : public CommandHandler {
230 protected:
231 SnoopingCommandHandler (CommandHandler* realHandler);
232
233 public:
234 nonvirtual CommandHandler* GetRealHandler () const;
235
236 public:
237 virtual void Post (Command* newCommand) override;
238 virtual void BreakInGroupedCommands () override;
239 virtual void BreakInGroupedCommandsIfDifferentCommand (const SDKString& cmdName) override;
240 virtual void DoUndo (TextInteractor& interactor) override;
241 virtual void DoRedo (TextInteractor& interactor) override;
242 virtual void Commit () override;
243 virtual bool CanUndo () override;
244 virtual bool CanRedo () override;
245 virtual const SDKChar* GetUndoCmdName () override;
246 virtual const SDKChar* GetRedoCmdName () override;
247
248 protected:
249 virtual void Snoop (Command* newCommand) = 0;
250
251 private:
252 CommandHandler* fRealHandler;
253 };
254
255 /*
256 @CLASS: InteractiveReplaceCommand
257 @BASES: @'Command'
258 @DESCRIPTION:
259 <p>This subsumes basic typing and cut/paste type commands.
260 Grab the text range BEFORE the command, and externize it.
261 Do the command, and then grab the range of text AFTER the command
262 and externalize that. The before/after text/ranges allow us to undo
263 and redo. Note we 'externalize' before/after text so we get font info, and
264 any embeddings...</p>
265 */
266 class TextInteractor;
267 class InteractiveReplaceCommand : public Command {
268 private:
269 using inherited = Command;
270
271 public:
272 class SavedTextRep;
273 class PlainTextRep;
274
275 public:
276 InteractiveReplaceCommand (SavedTextRep* beforeRegion, SavedTextRep* afterRegion, size_t at, const SDKString& cmdName);
277 ~InteractiveReplaceCommand ();
278
279 public:
280 virtual void Do (TextInteractor& /*interactor*/) override;
281 virtual void UnDo (TextInteractor& /*interactor*/) override;
282 virtual void ReDo (TextInteractor& /*interactor*/) override;
283
284 public:
285 virtual bool UpdateSimpleTextInsert (size_t insertAt, Led_tChar c) override;
286
287 public:
288 virtual const SDKChar* GetName () const override;
289
290 protected:
291 SavedTextRep* fBeforeRegion;
292 SavedTextRep* fAfterRegion;
293 size_t fAt;
294 SDKString fCmdName;
295 };
296
297 /*
298 @CLASS: InteractiveReplaceCommand::SavedTextRep
299 @DESCRIPTION: <p>Abstraction for what to keep track of in a typical text editing command.</p>
300 <p>Note: This fSelStart/fSelEnd refers to the users selection in the text. It has NOTHING todo
301 with the region of text which is saved.</p>
302 */
303 class InteractiveReplaceCommand::SavedTextRep {
304 public:
305 SavedTextRep (size_t selStart, size_t selEnd);
306 virtual ~SavedTextRep ();
307
308 public:
309 /*
310 @METHOD: InteractiveReplaceCommand::SavedTextRep::GetLength
311 @DESCRIPTION: Return the length of the region preserved in this text saved object.
312 */
313 virtual size_t GetLength () const = 0;
314
315 public:
316 /*
317 @METHOD: InteractiveReplaceCommand::SavedTextRep::InsertSelf
318 @DESCRIPTION: Insert text saved in this object at position 'at' and replacing 'nBytesToOverwrite' Led_tChars
319 worth of text.
320 */
321 virtual void InsertSelf (TextInteractor* imager, size_t at, size_t nBytesToOverwrite) = 0;
322
323 public:
324 /*
325 @METHOD: InteractiveReplaceCommand::SavedTextRep::ApplySelection
326 @DESCRIPTION: Apply the saved selection to the given imager. This must be called <em>after</em> the
327 call to @'InteractiveReplaceCommand::SavedTextRep::InsertSelf' so that the saved selection values
328 are legitimate.
329 */
330 virtual void ApplySelection (TextInteractor* imager);
331
332 protected:
333 size_t fSelStart;
334 size_t fSelEnd;
335 };
336
337 /*
338 @CLASS: InteractiveReplaceCommand::PlainTextRep
339 @BASES: @'InteractiveReplaceCommand::SavedTextRep'
340 @DESCRIPTION: <p>Keep track of only the text, and no font, or misc embedding info. On its
341 @'InteractiveReplaceCommand::SavedTextRep::InsertSelf' call, it uses @'TextInteractor::Replace'</p>
342 <p>Note that this class uses Memory::UseBlockAllocationIfAppropriate<> - so be carefull when subclass.
343 </p>
344 */
345 class InteractiveReplaceCommand::PlainTextRep : public InteractiveReplaceCommand::SavedTextRep,
346 public Foundation::Memory::UseBlockAllocationIfAppropriate<PlainTextRep> {
347 private:
348 using inherited = SavedTextRep;
349
350 public:
351 PlainTextRep (size_t selStart, size_t selEnd, const Led_tChar* text, size_t textLen);
352 ~PlainTextRep ();
353
354 public:
355 virtual size_t GetLength () const override;
356 virtual void InsertSelf (TextInteractor* imager, size_t at, size_t nBytesToOverwrite) override;
357
358 public:
359 nonvirtual bool AppendCharToRep (size_t insertAt, Led_tChar c);
360
361 private:
362 Led_tChar* fText;
363 size_t fTextLength;
364 };
365#endif
366
367}
368
369/*
370 ********************************************************************************
371 ***************************** Implementation Details ***************************
372 ********************************************************************************
373 */
374#include "Command.inl"
375
376#endif /*_Stroika_Framework_Led_Command_h_*/
conditional_t< qTargetPlatformSDKUseswchar_t, wchar_t, char > SDKChar
Definition SDKChar.h:71
basic_string< SDKChar > SDKString
Definition SDKString.h:38