4#include "Stroika/Frameworks/StroikaPreComp.h"
6#include "IdleManager.h"
7#include "TextInteractor.h"
13using namespace Stroika::Frameworks;
14using namespace Stroika::Frameworks::Led;
18#if qStroika_Frameworks_Led_SupportGDI
30const SDKChar* Command::GetName ()
const
32 return Led_SDK_TCHAROF (
"");
35bool Command::UpdateSimpleTextInsert (
size_t , Led_tChar )
45bool CommandHandler::PostUpdateSimpleTextInsert (
size_t , Led_tChar )
47 IdleManager::NonIdleContext nonIdleContext;
51size_t CommandHandler::GetUndoRedoWhatMessageText (
char* buf,
size_t bufSize)
53 const char kCantUndo[] =
"Can't Undo";
54 const char kUndo[] =
"Undo";
55 const char kReUndo[] =
"Redo";
57 bufSize = min (bufSize, strlen (kUndo));
58 memcpy (buf, kUndo, bufSize);
60 else if (CanRedo ()) {
61 bufSize = min (bufSize, strlen (kReUndo));
62 memcpy (buf, kReUndo, bufSize);
65 bufSize = min (bufSize, strlen (kCantUndo));
66 memcpy (buf, kCantUndo, bufSize);
76SingleUndoCommandHandler::SingleUndoCommandHandler ()
80 , fDoingCommands{false}
85void SingleUndoCommandHandler::Post (Command* newCommand)
87#if qStroika_Foundation_Debug_AssertionsChecked
88 Require (not fDoingCommands);
90 IdleManager::NonIdleContext nonIdleContext;
92 fLastCmd = newCommand;
95bool SingleUndoCommandHandler::PostUpdateSimpleTextInsert (
size_t insertAt, Led_tChar c)
97 IdleManager::NonIdleContext nonIdleContext;
98 if (fLastCmd !=
nullptr) {
99 return fLastCmd->UpdateSimpleTextInsert (insertAt, c);
104void SingleUndoCommandHandler::BreakInGroupedCommands ()
108void SingleUndoCommandHandler::BreakInGroupedCommandsIfDifferentCommand (
const SDKString& )
112void SingleUndoCommandHandler::DoUndo (TextInteractor& interactor)
114 Require (CanUndo ());
117 Require (GetDone ());
119 IdleManager::NonIdleContext nonIdleContext;
121#if qStroika_Foundation_Debug_AssertionsChecked
122 Require (not fDoingCommands);
123 fDoingCommands =
true;
126 fLastCmd->UnDo (interactor);
127#if qStroika_Foundation_Debug_AssertionsChecked
130 fDoingCommands =
false;
133 fDoingCommands =
false;
137void SingleUndoCommandHandler::DoRedo (TextInteractor& interactor)
139 Require (CanRedo ());
141 Require (not GetDone ());
143 IdleManager::NonIdleContext nonIdleContext;
145#if qStroika_Foundation_Debug_AssertionsChecked
146 Require (not fDoingCommands);
147 fDoingCommands =
true;
150 fLastCmd->ReDo (interactor);
151#if qStroika_Foundation_Debug_AssertionsChecked
154 fDoingCommands =
false;
157 fDoingCommands =
false;
161void SingleUndoCommandHandler::Commit ()
167bool SingleUndoCommandHandler::CanUndo ()
169 return fLastCmd !=
nullptr and GetDone ();
172bool SingleUndoCommandHandler::CanRedo ()
174 return fLastCmd !=
nullptr and not GetDone ();
177bool SingleUndoCommandHandler::GetDone ()
const
179 return (fLastCmd !=
nullptr and fLastCmd->GetDone ());
182const SDKChar* SingleUndoCommandHandler::GetUndoCmdName ()
185 return fLastCmd->GetName ();
188 return Led_SDK_TCHAROF (
"");
192const SDKChar* SingleUndoCommandHandler::GetRedoCmdName ()
195 return fLastCmd->GetName ();
198 return Led_SDK_TCHAROF (
"");
207MultiLevelUndoCommandHandler::MultiLevelUndoCommandHandler (
size_t maxUndoLevels,
size_t maxCmdsPerLevel)
209 , fMaxUndoLevels{maxUndoLevels}
210 , fMaxCmdsPerLevel{maxCmdsPerLevel}
213 , fCommandGroupCount{0}
214 , fUndoneGroupCount{0}
216 , fDoingCommands{false}
221MultiLevelUndoCommandHandler::~MultiLevelUndoCommandHandler ()
226void MultiLevelUndoCommandHandler::Post (Command* newCommand)
229#if qStroika_Foundation_Debug_AssertionsChecked
230 Require (not fDoingCommands);
233 IdleManager::NonIdleContext nonIdleContext;
237 Assert (fUndoCursor <= fCommands.size ());
238 Commit_After (fUndoCursor);
239 Assert (fCommandGroupCount >= fUndoneGroupCount);
240 fCommandGroupCount -= fUndoneGroupCount;
241 fUndoneGroupCount = 0;
243 Assert (fCommandGroupCount <= fMaxUndoLevels);
245 if (fMaxUndoLevels == 0) {
251 bool partOfNewCommand = (fCommands.size () == 0 or (fCommands.back () ==
nullptr));
255 if (partOfNewCommand) {
256 if (fCommandGroupCount == fMaxUndoLevels) {
257 size_t lastItemInFirstGroup = 0;
258 for (; lastItemInFirstGroup <= fUndoCursor; ++lastItemInFirstGroup) {
259 if (fCommands[lastItemInFirstGroup] ==
nullptr) {
260 Assert (lastItemInFirstGroup != 0);
265 Assert (lastItemInFirstGroup <= fUndoCursor);
266 Commit_Before (lastItemInFirstGroup);
267 fUndoCursor = fCommands.size ();
270 ++fCommandGroupCount;
285 fCommands.push_back (newCommand);
299 fUndoCursor = fCommands.size ();
302bool MultiLevelUndoCommandHandler::PostUpdateSimpleTextInsert (
size_t insertAt, Led_tChar c)
304 IdleManager::NonIdleContext nonIdleContext;
305 if (fUndoCursor != fCommands.size ()) {
309 Command* lastCmd = (fCommands.size () == 0) ?
nullptr : fCommands.back ();
310 if (lastCmd !=
nullptr) {
311 return lastCmd->UpdateSimpleTextInsert (insertAt, c);
316void MultiLevelUndoCommandHandler::BreakInGroupedCommands ()
318 size_t commandListLen = fCommands.size ();
324 if (commandListLen != 0 and (fUndoCursor == commandListLen)) {
325 if (fCommands[fUndoCursor - 1] !=
nullptr) {
326 fCommands.push_back (
nullptr);
327 fUndoCursor = fCommands.size ();
332void MultiLevelUndoCommandHandler::BreakInGroupedCommandsIfDifferentCommand (
const SDKString& cmdName)
334 if (fCommands.size () != 0 and fCommands.back () !=
nullptr and fCommands.back ()->GetName () != cmdName) {
335 BreakInGroupedCommands ();
339void MultiLevelUndoCommandHandler::DoUndo (TextInteractor& interactor)
341 Require (CanUndo ());
343 IdleManager::NonIdleContext nonIdleContext;
345 BreakInGroupedCommands ();
349 [[maybe_unused]]
bool result = GetLastCmdRangeBefore (&start, &end);
352#if qStroika_Foundation_Debug_AssertionsChecked
353 Require (not fDoingCommands);
354 fDoingCommands =
true;
357 for (
int i =
static_cast<int> (end); i >=
static_cast<int> (start); --i) {
358 fCommands[i]->UnDo (interactor);
360#if qStroika_Foundation_Debug_AssertionsChecked
363 fDoingCommands =
false;
366 fDoingCommands =
false;
371 Assert (fUndoneGroupCount <= fCommandGroupCount);
374void MultiLevelUndoCommandHandler::DoRedo (TextInteractor& interactor)
376 Require (CanRedo ());
378 IdleManager::NonIdleContext nonIdleContext;
382 [[maybe_unused]]
bool result = GetLastCmdRangeAfter (&start, &end);
385#if qStroika_Foundation_Debug_AssertionsChecked
386 Require (not fDoingCommands);
387 fDoingCommands =
true;
390 for (
size_t i = start; i <= end; ++i) {
391 fCommands[i]->ReDo (interactor);
393#if qStroika_Foundation_Debug_AssertionsChecked
396 fDoingCommands =
false;
399 fDoingCommands =
false;
402 fUndoCursor = end + 1;
403 Assert (fUndoCursor <= fCommands.size ());
404 if (fCommands[fUndoCursor] ==
nullptr) {
408 Assert (fUndoCursor <= fCommands.size ());
410 Assert (fUndoneGroupCount <= fCommandGroupCount);
411 Assert (fUndoneGroupCount >= 1);
415void MultiLevelUndoCommandHandler::Commit ()
417 for (
size_t i = 0; i < fCommands.size (); ++i) {
422 fUndoneGroupCount = 0;
423 fCommandGroupCount = 0;
426bool MultiLevelUndoCommandHandler::CanUndo ()
428 return (fCommandGroupCount > fUndoneGroupCount);
431bool MultiLevelUndoCommandHandler::CanRedo ()
433 return (fUndoneGroupCount > 0);
436const SDKChar* MultiLevelUndoCommandHandler::GetUndoCmdName ()
441 [[maybe_unused]]
bool result = GetLastCmdRangeBefore (&start, &end);
444 return fCommands[start]->GetName ();
447 return Led_SDK_TCHAROF (
"");
451const SDKChar* MultiLevelUndoCommandHandler::GetRedoCmdName ()
456 [[maybe_unused]]
bool result = GetLastCmdRangeAfter (&start, &end);
459 return fCommands[start]->GetName ();
462 return Led_SDK_TCHAROF (
"");
471void MultiLevelUndoCommandHandler::SetMaxUnDoLevels (
size_t maxUndoLevels)
473 if (fCommandGroupCount >= maxUndoLevels) {
476 fMaxUndoLevels = maxUndoLevels;
479bool MultiLevelUndoCommandHandler::GetLastCmdRangeBefore (
size_t* startIdx,
size_t* endIdx)
const
487 size_t commandListLen = fCommands.size ();
489 if (commandListLen == 0) {
492 Assert (fUndoCursor >= 0);
493 if (fUndoCursor == 0) {
497 size_t i = fUndoCursor - 1;
498 if (fCommands[i] ==
nullptr) {
507 if (fCommands[i] ==
nullptr) {
515bool MultiLevelUndoCommandHandler::GetLastCmdRangeAfter (
size_t* startIdx,
size_t* endIdx)
const
523 Assert (fUndoCursor != kBadIndex);
525 size_t commandListLen = fCommands.size ();
526 size_t listPastEnd = commandListLen;
527 Assert (fUndoCursor <= listPastEnd);
528 if (fUndoCursor == listPastEnd) {
532 size_t i = fUndoCursor;
533 if (fCommands[i] ==
nullptr) {
538 for (; i < listPastEnd; ++i) {
539 if (fCommands[i] ==
nullptr) {
544 Assert (listPastEnd > 0);
545 *endIdx = listPastEnd - 1;
550void MultiLevelUndoCommandHandler::Commit_After (
size_t after)
552 Require (after >= 0);
553 size_t commandsLen = fCommands.size ();
554 if (commandsLen != 0) {
555 for (
long i =
static_cast<long> (commandsLen) - 1; i >= long (after); --i) {
558 fCommands.erase (fCommands.begin () + i);
563void MultiLevelUndoCommandHandler::Commit_Before (
size_t before)
567 Require (before >= 0);
568 Require (before <= fCommands.size ());
569 size_t countToCommit = (before) + 1;
570 while (countToCommit != 0) {
572 fCommands.erase (fCommands.begin ());
583SnoopingCommandHandler::SnoopingCommandHandler (CommandHandler* realHandler)
584 : fRealHandler (realHandler)
588void SnoopingCommandHandler::Post (Command* newCommand)
590 IdleManager::NonIdleContext nonIdleContext;
592 if (fRealHandler !=
nullptr) {
593 fRealHandler->Post (newCommand);
597void SnoopingCommandHandler::BreakInGroupedCommands ()
599 if (fRealHandler !=
nullptr) {
600 fRealHandler->BreakInGroupedCommands ();
604void SnoopingCommandHandler::BreakInGroupedCommandsIfDifferentCommand (
const SDKString& cmdName)
606 if (fRealHandler !=
nullptr) {
607 fRealHandler->BreakInGroupedCommandsIfDifferentCommand (cmdName);
611void SnoopingCommandHandler::DoUndo (TextInteractor& interactor)
613 IdleManager::NonIdleContext nonIdleContext;
615 if (fRealHandler !=
nullptr) {
616 fRealHandler->DoUndo (interactor);
620void SnoopingCommandHandler::DoRedo (TextInteractor& interactor)
622 IdleManager::NonIdleContext nonIdleContext;
624 if (fRealHandler !=
nullptr) {
625 fRealHandler->DoRedo (interactor);
629void SnoopingCommandHandler::Commit ()
631 if (fRealHandler !=
nullptr) {
632 fRealHandler->Commit ();
636bool SnoopingCommandHandler::CanUndo ()
638 if (fRealHandler ==
nullptr) {
642 return fRealHandler->CanUndo ();
646bool SnoopingCommandHandler::CanRedo ()
648 if (fRealHandler ==
nullptr) {
652 return fRealHandler->CanRedo ();
656const SDKChar* SnoopingCommandHandler::GetUndoCmdName ()
658 if (fRealHandler ==
nullptr) {
659 return Led_SDK_TCHAROF (
"");
662 return fRealHandler->GetUndoCmdName ();
666const SDKChar* SnoopingCommandHandler::GetRedoCmdName ()
668 if (fRealHandler ==
nullptr) {
669 return Led_SDK_TCHAROF (
"");
672 return fRealHandler->GetRedoCmdName ();
689InteractiveReplaceCommand::InteractiveReplaceCommand (SavedTextRep* beforeRegion, SavedTextRep* afterRegion,
size_t at,
const SDKString& cmdName)
691 , fBeforeRegion (beforeRegion)
692 , fAfterRegion (afterRegion)
700InteractiveReplaceCommand::~InteractiveReplaceCommand ()
702 delete fBeforeRegion;
706void InteractiveReplaceCommand::Do (TextInteractor& )
711void InteractiveReplaceCommand::UnDo (TextInteractor& interactor)
715 fBeforeRegion->InsertSelf (&interactor, fAt, fAfterRegion->GetLength ());
716 fBeforeRegion->ApplySelection (&interactor);
718 inherited::UnDo (interactor);
721void InteractiveReplaceCommand::ReDo (TextInteractor& interactor)
725 fAfterRegion->InsertSelf (&interactor, fAt, fBeforeRegion->GetLength ());
726 fAfterRegion->ApplySelection (&interactor);
728 inherited::ReDo (interactor);
731bool InteractiveReplaceCommand::UpdateSimpleTextInsert (
size_t insertAt, Led_tChar c)
736 PlainTextRep* afterPTR =
dynamic_cast<PlainTextRep*
> (fAfterRegion);
737 if (afterPTR !=
nullptr) {
738 return afterPTR->AppendCharToRep (insertAt, c);
744const SDKChar* InteractiveReplaceCommand::GetName ()
const
746 return fCmdName.c_str ();
754void InteractiveReplaceCommand::SavedTextRep::ApplySelection (TextInteractor* imager)
757 imager->SetSelection (fSelStart, fSelEnd);
765InteractiveReplaceCommand::PlainTextRep::PlainTextRep (
size_t selStart,
size_t selEnd,
const Led_tChar* text,
size_t textLen)
766 : inherited (selStart, selEnd)
768 , fTextLength (textLen)
771 fText =
new Led_tChar[textLen];
772 memcpy (fText, text, textLen *
sizeof (Led_tChar));
776InteractiveReplaceCommand::PlainTextRep::~PlainTextRep ()
781size_t InteractiveReplaceCommand::PlainTextRep::GetLength ()
const
786void InteractiveReplaceCommand::PlainTextRep::InsertSelf (TextInteractor* interactor,
size_t at,
size_t nBytesToOverwrite)
789 interactor->Replace (at, at + nBytesToOverwrite, fText, fTextLength);
796bool InteractiveReplaceCommand::PlainTextRep::AppendCharToRep (
size_t insertAt, Led_tChar c)
798 if (fSelStart == insertAt and fSelEnd == insertAt) {
800 Led_tChar* newText =
new Led_tChar[fTextLength + 1];
801 (void)::memcpy (newText, fText, fTextLength *
sizeof (Led_tChar));
802 newText[fTextLength] = c;
#define qStroika_Foundation_Debug_AssertionsChecked
The qStroika_Foundation_Debug_AssertionsChecked flag determines if assertions are checked and validat...
#define RequireNotNull(p)
conditional_t< qTargetPlatformSDKUseswchar_t, wchar_t, char > SDKChar