4#include "Stroika/Frameworks/StroikaPreComp.h"
15#include "Stroika/Frameworks/Led/Command.h"
16#include "Stroika/Frameworks/Led/Config.h"
17#include "Stroika/Frameworks/Led/IdleManager.h"
18#include "Stroika/Frameworks/Led/Marker.h"
19#include "Stroika/Frameworks/Led/TextStore.h"
21#include "TextInteractor.h"
26using namespace Stroika::Frameworks;
27using namespace Stroika::Frameworks::Led;
29#if qStroika_Frameworks_Led_SupportGDI
30using SavedTextRep = InteractiveReplaceCommand::SavedTextRep;
33 class FlavorSavorTextRep :
public SavedTextRep {
35 using inherited = SavedTextRep;
38 FlavorSavorTextRep (TextInteractor* interactor,
size_t regionStart,
size_t regionEnd,
size_t selStart,
size_t selEnd)
39 : inherited (selStart, selEnd)
41 , fTextLength (regionEnd - regionStart)
44 interactor->GetExternalizer ()->ExternalizeBestFlavor (fSavedText, regionStart, regionEnd);
46 virtual size_t GetLength ()
const override
50 virtual void InsertSelf (TextInteractor* interactor,
size_t at,
size_t nBytesToOverwrite)
override
53 interactor->GetInternalizer ()->InternalizeBestFlavor (fSavedText, at, at + nBytesToOverwrite);
64 inline bool IsSmartSpace (Led_tChar c)
66 return (c ==
' ' or c ==
'\t');
68 inline bool IsShouldBeSepWithWhitespaceWordChar (Led_tChar c)
76 return Character (c).IsAlphaNumeric () and c < 127;
81 class MyCallback :
public TextInteractor::DialogSupport::SpellCheckDialogCallback {
83 using inherited = TextInteractor::DialogSupport::SpellCheckDialogCallback;
86 MyCallback (TextInteractor& ti)
93 DISABLE_COMPILER_MSC_WARNING_START (6262)
94 virtual MisspellingInfo* GetNextMisspelling ()
override
96 SpellCheckEngine* sce = fTI.GetSpellCheckEngine ();
98 Led_tChar charBuf[10 * 1024];
100 size_t startRegion = fTI.GetSelectionEnd ();
105 size_t wordStart = 0;
107 bool wordReal =
false;
108 fTI.GetTextStore ().FindWordBreaks (startRegion, &wordStart, &wordEnd, &wordReal, sce->PeekAtTextBreaksUsed ());
109 if (wordReal and wordStart + std::size (charBuf) > startRegion) {
110 startRegion = wordStart;
113 size_t endRegion = min (startRegion + std::size (charBuf), fTI.GetEnd ());
114 fTI.CopyOut (startRegion, endRegion - startRegion, charBuf);
115 const Led_tChar* cursor =
nullptr;
116 const Led_tChar* wordStart =
nullptr;
117 const Led_tChar* wordEnd =
nullptr;
118 if (sce->ScanForUndefinedWord (charBuf, charBuf + (endRegion - startRegion), &cursor, &wordStart, &wordEnd)) {
123 if (wordStart != charBuf and wordEnd == charBuf + (endRegion - startRegion)) {
124 startRegion += (wordStart - charBuf);
128 Led_tString undefinedWord = Led_tString{wordStart, wordEnd};
129 if (fIgnoredWords.find (undefinedWord) != fIgnoredWords.end ()) {
132 startRegion += (wordEnd - charBuf);
135 MisspellingInfo* mi =
new MisspellingInfo ();
136 mi->fUndefinedWord = undefinedWord;
137 mi->fSuggestions = sce->GenerateSuggestions (mi->fUndefinedWord);
138 size_t selStart = startRegion + wordStart - charBuf;
139 size_t selEnd = selStart + (wordEnd - wordStart);
140 fTI.SetSelection (selStart, selEnd);
141 fTI.ScrollToSelection ();
144 else if (endRegion < fTI.GetEnd ()) {
147 startRegion = endRegion;
159 DISABLE_COMPILER_MSC_WARNING_END (6262)
160 virtual
void DoIgnore ()
override
162 fTI.SetSelection (fTI.GetSelectionEnd (), fTI.GetSelectionEnd ());
163 fTI.ScrollToSelection ();
165 virtual void DoIgnoreAll ()
override
168 size_t origSelStart = fTI.GetSelectionStart ();
169 size_t origSelEnd = fTI.GetSelectionEnd ();
171 fTI.CopyOut (origSelStart, origSelEnd - origSelStart, text.data ());
172 Led_tString ignoredWord = Led_tString{text.
data (), origSelEnd - origSelStart};
173 fIgnoredWords.insert (ignoredWord);
177 virtual void DoChange (
const Led_tString& changeTo)
override
179 size_t origSelStart = fTI.GetSelectionStart ();
180 size_t origSelEnd = fTI.GetSelectionEnd ();
181 TextInteractor::SearchParameters sp;
184 fTI.CopyOut (origSelStart, origSelEnd - origSelStart, text.data ());
185 sp.fMatchString = Led_tString{text.
data (), origSelEnd - origSelStart};
187 fTI.SetSelection (origSelStart, origSelStart);
188 fTI.OnDoReplaceCommand (sp, changeTo);
190 virtual void DoChangeAll (
const Led_tString& changeTo)
override
192 size_t origSelStart = fTI.GetSelectionStart ();
193 size_t origSelEnd = fTI.GetSelectionEnd ();
194 TextInteractor::SearchParameters sp;
197 fTI.CopyOut (origSelStart, origSelEnd - origSelStart, text.data ());
198 sp.fMatchString = Led_tString{text.
data (), origSelEnd - origSelStart};
200 fTI.OnDoReplaceAllCommand (sp, changeTo);
202 virtual bool AddToDictionaryEnabled ()
const override
204 SpellCheckEngine* sce = fTI.GetSpellCheckEngine ();
205 if (sce !=
nullptr) {
206 SpellCheckEngine::UDInterface* udi = sce->GetUDInterface ();
207 if (udi !=
nullptr) {
208 return udi->AddWordToUserDictionarySupported ();
213 virtual void AddToDictionary (
const Led_tString& newWord)
override
215 SpellCheckEngine* sce = fTI.GetSpellCheckEngine ();
216 if (sce !=
nullptr) {
217 SpellCheckEngine::UDInterface* udi = sce->GetUDInterface ();
218 if (udi !=
nullptr) {
219 udi->AddWordToUserDictionary (newWord);
223 fTI.SetSelection (fTI.GetSelectionEnd (), fTI.GetSelectionEnd ());
224 fTI.ScrollToSelection ();
226 virtual void LookupOnWeb (
const Led_tString& word)
override
228 const char kURLBase[] =
"http://dictionary.reference.com/search?q=";
229 Led_URLManager::Get ().Open (kURLBase + Led_tString2ANSIString (word));
231 virtual bool OptionsDialogEnabled ()
const override
236 virtual void OptionsDialog ()
override
243 set<Led_tString> fIgnoredWords;
253void TextInteractor::DialogSupport::DisplayFindDialog (Led_tString* ,
const vector<Led_tString>& ,
254 bool* ,
bool* ,
bool* ,
bool* )
278TextInteractor::DialogSupport::ReplaceButtonPressed
279TextInteractor::DialogSupport::DisplayReplaceDialog (Led_tString* ,
const vector<Led_tString>& ,
280 Led_tString* ,
bool* ,
bool* ,
bool* )
283 return eReplaceButton_Cancel;
286void TextInteractor::DialogSupport::DisplaySpellCheckDialog (SpellCheckDialogCallback& )
296TextInteractor::UndoableContextHelper::UndoableContextHelper (TextInteractor& ti,
const SDKString& cmdName,
bool allowSmartCNPExpansion)
297 : fSimplePlainTextInsertOptimization (false)
298 , fTextInteractor (ti)
300 , fSelStart (ti.GetSelectionStart ())
301 , fSelEnd (ti.GetSelectionEnd ())
303 , fCommandComplete (false)
305 if (allowSmartCNPExpansion) {
306 ti.OptionallyExpandSelectionForSmartCutAndPasteModeDeletes (&fSelStart, &fSelEnd);
308 if (ti.GetCommandHandler () !=
nullptr) {
309 ti.PreInteractiveUndoHelper (&fBefore, fSelStart, fSelEnd, ti.GetSelectionStart (), ti.GetSelectionEnd ());
313TextInteractor::UndoableContextHelper::UndoableContextHelper (TextInteractor& ti,
const SDKString& cmdName,
size_t regionAndSelStart,
314 size_t regionAndSelEnd,
bool allowSmartCNPExpansion)
315 : fSimplePlainTextInsertOptimization (false)
316 , fTextInteractor (ti)
318 , fSelStart (regionAndSelStart)
319 , fSelEnd (regionAndSelEnd)
321 , fCommandComplete (false)
323 if (allowSmartCNPExpansion) {
324 ti.OptionallyExpandSelectionForSmartCutAndPasteModeDeletes (&fSelStart, &fSelEnd);
326 if (ti.GetCommandHandler () !=
nullptr) {
327 ti.PreInteractiveUndoHelper (&fBefore, fSelStart, fSelEnd, regionAndSelStart, regionAndSelEnd);
331TextInteractor::UndoableContextHelper::UndoableContextHelper (TextInteractor& ti,
const SDKString& cmdName,
size_t regionStart,
332 size_t regionEnd,
size_t selStart,
size_t selEnd,
bool allowSmartCNPExpansion)
333 : fSimplePlainTextInsertOptimization (false)
334 , fTextInteractor (ti)
336 , fSelStart (regionStart)
337 , fSelEnd (regionEnd)
339 , fCommandComplete (false)
341 if (allowSmartCNPExpansion) {
342 ti.OptionallyExpandSelectionForSmartCutAndPasteModeDeletes (&fSelStart, &fSelEnd);
344 if (ti.GetCommandHandler () !=
nullptr) {
345 ti.PreInteractiveUndoHelper (&fBefore, fSelStart, fSelEnd, selStart, selEnd);
349TextInteractor::UndoableContextHelper::~UndoableContextHelper ()
351 if (not fCommandComplete) {
370void TextInteractor::UndoableContextHelper::CommandComplete ()
372 fTextInteractor.ScrollToSelection ();
373 if (fTextInteractor.GetCommandHandler () !=
nullptr) {
374 if (GetSimplePlainTextInsertOptimization ()) {
375 fTextInteractor.PostInteractiveSimpleCharInsertUndoHelper (&fBefore, fSelStart, fTextInteractor.GetSelectionEnd (), fCmdName);
378 fTextInteractor.PostInteractiveUndoHelper (&fBefore, fSelStart, fTextInteractor.GetSelectionEnd (), fCmdName);
381 fCommandComplete =
true;
384void TextInteractor::UndoableContextHelper::CommandComplete (
size_t endOfInsert)
386 fTextInteractor.ScrollToSelection ();
387 if (fTextInteractor.GetCommandHandler () !=
nullptr) {
388 if (GetSimplePlainTextInsertOptimization ()) {
389 fTextInteractor.PostInteractiveSimpleCharInsertUndoHelper (&fBefore, fSelStart, endOfInsert, fCmdName);
392 fTextInteractor.PostInteractiveUndoHelper (&fBefore, fSelStart, endOfInsert, fCmdName);
395 fCommandComplete =
true;
403TextInteractor::PreReplaceInfo::PreReplaceInfo ()
404 : fTextInteractor (nullptr)
405 , fUpdateMode (TextInteractor::eNoUpdate)
408 , fWithWhatCharCount (0)
409 , fBoundingUpdateMarker ()
410 , fBoundingUpdateHeight (0)
411 , fStableTypingRegionHeight (0)
415TextInteractor::PreReplaceInfo::~PreReplaceInfo ()
417 if (fTextInteractor !=
nullptr) {
418 fTextInteractor->GetTextStore ().RemoveMarker (&fBoundingUpdateMarker);
419 fTextInteractor =
nullptr;
423TextInteractor::UpdateMode TextInteractor::PreReplaceInfo::GetUpdateMode ()
const
428size_t TextInteractor::PreReplaceInfo::GetFrom ()
const
433size_t TextInteractor::PreReplaceInfo::GetTo ()
const
444TextInteractor::CommandNames TextInteractor::sCommandNames = TextInteractor::MakeDefaultCommandNames ();
445TextInteractor::DialogSupport* TextInteractor::sDialogSupport =
nullptr;
446TextInteractor::SearchParameters TextInteractor::sSearchParameters;
447TextInteractor::ReplaceParameters TextInteractor::sReplaceParameters;
449TextInteractor::TextInteractor ()
450 : fCommandHandler (nullptr)
451 , fSpellCheckEngine (nullptr)
452 , fSuppressCommandBreaksContext (false)
453 , fDefaultUpdateMode (eDelayedUpdate)
454 , fSmartCutAndPasteMode (true)
456 , fLastMouseDownAt (Led_Point (0, 0))
457 , fWholeWindowInvalid (false)
458 , fUseSecondaryHilight (false)
459 , fUseBitmapScrollingOptimization (true)
460 , fSuppressTypedControlCharacters (true)
461 , fInteractiveUpdadeMode (eIndeterminateInteractiveUpdateMode)
462 , fTmpPreReplaceInfo ()
463 , fDoingUpdateModeReplaceOn (nullptr)
464 , fCaretShown (false)
465 , fLeftSideOfSelectionInteresting (false)
466 , fCaretShownAfterPos (true)
471 fScrollBarParamsValid (false)
473 fScrollBarType[h] = eScrollBarNever;
474 fScrollBarType[v] = eScrollBarNever;
477TextInteractor::~TextInteractor ()
479 Assert (fCommandHandler ==
nullptr);
481 Assert (fSpellCheckEngine ==
nullptr);
484TextInteractor::CommandNames TextInteractor::MakeDefaultCommandNames ()
486 TextInteractor::CommandNames cmdNames;
487 cmdNames.fTypingCommandName = Led_SDK_TCHAROF (
"Typing");
488 cmdNames.fCutCommandName = Led_SDK_TCHAROF (
"Cut");
489 cmdNames.fClearCommandName = Led_SDK_TCHAROF (
"Clear");
490 cmdNames.fPasteCommandName = Led_SDK_TCHAROF (
"Paste");
491 cmdNames.fUndoFormatString = Led_SDK_TCHAROF (
"Undo %s");
492 cmdNames.fRedoFormatString = Led_SDK_TCHAROF (
"ReDo %s");
493#if qStroika_Platform_Windows
494 cmdNames.fUndoFormatString += Led_SDK_TCHAROF (
"\tCtrl+Z");
495 cmdNames.fRedoFormatString += Led_SDK_TCHAROF (
"\tCtrl+Y");
497 cmdNames.fReplaceCommandName = Led_SDK_TCHAROF (
"Replace");
498 cmdNames.fReplaceAllCommandName = Led_SDK_TCHAROF (
"Replace All");
499 cmdNames.fReplaceAllInSelectionCommandName = Led_SDK_TCHAROF (
"Replace All In Selection");
509bool TextInteractor::OnUpdateCommand (CommandUpdater* enabler)
512 switch (enabler->GetCmdID ()) {
513 case kSelectAll_CmdID: {
514 enabler->SetEnabled (
true);
518 OnUpdateCutCopyClearCommand (enabler);
522 OnUpdateCutCopyClearCommand (enabler);
526 OnUpdatePasteCommand (enabler);
530 OnUpdateCutCopyClearCommand (enabler);
534 OnUpdateUndoRedoCommand (enabler);
538 OnUpdateUndoRedoCommand (enabler);
542 OnUpdateFindCommands (enabler);
545 case kFindAgain_CmdID: {
546 OnUpdateFindCommands (enabler);
549 case kEnterFindString_CmdID: {
550 OnUpdateFindCommands (enabler);
553 case kReplace_CmdID: {
554 OnUpdateFindCommands (enabler);
557 case kReplaceAgain_CmdID: {
558 OnUpdateFindCommands (enabler);
561 case kSpellCheck_CmdID: {
562 OnUpdateSpellCheckCommand (enabler);
565 case kSelectWord_CmdID:
566 case kSelectTextRow_CmdID:
567 case kSelectParagraph_CmdID: {
568 OnUpdateSelectTextCommand (enabler);
581bool TextInteractor::OnPerformCommand (CommandNumber commandNumber)
583 switch (commandNumber) {
584 case kSelectAll_CmdID: {
585 OnSelectAllCommand ();
616 case kFindAgain_CmdID: {
617 OnFindAgainCommand ();
620 case kEnterFindString_CmdID: {
621 OnEnterFindString ();
624 case kReplace_CmdID: {
628 case kReplaceAgain_CmdID: {
629 OnReplaceAgainCommand ();
632 case kSpellCheck_CmdID: {
633 OnSpellCheckCommand ();
636 case kSelectWord_CmdID:
637 case kSelectTextRow_CmdID:
638 case kSelectParagraph_CmdID: {
639 OnPerformSelectTextCommand (commandNumber);
646void TextInteractor::OnUpdateCutCopyClearCommand (CommandUpdater* enabler)
651 static_cast<TextImager*
> (
this)->GetSelection (&start, &end);
652 enabler->SetEnabled (start != end);
655void TextInteractor::OnUpdatePasteCommand (CommandUpdater* enabler)
659 enabler->SetEnabled (
true);
662void TextInteractor::OnUpdateUndoRedoCommand (CommandUpdater* enabler)
665 if (GetCommandHandler () ==
nullptr) {
666 enabler->SetEnabled (
false);
669 if (enabler->GetCmdID () == kUndo_CmdID) {
670 enabler->SetEnabled (GetCommandHandler ()->CanUndo ());
673 Characters::CString::Format (GetCommandNames ().fUndoFormatString.c_str (), GetCommandHandler ()->GetUndoCmdName ());
674 enabler->SetText (menuItemText.c_str ());
676 else if (enabler->GetCmdID () == kRedo_CmdID) {
677 enabler->SetEnabled (GetCommandHandler ()->CanRedo ());
680 Characters::CString::Format (GetCommandNames ().fRedoFormatString.c_str (), GetCommandHandler ()->GetRedoCmdName ());
681 enabler->SetText (menuItemText.c_str ());
686void TextInteractor::OnUpdateSelectTextCommand (CommandUpdater* enabler)
689 enabler->SetEnabled (
true);
692void TextInteractor::OnPerformSelectTextCommand (CommandNumber commandNumber)
695 size_t oldSelStart = GetSelectionStart ();
696 size_t oldSelEnd = GetSelectionEnd ();
697 size_t newSelStart = oldSelStart;
698 size_t newSelEnd = oldSelEnd;
699 switch (commandNumber) {
700 case kSelectWord_CmdID: {
701 size_t wordStart = 0;
703 bool wordReal =
false;
704 GetTextStore ().FindWordBreaks (oldSelStart, &wordStart, &wordEnd, &wordReal);
706 Assert (wordStart <= oldSelStart);
707 newSelStart = wordStart;
711 GetTextStore ().FindWordBreaks (FindPreviousCharacter (oldSelStart), &wordStart, &wordEnd, &wordReal);
713 Assert (wordStart <= oldSelStart);
714 newSelStart = wordStart;
718 GetTextStore ().FindWordBreaks (oldSelEnd, &wordStart, &wordEnd, &wordReal);
723 case kSelectTextRow_CmdID: {
724 newSelStart = GetStartOfRowContainingPosition (oldSelStart);
727 if (oldSelStart == oldSelEnd or oldSelEnd != GetStartOfRowContainingPosition (oldSelEnd)) {
728 newSelEnd = GetEndOfRowContainingPosition (oldSelEnd);
731 case kSelectParagraph_CmdID: {
732 newSelStart = GetTextStore ().GetStartOfLineContainingPosition (oldSelStart);
736 if (oldSelStart == oldSelEnd or oldSelEnd != GetTextStore ().GetStartOfLineContainingPosition (oldSelEnd)) {
737 newSelEnd = GetTextStore ().GetEndOfLineContainingPosition (oldSelEnd);
739 if (newSelEnd < GetEnd ()) {
741 GetTextStore ().CopyOut (newSelEnd, 1, &c);
743 newSelEnd = FindNextCharacter (newSelEnd);
749 SetSelection (newSelStart, newSelEnd);
753 vector<Led_tString> MergeRecentFindStrings (
const Led_tString& s,
const vector<Led_tString>& oldRecents);
756void TextInteractor::OnFindCommand ()
758 SearchParameters parameters = GetSearchParameters ();
759 bool pressedOK =
false;
760 GetDialogSupport ().DisplayFindDialog (¶meters.fMatchString, parameters.fRecentFindStrings, ¶meters.fWrapSearch,
761 ¶meters.fWholeWordSearch, ¶meters.fCaseSensativeSearch, &pressedOK);
762 parameters.fRecentFindStrings = MergeRecentFindStrings (parameters.fMatchString, parameters.fRecentFindStrings);
763 SetSearchParameters (parameters);
765 OnFindAgainCommand ();
769void TextInteractor::OnReplaceCommand ()
771 SearchParameters parameters = GetSearchParameters ();
772 ReplaceParameters rParameters = GetReplaceParameters ();
773 DialogSupport::ReplaceButtonPressed pressed =
774 GetDialogSupport ().DisplayReplaceDialog (¶meters.fMatchString, parameters.fRecentFindStrings, &rParameters.fReplaceWith,
775 ¶meters.fWrapSearch, ¶meters.fWholeWordSearch, ¶meters.fCaseSensativeSearch);
776 parameters.fRecentFindStrings = MergeRecentFindStrings (parameters.fMatchString, parameters.fRecentFindStrings);
777 SetSearchParameters (parameters);
778 SetReplaceParameters (rParameters);
780 case TextInteractor::DialogSupport::eReplaceButton_Find:
781 OnFindAgainCommand ();
783 case TextInteractor::DialogSupport::eReplaceButton_Replace:
784 OnDoReplaceCommand (parameters, rParameters.fReplaceWith);
786 case TextInteractor::DialogSupport::eReplaceButton_ReplaceAll:
787 OnDoReplaceAllCommand (parameters, rParameters.fReplaceWith);
789 case TextInteractor::DialogSupport::eReplaceButton_ReplaceAllInSelection:
790 OnDoReplaceAllInSelectionCommand (parameters, rParameters.fReplaceWith);
795void TextInteractor::OnReplaceAgainCommand ()
797 SearchParameters parameters = GetSearchParameters ();
798 ReplaceParameters rParameters = GetReplaceParameters ();
799 OnDoReplaceCommand (parameters, rParameters.fReplaceWith);
802void TextInteractor::OnDoReplaceCommand (
const SearchParameters& searchFor,
const Led_tString& replaceWith)
804 BreakInGroupedCommands ();
806 size_t origSelStart = GetSelectionStart ();
807 size_t origSelEnd = GetSelectionEnd ();
808 size_t whereTo = GetTextStore ().Find (searchFor, origSelEnd);
809 if ((whereTo == kBadIndex) or (whereTo == origSelStart and whereTo + searchFor.fMatchString.length () == origSelEnd)) {
813 InteractiveModeUpdater iuMode (*
this);
814 size_t replaceStart = whereTo;
815 size_t replaceEnd = whereTo + searchFor.fMatchString.length ();
816 UndoableContextHelper undoContext (*
this, GetCommandNames ().fReplaceCommandName, replaceStart, replaceEnd, GetSelectionStart (),
817 GetSelectionEnd (),
false);
819 InteractiveReplace_ (undoContext.GetUndoRegionStart (), undoContext.GetUndoRegionEnd (), replaceWith.c_str (), replaceWith.length ());
820 SetSelection (whereTo, whereTo + replaceWith.length ());
822 undoContext.CommandComplete ();
823 ScrollToSelection ();
825 BreakInGroupedCommands ();
828void TextInteractor::OnDoReplaceAllCommand (
const SearchParameters& searchFor,
const Led_tString& replaceWith)
830 InteractiveModeUpdater iuMode (*
this);
831 BreakInGroupedCommands ();
834 size_t whereTo = GetTextStore ().Find (searchFor, startAt, GetTextStore ().GetEnd ());
835 if (whereTo == kBadIndex) {
839 size_t replaceStart = whereTo;
840 size_t replaceEnd = whereTo + searchFor.fMatchString.length ();
841 UndoableContextHelper undoContext (*
this, GetCommandNames ().fReplaceAllCommandName, replaceStart, replaceEnd,
842 GetSelectionStart (), GetSelectionEnd (),
false);
844 InteractiveReplace_ (undoContext.GetUndoRegionStart (), undoContext.GetUndoRegionEnd (), replaceWith.c_str (), replaceWith.length ());
845 SetSelection (whereTo, whereTo + replaceWith.length ());
846 startAt = whereTo + replaceWith.length ();
848 undoContext.CommandComplete ();
851 ScrollToSelection ();
852 BreakInGroupedCommands ();
855void TextInteractor::OnDoReplaceAllInSelectionCommand (
const SearchParameters& searchFor,
const Led_tString& replaceWith)
857 InteractiveModeUpdater iuMode (*
this);
858 BreakInGroupedCommands ();
859 size_t startAt = GetSelectionStart ();
860 TempMarker selectionRegion (GetTextStore (), startAt, GetSelectionEnd ());
862 Assert (startAt <= selectionRegion.GetEnd ());
863 size_t whereTo = GetTextStore ().Find (searchFor, startAt, selectionRegion.GetEnd ());
864 if (whereTo == kBadIndex) {
868 size_t replaceStart = whereTo;
869 size_t replaceEnd = whereTo + searchFor.fMatchString.length ();
870 UndoableContextHelper undoContext (*
this, GetCommandNames ().fReplaceAllInSelectionCommandName, replaceStart, replaceEnd,
871 GetSelectionStart (), GetSelectionEnd (),
false);
873 InteractiveReplace_ (undoContext.GetUndoRegionStart (), undoContext.GetUndoRegionEnd (), replaceWith.c_str (), replaceWith.length ());
874 SetSelection (whereTo, whereTo + replaceWith.length ());
875 startAt = whereTo + replaceWith.length ();
877 undoContext.CommandComplete ();
880 ScrollToSelection ();
881 BreakInGroupedCommands ();
884void TextInteractor::OnFindAgainCommand ()
886 TextStore::SearchParameters parameters = GetSearchParameters ();
889 size_t origSelStart = GetSelectionStart ();
890 size_t origSelEnd = GetSelectionEnd ();
891 size_t whereTo = GetTextStore ().Find (parameters, origSelEnd);
892 if ((whereTo == kBadIndex) or (whereTo == origSelStart and whereTo + parameters.fMatchString.length () == origSelEnd)) {
896 SetSelection (whereTo, whereTo + parameters.fMatchString.length ());
897 ScrollToSelection ();
901void TextInteractor::OnEnterFindString ()
903 SearchParameters parameters = GetSearchParameters ();
905 size_t selStart = GetSelectionStart ();
906 size_t selEnd = GetSelectionEnd ();
907 size_t selLength = selEnd - selStart;
910 CopyOut (selStart, selLength, buf.data ());
911 parameters.fMatchString = Led_tString{buf.data (), selLength};
912 parameters.fRecentFindStrings = MergeRecentFindStrings (parameters.fMatchString, parameters.fRecentFindStrings);
913 SetSearchParameters (parameters);
916void TextInteractor::OnUpdateFindCommands (CommandUpdater* enabler)
919 if (enabler->GetCmdID () == kFind_CmdID) {
920 enabler->SetEnabled (
true);
922 else if (enabler->GetCmdID () == kFindAgain_CmdID) {
923 enabler->SetEnabled (GetSearchParameters ().fMatchString.length () != 0);
925 else if (enabler->GetCmdID () == kEnterFindString_CmdID) {
926 enabler->SetEnabled (GetSelectionStart () != GetSelectionEnd ());
928 else if (enabler->GetCmdID () == kReplace_CmdID) {
929 enabler->SetEnabled (
true);
931 else if (enabler->GetCmdID () == kReplaceAgain_CmdID) {
932 enabler->SetEnabled (GetSearchParameters ().fMatchString.length () != 0);
936TextInteractor::SearchParameters TextInteractor::GetSearchParameters ()
const
938 return sSearchParameters;
941void TextInteractor::SetSearchParameters (
const SearchParameters& sp)
943 sSearchParameters = sp;
946TextInteractor::ReplaceParameters TextInteractor::GetReplaceParameters ()
const
948 return sReplaceParameters;
951void TextInteractor::SetReplaceParameters (
const ReplaceParameters& rp)
953 sReplaceParameters = rp;
956vector<Led_tString> TextInteractor::MergeRecentFindStrings (
const Led_tString& s,
const vector<Led_tString>& oldRecents)
958 const unsigned int kMaxEntries = 20;
959 vector<Led_tString> result = oldRecents;
962 vector<Led_tString>::iterator i = std::find (result.begin (), result.end (), s);
963 if (i != result.end ()) {
966 result.insert (result.begin (), s);
967 if (result.size () > kMaxEntries) {
968 result.erase (result.begin () + kMaxEntries, result.end ());
973void TextInteractor::OnSpellCheckCommand ()
975 if (GetSpellCheckEngine () ==
nullptr) {
979 MyCallback cb (*
this);
980 GetDialogSupport ().DisplaySpellCheckDialog (cb);
984void TextInteractor::OnUpdateSpellCheckCommand (CommandUpdater* enabler)
987 enabler->SetEnabled (GetSpellCheckEngine () !=
nullptr);
998void TextInteractor::SetDefaultUpdateMode (UpdateMode defaultUpdateMode)
1000 if (defaultUpdateMode != eDefaultUpdate) {
1001 fDefaultUpdateMode = defaultUpdateMode;
1016bool TextInteractor::LooksLikeSmartPastableText ([[maybe_unused]]
const Led_tChar* text,
size_t , SmartCNPInfo* smartCNPInfo)
const
1020 if (GetSmartCutAndPasteMode ()) {
1021 size_t selStart = GetSelectionStart ();
1022 size_t selEnd = GetSelectionEnd ();
1025 if (selStart == 0 or selStart >= GetEnd ()) {
1026 smartCNPInfo->fWordBreakAtSelStart =
true;
1029 size_t prev = FindPreviousCharacter (selStart);
1031 CopyOut (prev, 1, &prevC);
1033 CopyOut (selStart, 1, &c);
1034 smartCNPInfo->fWordBreakAtSelStart = (IsShouldBeSepWithWhitespaceWordChar (c) != IsShouldBeSepWithWhitespaceWordChar (prevC));
1037 if (selEnd == 0 or selEnd >= GetEnd ()) {
1038 smartCNPInfo->fWordBreakAtSelEnd =
true;
1041 size_t prev = FindPreviousCharacter (selEnd);
1043 CopyOut (prev, 1, &prevC);
1045 CopyOut (selEnd, 1, &c);
1046 smartCNPInfo->fWordBreakAtSelEnd = (IsShouldBeSepWithWhitespaceWordChar (c) != IsShouldBeSepWithWhitespaceWordChar (prevC));
1066void TextInteractor::OptionallyAddExtraSpaceForSmartCutAndPasteModeAdds (
size_t selStart,
const SmartCNPInfo& smartCNPInfo)
1068 size_t selEnd = GetSelectionEnd ();
1070 Require (0 <= selStart);
1071 Require (selStart <= selEnd);
1072 Require (selEnd <= GetTextStore ().GetEnd ());
1074 if (GetSmartCutAndPasteMode ()) {
1075 if (selEnd > 0 and selEnd < GetTextStore ().GetEnd ()) {
1076 size_t prev = FindPreviousCharacter (selEnd);
1078 CopyOut (prev, 1, &prevC);
1080 CopyOut (selEnd, 1, &c);
1081 if (smartCNPInfo.fWordBreakAtSelEnd and IsShouldBeSepWithWhitespaceWordChar (c) and IsShouldBeSepWithWhitespaceWordChar (prevC)) {
1082 InteractiveReplace_ (selEnd, selEnd, LED_TCHAR_OF (
" "), 1,
false);
1091 SetSelection (selEnd, selEnd);
1095 if (selStart > 0 and selStart < GetTextStore ().GetEnd ()) {
1096 size_t prev = FindPreviousCharacter (selStart);
1098 CopyOut (prev, 1, &prevC);
1100 CopyOut (selStart, 1, &c);
1101 if (smartCNPInfo.fWordBreakAtSelStart and IsShouldBeSepWithWhitespaceWordChar (c) and IsShouldBeSepWithWhitespaceWordChar (prevC)) {
1107 InteractiveReplace_ (selStart, selStart, LED_TCHAR_OF (
" "), 1,
false);
1110 if (IsSmartSpace (c) and IsSmartSpace (prevC)) {
1111 InteractiveReplace_ (selStart, FindNextCharacter (selStart),
nullptr, 0,
false);
1122void TextInteractor::OptionallyExpandSelectionForSmartCutAndPasteModeDeletes (
size_t* selStart,
size_t* selEnd)
1126 Require (0 <= *selStart);
1127 Require (*selStart <= *selEnd);
1128 Require (*selEnd <= GetTextStore ().GetEnd ());
1130 if (GetSmartCutAndPasteMode ()) {
1131 size_t realStart = *selStart;
1132 size_t realEnd = *selEnd;
1133 size_t newStart = realStart;
1134 size_t newEnd = realEnd;
1140 if (realStart < GetEnd ()) {
1142 CopyOut (realStart, 1, &c);
1146 if (realStart > 0) {
1147 CopyOut (FindPreviousCharacter (realStart), 1, &c);
1148 if (IsShouldBeSepWithWhitespaceWordChar (c)) {
1154 if (realStart < realEnd and realEnd < GetEnd ()) {
1156 CopyOut (FindPreviousCharacter (realEnd), 1, &c);
1160 if (realEnd < GetEnd ()) {
1161 CopyOut (realEnd, 1, &c);
1162 if (IsShouldBeSepWithWhitespaceWordChar (c)) {
1174 size_t prev = FindPreviousCharacter (newStart);
1175 CopyOut (prev, 1, &c);
1176 size_t prevprev = FindPreviousCharacter (prev);
1177 Led_tChar prevprevC;
1178 CopyOut (prevprev, 1, &prevprevC);
1179 if (prevprev != prev and IsSmartSpace (c) and not(IsSmartSpace (prevprevC) or prevprevC ==
'\n')) {
1186 if (newEnd < GetEnd ()) {
1187 CopyOut (newEnd, 1, &c);
1188 Led_tChar charBeforeStart =
'\0';
1189 if (newStart != 0) {
1190 CopyOut (FindPreviousCharacter (newStart), 1, &charBeforeStart);
1192 if (IsSmartSpace (c)) {
1193 Assert (newEnd < FindNextCharacter (newEnd));
1194 newEnd = FindNextCharacter (newEnd);
1195 if (newEnd < GetEnd ()) {
1197 Led_tChar nextChar =
'\0';
1198 CopyOut (newEnd, 1, &nextChar);
1199 if (IsShouldBeSepWithWhitespaceWordChar (charBeforeStart) and IsShouldBeSepWithWhitespaceWordChar (nextChar)) {
1200 newEnd = FindPreviousCharacter (newEnd);
1207 *selStart = newStart;
1217void TextInteractor::SetSelectionShown (
bool shown)
1219 SetSelectionShown (shown, eDefaultUpdate);
1222void TextInteractor::SetSelectionShown (
bool shown, UpdateMode updateMode)
1224 if (GetSelectionShown () != shown) {
1225 TextImager::SetSelectionShown (shown);
1226 Refresh (GetSelectionStart (), GetSelectionEnd (), updateMode);
1234void TextInteractor::SetSelection (
size_t start,
size_t end)
1236 Assert (end <= GetEnd ());
1237 UpdateMode updateMode = GetDefaultUpdateMode ();
1240 if (start != GetSelectionStart () or end != GetSelectionEnd ()) {
1241 IdleManager::NonIdleContext nonIdleContext;
1243 size_t oldSelectionStart = GetSelectionStart ();
1244 size_t oldSelectionEnd = GetSelectionEnd ();
1251 if (start == GetSelectionStart ()) {
1252 fLeftSideOfSelectionInteresting =
false;
1254 else if (end == GetSelectionEnd ()) {
1255 fLeftSideOfSelectionInteresting =
true;
1257 else if ((start < GetSelectionStart ()) == (end < GetSelectionEnd ())) {
1258 fLeftSideOfSelectionInteresting = (start < GetSelectionStart ());
1265 InvalidateCaretState ();
1267 TextImager::SetSelection (start, end);
1269 if ((GetSelectionShown () or GetUseSecondaryHilight ()) and updateMode != eNoUpdate) {
1277 UpdateMode useUpdateMode = (updateMode == eImmediateUpdate) ? eDelayedUpdate : updateMode;
1279 size_t lhsOuter = min (oldSelectionStart, GetSelectionStart ());
1280 size_t rhsOuter = max (oldSelectionEnd, GetSelectionEnd ());
1281 size_t lhsInner = max (oldSelectionStart, GetSelectionStart ());
1282 size_t rhsInner = min (oldSelectionEnd, GetSelectionEnd ());
1283 Assert (lhsOuter <= rhsOuter);
1284 Assert (lhsOuter <= lhsInner);
1285 Assert (lhsOuter <= rhsInner);
1286 Assert (lhsOuter <= rhsOuter);
1287 Assert (lhsInner <= rhsOuter);
1288 Assert (rhsInner <= rhsOuter);
1294 Refresh (FindPreviousCharacter (lhsOuter), FindNextCharacter (lhsInner), useUpdateMode);
1295 Refresh (FindPreviousCharacter (rhsInner), FindNextCharacter (rhsOuter), useUpdateMode);
1298 InvalidateCaretState ();
1300 if (updateMode == eImmediateUpdate) {
1303 RecomputeSelectionGoalColumn ();
1306void TextInteractor::SetSelection (
size_t start,
size_t end, UpdateMode updateMode)
1308 TemporarilySetUpdateMode updateModeSetter (*
this, updateMode);
1309 TextImager* tim =
this;
1310 tim->SetSelection (start, end);
1324void TextInteractor::ScrollToSelection (UpdateMode updateMode,
bool forceShowSelectionEndpoint)
1326 size_t selStart = GetSelectionStart ();
1327 size_t selEnd = GetSelectionEnd ();
1337 if (not forceShowSelectionEndpoint and selStart <= GetMarkerPositionOfStartOfWindow () and selEnd >= GetMarkerPositionOfEndOfWindow ()) {
1359 size_t firstCharShown = selStart;
1360 size_t lastCharShown = FindPreviousCharacter (selEnd);
1361 if (fLeftSideOfSelectionInteresting) {
1362 if (firstCharShown != lastCharShown and GetStartOfRowContainingPosition (firstCharShown) != GetStartOfRowContainingPosition (lastCharShown)) {
1363 lastCharShown = firstCharShown;
1365 ScrollSoShowing (firstCharShown, lastCharShown, updateMode);
1368 if (GetCaretShownAfterPos ()) {
1369 lastCharShown = FindNextCharacter (lastCharShown);
1371 if (firstCharShown != lastCharShown and GetStartOfRowContainingPosition (firstCharShown) != GetStartOfRowContainingPosition (lastCharShown)) {
1372 firstCharShown = lastCharShown;
1374 ScrollSoShowing (lastCharShown, firstCharShown, updateMode);
1378void TextInteractor::HookLosingTextStore ()
1380 HookLosingTextStore_ ();
1381 TextImager::HookLosingTextStore ();
1384void TextInteractor::HookLosingTextStore_ ()
1386 AbortReplace (fTmpPreReplaceInfo);
1387 fExternalizer.reset ();
1388 fInternalizer.reset ();
1391void TextInteractor::HookGainedNewTextStore ()
1393 HookGainedNewTextStore_ ();
1394 TextImager::HookGainedNewTextStore ();
1397void TextInteractor::HookGainedNewTextStore_ ()
1399 if (fExternalizer.get () ==
nullptr) {
1400 SetExternalizer (MakeDefaultExternalizer ());
1402 if (fInternalizer.get () ==
nullptr) {
1403 SetInternalizer (MakeDefaultInternalizer ());
1421bool TextInteractor::ProcessSimpleClick (Led_Point clickedAt,
unsigned clickCount,
bool extendSelection,
size_t* dragAnchor)
1424 switch (clickCount) {
1426 size_t newPos = GetCharAtClickLocation (clickedAt);
1427 size_t newSelStart = newPos;
1428 size_t newSelEnd = newPos;
1430 WhileTrackingConstrainSelection (&newSelStart, &newSelEnd);
1431 newPos = newSelStart;
1433 if (extendSelection) {
1434 newSelStart = min (newSelStart, GetSelectionStart ());
1435 newSelEnd = max (newSelEnd, GetSelectionEnd ());
1447 if (extendSelection) {
1449 if (newPos == newSelStart) {
1450 *dragAnchor = newSelEnd;
1453 *dragAnchor = newSelStart;
1457 *dragAnchor = newPos;
1461 if (not extendSelection) {
1462 SetCaretShownAfterPos (GetCharWindowLocation (newPos).top <= clickedAt.v);
1465 WhileTrackingConstrainSelection (&newSelStart, &newSelEnd);
1466 SetSelection (newSelStart, newSelEnd);
1474 DbgTrace (
"TextInteractor::ProcessSimpleClick (tickCount=%f, newMousePos=(%d,%d), clickCount=%d, extendSel=%d, newSelStart=%d, newSelEnd=%d)\n",
1475 Time::GetTickCount (), clickedAt.v, clickedAt.h, clickCount, extendSelection, GetSelectionStart (), GetSelectionEnd ()
1485void TextInteractor::UpdateClickCount (
Time::TimePointSeconds clickAtTime,
const Led_Point& clickAtLocation)
1487 if (ClickTimesAreCloseForDoubleClick (clickAtTime) and PointsAreCloseForDoubleClick (clickAtLocation)) {
1488 IncrementCurClickCount (clickAtTime);
1491 SetCurClickCount (1, clickAtTime);
1493 fLastMouseDownAt = clickAtLocation;
1502 return (fLastClickedAt + Led_GetDoubleClickTime () >= thisClick);
1509bool TextInteractor::PointsAreCloseForDoubleClick (
const Led_Point& p)
1511 const CoordinateType kMultiClickDistance = 4;
1512 CoordinateType hDelta = p.h - fLastMouseDownAt.h;
1516 CoordinateType vDelta = p.v - fLastMouseDownAt.v;
1520 return ((hDelta <= kMultiClickDistance) and (vDelta <= kMultiClickDistance));
1528void TextInteractor::WhileSimpleMouseTracking (Led_Point newMousePos,
size_t dragAnchor)
1530#if qStroika_Frameworks_Led_DynamiclyChooseAutoScrollIncrement
1534 bool firstClick = (now - sLastTimeThrough > kClickThreshold);
1536 int increment = firstClick ? 1 : 2;
1538 const int increment = 1;
1541 size_t rhsPos = GetCharAtClickLocation (newMousePos);
1543 size_t ignored = rhsPos;
1544 WhileTrackingConstrainSelection (&rhsPos, &ignored);
1550 if (rhsPos < GetMarkerPositionOfStartOfWindow ()) {
1551 ScrollByIfRoom (-1, eImmediateUpdate);
1552 rhsPos = GetMarkerPositionOfStartOfWindow ();
1554 else if (rhsPos > GetMarkerPositionOfEndOfWindow ()) {
1555 ScrollByIfRoom (1, eImmediateUpdate);
1556 rhsPos = FindNextCharacter (GetMarkerPositionOfEndOfWindow ());
1562 const int kHScrollIncrementFactor = 4;
1563 if (newMousePos.h < GetWindowRect ().left) {
1564 if (GetHScrollPos () > 0) {
1565 SetHScrollPos (max (0,
int (GetHScrollPos ()) - increment * kHScrollIncrementFactor));
1568 else if (newMousePos.h > GetWindowRect ().right) {
1569 SetHScrollPos (min (
static_cast<CoordinateType
> (GetHScrollPos () + increment * kHScrollIncrementFactor),
1570 static_cast<CoordinateType
> (ComputeMaxHScrollPos ())));
1573 size_t newSelStart = min (rhsPos, dragAnchor);
1574 size_t newSelEnd = max (rhsPos, dragAnchor);
1575 WhileTrackingConstrainSelection (&newSelStart, &newSelEnd);
1576 SetSelection (newSelStart, newSelEnd, eImmediateUpdate);
1578#if qStroika_Frameworks_Led_DynamiclyChooseAutoScrollIncrement
1579 sLastTimeThrough = now;
1593void TextInteractor::WhileTrackingConstrainSelection (
size_t* selStart,
size_t* selEnd)
1597 Require (GetCurClickCount () > 0);
1598 switch (GetCurClickCount ()) {
1604 WhileTrackingConstrainSelection_ForWholeWords (selStart, selEnd);
1609 WhileTrackingConstrainSelection_ForWholeRows (selStart, selEnd);
1618void TextInteractor::WhileTrackingConstrainSelection_ForWholeWords (
size_t* selStart,
size_t* selEnd)
1623 size_t wordStart = 0;
1625 bool wordReal =
false;
1626 GetTextStore ().FindWordBreaks (*selStart, &wordStart, &wordEnd, &wordReal);
1627 *selStart = wordStart;
1629 GetTextStore ().FindWordBreaks (*selEnd, &wordStart, &wordEnd, &wordReal);
1630#if qStroika_Frameworks_Led_DoubleClickSelectsSpaceAfterWord
1633 size_t xWordStart = 0;
1634 size_t xWordEnd = 0;
1635 bool xWordReal =
false;
1636 GetTextStore ().FindWordBreaks (wordEnd, &xWordStart, &xWordEnd, &xWordReal);
1637 if (not xWordReal) {
1649void TextInteractor::WhileTrackingConstrainSelection_ForWholeRows (
size_t* selStart,
size_t* selEnd)
1653 Require (*selStart <= *selEnd);
1655 size_t origSelStart = *selStart;
1656 size_t origSelEnd = *selEnd;
1658 *selStart = GetStartOfRowContainingPosition (origSelStart);
1659 *selEnd = GetStartOfNextRowFromRowContainingPosition (*selEnd);
1661 if (*selEnd <= origSelEnd) {
1662 *selEnd = GetEndOfRowContainingPosition (origSelEnd);
1675size_t TextInteractor::GetCharAtClickLocation (
const Led_Point& where)
const
1677 size_t clickedOnChar = GetCharAtWindowLocation (where);
1678 size_t endOfClickedRow = GetEndOfRowContainingPosition (clickedOnChar);
1691 if (GetEndOfRowContainingPosition (FindPreviousCharacter (clickedOnChar)) == clickedOnChar) {
1692 endOfClickedRow = clickedOnChar;
1695 Assert (GetStartOfRowContainingPosition (clickedOnChar) <= clickedOnChar);
1696 Assert (clickedOnChar <= endOfClickedRow);
1698 if (clickedOnChar < endOfClickedRow) {
1699 Led_Rect charRect = GetCharWindowLocation (clickedOnChar);
1700 bool clickedToLHSOfChar = (where.h <= charRect.left + CoordinateType (charRect.GetWidth ()) / 2 and charRect.GetWidth () != 0);
1701 if (GetTextDirection (clickedOnChar) == eLeftToRight) {
1702 if (not clickedToLHSOfChar) {
1703 clickedOnChar = FindNextCharacter (clickedOnChar);
1707 if (clickedToLHSOfChar) {
1708 clickedOnChar = FindNextCharacter (clickedOnChar);
1712 return (clickedOnChar);
1715void TextInteractor::Draw (
const Led_Rect& ,
bool )
1717 NoteWindowPartiallyUpdated ();
1721void TextInteractor::DrawBefore (
const Led_Rect& ,
bool )
1725 if (not fScrollBarParamsValid) {
1726 UpdateScrollBars ();
1730void TextInteractor::DrawAfter (
const Led_Rect& ,
bool printing)
1732 if (GetUseSecondaryHilight () and not GetSelectionShown () and not printing) {
1734 GetSelectionWindowRegion (&r, GetSelectionStart (), GetSelectionEnd ());
1735 Tablet_Acquirer tablet_ (
this);
1736 Tablet* tablet = tablet_;
1737 tablet->FrameRegion (r, Led_GetSelectedTextBackgroundColor ());
1741void TextInteractor::SetTopRowInWindow (
size_t newTopRow, UpdateMode updateMode)
1743 TemporarilySetUpdateMode updateModeSetter (*
this, updateMode);
1744 TextImager* tim =
this;
1745 tim->SetTopRowInWindow (newTopRow);
1748void TextInteractor::ScrollByIfRoom (ptrdiff_t downBy, UpdateMode updateMode)
1750 TemporarilySetUpdateMode updateModeSetter (*
this, updateMode);
1751 TextImager* tim =
this;
1752 tim->ScrollByIfRoom (downBy);
1755void TextInteractor::ScrollSoShowing (
size_t markerPos,
size_t andTryToShowMarkerPos, UpdateMode updateMode)
1757 TemporarilySetUpdateMode updateModeSetter (*
this, updateMode);
1758 TextImager* tim =
this;
1759 tim->ScrollSoShowing (markerPos, andTryToShowMarkerPos);
1762void TextInteractor::SetDefaultFont (
const IncrementalFontSpecification& defaultFont, UpdateMode updateMode)
1764 TemporarilySetUpdateMode updateModeSetter{*
this, updateMode};
1765 TextImager* tim =
this;
1766 tim->SetDefaultFont (defaultFont);
1770void TextInteractor::SetWindowRect (
const Led_Rect& windowRect, UpdateMode updateMode)
1772 TemporarilySetUpdateMode updateModeSetter (*
this, updateMode);
1773 TextImager* tim =
this;
1774 tim->SetWindowRect (windowRect);
1777void TextInteractor::SetHScrollPos (CoordinateType hScrollPos)
1779 PreScrollInfo preScrollInfo;
1780 PreScrollHelper (eDefaultUpdate, &preScrollInfo);
1781 TextImager::SetHScrollPos (hScrollPos);
1782 PostScrollHelper (preScrollInfo);
1785void TextInteractor::SetHScrollPos (CoordinateType hScrollPos, UpdateMode updateMode)
1787 TemporarilySetUpdateMode updateModeSetter (*
this, updateMode);
1788 TextImager* tim =
this;
1789 tim->SetHScrollPos (hScrollPos);
1792void TextInteractor::PreScrollHelper (UpdateMode updateMode, PreScrollInfo* preScrollInfo)
1794 UpdateMode realUpdateMode = RealUpdateMode (updateMode);
1795 preScrollInfo->fUpdateMode = realUpdateMode;
1796 if (realUpdateMode != eNoUpdate) {
1797 preScrollInfo->fOldWindowStart = GetMarkerPositionOfStartOfWindow ();
1798 preScrollInfo->fOldHScrollPos = GetHScrollPos ();
1799 preScrollInfo->fTryTodoScrollbits = GetUseBitmapScrollingOptimization () and
bool (realUpdateMode == eImmediateUpdate);
1800 if (preScrollInfo->fTryTodoScrollbits) {
1801 preScrollInfo->fOldLastRowStart = GetMarkerPositionOfStartOfLastRowOfWindow ();
1807 preScrollInfo->fTryTodoScrollbits =
false;
1813void TextInteractor::PostScrollHelper (PreScrollInfo preScrollInfo)
1818 size_t newStartOfWindow = GetMarkerPositionOfStartOfWindow ();
1819 preScrollInfo.fUpdateMode = RealUpdateMode (preScrollInfo.fUpdateMode);
1820 if (preScrollInfo.fUpdateMode != eNoUpdate and
1821 ((preScrollInfo.fOldWindowStart != newStartOfWindow) or (preScrollInfo.fOldHScrollPos != GetHScrollPos ()))) {
1824 if (preScrollInfo.fTryTodoScrollbits and preScrollInfo.fOldHScrollPos == GetHScrollPos ()) {
1825 Led_Rect windowRect = GetWindowRect ();
1827 Tablet_Acquirer tablet_{
this};
1828 Tablet* tablet = tablet_;
1829 if (preScrollInfo.fOldWindowStart > newStartOfWindow) {
1835 CoordinateType newPos = GetCharWindowLocation (GetStartOfRowContainingPosition (preScrollInfo.fOldWindowStart)).top;
1838 tablet->ScrollBitsAndInvalRevealed (windowRect, newPos - windowRect.top);
1842 preScrollInfo.fTryTodoScrollbits =
false;
1846 preScrollInfo.fTryTodoScrollbits =
false;
1848 if (preScrollInfo.fTryTodoScrollbits) {
1863 CoordinateType lastRowBottom = GetCharWindowLocation (GetMarkerPositionOfStartOfLastRowOfWindow ()).bottom;
1864 Led_Rect eraser = windowRect;
1865 eraser.top = lastRowBottom;
1866 RefreshWindowRect (eraser, preScrollInfo.fUpdateMode);
1876 CoordinateType newPos = GetCharLocationRowRelativeByPosition (newStartOfWindow, preScrollInfo.fOldWindowStart, 5).top;
1880 tablet->ScrollBitsAndInvalRevealed (windowRect, -newPos);
1884 preScrollInfo.fTryTodoScrollbits =
false;
1888 preScrollInfo.fTryTodoScrollbits =
false;
1891 if (preScrollInfo.fTryTodoScrollbits) {
1895 CoordinateType lastRowBottom = GetCharWindowLocation (preScrollInfo.fOldLastRowStart).bottom;
1896 Led_Rect eraser = windowRect;
1897 eraser.top = lastRowBottom;
1898 RefreshWindowRect (eraser, eDelayedUpdate);
1901 if (preScrollInfo.fUpdateMode == eImmediateUpdate) {
1908 if (preScrollInfo.fTryTodoScrollbits) {
1913 Refresh (preScrollInfo.fUpdateMode);
1917void TextInteractor::Replace (
size_t from,
size_t to,
const Led_tChar* withWhat,
size_t withWhatCharCount, UpdateMode updateMode)
1919 if (from != to or withWhatCharCount != 0) {
1920 Assert (fDoingUpdateModeReplaceOn ==
nullptr);
1921 fDoingUpdateModeReplaceOn =
this;
1923 PreReplaceInfo preReplaceInfo;
1924 PreReplace (from, to, withWhatCharCount, updateMode, &preReplaceInfo);
1925 GetTextStore ().Replace (from, to, withWhat, withWhatCharCount);
1926 PostReplace (preReplaceInfo);
1927 Assert (fDoingUpdateModeReplaceOn ==
this);
1930 Assert (fDoingUpdateModeReplaceOn ==
this);
1931 fDoingUpdateModeReplaceOn =
nullptr;
1934 fDoingUpdateModeReplaceOn =
nullptr;
1938void TextInteractor::AboutToUpdateText (
const MarkerOwner::UpdateInfo& updateInfo)
1940 TextImager::AboutToUpdateText (updateInfo);
1941 if (fDoingUpdateModeReplaceOn !=
this) {
1945 PreReplace (updateInfo.fReplaceFrom, min (updateInfo.fReplaceTo, GetEnd ()), updateInfo.fTextLength, eDefaultUpdate, &fTmpPreReplaceInfo);
1949void TextInteractor::DidUpdateText (
const UpdateInfo& updateInfo)
noexcept
1951 TextImager::DidUpdateText (updateInfo);
1952 if (fDoingUpdateModeReplaceOn !=
this) {
1954 PostReplace (fTmpPreReplaceInfo);
1962void TextInteractor::PreReplace (
size_t from,
size_t to,
size_t withWhatCharCount, UpdateMode updateMode, PreReplaceInfo* preReplaceInfo)
1966 if (preReplaceInfo->fTextInteractor !=
nullptr) {
1967 AbortReplace (*preReplaceInfo);
1970 updateMode = RealUpdateMode (updateMode);
1972 preReplaceInfo->fFrom = from;
1973 preReplaceInfo->fTo = to;
1974 preReplaceInfo->fWithWhatCharCount = withWhatCharCount;
1975 preReplaceInfo->fUpdateMode = updateMode;
1977 if (updateMode == eNoUpdate) {
1981 if (updateMode == eDelayedUpdate and IsWholeWindowInvalid ()) {
1982 preReplaceInfo->fUpdateMode = eNoUpdate;
2001 size_t endOfWindow = GetMarkerPositionOfEndOfWindow ();
2002 if (from > endOfWindow) {
2007 size_t endOfNextRow = GetEndOfRowContainingPosition (FindNextCharacter (endOfWindow));
2008 if (from > endOfNextRow) {
2009 updateMode = eNoUpdate;
2010 preReplaceInfo->fUpdateMode = eNoUpdate;
2016 Assert (updateMode != eNoUpdate);
2028 size_t startPositionOfRowWhereReplaceBegins = GetStartOfRowContainingPosition (from);
2029 size_t startPositionOfRowAfterReplaceEnds = GetEndOfRowContainingPosition (to);
2030 if (startPositionOfRowAfterReplaceEnds < GetTextStore ().GetEnd ()) {
2031 startPositionOfRowAfterReplaceEnds = GetStartOfRowContainingPosition (FindNextCharacter (startPositionOfRowAfterReplaceEnds));
2032 Assert (GetEndOfRowContainingPosition (to) <= startPositionOfRowAfterReplaceEnds);
2034 Assert (startPositionOfRowWhereReplaceBegins <= startPositionOfRowAfterReplaceEnds);
2036 preReplaceInfo->fBoundingUpdateHeight =
2037 GetTextWindowBoundingRect (startPositionOfRowWhereReplaceBegins, startPositionOfRowAfterReplaceEnds).GetHeight ();
2044 size_t expandedFromMarkerPos = 0;
2045 size_t expandedToMarkerPos = 0;
2046 GetStableTypingRegionContaingMarkerRange (from, to, &expandedFromMarkerPos, &expandedToMarkerPos);
2047 if (expandedFromMarkerPos == startPositionOfRowWhereReplaceBegins and expandedToMarkerPos == startPositionOfRowAfterReplaceEnds) {
2049 preReplaceInfo->fStableTypingRegionHeight = preReplaceInfo->fBoundingUpdateHeight;
2050 Assert (preReplaceInfo->fStableTypingRegionHeight == GetTextWindowBoundingRect (expandedFromMarkerPos, expandedToMarkerPos).GetHeight ());
2053 preReplaceInfo->fStableTypingRegionHeight = GetTextWindowBoundingRect (expandedFromMarkerPos, expandedToMarkerPos).GetHeight ();
2061 GetTextStore ().AddMarker (&preReplaceInfo->fBoundingUpdateMarker, startPositionOfRowWhereReplaceBegins,
2062 (startPositionOfRowAfterReplaceEnds - startPositionOfRowWhereReplaceBegins) + 1,
this);
2063 preReplaceInfo->fTextInteractor =
this;
2065 catch (NotFullyInitialized&) {
2067 preReplaceInfo->fUpdateMode = eNoUpdate;
2075void TextInteractor::PostReplace (PreReplaceInfo& preReplaceInfo)
2077 UpdateMode updateMode = preReplaceInfo.fUpdateMode;
2078 if (updateMode != eNoUpdate) {
2079 size_t from = preReplaceInfo.fFrom;
2080 size_t withWhatCharCount = preReplaceInfo.fWithWhatCharCount;
2082 size_t newTo = from + withWhatCharCount;
2085 size_t startPositionOfRowWhereReplaceBegins = preReplaceInfo.fBoundingUpdateMarker.GetStart ();
2086 size_t startPositionOfRowAfterReplaceEnds = preReplaceInfo.fBoundingUpdateMarker.GetEnd () - 1;
2087 Assert (startPositionOfRowWhereReplaceBegins <= startPositionOfRowAfterReplaceEnds);
2089 size_t stableTypingRegionStart = 0;
2090 size_t stableTypingRegionEnd = 0;
2091 GetStableTypingRegionContaingMarkerRange (from, newTo, &stableTypingRegionStart, &stableTypingRegionEnd);
2093 size_t expandedFromMarkerPos = 0;
2094 size_t expandedToMarkerPos = 0;
2095 ExpandedFromAndToInPostReplace (from, newTo, stableTypingRegionStart, stableTypingRegionEnd, startPositionOfRowWhereReplaceBegins,
2096 startPositionOfRowAfterReplaceEnds, &expandedFromMarkerPos, &expandedToMarkerPos);
2098 Led_Rect windowRect = GetWindowRect ();
2099 Led_Rect expandedFromToMarkerRect = GetTextWindowBoundingRect (expandedFromMarkerPos, expandedToMarkerPos);
2100 Led_Rect updateRect = expandedFromToMarkerRect;
2103 updateRect.right = windowRect.right;
2107 Led_Rect revisedRect = expandedFromToMarkerRect;
2108 if (expandedFromMarkerPos != startPositionOfRowWhereReplaceBegins or expandedToMarkerPos != startPositionOfRowAfterReplaceEnds) {
2109 revisedRect = GetTextWindowBoundingRect (startPositionOfRowWhereReplaceBegins, startPositionOfRowAfterReplaceEnds);
2111 Assert (revisedRect == GetTextWindowBoundingRect (startPositionOfRowWhereReplaceBegins, startPositionOfRowAfterReplaceEnds));
2113 if (preReplaceInfo.fBoundingUpdateHeight != revisedRect.GetHeight ()) {
2114 updateRect = Led_Rect (updateRect.top, windowRect.left, windowRect.bottom - updateRect.top, windowRect.GetWidth ());
2120 Led_Rect revisedRect = expandedFromToMarkerRect;
2121 if (expandedFromMarkerPos != stableTypingRegionStart or expandedToMarkerPos != stableTypingRegionEnd) {
2122 revisedRect = GetTextWindowBoundingRect (stableTypingRegionStart, stableTypingRegionEnd);
2124 Assert (revisedRect == GetTextWindowBoundingRect (stableTypingRegionStart, stableTypingRegionEnd));
2125 if (preReplaceInfo.fStableTypingRegionHeight != revisedRect.GetHeight ()) {
2126 updateRect = Led_Rect (updateRect.top, windowRect.left, windowRect.bottom - updateRect.top, windowRect.GetWidth ());
2130 RefreshWindowRect (updateRect, updateMode);
2134void TextInteractor::AbortReplace (PreReplaceInfo& preReplaceInfo)
2136 if (preReplaceInfo.fTextInteractor !=
nullptr) {
2138 preReplaceInfo.fTextInteractor->GetTextStore ().RemoveMarker (&preReplaceInfo.fBoundingUpdateMarker);
2139 preReplaceInfo.fTextInteractor =
nullptr;
2143void TextInteractor::ExpandedFromAndToInPostReplace (
size_t from,
size_t newTo,
size_t stableTypingRegionStart,
2144 size_t stableTypingRegionEnd,
size_t startPositionOfRowWhereReplaceBegins,
2145 size_t startPositionOfRowAfterReplaceEnds,
size_t* expandedFrom,
size_t* expandedTo)
2155 size_t expandedFromMarkerPos = 0;
2156 if (GetStartOfRowContainingPosition (from) == startPositionOfRowWhereReplaceBegins) {
2157 expandedFromMarkerPos = from;
2163 if (expandedFromMarkerPos > startPositionOfRowWhereReplaceBegins) {
2164 expandedFromMarkerPos = FindPreviousCharacter (expandedFromMarkerPos);
2169 expandedFromMarkerPos = stableTypingRegionStart;
2175 size_t expandedToMarkerPos = GetEndOfRowContainingPosition (newTo);
2177 size_t nowStartOfNextRow = expandedToMarkerPos;
2178 if (nowStartOfNextRow < GetTextStore ().GetEnd ()) {
2179 nowStartOfNextRow = GetStartOfRowContainingPosition (FindNextCharacter (nowStartOfNextRow));
2180 Assert (expandedToMarkerPos <= nowStartOfNextRow);
2183 if (nowStartOfNextRow != startPositionOfRowAfterReplaceEnds) {
2185 expandedToMarkerPos = stableTypingRegionEnd;
2189 *expandedFrom = expandedFromMarkerPos;
2190 *expandedTo = expandedToMarkerPos;
2193void TextInteractor::InteractiveReplace (
const Led_tChar* withWhat,
size_t withWhatCharCount, UpdateMode updateMode)
2195 BreakInGroupedCommandsIfDifferentCommand (GetCommandNames ().fTypingCommandName);
2196 InteractiveModeUpdater iuMode{*
this};
2197 UndoableContextHelper undoContext{*
this, GetCommandNames ().fTypingCommandName, withWhatCharCount == 0};
2199 InteractiveReplace_ (undoContext.GetUndoRegionStart (), undoContext.GetUndoRegionEnd (), withWhat, withWhatCharCount,
true,
true, updateMode);
2200 bool anyChanges = InteractiveReplaceEarlyPostReplaceHook (withWhatCharCount);
2201 if (withWhatCharCount == 1 and not anyChanges) {
2203 undoContext.SetSimplePlainTextInsertOptimization (
true);
2206 undoContext.CommandComplete ();
2213void TextInteractor::InteractiveReplace_ (
size_t from,
size_t to,
const Led_tChar* withWhat,
size_t withWhatCharCount,
2214 bool updateCursorPosition,
bool validateTextForCharsetConformance, UpdateMode updateMode)
2217 if (validateTextForCharsetConformance) {
2219 if (not ValidateTextForCharsetConformance (withWhat, withWhatCharCount)) {
2225 TempMarker newSel{GetTextStore (), to + 1, to + 1};
2231 Tablet_Acquirer performanceHackTablet{
this};
2237 SetCaretShownAfterPos (
true);
2239 Replace (from, to, withWhat, withWhatCharCount, updateMode);
2240 if (updateCursorPosition) {
2241 size_t newSelection = newSel.GetStart ();
2242 if (newSelection > 0) {
2246 SetSelection (newSelection, newSelection);
2261bool TextInteractor::InteractiveReplaceEarlyPostReplaceHook (
size_t )
2278void TextInteractor::PreInteractiveUndoHelper (InteractiveReplaceCommand::SavedTextRep** beforeRep,
size_t regionStart,
size_t regionEnd,
2279 size_t selStart,
size_t selEnd)
2281 Require (regionStart <= regionEnd);
2282 Require (selStart <= selEnd);
2284 Require ((*beforeRep) ==
nullptr);
2288 (*beforeRep) = InteractiveUndoHelperMakeTextRep (regionStart, regionEnd, selStart, selEnd);
2297 fCommandHandler->Commit ();
2312void TextInteractor::PostInteractiveUndoHelper (InteractiveReplaceCommand::SavedTextRep** beforeRep,
size_t startOfInsert,
2313 size_t endOfInsert,
const SDKString& cmdName)
2318 SavedTextRep* afterRep =
nullptr;
2320 afterRep = InteractiveUndoHelperMakeTextRep (startOfInsert, endOfInsert, GetSelectionStart (), GetSelectionEnd ());
2321 PostInteractiveUndoPostHelper (beforeRep, &afterRep, startOfInsert, cmdName);
2324 delete (*beforeRep);
2325 (*beforeRep) =
nullptr;
2337void TextInteractor::PostInteractiveSimpleCharInsertUndoHelper (InteractiveReplaceCommand::SavedTextRep** beforeRep,
size_t startOfInsert,
2338 size_t endOfInsert,
const SDKString& cmdName)
2343 if (endOfInsert - startOfInsert == 1) {
2345 CopyOut (startOfInsert, 1, &c);
2346 if (fCommandHandler->PostUpdateSimpleTextInsert (startOfInsert, c)) {
2348 *beforeRep =
nullptr;
2352 SavedTextRep* afterRep =
new InteractiveReplaceCommand::PlainTextRep (GetSelectionStart (), GetSelectionEnd (), &c, 1);
2353 PostInteractiveUndoPostHelper (beforeRep, &afterRep, startOfInsert, cmdName);
2354 Assert (afterRep ==
nullptr);
2358 PostInteractiveUndoHelper (beforeRep, startOfInsert, endOfInsert, cmdName);
2369void TextInteractor::PostInteractiveUndoPostHelper (InteractiveReplaceCommand::SavedTextRep** beforeRep,
2370 InteractiveReplaceCommand::SavedTextRep** afterRep,
size_t startOfInsert,
const SDKString& cmdName)
2376 if (*beforeRep !=
nullptr and *afterRep !=
nullptr) {
2379 InteractiveReplaceCommand* cmd =
new InteractiveReplaceCommand (*beforeRep, *afterRep, startOfInsert, cmdName);
2380 *beforeRep =
nullptr;
2381 *afterRep =
nullptr;
2382 fCommandHandler->Post (cmd);
2387 *beforeRep =
nullptr;
2389 *afterRep =
nullptr;
2394InteractiveReplaceCommand::SavedTextRep* TextInteractor::InteractiveUndoHelperMakeTextRep (
size_t regionStart,
size_t regionEnd,
2395 size_t selStart,
size_t selEnd)
2397 if (regionStart == regionEnd) {
2399 return new InteractiveReplaceCommand::PlainTextRep (selStart, selEnd,
nullptr, 0);
2402 return new FlavorSavorTextRep (
this, regionStart, regionEnd, selStart, selEnd);
2406void TextInteractor::OnUndoCommand ()
2408 InteractiveModeUpdater iuMode (*
this);
2409 if (GetCommandHandler () !=
nullptr and GetCommandHandler ()->CanUndo ()) {
2410 GetCommandHandler ()->DoUndo (*
this);
2411 ScrollToSelection ();
2418void TextInteractor::OnRedoCommand ()
2420 InteractiveModeUpdater iuMode (*
this);
2421 if (GetCommandHandler () !=
nullptr and GetCommandHandler ()->CanRedo ()) {
2422 GetCommandHandler ()->DoRedo (*
this);
2423 ScrollToSelection ();
2430void TextInteractor::Refresh (
size_t from,
size_t to, UpdateMode updateMode)
const
2432 Require (from <= to);
2433 updateMode = RealUpdateMode (updateMode);
2434 if ((updateMode != eNoUpdate) and (from != to)) {
2435 if (updateMode == eDelayedUpdate and IsWholeWindowInvalid ()) {
2443 Led_Rect refreshRect = GetTextWindowBoundingRect (from, to);
2444 RefreshWindowRect_ (refreshRect, updateMode);
2448void TextInteractor::Refresh (
const Marker* range, UpdateMode updateMode)
const
2451 updateMode = RealUpdateMode (updateMode);
2452 if (updateMode != eNoUpdate) {
2453 if (updateMode == eDelayedUpdate and IsWholeWindowInvalid ()) {
2456 Refresh (range->GetStart (), range->GetEnd (), updateMode);
2465void TextInteractor::DoSingleCharCursorEdit (CursorMovementDirection direction, CursorMovementUnit movementUnit,
2466 CursorMovementAction action, UpdateMode updateMode,
bool scrollToSelection)
2468 IdleManager::NonIdleContext nonIdleContext;
2470 size_t oldStartSel = GetSelectionStart ();
2471 size_t oldEndSel = GetSelectionEnd ();
2472 Assert (GetSelectionEnd () <= GetLength () + 1);
2474 size_t newStartSel = oldStartSel;
2475 size_t newEndSel = oldEndSel;
2477 UpdateMode useUpdateMode = (updateMode == eImmediateUpdate) ? eDelayedUpdate : updateMode;
2479 GoalColumnRecomputerControlContext skipRecompute (*
this, action == eCursorMoving and movementUnit == eCursorByRow and
2480 (direction == eCursorBack or direction == eCursorForward));
2491 if (movementUnit == eCursorByRow and GetStartOfRowContainingPosition (newStartSel) == newStartSel) {
2492 if (GetCaretShownAfterPos ()) {
2493 if (direction == eCursorToEnd and GetEndOfRowContainingPosition (newStartSel) == newStartSel) {
2494 goto SkipNavigation;
2498 if (direction == eCursorToStart) {
2501 newStartSel = FindPreviousCharacter (newStartSel);
2503 if (direction == eCursorToEnd) {
2504 goto SkipNavigation;
2512 if (movementUnit == eCursorByChar) {
2513 SetCaretShownAfterPos (
true);
2515 if (movementUnit == eCursorByRow) {
2516 switch (direction) {
2517 case eCursorToStart:
2518 SetCaretShownAfterPos (
true);
2521 SetCaretShownAfterPos (
false);
2526 if (action != eCursorExtendingSelection or (oldStartSel == oldEndSel)) {
2527 fLeftSideOfSelectionInteresting = (direction == eCursorBack or direction == eCursorToStart);
2529 if (fLeftSideOfSelectionInteresting) {
2530 newStartSel = ComputeRelativePosition (newStartSel, direction, movementUnit);
2531 if (action == eCursorMoving) {
2532 newEndSel = newStartSel;
2536 newEndSel = ComputeRelativePosition (newEndSel, direction, movementUnit);
2537 if (action == eCursorMoving) {
2538 newStartSel = newEndSel;
2545 if (newEndSel < newStartSel) {
2546 size_t tmp = newStartSel;
2547 newStartSel = newEndSel;
2550 Assert (newStartSel <= newEndSel);
2556 case eCursorDestroying: {
2557 if (oldStartSel == oldEndSel) {
2562 Assert (newEndSel >= newStartSel);
2563 size_t howMany = newEndSel - newStartSel;
2565 bool oldCutAndPaste = GetSmartCutAndPasteMode ();
2567 SetSmartCutAndPasteMode (
false);
2569 BreakInGroupedCommandsIfDifferentCommand (GetCommandNames ().fClearCommandName);
2570 InteractiveModeUpdater iuMode (*
this);
2571 UndoableContextHelper undoContext (*
this, GetCommandNames ().fClearCommandName, newStartSel, newEndSel,
2572 GetSelectionStart (), GetSelectionEnd (),
true);
2574 InteractiveReplace_ (undoContext.GetUndoRegionStart (), undoContext.GetUndoRegionEnd (), LED_TCHAR_OF (
""),
2575 0,
true,
true, useUpdateMode);
2577 undoContext.CommandComplete ();
2582 GetSelection (&newStartSel, &newEndSel);
2583 SetSmartCutAndPasteMode (oldCutAndPaste);
2586 SetSmartCutAndPasteMode (oldCutAndPaste);
2598 Assert (oldEndSel >= oldStartSel);
2599 Assert (oldStartSel == GetSelectionStart ());
2600 Assert (oldEndSel == GetSelectionEnd ());
2605 GetSelection (&newStartSel, &newEndSel);
2609 case eCursorMoving: {
2613 case eCursorExtendingSelection: {
2627 SetSelection (newStartSel, newEndSel, useUpdateMode);
2629 if (scrollToSelection) {
2630 ScrollToSelection (useUpdateMode,
true);
2633 if (updateMode == eImmediateUpdate) {
2638void TextInteractor::OnCutCommand ()
2640 InteractiveModeUpdater iuMode (*
this);
2641 BreakInGroupedCommands ();
2642 if (GetSelectionStart () != GetSelectionEnd ()) {
2644 UndoableContextHelper undoContext (*
this, GetCommandNames ().fCutCommandName,
true);
2646 InteractiveReplace_ (undoContext.GetUndoRegionStart (), undoContext.GetUndoRegionEnd (), LED_TCHAR_OF (
""), 0);
2648 undoContext.CommandComplete ();
2650 BreakInGroupedCommands ();
2653void TextInteractor::OnCopyCommand ()
2655 size_t start = GetSelectionStart ();
2656 size_t end = GetSelectionEnd ();
2657 Assert (start <= end);
2659 BreakInGroupedCommands ();
2661 if (OnCopyCommand_Before ()) {
2663 OnCopyCommand_CopyFlavors ();
2666 OnCopyCommand_After ();
2669 OnCopyCommand_After ();
2674void TextInteractor::OnPasteCommand ()
2676 InteractiveModeUpdater iuMode (*
this);
2677 BreakInGroupedCommands ();
2679 if (OnPasteCommand_Before ()) {
2681 UndoableContextHelper undoContext (*
this, GetCommandNames ().fPasteCommandName,
false);
2683 OnPasteCommand_PasteBestFlavor ();
2685 undoContext.CommandComplete ();
2688 OnPasteCommand_After ();
2691 OnPasteCommand_After ();
2693 BreakInGroupedCommands ();
2696void TextInteractor::OnClearCommand ()
2698 InteractiveModeUpdater iuMode (*
this);
2699 BreakInGroupedCommands ();
2700 UndoableContextHelper undoContext (*
this, GetCommandNames ().fClearCommandName,
true);
2702 InteractiveReplace_ (undoContext.GetUndoRegionStart (), undoContext.GetUndoRegionEnd (), LED_TCHAR_OF (
""), 0);
2704 undoContext.CommandComplete ();
2705 BreakInGroupedCommands ();
2721bool TextInteractor::OnCopyCommand_Before ()
2731void TextInteractor::OnCopyCommand_After ()
2740void TextInteractor::OnCopyCommand_CopyFlavors ()
2742 WriterClipboardFlavorPackage writer;
2743 ExternalizeFlavors (writer);
2746bool TextInteractor::ShouldEnablePasteCommand ()
const
2748 return Led_ClipboardObjectAcquire::FormatAvailable_TEXT ();
2763bool TextInteractor::OnPasteCommand_Before ()
2773void TextInteractor::OnPasteCommand_After ()
2782void TextInteractor::OnPasteCommand_PasteBestFlavor ()
2784#if qStroika_Platform_Windows && 0
2788 long clipFormat = 0;
2789 while ((clipFormat = ::EnumClipboardFormats (clipFormat)) != 0) {
2791 int nChars = ::GetClipboardFormatName (clipFormat, buf, std::size (buf));
2796 ReaderClipboardFlavorPackage clipData;
2798 SmartCNPInfo smartCNPInfo;
2799 bool doSmartCNP = PasteLooksLikeSmartCNP (&smartCNPInfo);
2800 size_t savedSelStart = GetSelectionStart ();
2801 InternalizeBestFlavor (clipData);
2802 Assert (savedSelStart <= GetSelectionStart ());
2804 OptionallyAddExtraSpaceForSmartCutAndPasteModeAdds (savedSelStart, smartCNPInfo);
2813void TextInteractor::OnPasteCommand_PasteFlavor_Specific (Led_ClipFormat format)
2815 ReaderClipboardFlavorPackage clipData;
2816 SmartCNPInfo smartCNPInfo;
2817 bool doSmartCNP = PasteLooksLikeSmartCNP (&smartCNPInfo);
2818 size_t savedSelStart = GetSelectionStart ();
2819 InternalizeFlavor_Specific (clipData, format);
2820 Assert (savedSelStart <= GetSelectionStart ());
2822 OptionallyAddExtraSpaceForSmartCutAndPasteModeAdds (savedSelStart, smartCNPInfo);
2831bool TextInteractor::PasteLooksLikeSmartCNP (SmartCNPInfo* scnpInfo)
const
2834 ReaderClipboardFlavorPackage clipData;
2835 bool doSmartCNP = GetSmartCutAndPasteMode () and clipData.GetFlavorAvailable_TEXT ();
2840 size_t length = clipData.GetFlavorSize (kTEXTClipFormat);
2841 Led_ClipFormat textFormat = kTEXTClipFormat;
2844 length = clipData.ReadFlavorData (textFormat, length, buf.data ());
2846 Led_tChar* buffp =
reinterpret_cast<Led_tChar*
> (
static_cast<char*
> (buf));
2847 size_t nTChars = length /
sizeof (Led_tChar);
2848 doSmartCNP = LooksLikeSmartPastableText (buffp, nTChars, scnpInfo);
2859void TextInteractor::OnSelectAllCommand ()
2861 SetSelection (0, GetLength ());
2864bool TextInteractor::CanAcceptFlavor (Led_ClipFormat clipFormat)
const
2866 return (kTEXTClipFormat == clipFormat or kFILEClipFormat == clipFormat);
2869void TextInteractor::InternalizeBestFlavor (
ReaderFlavorPackage& flavorPackage,
bool updateCursorPosition,
bool autoScroll, UpdateMode updateMode)
2871 size_t start = GetSelectionStart ();
2872 size_t end = GetSelectionEnd ();
2876 TempMarker newSel{GetTextStore (), end + 1, end + 1};
2881 good = fInternalizer->InternalizeBestFlavor (flavorPackage, start, end);
2882 if (good and updateCursorPosition) {
2883 SetCaretShownAfterPos (
true);
2888 size_t newSelection = newSel.GetStart ();
2889 if (newSelection > start) {
2893 SetSelection (newSelection, newSelection);
2899 ScrollToSelection ();
2901 if (updateMode == eImmediateUpdate) {
2914void TextInteractor::InternalizeFlavor_Specific (
ReaderFlavorPackage& flavorPackage, Led_ClipFormat format,
bool updateCursorPosition,
2915 bool autoScroll, UpdateMode updateMode)
2917 size_t start = GetSelectionStart ();
2918 size_t end = GetSelectionEnd ();
2922 TempMarker newSel{GetTextStore (), end + 1, end + 1};
2928 if (format == kTEXTClipFormat) {
2929 good = fInternalizer->InternalizeFlavor_TEXT (flavorPackage, start, end);
2931 else if (format == kFILEClipFormat) {
2932 good = fInternalizer->InternalizeFlavor_FILE (flavorPackage, start, end);
2935 good = fInternalizer->InternalizeBestFlavor (flavorPackage, start, end);
2938 if (good and updateCursorPosition) {
2939 SetCaretShownAfterPos (
true);
2944 size_t newSelection = newSel.GetStart ();
2945 if (newSelection > start) {
2949 SetSelection (newSelection, newSelection);
2955 ScrollToSelection ();
2957 if (updateMode == eImmediateUpdate) {
2972shared_ptr<FlavorPackageInternalizer> TextInteractor::MakeDefaultInternalizer ()
2974 return Memory::MakeSharedPtr<FlavorPackageInternalizer> (GetTextStore ());
2981void TextInteractor::HookInternalizerChanged ()
2992 fExternalizer->ExternalizeFlavors (flavorPackage, GetSelectionStart (), GetSelectionEnd ());
3002 fExternalizer->ExternalizeBestFlavor (flavorPackage, GetSelectionStart (), GetSelectionEnd ());
3011shared_ptr<FlavorPackageExternalizer> TextInteractor::MakeDefaultExternalizer ()
3013 return Memory::MakeSharedPtr<FlavorPackageExternalizer> (GetTextStore ());
3020void TextInteractor::HookExternalizerChanged ()
3030void TextInteractor::OnBadUserInput ()
3034 throw BadUserInput{};
3056void TextInteractor::SetScrollBarType (VHSelect vh, ScrollBarType scrollBarType)
3058 if (GetScrollBarType (vh) != scrollBarType) {
3059 InvalidateScrollBarParameters ();
3060 SetScrollBarType_ (vh, scrollBarType);
3069void TextInteractor::InvalidateScrollBarParameters ()
3071 InvalidateScrollBarParameters_ ();
3081void TextInteractor::UpdateScrollBars ()
3083 UpdateScrollBars_ ();
3090void TextInteractor::SetCaretShown (
bool shown)
3092 if (GetCaretShown () != shown) {
3093 fCaretShown = shown;
3094 InvalidateCaretState ();
3106bool TextInteractor::GetCaretShownSituation ()
const
3108 size_t selStart = 0;
3110 GetSelection (&selStart, &selEnd);
3111 return selStart == selEnd;
3118void TextInteractor::SetCaretShownAfterPos (
bool shownAfterPos)
3120 if (GetCaretShownAfterPos () != shownAfterPos) {
3121 InvalidateCaretState ();
3122 fCaretShownAfterPos = shownAfterPos;
3123 InvalidateCaretState ();
3143Led_Rect TextInteractor::CalculateCaretRect ()
const
3145 size_t selEnd = GetSelectionEnd ();
3146 if (GetSelectionStart () == selEnd) {
3147 bool showAfter = GetCaretShownAfterPos ();
3152 size_t charAfterPos = showAfter ? selEnd : FindPreviousCharacter (selEnd);
3153 TextDirection textDirection = GetTextDirection (charAfterPos);
3154 Led_Rect caretRect = GetCharWindowLocation (charAfterPos);
3156 if (caretRect.GetBottom () < GetWindowRect ().GetTop () or caretRect.GetTop () > GetWindowRect ().GetBottom ()) {
3157 return Led_Rect{0, 0, 0, 0};
3160 Led_Rect origCaretRect = caretRect;
3161 FontMetrics fontMetrics = GetFontMetricsAt (charAfterPos);
3164 DistanceType baseLineFromTop = GetRowRelativeBaselineOfRowContainingPosition (charAfterPos);
3165 CoordinateType realBaseLine = baseLineFromTop + caretRect.top;
3168 caretRect.top = realBaseLine - fontMetrics.GetAscent ();
3169 caretRect.bottom = realBaseLine + fontMetrics.GetDescent ();
3174 if (caretRect.top < origCaretRect.top) {
3175 DistanceType diff = origCaretRect.GetTop () - caretRect.GetTop ();
3176 caretRect += Led_Point (diff, 0);
3180 caretRect.SetTop (max (caretRect.GetTop (), origCaretRect.GetTop ()));
3181 caretRect.bottom = min (caretRect.GetBottom (), origCaretRect.GetBottom ());
3183 if (textDirection == eLeftToRight) {
3184 if (not showAfter) {
3185 caretRect.left = caretRect.right;
3189 caretRect.left = caretRect.right;
3192 const CoordinateType kCaretWidth = 1;
3194 const CoordinateType kSluff = kCaretWidth + 1;
3195 if (caretRect.GetLeft () + kSluff > GetWindowRect ().GetRight ()) {
3196 caretRect.SetLeft (GetWindowRect ().GetRight () - kSluff);
3198 caretRect.SetRight (caretRect.GetLeft () + kCaretWidth);
3200 Ensure (not caretRect.IsEmpty ());
3204 return (Led_Rect{0, 0, 0, 0});
3208void TextInteractor::InvalidateCaretState ()
3210 if (IsWholeWindowInvalid ()) {
3213 if (GetCaretShown () and (GetSelectionStart () == GetSelectionEnd ())) {
3214 RefreshWindowRect (CalculateCaretRect ());
3226void TextInteractor::OnTypedNormalCharacter (Led_tChar theChar,
bool ,
bool ,
bool ,
3227 bool controlPressed,
bool )
3229 IdleManager::NonIdleContext nonIdleContext;
3231 Assert (GetSelectionEnd () <= GetLength () + 1);
3233 if (GetSuppressTypedControlCharacters ()) {
3235 if (controlChar && (theChar ==
'\r' || theChar ==
'\n' || theChar ==
' ' || theChar ==
'\t' || theChar ==
'\b')) {
3236 controlChar =
false;
3246 CursorMovementDirection dir = eCursorBack;
3247 CursorMovementUnit unit = controlPressed ? eCursorByWord : eCursorByChar;
3248 CursorMovementAction action = eCursorDestroying;
3249 DoSingleCharCursorEdit (dir, unit, action, eDefaultUpdate);
3253 if (theChar ==
'\n') {
3254 BreakInGroupedCommands ();
3256 InteractiveReplace (&theChar, 1, eDefaultUpdate);
3260 ScrollToSelection ();
3261#if qStroika_Frameworks_Led_PeekForMoreCharsOnUserTyping
3262 UpdateIfNoKeysPending ();
3268bool TextInteractor::DelaySomeForScrollBarClick ()
3272 const int kTimesForFirstClick = 2;
3274 static short sTimesThruBeforeReset;
3278 fLastScrolledAt = now + kDelayAfterFirstTicks;
3279 sTimesThruBeforeReset = 1;
3282 else if (fLastScrolledAt < now) {
3283 ++sTimesThruBeforeReset;
3284 fLastScrolledAt = now + (sTimesThruBeforeReset <= kTimesForFirstClick ? kDelayAfterFirstTicks : kDelayAfterOtherTicks);
#define RequireNotNull(p)
time_point< RealtimeClock, DurationSeconds > TimePointSeconds
TimePointSeconds is a simpler approach to chrono::time_point, which doesn't require using templates e...
chrono::duration< double > DurationSeconds
chrono::duration<double> - a time span (length of time) measured in seconds, but high precision.
constexpr bool IsControl() const noexcept
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...
nonvirtual pointer data() noexcept
returns a (possibly const) pointer to the start of the live buffer data. This return value can be inv...
basic_string< SDKChar > SDKString