From ca1dbb7437efa0dfd6a4adba59529ca0ae102c3d Mon Sep 17 00:00:00 2001 From: Aussiemon <1407091-Aussiemon@users.noreply.gitlab.com> Date: Thu, 22 Jan 2026 16:49:46 -0700 Subject: [PATCH 01/14] Validate regex patterns in global search --- apps/opencs/view/tools/searchbox.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/apps/opencs/view/tools/searchbox.cpp b/apps/opencs/view/tools/searchbox.cpp index a2871b5489..9cfc2f3815 100644 --- a/apps/opencs/view/tools/searchbox.cpp +++ b/apps/opencs/view/tools/searchbox.cpp @@ -17,13 +17,21 @@ void CSVTools::SearchBox::updateSearchButton() switch (mMode.currentIndex()) { case 0: - case 1: case 2: - case 3: mSearch.setEnabled(!mText.text().isEmpty()); break; + case 1: + case 3: + { + if (mText.text().isEmpty()) + mSearch.setEnabled(false); + else + mSearch.setEnabled(QRegularExpression(mText.text()).isValid()); + break; + } + case 4: mSearch.setEnabled(true); From 2b43186d0ee9a56e3144ad5506a76f6303a01703 Mon Sep 17 00:00:00 2001 From: Aussiemon <1407091-Aussiemon@users.noreply.gitlab.com> Date: Fri, 23 Jan 2026 01:14:22 -0700 Subject: [PATCH 02/14] More complex global search handling --- apps/opencs/view/tools/searchbox.cpp | 60 +++++++++++++++++++++------- apps/opencs/view/tools/searchbox.hpp | 5 ++- 2 files changed, 49 insertions(+), 16 deletions(-) diff --git a/apps/opencs/view/tools/searchbox.cpp b/apps/opencs/view/tools/searchbox.cpp index 9cfc2f3815..6a77dda5c0 100644 --- a/apps/opencs/view/tools/searchbox.cpp +++ b/apps/opencs/view/tools/searchbox.cpp @@ -8,33 +8,53 @@ #include "../../model/tools/search.hpp" -void CSVTools::SearchBox::updateSearchButton() +void CSVTools::SearchBox::updateSearchButtons() { if (!mSearchEnabled) - mSearch.setEnabled(false); + { + setSearchLock(true); + setEditLock(true); + } else { + constexpr const char* redLineEditStyle = "QLineEdit { color: red; }"; + bool isInvalidText = hasInvalidText(); + switch (mMode.currentIndex()) { case 0: case 2: - - mSearch.setEnabled(!mText.text().isEmpty()); + // Text modes + mText.setStyleSheet(QString()); + mReplaceText.setStyleSheet(isInvalidText ? redLineEditStyle : QString()); + setSearchLock(isInvalidText); + setEditLock(isInvalidText); break; case 1: case 3: - { - if (mText.text().isEmpty()) - mSearch.setEnabled(false); + // Regex modes + if (isInvalidText) + { + mText.setStyleSheet(QString()); + mReplaceText.setStyleSheet(isInvalidText ? redLineEditStyle : QString()); + setSearchLock(true); + setEditLock(true); + } else - mSearch.setEnabled(QRegularExpression(mText.text()).isValid()); + { + bool isInvalidRegex = !QRegularExpression(mText.text()).isValid(); + mText.setStyleSheet(isInvalidRegex ? redLineEditStyle : QString()); + mReplaceText.setStyleSheet(isInvalidRegex ? redLineEditStyle : QString()); + setSearchLock(isInvalidRegex); + setEditLock(isInvalidRegex); + } break; - } case 4: - - mSearch.setEnabled(true); + // Record state mode + setSearchLock(false); + setEditLock(true); break; } } @@ -96,13 +116,13 @@ CSVTools::SearchBox::SearchBox(QWidget* parent) // update modeSelected(0); - updateSearchButton(); + updateSearchButtons(); } void CSVTools::SearchBox::setSearchMode(bool enabled) { mSearchEnabled = enabled; - updateSearchButton(); + updateSearchButtons(); } CSMTools::Search CSVTools::SearchBox::getSearch() const @@ -159,6 +179,16 @@ void CSVTools::SearchBox::setEditLock(bool locked) mReplace.setEnabled(!locked); } +void CSVTools::SearchBox::setSearchLock(bool locked) +{ + mSearch.setEnabled(!locked); +} + +bool CSVTools::SearchBox::hasInvalidText() +{ + return mText.text().isEmpty(); +} + void CSVTools::SearchBox::focus() { mInput.currentWidget()->setFocus(); @@ -185,12 +215,12 @@ void CSVTools::SearchBox::modeSelected(int index) mInput.currentWidget()->setFocus(); - updateSearchButton(); + updateSearchButtons(); } void CSVTools::SearchBox::textChanged(const QString& text) { - updateSearchButton(); + updateSearchButtons(); } void CSVTools::SearchBox::startSearch(bool checked) diff --git a/apps/opencs/view/tools/searchbox.hpp b/apps/opencs/view/tools/searchbox.hpp index 76712fee4b..211c8b8ddb 100644 --- a/apps/opencs/view/tools/searchbox.hpp +++ b/apps/opencs/view/tools/searchbox.hpp @@ -36,7 +36,7 @@ namespace CSVTools QPushButton mReplace; private: - void updateSearchButton(); + void updateSearchButtons(); public: SearchBox(QWidget* parent = nullptr); @@ -48,6 +48,9 @@ namespace CSVTools std::string getReplaceText() const; void setEditLock(bool locked); + void setSearchLock(bool locked); + + bool hasInvalidText(); void focus(); From 12ec0dd60d94b8ffb440945fab606a60ef7501f1 Mon Sep 17 00:00:00 2001 From: Aussiemon <1407091-Aussiemon@users.noreply.gitlab.com> Date: Fri, 23 Jan 2026 01:14:37 -0700 Subject: [PATCH 03/14] Fix bad dynamic cast in subview --- apps/opencs/view/tools/reporttable.hpp | 3 +++ apps/opencs/view/tools/searchsubview.cpp | 8 ++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/apps/opencs/view/tools/reporttable.hpp b/apps/opencs/view/tools/reporttable.hpp index 25f8d1b017..6f94b0c5e9 100644 --- a/apps/opencs/view/tools/reporttable.hpp +++ b/apps/opencs/view/tools/reporttable.hpp @@ -86,6 +86,9 @@ namespace CSVTools /// \return rows in the original model std::vector getReplaceIndices(bool selection) const; + /// Return underlying report model + CSMTools::ReportModel* getReportModel() const { return mModel; } + /// \param index row in the original model void flagAsReplaced(int index); diff --git a/apps/opencs/view/tools/searchsubview.cpp b/apps/opencs/view/tools/searchsubview.cpp index 98fd97e7ae..c4dbd8e6f7 100644 --- a/apps/opencs/view/tools/searchsubview.cpp +++ b/apps/opencs/view/tools/searchsubview.cpp @@ -29,7 +29,7 @@ void CSVTools::SearchSubView::replace(bool selection) std::string replace = mSearchBox.getReplaceText(); - const CSMTools::ReportModel& model = dynamic_cast(*mTable->model()); + const CSMTools::ReportModel* model = mTable->getReportModel(); bool autoDelete = CSMPrefs::get()["Search & Replace"]["auto-delete"].isTrue(); @@ -40,7 +40,7 @@ void CSVTools::SearchSubView::replace(bool selection) // in a single string. for (std::vector::const_reverse_iterator iter(indices.rbegin()); iter != indices.rend(); ++iter) { - const CSMWorld::UniversalId& id = model.getUniversalId(*iter); + const CSMWorld::UniversalId& id = model->getUniversalId(*iter); CSMWorld::UniversalId::Type type = CSMWorld::UniversalId::getParentType(id.getType()); @@ -52,7 +52,7 @@ void CSVTools::SearchSubView::replace(bool selection) currentTable = table; } - std::string hint = model.getHint(*iter); + std::string hint = model->getHint(*iter); if (search.verify(mDocument, table, id, hint)) { @@ -113,7 +113,7 @@ CSVTools::SearchSubView::SearchSubView(const CSMWorld::UniversalId& id, CSMDoc:: void CSVTools::SearchSubView::setEditLock(bool locked) { mLocked = locked; - mSearchBox.setEditLock(locked); + mSearchBox.setEditLock(locked || mSearchBox.hasInvalidText()); } void CSVTools::SearchSubView::setStatusBar(bool show) From 98c912f813c4887e83751c790bf469ad7068f08f Mon Sep 17 00:00:00 2001 From: Aussiemon <1407091-Aussiemon@users.noreply.gitlab.com> Date: Sat, 24 Jan 2026 12:04:16 -0700 Subject: [PATCH 04/14] Don't tint replace text --- apps/opencs/view/tools/searchbox.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/apps/opencs/view/tools/searchbox.cpp b/apps/opencs/view/tools/searchbox.cpp index 6a77dda5c0..532a6ea3f0 100644 --- a/apps/opencs/view/tools/searchbox.cpp +++ b/apps/opencs/view/tools/searchbox.cpp @@ -26,7 +26,6 @@ void CSVTools::SearchBox::updateSearchButtons() case 2: // Text modes mText.setStyleSheet(QString()); - mReplaceText.setStyleSheet(isInvalidText ? redLineEditStyle : QString()); setSearchLock(isInvalidText); setEditLock(isInvalidText); break; @@ -37,7 +36,6 @@ void CSVTools::SearchBox::updateSearchButtons() if (isInvalidText) { mText.setStyleSheet(QString()); - mReplaceText.setStyleSheet(isInvalidText ? redLineEditStyle : QString()); setSearchLock(true); setEditLock(true); } @@ -45,7 +43,6 @@ void CSVTools::SearchBox::updateSearchButtons() { bool isInvalidRegex = !QRegularExpression(mText.text()).isValid(); mText.setStyleSheet(isInvalidRegex ? redLineEditStyle : QString()); - mReplaceText.setStyleSheet(isInvalidRegex ? redLineEditStyle : QString()); setSearchLock(isInvalidRegex); setEditLock(isInvalidRegex); } From 911d588d0a38a0ac8fd8d2d7e51197f588ec3917 Mon Sep 17 00:00:00 2001 From: Aussiemon <1407091-Aussiemon@users.noreply.gitlab.com> Date: Sat, 24 Jan 2026 23:56:38 -0700 Subject: [PATCH 05/14] Cleaner condition handling and edit lock --- apps/opencs/view/tools/searchbox.cpp | 76 ++++++++++++++---------- apps/opencs/view/tools/searchbox.hpp | 12 +++- apps/opencs/view/tools/searchsubview.cpp | 2 +- 3 files changed, 54 insertions(+), 36 deletions(-) diff --git a/apps/opencs/view/tools/searchbox.cpp b/apps/opencs/view/tools/searchbox.cpp index 532a6ea3f0..4470f78177 100644 --- a/apps/opencs/view/tools/searchbox.cpp +++ b/apps/opencs/view/tools/searchbox.cpp @@ -12,56 +12,75 @@ void CSVTools::SearchBox::updateSearchButtons() { if (!mSearchEnabled) { - setSearchLock(true); - setEditLock(true); + mSearch.setEnabled(false); + mReplace.setEnabled(false); } else { constexpr const char* redLineEditStyle = "QLineEdit { color: red; }"; - bool isInvalidText = hasInvalidText(); - switch (mMode.currentIndex()) { case 0: case 2: // Text modes mText.setStyleSheet(QString()); - setSearchLock(isInvalidText); - setEditLock(isInvalidText); + mSearch.setEnabled(canSearch()); + mReplace.setEnabled(canReplace()); break; case 1: case 3: // Regex modes - if (isInvalidText) - { - mText.setStyleSheet(QString()); - setSearchLock(true); - setEditLock(true); - } - else - { - bool isInvalidRegex = !QRegularExpression(mText.text()).isValid(); - mText.setStyleSheet(isInvalidRegex ? redLineEditStyle : QString()); - setSearchLock(isInvalidRegex); - setEditLock(isInvalidRegex); - } + mText.setStyleSheet(canSearchRegex() ? QString() : redLineEditStyle); + mSearch.setEnabled(canSearchRegex()); + mReplace.setEnabled(canReplaceRegex()); break; case 4: // Record state mode - setSearchLock(false); - setEditLock(true); + mSearch.setEnabled(true); + mReplace.setEnabled(false); break; } } } +bool CSVTools::SearchBox::canReplace() +{ + return !mLocked && !hasInvalidSearchText(); +} + +bool CSVTools::SearchBox::canReplaceRegex() +{ + return canReplace() && !hasInvalidSearchRegex(); +} + +bool CSVTools::SearchBox::canSearch() +{ + return !hasInvalidSearchText(); +} + +bool CSVTools::SearchBox::canSearchRegex() +{ + return canSearch() && !hasInvalidSearchRegex(); +} + +bool CSVTools::SearchBox::hasInvalidSearchText() +{ + return mText.text().isEmpty(); +} + +bool CSVTools::SearchBox::hasInvalidSearchRegex() +{ + return !QRegularExpression(mText.text()).isValid(); +} + CSVTools::SearchBox::SearchBox(QWidget* parent) : QWidget(parent) , mSearch(tr("Search")) , mSearchEnabled(false) , mReplace(tr("Replace All")) + , mLocked(false) { mLayout = new QGridLayout(this); @@ -101,6 +120,7 @@ CSVTools::SearchBox::SearchBox(QWidget* parent) mLayout->addWidget(&mReplaceInput, 1, 1); mLayout->addWidget(&mReplace, 1, 3); + mReplace.setEnabled(false); // layout adjustments mLayout->setColumnMinimumWidth(2, 50); @@ -173,17 +193,9 @@ std::string CSVTools::SearchBox::getReplaceText() const void CSVTools::SearchBox::setEditLock(bool locked) { - mReplace.setEnabled(!locked); -} - -void CSVTools::SearchBox::setSearchLock(bool locked) -{ - mSearch.setEnabled(!locked); -} - -bool CSVTools::SearchBox::hasInvalidText() -{ - return mText.text().isEmpty(); + mLocked = locked; + if (mLocked) + mReplace.setEnabled(false); } void CSVTools::SearchBox::focus() diff --git a/apps/opencs/view/tools/searchbox.hpp b/apps/opencs/view/tools/searchbox.hpp index 211c8b8ddb..50a74f1522 100644 --- a/apps/opencs/view/tools/searchbox.hpp +++ b/apps/opencs/view/tools/searchbox.hpp @@ -28,6 +28,7 @@ namespace CSVTools QCheckBox mCaseSensitive; QPushButton mSearch; QGridLayout* mLayout; + bool mLocked; QComboBox mMode; bool mSearchEnabled; QStackedWidget mReplaceInput; @@ -38,6 +39,14 @@ namespace CSVTools private: void updateSearchButtons(); + bool canReplace(); + bool canReplaceRegex(); + bool canSearch(); + bool canSearchRegex(); + + bool hasInvalidSearchText(); + bool hasInvalidSearchRegex(); + public: SearchBox(QWidget* parent = nullptr); @@ -48,9 +57,6 @@ namespace CSVTools std::string getReplaceText() const; void setEditLock(bool locked); - void setSearchLock(bool locked); - - bool hasInvalidText(); void focus(); diff --git a/apps/opencs/view/tools/searchsubview.cpp b/apps/opencs/view/tools/searchsubview.cpp index c4dbd8e6f7..573545016a 100644 --- a/apps/opencs/view/tools/searchsubview.cpp +++ b/apps/opencs/view/tools/searchsubview.cpp @@ -113,7 +113,7 @@ CSVTools::SearchSubView::SearchSubView(const CSMWorld::UniversalId& id, CSMDoc:: void CSVTools::SearchSubView::setEditLock(bool locked) { mLocked = locked; - mSearchBox.setEditLock(locked || mSearchBox.hasInvalidText()); + mSearchBox.setEditLock(locked); } void CSVTools::SearchSubView::setStatusBar(bool show) From 5269688efc9527fe6b940f91165e4345aef8333e Mon Sep 17 00:00:00 2001 From: Aussiemon <1407091-Aussiemon@users.noreply.gitlab.com> Date: Sun, 25 Jan 2026 00:15:19 -0700 Subject: [PATCH 06/14] Minor comment consistency --- apps/opencs/view/tools/reporttable.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/view/tools/reporttable.hpp b/apps/opencs/view/tools/reporttable.hpp index 6f94b0c5e9..20bed8e233 100644 --- a/apps/opencs/view/tools/reporttable.hpp +++ b/apps/opencs/view/tools/reporttable.hpp @@ -86,7 +86,7 @@ namespace CSVTools /// \return rows in the original model std::vector getReplaceIndices(bool selection) const; - /// Return underlying report model + /// \return underlying report model CSMTools::ReportModel* getReportModel() const { return mModel; } /// \param index row in the original model From 277014527264a9933bc607ede748baaf8aa994d6 Mon Sep 17 00:00:00 2001 From: Aussiemon <1407091-Aussiemon@users.noreply.gitlab.com> Date: Sun, 25 Jan 2026 00:23:12 -0700 Subject: [PATCH 07/14] We don't need so many negations --- apps/opencs/view/tools/searchbox.cpp | 16 ++++++++-------- apps/opencs/view/tools/searchbox.hpp | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/opencs/view/tools/searchbox.cpp b/apps/opencs/view/tools/searchbox.cpp index 4470f78177..be41dcd4f0 100644 --- a/apps/opencs/view/tools/searchbox.cpp +++ b/apps/opencs/view/tools/searchbox.cpp @@ -47,32 +47,32 @@ void CSVTools::SearchBox::updateSearchButtons() bool CSVTools::SearchBox::canReplace() { - return !mLocked && !hasInvalidSearchText(); + return !mLocked && hasValidSearchText(); } bool CSVTools::SearchBox::canReplaceRegex() { - return canReplace() && !hasInvalidSearchRegex(); + return canReplace() && hasValidSearchRegex(); } bool CSVTools::SearchBox::canSearch() { - return !hasInvalidSearchText(); + return hasValidSearchText(); } bool CSVTools::SearchBox::canSearchRegex() { - return canSearch() && !hasInvalidSearchRegex(); + return canSearch() && hasValidSearchRegex(); } -bool CSVTools::SearchBox::hasInvalidSearchText() +bool CSVTools::SearchBox::hasValidSearchText() { - return mText.text().isEmpty(); + return !mText.text().isEmpty(); } -bool CSVTools::SearchBox::hasInvalidSearchRegex() +bool CSVTools::SearchBox::hasValidSearchRegex() { - return !QRegularExpression(mText.text()).isValid(); + return QRegularExpression(mText.text()).isValid(); } CSVTools::SearchBox::SearchBox(QWidget* parent) diff --git a/apps/opencs/view/tools/searchbox.hpp b/apps/opencs/view/tools/searchbox.hpp index 50a74f1522..dd486e8dad 100644 --- a/apps/opencs/view/tools/searchbox.hpp +++ b/apps/opencs/view/tools/searchbox.hpp @@ -44,8 +44,8 @@ namespace CSVTools bool canSearch(); bool canSearchRegex(); - bool hasInvalidSearchText(); - bool hasInvalidSearchRegex(); + bool hasValidSearchText(); + bool hasValidSearchRegex(); public: SearchBox(QWidget* parent = nullptr); From b607a6a98afe8a4973541e55da2d7ff9c74db405 Mon Sep 17 00:00:00 2001 From: Aussiemon <1407091-Aussiemon@users.noreply.gitlab.com> Date: Sun, 25 Jan 2026 01:13:47 -0700 Subject: [PATCH 08/14] Call update instead of direct disable --- apps/opencs/view/tools/searchbox.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/opencs/view/tools/searchbox.cpp b/apps/opencs/view/tools/searchbox.cpp index be41dcd4f0..73e3364304 100644 --- a/apps/opencs/view/tools/searchbox.cpp +++ b/apps/opencs/view/tools/searchbox.cpp @@ -194,8 +194,7 @@ std::string CSVTools::SearchBox::getReplaceText() const void CSVTools::SearchBox::setEditLock(bool locked) { mLocked = locked; - if (mLocked) - mReplace.setEnabled(false); + updateSearchButtons(); } void CSVTools::SearchBox::focus() From e7933da84ec22a48821cadf81236f6114923ed47 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Sun, 25 Jan 2026 12:18:19 -0700 Subject: [PATCH 09/14] Pattern validation rewrite --- apps/opencs/view/tools/searchbox.cpp | 78 ++++++++-------------------- apps/opencs/view/tools/searchbox.hpp | 10 +--- 2 files changed, 23 insertions(+), 65 deletions(-) diff --git a/apps/opencs/view/tools/searchbox.cpp b/apps/opencs/view/tools/searchbox.cpp index 73e3364304..bfeed31b42 100644 --- a/apps/opencs/view/tools/searchbox.cpp +++ b/apps/opencs/view/tools/searchbox.cpp @@ -10,69 +10,36 @@ void CSVTools::SearchBox::updateSearchButtons() { + mReplace.setEnabled(false); if (!mSearchEnabled) { mSearch.setEnabled(false); - mReplace.setEnabled(false); + return; } - else + + const CSMTools::Search::Type type = static_cast(mMode.currentIndex()); + if (type == CSMTools::Search::Type_RecordState) { - constexpr const char* redLineEditStyle = "QLineEdit { color: red; }"; - switch (mMode.currentIndex()) + mSearch.setEnabled(true); + return; + } + + bool canSearch = false; + QString style; + if (!mText.text().isEmpty()) + { + canSearch = true; + if (type == CSMTools::Search::Type_TextRegEx || type == CSMTools::Search::Type_IdRegEx) { - case 0: - case 2: - // Text modes - mText.setStyleSheet(QString()); - mSearch.setEnabled(canSearch()); - mReplace.setEnabled(canReplace()); - break; - - case 1: - case 3: - // Regex modes - mText.setStyleSheet(canSearchRegex() ? QString() : redLineEditStyle); - mSearch.setEnabled(canSearchRegex()); - mReplace.setEnabled(canReplaceRegex()); - break; - - case 4: - // Record state mode - mSearch.setEnabled(true); - mReplace.setEnabled(false); - break; + canSearch = QRegularExpression(mText.text()).isValid(); + if (!canSearch) + style = "QLineEdit { color: red; }"; } } -} -bool CSVTools::SearchBox::canReplace() -{ - return !mLocked && hasValidSearchText(); -} - -bool CSVTools::SearchBox::canReplaceRegex() -{ - return canReplace() && hasValidSearchRegex(); -} - -bool CSVTools::SearchBox::canSearch() -{ - return hasValidSearchText(); -} - -bool CSVTools::SearchBox::canSearchRegex() -{ - return canSearch() && hasValidSearchRegex(); -} - -bool CSVTools::SearchBox::hasValidSearchText() -{ - return !mText.text().isEmpty(); -} - -bool CSVTools::SearchBox::hasValidSearchRegex() -{ - return QRegularExpression(mText.text()).isValid(); + mText.setStyleSheet(style); + mSearch.setEnabled(canSearch); + mReplace.setEnabled(mAllowReplace); } CSVTools::SearchBox::SearchBox(QWidget* parent) @@ -80,7 +47,6 @@ CSVTools::SearchBox::SearchBox(QWidget* parent) , mSearch(tr("Search")) , mSearchEnabled(false) , mReplace(tr("Replace All")) - , mLocked(false) { mLayout = new QGridLayout(this); @@ -193,7 +159,7 @@ std::string CSVTools::SearchBox::getReplaceText() const void CSVTools::SearchBox::setEditLock(bool locked) { - mLocked = locked; + mAllowReplace = !locked; updateSearchButtons(); } diff --git a/apps/opencs/view/tools/searchbox.hpp b/apps/opencs/view/tools/searchbox.hpp index dd486e8dad..5b7f7df4c8 100644 --- a/apps/opencs/view/tools/searchbox.hpp +++ b/apps/opencs/view/tools/searchbox.hpp @@ -28,9 +28,9 @@ namespace CSVTools QCheckBox mCaseSensitive; QPushButton mSearch; QGridLayout* mLayout; - bool mLocked; QComboBox mMode; bool mSearchEnabled; + bool mAllowReplace{ false }; QStackedWidget mReplaceInput; QLineEdit mReplaceText; QLabel mReplacePlaceholder; @@ -39,14 +39,6 @@ namespace CSVTools private: void updateSearchButtons(); - bool canReplace(); - bool canReplaceRegex(); - bool canSearch(); - bool canSearchRegex(); - - bool hasValidSearchText(); - bool hasValidSearchRegex(); - public: SearchBox(QWidget* parent = nullptr); From 294e62719bf540a5a0a2624bdc8df7257ef240d1 Mon Sep 17 00:00:00 2001 From: Aussiemon <1407091-Aussiemon@users.noreply.gitlab.com> Date: Sun, 25 Jan 2026 12:52:43 -0700 Subject: [PATCH 10/14] Group setter methods together --- apps/opencs/view/tools/searchbox.cpp | 12 ++++++------ apps/opencs/view/tools/searchbox.hpp | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/opencs/view/tools/searchbox.cpp b/apps/opencs/view/tools/searchbox.cpp index bfeed31b42..e670b6f893 100644 --- a/apps/opencs/view/tools/searchbox.cpp +++ b/apps/opencs/view/tools/searchbox.cpp @@ -102,6 +102,12 @@ CSVTools::SearchBox::SearchBox(QWidget* parent) updateSearchButtons(); } +void CSVTools::SearchBox::setEditLock(bool locked) +{ + mAllowReplace = !locked; + updateSearchButtons(); +} + void CSVTools::SearchBox::setSearchMode(bool enabled) { mSearchEnabled = enabled; @@ -157,12 +163,6 @@ std::string CSVTools::SearchBox::getReplaceText() const } } -void CSVTools::SearchBox::setEditLock(bool locked) -{ - mAllowReplace = !locked; - updateSearchButtons(); -} - void CSVTools::SearchBox::focus() { mInput.currentWidget()->setFocus(); diff --git a/apps/opencs/view/tools/searchbox.hpp b/apps/opencs/view/tools/searchbox.hpp index 5b7f7df4c8..d4f518122e 100644 --- a/apps/opencs/view/tools/searchbox.hpp +++ b/apps/opencs/view/tools/searchbox.hpp @@ -42,14 +42,14 @@ namespace CSVTools public: SearchBox(QWidget* parent = nullptr); + void setEditLock(bool locked); + void setSearchMode(bool enabled); CSMTools::Search getSearch() const; std::string getReplaceText() const; - void setEditLock(bool locked); - void focus(); private slots: From 8a769464fb3fd7ca97c9acabfa7c49e4650e34ab Mon Sep 17 00:00:00 2001 From: Aussiemon <1407091-Aussiemon@users.noreply.gitlab.com> Date: Sun, 25 Jan 2026 13:35:15 -0700 Subject: [PATCH 11/14] Lock replace button when no search results --- apps/opencs/view/tools/searchsubview.cpp | 10 ++++++---- apps/opencs/view/tools/searchsubview.hpp | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/opencs/view/tools/searchsubview.cpp b/apps/opencs/view/tools/searchsubview.cpp index 573545016a..15d2e34083 100644 --- a/apps/opencs/view/tools/searchsubview.cpp +++ b/apps/opencs/view/tools/searchsubview.cpp @@ -22,10 +22,12 @@ void CSVTools::SearchSubView::replace(bool selection) { - if (mLocked) + if (!mAllowReplace) return; std::vector indices = mTable->getReplaceIndices(selection); + if (indices.empty()) + return; std::string replace = mSearchBox.getReplaceText(); @@ -74,7 +76,6 @@ void CSVTools::SearchSubView::showEvent(QShowEvent* event) CSVTools::SearchSubView::SearchSubView(const CSMWorld::UniversalId& id, CSMDoc::Document& document) : CSVDoc::SubView(id) , mDocument(document) - , mLocked(false) { QVBoxLayout* layout = new QVBoxLayout; @@ -112,8 +113,9 @@ CSVTools::SearchSubView::SearchSubView(const CSMWorld::UniversalId& id, CSMDoc:: void CSVTools::SearchSubView::setEditLock(bool locked) { - mLocked = locked; - mSearchBox.setEditLock(locked); + int reportSize = mDocument.getReport(getUniversalId())->rowCount(); + mAllowReplace = !(locked || reportSize == 0); + mSearchBox.setEditLock(locked || reportSize == 0); } void CSVTools::SearchSubView::setStatusBar(bool show) diff --git a/apps/opencs/view/tools/searchsubview.hpp b/apps/opencs/view/tools/searchsubview.hpp index 270706d7aa..86017ba3c1 100644 --- a/apps/opencs/view/tools/searchsubview.hpp +++ b/apps/opencs/view/tools/searchsubview.hpp @@ -31,7 +31,7 @@ namespace CSVTools SearchBox mSearchBox; CSMDoc::Document& mDocument; CSMTools::Search mSearch; - bool mLocked; + bool mAllowReplace{ false }; CSVWorld::TableBottomBox* mBottom; private: From a969a7fc44f4ccb4edf8012338bab3d8203fe501 Mon Sep 17 00:00:00 2001 From: Aussiemon <1407091-Aussiemon@users.noreply.gitlab.com> Date: Mon, 26 Jan 2026 01:35:41 -0700 Subject: [PATCH 12/14] Set result count through operationDone instead --- apps/opencs/view/tools/searchbox.cpp | 8 +++++++- apps/opencs/view/tools/searchbox.hpp | 4 ++++ apps/opencs/view/tools/searchsubview.cpp | 12 +++++++----- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/apps/opencs/view/tools/searchbox.cpp b/apps/opencs/view/tools/searchbox.cpp index e670b6f893..f2207f00d1 100644 --- a/apps/opencs/view/tools/searchbox.cpp +++ b/apps/opencs/view/tools/searchbox.cpp @@ -39,7 +39,7 @@ void CSVTools::SearchBox::updateSearchButtons() mText.setStyleSheet(style); mSearch.setEnabled(canSearch); - mReplace.setEnabled(mAllowReplace); + mReplace.setEnabled(mAllowReplace && mSearchResultCount > 0); } CSVTools::SearchBox::SearchBox(QWidget* parent) @@ -114,6 +114,12 @@ void CSVTools::SearchBox::setSearchMode(bool enabled) updateSearchButtons(); } +void CSVTools::SearchBox::setSearchResultCount(int resultCount) +{ + mSearchResultCount = resultCount; + updateSearchButtons(); +} + CSMTools::Search CSVTools::SearchBox::getSearch() const { CSMTools::Search::Type type = static_cast(mMode.currentIndex()); diff --git a/apps/opencs/view/tools/searchbox.hpp b/apps/opencs/view/tools/searchbox.hpp index d4f518122e..69490f34bd 100644 --- a/apps/opencs/view/tools/searchbox.hpp +++ b/apps/opencs/view/tools/searchbox.hpp @@ -37,6 +37,8 @@ namespace CSVTools QPushButton mReplace; private: + int mSearchResultCount = 0; + void updateSearchButtons(); public: @@ -46,6 +48,8 @@ namespace CSVTools void setSearchMode(bool enabled); + void setSearchResultCount(int resultCount); + CSMTools::Search getSearch() const; std::string getReplaceText() const; diff --git a/apps/opencs/view/tools/searchsubview.cpp b/apps/opencs/view/tools/searchsubview.cpp index 15d2e34083..d76165e1b8 100644 --- a/apps/opencs/view/tools/searchsubview.cpp +++ b/apps/opencs/view/tools/searchsubview.cpp @@ -113,9 +113,8 @@ CSVTools::SearchSubView::SearchSubView(const CSMWorld::UniversalId& id, CSMDoc:: void CSVTools::SearchSubView::setEditLock(bool locked) { - int reportSize = mDocument.getReport(getUniversalId())->rowCount(); - mAllowReplace = !(locked || reportSize == 0); - mSearchBox.setEditLock(locked || reportSize == 0); + mAllowReplace = !locked; + mSearchBox.setEditLock(locked); } void CSVTools::SearchSubView::setStatusBar(bool show) @@ -156,8 +155,11 @@ void CSVTools::SearchSubView::tableSizeUpdate() void CSVTools::SearchSubView::operationDone(int type, bool failed) { - if (type == CSMDoc::State_Searching && !failed && !mDocument.getReport(getUniversalId())->rowCount()) + if (type == CSMDoc::State_Searching) { - mBottom->setStatusMessage("No Results"); + int resultCount = mDocument.getReport(getUniversalId())->rowCount(); + if (!failed && !resultCount) + mBottom->setStatusMessage("No Results"); + mSearchBox.setSearchResultCount(resultCount); } } From 98f309d7f8b74025c9d433eb1ac75a446deb5352 Mon Sep 17 00:00:00 2001 From: Aussiemon <1407091-Aussiemon@users.noreply.gitlab.com> Date: Mon, 26 Jan 2026 01:47:12 -0700 Subject: [PATCH 13/14] Update result count after replace --- apps/opencs/view/tools/searchsubview.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/opencs/view/tools/searchsubview.cpp b/apps/opencs/view/tools/searchsubview.cpp index d76165e1b8..009f689d91 100644 --- a/apps/opencs/view/tools/searchsubview.cpp +++ b/apps/opencs/view/tools/searchsubview.cpp @@ -65,6 +65,8 @@ void CSVTools::SearchSubView::replace(bool selection) mTable->model()->removeRows(*iter, 1); } } + + mSearchBox.setSearchResultCount(mTable->model()->rowCount()); } void CSVTools::SearchSubView::showEvent(QShowEvent* event) From 0ca93eb23ea89a7b54fab04adc57e56c0a89d18a Mon Sep 17 00:00:00 2001 From: Aussiemon <1407091-Aussiemon@users.noreply.gitlab.com> Date: Mon, 26 Jan 2026 02:57:54 -0700 Subject: [PATCH 14/14] Move search result count persistence to tableSizeUpdate --- apps/opencs/view/tools/searchbox.cpp | 6 +++++- apps/opencs/view/tools/searchsubview.cpp | 13 +++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/apps/opencs/view/tools/searchbox.cpp b/apps/opencs/view/tools/searchbox.cpp index f2207f00d1..c632de99eb 100644 --- a/apps/opencs/view/tools/searchbox.cpp +++ b/apps/opencs/view/tools/searchbox.cpp @@ -116,8 +116,12 @@ void CSVTools::SearchBox::setSearchMode(bool enabled) void CSVTools::SearchBox::setSearchResultCount(int resultCount) { + int priorResultCount = mSearchResultCount; mSearchResultCount = resultCount; - updateSearchButtons(); + + // Update search buttons only if we're changing between zero and non-zero + if ((priorResultCount == 0) != (mSearchResultCount == 0)) + updateSearchButtons(); } CSMTools::Search CSVTools::SearchBox::getSearch() const diff --git a/apps/opencs/view/tools/searchsubview.cpp b/apps/opencs/view/tools/searchsubview.cpp index 009f689d91..dc8269490e 100644 --- a/apps/opencs/view/tools/searchsubview.cpp +++ b/apps/opencs/view/tools/searchsubview.cpp @@ -65,8 +65,6 @@ void CSVTools::SearchSubView::replace(bool selection) mTable->model()->removeRows(*iter, 1); } } - - mSearchBox.setSearchResultCount(mTable->model()->rowCount()); } void CSVTools::SearchSubView::showEvent(QShowEvent* event) @@ -152,16 +150,15 @@ void CSVTools::SearchSubView::replaceAllRequest() void CSVTools::SearchSubView::tableSizeUpdate() { - mBottom->tableSizeChanged(mDocument.getReport(getUniversalId())->rowCount(), 0, 0); + int resultCount = mDocument.getReport(getUniversalId())->rowCount(); + mBottom->tableSizeChanged(resultCount, 0, 0); + mSearchBox.setSearchResultCount(resultCount); } void CSVTools::SearchSubView::operationDone(int type, bool failed) { - if (type == CSMDoc::State_Searching) + if (type == CSMDoc::State_Searching && !failed && !mDocument.getReport(getUniversalId())->rowCount()) { - int resultCount = mDocument.getReport(getUniversalId())->rowCount(); - if (!failed && !resultCount) - mBottom->setStatusMessage("No Results"); - mSearchBox.setSearchResultCount(resultCount); + mBottom->setStatusMessage("No Results"); } }