diff --git a/far/changelog b/far/changelog index e8e014ad13b..bf3c999c80a 100644 --- a/far/changelog +++ b/far/changelog @@ -1,3 +1,10 @@ +-------------------------------------------------------------------------------- +MZK 2023-03-04 16:30:43-08:00 - build 6111 + +1. gh-600: Shift+F7 and Alt+F7 now search in absolute directions (down or up respectfully) + instead of reversing the direction defined by the last invocation of the Search / Replace dialog. + Also, on the dialog itself, replaced "Reverse" checkbox with "Search Down / Up" buttons. + -------------------------------------------------------------------------------- drkns 2023-02-28 21:43:49+00:00 - build 6110 diff --git a/far/editor.cpp b/far/editor.cpp index 78aba67d878..85ad107aa0a 100644 --- a/far/editor.cpp +++ b/far/editor.cpp @@ -89,6 +89,16 @@ enum class Editor::undo_type: char end }; +enum class Editor::SearchReplaceDisposition +{ + Cancel, + Up, + Down, + All, + ContinueUp, + ContinueDown, +}; + /* $ 04.11.2003 SKV на любом выходе если была нажата кнопка выделения, и она его "сняла" (сделала 0-й ширины), то его надо убрать. @@ -129,11 +139,9 @@ Editor::Editor(window_ptr Owner, uintptr_t Codepage, bool DialogUsed): m_codepage(Codepage), EdOpt(Global->Opt->EdOpt), LastSearchDlgParams{ - .ShowButtonAll = true, .SearchStr = Global->GetSearchString(Codepage), .CaseSensitive = Global->GlobalSearchCaseSensitive, .WholeWords = Global->GlobalSearchWholeWords, - .Reverse = Global->GlobalSearchReverse, .Regexp = Global->Opt->EdOpt.SearchRegexp, .Fuzzy = Global->GlobalSearchFuzzy, .PreserveStyle = false // Consider: Should we introduce Global->Opt->EdOpt.ReplacePreserveStyle? @@ -212,7 +220,6 @@ void Editor::KeepInitParameters() const Global->StoreSearchString(LastSearchDlgParams.SearchStr, m_codepage, Global->GetSearchHex()); Global->GlobalSearchCaseSensitive = LastSearchDlgParams.CaseSensitive.value(); Global->GlobalSearchWholeWords = LastSearchDlgParams.WholeWords.value(); - Global->GlobalSearchReverse = LastSearchDlgParams.Reverse.value(); Global->Opt->EdOpt.SearchRegexp = LastSearchDlgParams.Regexp.value(); Global->GlobalSearchFuzzy = LastSearchDlgParams.Fuzzy.value(); } @@ -1867,16 +1874,7 @@ bool Editor::ProcessKeyInternal(const Manager::Key& Key, bool& Refresh) case KEY_F7: { - bool ReplaceMode0 = LastSearchDlgParams.ReplaceMode; - bool ReplaceAll0 = ReplaceAll; - LastSearchDlgParams.ReplaceMode=ReplaceAll=false; - - if (!Search(false)) - { - LastSearchDlgParams.ReplaceMode = ReplaceMode0; - ReplaceAll = ReplaceAll0; - } - + DoSearchReplace(ShowSearchReplaceDialog(false)); return true; } @@ -1885,33 +1883,15 @@ bool Editor::ProcessKeyInternal(const Manager::Key& Key, bool& Refresh) { if (!m_Flags.Check(FEDITOR_LOCKMODE)) { - bool ReplaceMode0 = LastSearchDlgParams.ReplaceMode; - bool ReplaceAll0 = ReplaceAll; - LastSearchDlgParams.ReplaceMode = true; - ReplaceAll = false; - - if (!Search(false)) - { - LastSearchDlgParams.ReplaceMode = ReplaceMode0; - ReplaceAll = ReplaceAll0; - } + DoSearchReplace(ShowSearchReplaceDialog(true)); } - return true; } case KEY_SHIFTF7: { - /* $ 20.09.2000 SVS - При All после нажатия Shift-F7 надобно снова спросить... - */ - //ReplaceAll=FALSE; - /* $ 07.05.2001 IS - Сказано в хелпе "Shift-F7 Продолжить _поиск_" - */ - //ReplaceMode=FALSE; TurnOffMarkingBlock(); - Search(true); + DoSearchReplace(SearchReplaceDisposition::ContinueDown); return true; } @@ -1919,10 +1899,7 @@ bool Editor::ProcessKeyInternal(const Manager::Key& Key, bool& Refresh) case KEY_RALTF7: { TurnOffMarkingBlock(); - bool LastSearchReverseOrig = LastSearchDlgParams.Reverse.value(); - LastSearchDlgParams.Reverse = !LastSearchReverseOrig; - Search(true); - LastSearchDlgParams.Reverse = LastSearchReverseOrig; + DoSearchReplace(SearchReplaceDisposition::ContinueUp); return true; } @@ -3278,81 +3255,97 @@ class undo_block Editor* m_Owner; }; -bool Editor::Search(bool Next) +Editor::SearchReplaceDisposition Editor::ShowSearchReplaceDialog(const bool ReplaceMode) { - bool FindAll{}; - - if (!Next) + const auto Picker = [this](bool PickSelection) { - const auto Picker = [this](bool PickSelection) + if (PickSelection) { - if (PickSelection) + if (IsAnySelection()) { - if (IsAnySelection()) + if (IsStreamSelection()) { - if (IsStreamSelection()) - { - intptr_t StartSel, EndSel; - m_it_AnyBlockStart->GetSelection(StartSel, EndSel); - if (StartSel != -1) - { - return m_it_AnyBlockStart->GetString().substr(StartSel, EndSel == -1 ? string::npos : EndSel - StartSel); - } - } - else + intptr_t StartSel, EndSel; + m_it_AnyBlockStart->GetSelection(StartSel, EndSel); + if (StartSel != -1) { - const size_t TBlockX = m_it_AnyBlockStart->VisualPosToReal(VBlockX); - const size_t TBlockSizeX = m_it_AnyBlockStart->VisualPosToReal(VBlockX + VBlockSizeX) - TBlockX; - const auto& Str = m_it_AnyBlockStart->GetString(); - if (TBlockX <= Str.size()) - { - const auto CopySize = std::min(Str.size() - TBlockX, TBlockSizeX); - return Str.substr(TBlockX, CopySize); - } + return m_it_AnyBlockStart->GetString().substr(StartSel, EndSel == -1 ? string::npos : EndSel - StartSel); } } else { - return m_it_CurLine->GetString(); + const size_t TBlockX = m_it_AnyBlockStart->VisualPosToReal(VBlockX); + const size_t TBlockSizeX = m_it_AnyBlockStart->VisualPosToReal(VBlockX + VBlockSizeX) - TBlockX; + const auto& Str = m_it_AnyBlockStart->GetString(); + if (TBlockX <= Str.size()) + { + const auto CopySize = std::min(Str.size() - TBlockX, TBlockSizeX); + return Str.substr(TBlockX, CopySize); + } } } else { - size_t PickBegin, PickEnd; - const auto& Str = m_it_CurLine->GetString(); - if (FindWordInString(Str, m_it_CurLine->GetCurPos(), PickBegin, PickEnd, EdOpt.strWordDiv)) - { - return Str.substr(PickBegin, PickEnd - PickBegin); - } + return m_it_CurLine->GetString(); } - return string{}; - }; + } + else + { + size_t PickBegin, PickEnd; + const auto& Str = m_it_CurLine->GetString(); + if (FindWordInString(Str, m_it_CurLine->GetCurPos(), PickBegin, PickEnd, EdOpt.strWordDiv)) + { + return Str.substr(PickBegin, PickEnd - PickBegin); + } + } + return string{}; + }; - switch (GetSearchReplaceString( - LastSearchDlgParams, - L"SearchText"sv, - L"ReplaceText"sv, - m_codepage, - L"EditorSearch"sv, - LastSearchDlgParams.ReplaceMode? &EditorReplaceId : &EditorSearchId, - Picker)) + switch (GetSearchReplaceString( { - case 0: - return false; + .ReplaceMode = ReplaceMode, + .ShowButtonsUpDown = true, + .ShowButtonAll = !ReplaceMode, + }, + LastSearchDlgParams, + L"SearchText"sv, + L"ReplaceText"sv, + m_codepage, + L"EditorSearch"sv, + ReplaceMode? &EditorReplaceId : &EditorSearchId, + Picker)) + { + case SearchReplaceDlgResult::Cancel: + return SearchReplaceDisposition::Cancel; - case 2: - FindAll = true; - break; + case SearchReplaceDlgResult::Up: + IsReplaceMode = ReplaceMode; + return SearchReplaceDisposition::Up; + + case SearchReplaceDlgResult::Down: + IsReplaceMode = ReplaceMode; + return SearchReplaceDisposition::Down; + + case SearchReplaceDlgResult::All: + IsReplaceMode = ReplaceMode; + return SearchReplaceDisposition::All; + case SearchReplaceDlgResult::Ok: default: - break; - } + UNREACHABLE; } +} - if (LastSearchDlgParams.SearchStr.empty()) - return true; +void Editor::DoSearchReplace(const SearchReplaceDisposition Disposition) +{ + if (Disposition == SearchReplaceDisposition::Cancel || LastSearchDlgParams.SearchStr.empty()) + return; - const auto Reverse = !FindAll && LastSearchDlgParams.Reverse.value(); + IsReplaceAll = false; + + const auto Backward{ Disposition == SearchReplaceDisposition::Up || Disposition == SearchReplaceDisposition::ContinueUp }; + const auto FindAll{ Disposition == SearchReplaceDisposition::All }; + const auto Continue{ Disposition == SearchReplaceDisposition::ContinueUp || Disposition == SearchReplaceDisposition::ContinueDown }; bool MatchFound{}, UserBreak{}; std::optional UndoBlock; @@ -3365,14 +3358,14 @@ bool Editor::Search(bool Next) auto CurPos = FindAll? 0 : m_it_CurLine->GetCurPos(); - if (Next && m_FoundLine == m_it_CurLine) + if (Continue && m_FoundLine == m_it_CurLine) { - if (Reverse) + if (Backward) { if (EdOpt.SearchCursorAtEnd) { if (CurPos == m_FoundPos + m_FoundSize) - CurPos -= m_FoundSize; + CurPos -= m_FoundSize; // 2023-03-04 MZK: See gh-542 } } else @@ -3405,7 +3398,7 @@ bool Editor::Search(bool Next) catch (regex_exception const& e) { ReCompileErrorMessage(e, LastSearchDlgParams.SearchStr); - return false; //BUGBUG + return; // Broken regex could not and was not found. Do as if the search string was not found. Do NOT restore IsReplaceAll. } } @@ -3435,14 +3428,14 @@ bool Editor::Search(bool Next) Progress.emplace(msg(lng::MSearchReplaceSearchTitle), format(msg(lng::MEditSearchingFor), QuotedStr), 0); SetCursorType(false, -1); - const auto Total = FindAll? Lines.size() : Reverse? StartLine : Lines.size() - StartLine; + const auto Total = FindAll? Lines.size() : Backward ? StartLine : Lines.size() - StartLine; const auto Current = std::abs(CurPtr.Number() - StartLine); Progress->update(ToPercent(Current, Total)); taskbar::set_value(Current,Total); } // $ 2023-01-15 MZK: Why do we need it? - auto strReplaceStrCurrent = LastSearchDlgParams.ReplaceMode? LastSearchDlgParams.ReplaceStr : L""s; + auto strReplaceStrCurrent = IsReplaceMode ? LastSearchDlgParams.ReplaceStr : L""s; int SearchLength; if (SearchAndReplaceString( @@ -3457,7 +3450,7 @@ bool Editor::Search(bool Next) { .CaseSensitive = LastSearchDlgParams.CaseSensitive.value(), .WholeWords = LastSearchDlgParams.WholeWords.value(), - .Reverse = LastSearchDlgParams.Reverse.value(), + .Reverse = Backward, .Regexp = LastSearchDlgParams.Regexp.value(), .PreserveStyle = LastSearchDlgParams.PreserveStyle.value() }, @@ -3496,7 +3489,7 @@ bool Editor::Search(bool Next) if (!EdOpt.PersistentBlocks) UnmarkBlock(); - if (EdOpt.SearchSelFound && !LastSearchDlgParams.ReplaceMode) + if (EdOpt.SearchSelFound && !IsReplaceMode) { Pasting++; UnmarkBlock(); @@ -3529,7 +3522,7 @@ bool Editor::Search(bool Next) if (TabCurPos + SearchLength + 8 > CurPtr->GetLeftPos() + ObjWidth()) CurPtr->SetLeftPos(TabCurPos + SearchLength + 8 - ObjWidth()); - if (!LastSearchDlgParams.ReplaceMode) + if (!IsReplaceMode) { CurPtr->SetCurPos(m_FoundPos + (EdOpt.SearchCursorAtEnd? SearchLength : 0)); break; @@ -3538,7 +3531,7 @@ bool Editor::Search(bool Next) { auto MsgCode = message_result::first_button; - if (!ReplaceAll) + if (!IsReplaceAll) { ColorItem newcol{}; newcol.StartPos=m_FoundPos; @@ -3566,7 +3559,7 @@ bool Editor::Search(bool Next) CurPtr->DeleteColor([&](const ColorItem& Item) { return newcol.StartPos == Item.StartPos && newcol.GetOwner() == Item.GetOwner();}); if (MsgCode == message_result::second_button) - ReplaceAll = true; + IsReplaceAll = true; if (MsgCode == message_result::third_button) Skip = true; @@ -3579,7 +3572,7 @@ bool Editor::Search(bool Next) } } - if (ReplaceAll) + if (IsReplaceAll) UndoBlock.emplace(this); if (MsgCode == message_result::first_button || MsgCode == message_result::second_button) @@ -3674,7 +3667,7 @@ bool Editor::Search(bool Next) CurPtr->SetString(NewStr, true); CurPtr->SetCurPos(CurPos + static_cast(strReplaceStrCurrent.size())); - if (EdOpt.SearchSelFound && !LastSearchDlgParams.ReplaceMode) + if (EdOpt.SearchSelFound && !IsReplaceMode) { UnmarkBlock(); BeginStreamMarking(CurPtr); @@ -3691,11 +3684,11 @@ bool Editor::Search(bool Next) } CurPos = m_it_CurLine->GetCurPos(); - if ((Skip || ZeroLength) && !Reverse) + if ((Skip || ZeroLength) && !Backward) { CurPos++; } - if (!(Skip || ZeroLength) && Reverse) + if (!(Skip || ZeroLength) && Backward) { (m_it_CurLine = CurPtr = m_FoundLine)->SetCurPos(CurPos = m_FoundPos); } @@ -3703,7 +3696,7 @@ bool Editor::Search(bool Next) } else { - if (Reverse) + if (Backward) { if (CurPtr == Lines.begin()) { @@ -3854,8 +3847,6 @@ bool Editor::Search(bool Next) QuotedStr }, { lng::MOk }); - - return true; } void Editor::PasteFromClipboard() diff --git a/far/editor.hpp b/far/editor.hpp index e26a684076f..a69dcbaa970 100644 --- a/far/editor.hpp +++ b/far/editor.hpp @@ -208,7 +208,6 @@ class Editor final: public SimpleScreenObject void Down(); void ScrollDown(); void ScrollUp(); - bool Search(bool Next); void GoToLine(size_t Line); void GoToLineAndShow(size_t Line); void GoToPosition(); @@ -287,6 +286,10 @@ class Editor final: public SimpleScreenObject void SwapState(Editor& swap_state); bool ProcessKeyInternal(const Manager::Key& Key, bool& Refresh); + enum class SearchReplaceDisposition; + SearchReplaceDisposition ShowSearchReplaceDialog(bool ReplaceMode); + void DoSearchReplace(SearchReplaceDisposition Disposition); + template void UpdateIteratorAndKeepPos(numbered_iterator& Iter, const F& Func); @@ -382,7 +385,8 @@ class Editor final: public SimpleScreenObject int XX2{}; //scrollbar SearchReplaceDlgParams LastSearchDlgParams; - bool ReplaceAll{}; + bool IsReplaceMode{}; + bool IsReplaceAll{}; int EditorID{}; int EditorControlLock{}; diff --git a/far/farlang.templ.m4 b/far/farlang.templ.m4 index d119c45db0f..bfc67750285 100644 --- a/far/farlang.templ.m4 +++ b/far/farlang.templ.m4 @@ -6568,7 +6568,7 @@ MSearchReplaceSearchFor "&Rasti" MSearchReplaceReplaceWith -"Заменить &на" +"&Заменить на" "&Replace with" "Nahradit &s" "&Ersetzen mit" @@ -6679,6 +6679,34 @@ MSearchReplaceSearch "Шукаць" "&Ieškoti" +MSearchReplaceSearchDown +"Искать вперёд" +"Search down" +upd:"Search down" +upd:"Search down" +upd:"Search down" +upd:"Search down" +upd:"Search down" +upd:"Search down" +upd:"Search down" +upd:"Search down" +upd:"Search down" +upd:"Search down" + +MSearchReplaceSearchUp +"Искать &назад" +"Search &up" +upd:"Search &up" +upd:"Search &up" +upd:"Search &up" +upd:"Search &up" +upd:"Search &up" +upd:"Search &up" +upd:"Search &up" +upd:"Search &up" +upd:"Search &up" +upd:"Search &up" + MSearchReplaceReplace "Замена" "Replace" @@ -6693,6 +6721,34 @@ MSearchReplaceReplace "&Змена" "Pakeisti &į" +MSearchReplaceReplaceDown +"Заменять вперёд" +"Replace down" +upd:"Replace down" +upd:"Replace down" +upd:"Replace down" +upd:"Replace down" +upd:"Replace down" +upd:"Replace down" +upd:"Replace down" +upd:"Replace down" +upd:"Replace down" +upd:"Replace down" + +MSearchReplaceReplaceUp +"Заменять &назад" +"Replace &up" +upd:"Replace &up" +upd:"Replace &up" +upd:"Replace &up" +upd:"Replace &up" +upd:"Replace &up" +upd:"Replace &up" +upd:"Replace &up" +upd:"Replace &up" +upd:"Replace &up" +upd:"Replace &up" + MSearchReplaceAll "Вс&ё" "&All" diff --git a/far/global.hpp b/far/global.hpp index a98b284329e..c391e557a13 100644 --- a/far/global.hpp +++ b/far/global.hpp @@ -80,7 +80,6 @@ class global bool GlobalSearchCaseSensitive{}; bool GlobalSearchFuzzy{}; bool GlobalSearchWholeWords{}; // значение "Whole words" для поиска - bool GlobalSearchReverse{}; std::atomic_size_t SuppressClock{}; std::atomic_size_t SuppressIndicators{}; bool CloseFAR{}, CloseFARMenu{}, AllowCancelExit{true}; diff --git a/far/help.cpp b/far/help.cpp index 4b9ef3b6fd4..292e5fed915 100644 --- a/far/help.cpp +++ b/far/help.cpp @@ -1430,7 +1430,7 @@ bool Help::ProcessKey(const Manager::Key& Key) // не поганим SelTopic, если и так в FoundContents if (StackData->strHelpTopic != FoundContents) { - if (GetSearchReplaceString(LastSearchDlgParams, L"HelpSearch"sv, {}, CP_DEFAULT, {}, &HelpSearchId) <= 0) + if (GetSearchReplaceString({}, LastSearchDlgParams, L"HelpSearch"sv, {}, CP_DEFAULT, {}, &HelpSearchId) == SearchReplaceDlgResult::Cancel) return true; Stack.emplace(*StackData); diff --git a/far/stddlg.cpp b/far/stddlg.cpp index ae417fec8c0..c468e94a835 100644 --- a/far/stddlg.cpp +++ b/far/stddlg.cpp @@ -76,7 +76,8 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. //---------------------------------------------------------------------------- -int GetSearchReplaceString( +SearchReplaceDlgResult GetSearchReplaceString( + SearchReplaceDlgProps Props, SearchReplaceDlgParams& Params, string_view TextHistoryName, string_view ReplaceHistoryName, @@ -92,7 +93,7 @@ int GetSearchReplaceString( string SearchForLabel{ msg(lng::MSearchReplaceSearchFor) }; if (HasHex) inplace::remove_highlight(SearchForLabel); - const auto& DialogTitle{ msg(Params.ReplaceMode ? lng::MSearchReplaceReplaceTitle : lng::MSearchReplaceSearchTitle) }; + const auto& DialogTitle{ msg(Props.ReplaceMode ? lng::MSearchReplaceReplaceTitle : lng::MSearchReplaceSearchTitle) }; const auto& TextLabel{ msg(lng::MSearchReplaceText) }; const auto& HexLabel{ msg(lng::MSearchReplaceHex) }; const auto& WordLabel{ msg(lng::MSearchReplacePickWord) }; @@ -123,7 +124,13 @@ int GetSearchReplaceString( const auto TextRadioX1{ TextRadioX1_ - HexRadioOverage_ }; const auto TextRadioX2{ TextRadioX2_ - HexRadioOverage_ }; const auto HexRadioX1{ HexRadioX1_ - HexRadioOverage_ }; const auto HexRadioX2{ HexRadioX2_ - HexRadioOverage_ }; - const auto YFix = Params.ReplaceMode ? 0 : 2; + const auto YFix = Props.ReplaceMode ? 0 : 2; + + const auto& ActionButtonLabel{ msg( + Props.ReplaceMode + ? (Props.ShowButtonsUpDown ? lng::MSearchReplaceReplaceDown : lng::MSearchReplaceReplace) + : (Props.ShowButtonsUpDown ? lng::MSearchReplaceSearchDown : lng::MSearchReplaceSearch)) }; + const auto& SearchReplaceUpLabel{ msg(Props.ReplaceMode ? lng::MSearchReplaceReplaceUp : lng::MSearchReplaceSearchUp) }; enum item_id { @@ -140,12 +147,12 @@ int GetSearchReplaceString( dlg_separator_1, dlg_checkbox_case, dlg_checkbox_words, - dlg_checkbox_reverse, - dlg_checkbox_regex, dlg_checkbox_fuzzy, + dlg_checkbox_regex, dlg_checkbox_style, dlg_separator_2, dlg_button_action, + dlg_button_up, dlg_button_all, dlg_button_cancel, @@ -167,12 +174,12 @@ int GetSearchReplaceString( { DI_TEXT, {{-1, 6-YFix }, {0, 6-YFix }}, DIF_SEPARATOR, }, { DI_CHECKBOX, {{5, 7-YFix }, {0, 7-YFix }}, DIF_NONE, msg(lng::MSearchReplaceCase), }, { DI_CHECKBOX, {{5, 8-YFix }, {0, 8-YFix }}, DIF_NONE, msg(lng::MSearchReplaceWholeWords), }, - { DI_CHECKBOX, {{5, 9-YFix }, {0, 9-YFix }}, DIF_NONE, msg(lng::MSearchReplaceReverse), }, + { DI_CHECKBOX, {{5, 9-YFix }, {0, 9-YFix }}, DIF_NONE, msg(lng::MSearchReplaceFuzzy), }, { DI_CHECKBOX, {{40, 7-YFix }, {0, 7-YFix }}, DIF_NONE, msg(lng::MSearchReplaceRegexp), }, - { DI_CHECKBOX, {{40, 8-YFix }, {0, 8-YFix }}, DIF_NONE, msg(lng::MSearchReplaceFuzzy), }, { DI_CHECKBOX, {{40, 9-YFix }, {0, 9-YFix }}, DIF_NONE, msg(lng::MSearchReplacePreserveStyle), }, { DI_TEXT, {{-1, 10-YFix}, {0, 10-YFix}}, DIF_SEPARATOR, }, - { DI_BUTTON, {{0, 11-YFix}, {0, 11-YFix}}, DIF_CENTERGROUP | DIF_DEFAULTBUTTON, msg(Params.ReplaceMode ? lng::MSearchReplaceReplace : lng::MSearchReplaceSearch), }, + { DI_BUTTON, {{0, 11-YFix}, {0, 11-YFix}}, DIF_CENTERGROUP | DIF_DEFAULTBUTTON, ActionButtonLabel, }, + { DI_BUTTON, {{0, 11-YFix}, {0, 11-YFix}}, DIF_CENTERGROUP, SearchReplaceUpLabel, }, { DI_BUTTON, {{0, 11-YFix}, {0, 11-YFix}}, DIF_CENTERGROUP, msg(lng::MSearchReplaceAll), }, { DI_BUTTON, {{0, 11-YFix}, {0, 11-YFix}}, DIF_CENTERGROUP, msg(lng::MSearchReplaceCancel), }, }); @@ -218,11 +225,11 @@ int GetSearchReplaceString( SetMaskIf(dlg_edit_search_hex, HasHex); // dlg_label_replace - SetFlagIf(dlg_label_replace, DIF_HIDDEN, !Params.ReplaceMode); + SetFlagIf(dlg_label_replace, DIF_HIDDEN, !Props.ReplaceMode); // dlg_edit_replace - SetFlagIf(dlg_edit_replace, DIF_HIDDEN, !Params.ReplaceMode); - SetStringIf(dlg_edit_replace, Params.ReplaceStr, Params.ReplaceMode); + SetFlagIf(dlg_edit_replace, DIF_HIDDEN, !Props.ReplaceMode); + SetStringIf(dlg_edit_replace, Params.ReplaceStr, Props.ReplaceMode); SetHistory(dlg_edit_replace, ReplaceHistoryName); // dlg_checkbox_case @@ -233,10 +240,6 @@ int GetSearchReplaceString( SetFlagIf(dlg_checkbox_words, DIF_DISABLE, !Params.WholeWords.has_value() || HexVal); SetSelected(dlg_checkbox_words, Params.WholeWords.value_or(false)); - // dlg_checkbox_reverse - SetFlagIf(dlg_checkbox_reverse, DIF_HIDDEN, !Params.Reverse.has_value()); - SetSelected(dlg_checkbox_reverse, Params.Reverse.value_or(false)); - // dlg_checkbox_regex SetFlagIf(dlg_checkbox_regex, DIF_DISABLE, !Params.Regexp.has_value() || HexVal); SetSelected(dlg_checkbox_regex, Params.Regexp.value_or(false)); @@ -246,11 +249,14 @@ int GetSearchReplaceString( SetSelected(dlg_checkbox_fuzzy, Params.Fuzzy.value_or(false)); // dlg_checkbox_style - SetFlagIf(dlg_checkbox_style, DIF_HIDDEN, !Params.ReplaceMode || !Params.PreserveStyle.has_value()); + SetFlagIf(dlg_checkbox_style, DIF_HIDDEN, !Props.ReplaceMode || !Params.PreserveStyle.has_value()); SetSelected(dlg_checkbox_style, Params.PreserveStyle.value_or(false)); + // dlg_button_search_up == dlg_button_replace_up + SetFlagIf(dlg_button_up, DIF_HIDDEN, !Props.ShowButtonsUpDown); + // dlg_button_all - SetFlagIf(dlg_button_all, DIF_HIDDEN, Params.ReplaceMode || !Params.ShowButtonAll); + SetFlagIf(dlg_button_all, DIF_HIDDEN, !Props.ShowButtonAll); bool TextOrHexHotkeyUsed{}; @@ -348,41 +354,48 @@ int GetSearchReplaceString( Dlg->Process(); - if (const auto ExitCode = Dlg->GetExitCode(); ExitCode == dlg_button_action || ExitCode == dlg_button_all) + const auto ExitCode = Dlg->GetExitCode(); + + if (none_of(ExitCode, dlg_button_action, dlg_button_up, dlg_button_all)) { - if (DlgItems[dlg_edit_search_hex].Flags & DIF_HIDDEN) - { - Params.SearchStr = DlgItems[dlg_edit_search_text].strData; - } - else - { - Params.SearchStr = ExtractHexString(DlgItems[dlg_edit_search_hex].strData); - Params.SearchBytes = HexStringToBlob(Params.SearchStr, 0); - } + return SearchReplaceDlgResult::Cancel; + } - if (Params.ReplaceMode) - { - Params.ReplaceStr = DlgItems[dlg_edit_replace].strData; - } + if (DlgItems[dlg_edit_search_hex].Flags & DIF_HIDDEN) + { + Params.SearchStr = DlgItems[dlg_edit_search_text].strData; + } + else + { + Params.SearchStr = ExtractHexString(DlgItems[dlg_edit_search_hex].strData); + Params.SearchBytes = HexStringToBlob(Params.SearchStr, 0); + } - const auto SaveParam{ [&](auto& Param, const item_id ItemId) - { - if (Param.has_value()) - Param = DlgItems[ItemId].Selected == BSTATE_CHECKED; - } }; - - SaveParam(Params.Hex, dlg_radio_hex); - SaveParam(Params.CaseSensitive, dlg_checkbox_case); - SaveParam(Params.WholeWords, dlg_checkbox_words); - SaveParam(Params.Reverse, dlg_checkbox_reverse); - SaveParam(Params.Regexp, dlg_checkbox_regex); - SaveParam(Params.Fuzzy, dlg_checkbox_fuzzy); - SaveParam(Params.PreserveStyle, dlg_checkbox_style); - - return ExitCode == dlg_button_action ? 1 : 2; + if (Props.ReplaceMode) + { + Params.ReplaceStr = DlgItems[dlg_edit_replace].strData; } - return 0; + const auto SaveParam{ [&](auto& Param, const item_id ItemId) + { + if (Param.has_value()) + Param = DlgItems[ItemId].Selected == BSTATE_CHECKED; + } }; + + SaveParam(Params.Hex, dlg_radio_hex); + SaveParam(Params.CaseSensitive, dlg_checkbox_case); + SaveParam(Params.WholeWords, dlg_checkbox_words); + SaveParam(Params.Regexp, dlg_checkbox_regex); + SaveParam(Params.Fuzzy, dlg_checkbox_fuzzy); + SaveParam(Params.PreserveStyle, dlg_checkbox_style); + + switch (ExitCode) + { + case dlg_button_action: return Props.ShowButtonsUpDown ? SearchReplaceDlgResult::Down : SearchReplaceDlgResult::Ok; + case dlg_button_up: return SearchReplaceDlgResult::Up; + case dlg_button_all: return SearchReplaceDlgResult::All; + default: UNREACHABLE; + }; } bool GetString( diff --git a/far/stddlg.hpp b/far/stddlg.hpp index 4dede525a6d..92dda703275 100644 --- a/far/stddlg.hpp +++ b/far/stddlg.hpp @@ -55,50 +55,61 @@ enum class lng : int; class regex_exception; struct error_state_ex; -struct SearchReplaceDlgParams +struct SearchReplaceDlgProps { bool ReplaceMode{}; + bool ShowButtonsUpDown{}; bool ShowButtonAll{}; +}; + +struct SearchReplaceDlgParams +{ string SearchStr; bytes SearchBytes; string ReplaceStr; std::optional Hex; std::optional CaseSensitive; std::optional WholeWords; - std::optional Reverse; std::optional Regexp; std::optional Fuzzy; std::optional PreserveStyle; }; +enum class SearchReplaceDlgResult +{ + Cancel, + Ok, + Up, + Down, + All, +}; + /* - Функция GetSearchReplaceString выводит диалог поиска или замены, принимает - от пользователя данные и в случае успешного выполнения диалога возвращает - TRUE. - Параметры: - Params - InOut parameter. Specifies which options to show in the dialog and provides initial values. - On exit, contains the values selected by the user. - - TextHistoryName + Shows Search / Replace dialog, collects user's input, and returns the action selected by the user. + + Parameters: + Props + Define various aspects of dialog UX + + Params + InOut parameter. Specifies which options to show in the dialog and provides initial values. + If the dialog was closed by one of the action buttons, contains the values selected by the user. + If the dialog was dismissed, the values stay unchanged. + + TextHistoryName Имя истории строки поиска. - Если пустая строка, то принимается значение "SearchText" ReplaceHistoryName Имя истории строки замены. - Если пустая строка, то принимается значение "ReplaceText" HelpTopic Имя темы помощи. Если пустая строка - тема помощи не назначается. - Возвращаемое значение: - 0 - пользователь отказался от диалога (Esc) - 1 - пользователь подтвердил свои намерения - 2 - выбран поиск всех вхождений - + Returns the action selected by the user. */ -int GetSearchReplaceString( +SearchReplaceDlgResult GetSearchReplaceString( + SearchReplaceDlgProps Props, SearchReplaceDlgParams& Params, string_view TextHistoryName, string_view ReplaceHistoryName, diff --git a/far/vbuild.m4 b/far/vbuild.m4 index c8ad85c3043..124a6d8c38f 100644 --- a/far/vbuild.m4 +++ b/far/vbuild.m4 @@ -1 +1 @@ -6110 +6111 diff --git a/far/viewer.cpp b/far/viewer.cpp index 00358abfc0e..39e4c64d2ae 100644 --- a/far/viewer.cpp +++ b/far/viewer.cpp @@ -114,6 +114,14 @@ enum saved_modes m_mode_wrap_words = 0x40, }; +enum class Viewer::SearchDisposition +{ + Cancel, + Up, + Down, + ContinueUp, + ContinueDown, +}; static int ViewerID=0; @@ -135,11 +143,9 @@ Viewer::Viewer(window_ptr Owner, bool bQuickView, uintptr_t aCodePage): .Hex = Global->GetSearchHex(), .CaseSensitive = Global->GlobalSearchCaseSensitive, .WholeWords = Global->GlobalSearchWholeWords, - .Reverse = Global->GlobalSearchReverse, .Regexp = Global->Opt->ViOpt.SearchRegexp, .Fuzzy = Global->GlobalSearchFuzzy, }, - LastSearchReverse(Global->GlobalSearchReverse), m_DefCodepage(aCodePage), m_Codepage(m_DefCodepage), m_Wrap(Global->Opt->ViOpt.ViewerIsWrap), @@ -268,7 +274,6 @@ void Viewer::KeepInitParameters() const Global->StoreSearchString(LastSearchDlgParams.SearchStr, LastSearchDlgParams.Hex.value()); Global->GlobalSearchCaseSensitive = LastSearchDlgParams.CaseSensitive.value(); Global->GlobalSearchWholeWords = LastSearchDlgParams.WholeWords.value(); - Global->GlobalSearchReverse = LastSearchDlgParams.Reverse.value(); Global->Opt->ViOpt.SearchRegexp = LastSearchDlgParams.Regexp.value(); Global->GlobalSearchFuzzy = LastSearchDlgParams.Fuzzy.value(); Global->Opt->ViOpt.ViewerIsWrap = m_Wrap; @@ -1627,19 +1632,19 @@ bool Viewer::process_key(const Manager::Key& Key) case KEY_F7: { - Search(0); + DoSearchReplace(ShowSearchReplaceDialog()); return true; } case KEY_SHIFTF7: case KEY_SPACE: { - Search(1); + DoSearchReplace(SearchDisposition::ContinueDown); return true; } case KEY_ALTF7: case KEY_RALTF7: { - Search(-1); + DoSearchReplace(SearchDisposition::ContinueUp); return true; } case KEY_F8: @@ -2983,32 +2988,42 @@ SEARCHER_RESULT Viewer::search_regex_backward(search_data* sd) return Search_Continue; } -/* - + Параметр Next может принимать значения: - 0 - Новый поиск - 1 - Продолжить поиск со следующей позиции --1 - Продолжить поиск со следующей позиции в противоположном направлении -*/ -void Viewer::Search(int Next) -{ - if (!ViewFile || (Next && LastSearchDlgParams.SearchStr.empty())) - return; - if (!Next) +Viewer::SearchDisposition Viewer::ShowSearchReplaceDialog() +{ + switch (GetSearchReplaceString( + { .ShowButtonsUpDown = true }, + LastSearchDlgParams, + L"SearchText"sv, + {}, + m_Codepage, + L"ViewerSearch"sv, + &ViewerSearchId)) { - if (!GetSearchReplaceString( - LastSearchDlgParams, - L"SearchText"sv, - {}, - m_Codepage, - L"ViewerSearch"sv, - &ViewerSearchId)) - { - return; - } + case SearchReplaceDlgResult::Cancel: + return SearchDisposition::Cancel; + + case SearchReplaceDlgResult::Up: + return SearchDisposition::Up; + + case SearchReplaceDlgResult::Down: + return SearchDisposition::Down; + + case SearchReplaceDlgResult::Ok: + case SearchReplaceDlgResult::All: + default: + UNREACHABLE; } +} + +void Viewer::DoSearchReplace(SearchDisposition Disposition) +{ + if (!ViewFile || Disposition == SearchDisposition::Cancel || LastSearchDlgParams.SearchStr.empty()) + return; + + const auto Backward{ Disposition == SearchDisposition::Up || Disposition == SearchDisposition::ContinueUp }; + const auto Continue{ Disposition == SearchDisposition::ContinueUp || Disposition == SearchDisposition::ContinueDown }; - auto SearchReverse{ Next == -1 ? !LastSearchDlgParams.Reverse.value() : LastSearchDlgParams.Reverse.value() }; auto strMsgStr = LastSearchDlgParams.SearchStr; searchers Searchers; @@ -3021,7 +3036,7 @@ void Viewer::Search(int Next) sd.ch_size = 1; sd.search_bytes = LastSearchDlgParams.SearchBytes; sd.search_len = static_cast(LastSearchDlgParams.SearchBytes.size()); - searcher = (SearchReverse ? &Viewer::search_hex_backward : &Viewer::search_hex_forward); + searcher = (Backward ? &Viewer::search_hex_backward : &Viewer::search_hex_forward); } else { @@ -3031,7 +3046,7 @@ void Viewer::Search(int Next) if (LastSearchDlgParams.Regexp.value()) { - searcher = (SearchReverse ? &Viewer::search_regex_backward : &Viewer::search_regex_forward); + searcher = (Backward ? &Viewer::search_regex_backward : &Viewer::search_regex_forward); const auto strSlash = InsertRegexpQuote(LastSearchDlgParams.SearchStr); @@ -3051,53 +3066,54 @@ void Viewer::Search(int Next) { sd.searcher = &init_searcher(Searchers, LastSearchDlgParams.CaseSensitive.value(), LastSearchDlgParams.Fuzzy.value(), LastSearchDlgParams.SearchStr); sd.word_div = get_word_div(); - searcher = (SearchReverse ? &Viewer::search_text_backward : &Viewer::search_text_forward); + searcher = (Backward ? &Viewer::search_text_backward : &Viewer::search_text_forward); inplace::quote_unconditional(strMsgStr); } } - switch (Next) + auto advanceSelectPositionForward{ [&]() { - case +1: - case -1: - if ( SelectPos >= 0 && SelectSize >= 0 ) - { - if (sd.ch_size >= 1) - LastSelectPos = SelectPos + (SearchReverse ? LastSelectSize-sd.ch_size : sd.ch_size); - else - { - long long prev_pos = SelectPos; - vseek(SelectPos, FILE_BEGIN); - for (;;) - { - wchar_t ch; - bool ok_getc = vgetc(&ch); - LastSelectPos = vtell(); - if (!SearchReverse || !ok_getc) - break; - if ( LastSelectPos >= SelectPos + LastSelectSize ) - { - LastSelectPos = prev_pos; - break; - } - prev_pos = LastSelectPos; - } - } - if (SearchReverse != LastSearchReverse) - StartSearchPos = LastSelectPos; + if (sd.ch_size >= 1) return SelectPos + sd.ch_size; - break; - } - [[fallthrough]]; - case 0: - default: - assert(Next >= -1 && Next <= +1); - if (!Next || LastSelectSize < 0) - LastSelectSize = SelectSize = -1; - StartSearchPos = LastSelectPos = (SearchReverse ? EndOfScreen(0) : BegOfScreen()); - break; + vseek(SelectPos, FILE_BEGIN); + wchar_t ch; + vgetc(&ch); + return vtell(); + } }; + + auto advanceSelectPositionBackward{ [&]() + { + if (sd.ch_size >= 1) return SelectPos + LastSelectSize - sd.ch_size; + + auto prev_pos = SelectPos; + vseek(SelectPos, FILE_BEGIN); + for (;;) + { + wchar_t ch; + const auto has_next = vgetc(&ch); + const auto next_pos = vtell(); + + if (!has_next) return next_pos; + if (next_pos >= SelectPos + LastSelectSize) return prev_pos; + + prev_pos = next_pos; + } + } }; + + if (Continue && SelectPos >= 0 && SelectSize >= 0) + { + LastSelectPos = Backward ? advanceSelectPositionBackward() : advanceSelectPositionForward(); + if (Backward != LastSearchBackward) + StartSearchPos = LastSelectPos; + } + else + { + if (LastSelectSize < 0) + LastSelectSize = SelectSize = -1; + StartSearchPos = LastSelectPos = (Backward ? EndOfScreen(0) : BegOfScreen()); } - LastSearchReverse = SearchReverse; + + LastSearchBackward = Backward; if (!sd.search_len || !FileSize) return; @@ -3169,7 +3185,7 @@ void Viewer::Search(int Next) if ( total > 0 ) { long long done; - if (!SearchReverse) + if (!Backward) { if ( sd.CurPos >= StartSearchPos ) done = sd.CurPos - StartSearchPos; @@ -3199,7 +3215,7 @@ void Viewer::Search(int Next) if ( sd.MatchPos >= 0 ) { - DWORD flags = SearchReverse ? 0x2 : 0; + DWORD flags = Backward ? 0x2 : 0; if (sd.search_len < 0 || (sd.MatchPos >= BegOfScreen() && sd.MatchPos + sd.search_len <= EndOfScreen(0))) diff --git a/far/viewer.hpp b/far/viewer.hpp index b9111d392c2..c811b8525bc 100644 --- a/far/viewer.hpp +++ b/far/viewer.hpp @@ -136,7 +136,10 @@ class Viewer:public SimpleScreenObject long long BegOfScreen(); long long XYfilepos(int col, int row); void ChangeViewKeyBar(); - void Search(int Next); + + enum class SearchDisposition; + SearchDisposition ShowSearchReplaceDialog(); + void DoSearchReplace(SearchDisposition Disposition); struct search_data; SEARCHER_RESULT search_hex_forward( search_data* sd ); SEARCHER_RESULT search_hex_backward( search_data* sd ); @@ -145,6 +148,7 @@ class Viewer:public SimpleScreenObject SEARCHER_RESULT search_regex_forward( search_data* sd ); SEARCHER_RESULT search_regex_backward( search_data* sd ); int read_line(wchar_t *buf, wchar_t *tbuf, long long cpos, int adjust, long long& lpos, int &lsize); + int vread(wchar_t *Buf, int Count, wchar_t *Buf2 = nullptr); bool vseek(long long Offset, int Whence); long long vtell() const; @@ -194,7 +198,7 @@ class Viewer:public SimpleScreenObject SearchReplaceDlgParams LastSearchDlgParams; - bool LastSearchReverse{}; // Used to adjust StartSearchPos. NOT the same as LastSearchDlgOptions.Reverse. + bool LastSearchBackward{}; // Used to adjust StartSearchPos long long StartSearchPos{}; uintptr_t m_DefCodepage;