diff --git a/src/blackgui/components/logcomponent.cpp b/src/blackgui/components/logcomponent.cpp index c0f0d2a51..7f41c8068 100644 --- a/src/blackgui/components/logcomponent.cpp +++ b/src/blackgui/components/logcomponent.cpp @@ -63,6 +63,11 @@ namespace BlackGui { ui->comp_StatusMessages->appendStatusMessagesToList(messages); }); + connect(ui->comp_StatusMessages, &CStatusMessagesDetail::filterChanged, this, [this](const CVariant &filter) + { + clearMessages(); + m_history.setFilter(filter.to()); + }); m_history.setFilter(CLogPattern().withSeverityAtOrAbove(CStatusMessage::SeverityInfo)); m_history.initialize(sApp->getDataLinkDBus()); } diff --git a/src/blackgui/components/statusmessagesdetail.cpp b/src/blackgui/components/statusmessagesdetail.cpp index 38eda50c3..b2b4f56c3 100644 --- a/src/blackgui/components/statusmessagesdetail.cpp +++ b/src/blackgui/components/statusmessagesdetail.cpp @@ -8,10 +8,13 @@ #include "statusmessagesdetail.h" #include "ui_statusmessagesdetail.h" +#include "blackgui/filters/statusmessagefilterbar.h" +#include "blackgui/filters/statusmessagefilterdialog.h" using namespace BlackMisc; using namespace BlackGui::Menus; using namespace BlackGui::Views; +using namespace BlackGui::Filters; namespace BlackGui { @@ -29,6 +32,11 @@ namespace BlackGui ui->tvp_StatusMessages->setCustomMenu(new CMessageMenu(this)); ui->tvp_StatusMessages->menuAddItems(CStatusMessageView::MenuSave); this->showFilterBar(); // default + + connect(ui->filter_LogMessages, &CStatusMessageFilterBar::changeFilter, this, [this](bool enable) + { + emit filterChanged(enable ? ui->filter_LogMessages->createModelFilter()->getAsValueObject() : CVariant{}); + }); } CStatusMessagesDetail::~CStatusMessagesDetail() @@ -57,6 +65,11 @@ namespace BlackGui { ui->tvp_StatusMessages->addFilterDialog(); ui->filter_LogMessages->hide(); + + connect(ui->tvp_StatusMessages->getFilterDialog(), &QDialog::accepted, [this] + { + emit filterChanged(ui->tvp_StatusMessages->getFilterDialog()->createModelFilter()->getAsValueObject()); + }); } void CStatusMessagesDetail::showFilterBar() diff --git a/src/blackgui/components/statusmessagesdetail.h b/src/blackgui/components/statusmessagesdetail.h index 2596c0f9b..359ca282e 100644 --- a/src/blackgui/components/statusmessagesdetail.h +++ b/src/blackgui/components/statusmessagesdetail.h @@ -78,6 +78,9 @@ namespace BlackGui //! \copydoc BlackGui::Views::CStatusMessageView::modelDataChangedDigest void modelDataChangedDigest(int count, bool withFilter); + //! The user changed their message filter + void filterChanged(const BlackMisc::CVariant &filter); + private: QScopedPointer ui; int m_maxLogMessages = -1; diff --git a/src/blackgui/models/modelfilter.h b/src/blackgui/models/modelfilter.h index e19ef0caa..ca125a53c 100644 --- a/src/blackgui/models/modelfilter.h +++ b/src/blackgui/models/modelfilter.h @@ -27,6 +27,7 @@ #include "blackmisc/simulation/distributorlist.h" #include "blackmisc/simulation/simulatedaircraftlist.h" #include "blackmisc/statusmessagelist.h" +#include "blackmisc/variant.h" #include #include @@ -58,6 +59,9 @@ namespace BlackGui //! Enabled? void setEnabled(bool enable); + //! Return an implementation-specific value object representing the filter + virtual BlackMisc::CVariant getAsValueObject() const { return {}; } + protected: //! Standard string search supporting wildcard at begin and end: "*xyz", "abc*" bool stringMatchesFilterExpression(const QString &value, const QString &filter, Qt::CaseSensitivity cs = Qt::CaseInsensitive) const; diff --git a/src/blackgui/models/statusmessagefilter.cpp b/src/blackgui/models/statusmessagefilter.cpp index 707695d72..802db0a1b 100644 --- a/src/blackgui/models/statusmessagefilter.cpp +++ b/src/blackgui/models/statusmessagefilter.cpp @@ -7,6 +7,7 @@ */ #include "blackgui/models/statusmessagefilter.h" +#include "blackmisc/logpattern.h" using namespace BlackMisc; @@ -46,5 +47,29 @@ namespace BlackGui } return outContainer; } + + CVariant CStatusMessageFilter::getAsValueObject() const + { + if (!m_category.contains('*')) + { + if (CLogPattern::allHumanReadableNames().contains(m_category)) + { + return CVariant::from(CLogPattern::fromHumanReadableName(m_category).withSeverityAtOrAbove(m_severity)); + } + return CVariant::from(CLogPattern::exactMatch(m_category).withSeverityAtOrAbove(m_severity)); + } + + CLogCategoryList categories = CLogCategory::allSpecialCategories(); + categories.removeIf([this](const CLogCategory &cat) { return this->stringMatchesFilterExpression(cat.toQString(), this->m_category); }); + CSequence humanNames = CLogPattern::allHumanReadableNames(); + humanNames.removeIf([this](const QString &name) { return this->stringMatchesFilterExpression(name, this->m_category); }); + auto humanCats = humanNames.transform([](const QString &name) + { + const auto strings = CLogPattern::fromHumanReadableName(name).getCategoryStrings(); + return strings.isEmpty() ? QString{} : *strings.begin(); + }); + + return CVariant::from(CLogPattern::anyOf(categories.join(humanCats)).withSeverityAtOrAbove(m_severity)); + } } // namespace } // namespace diff --git a/src/blackgui/models/statusmessagefilter.h b/src/blackgui/models/statusmessagefilter.h index 3681028c4..1fe63a124 100644 --- a/src/blackgui/models/statusmessagefilter.h +++ b/src/blackgui/models/statusmessagefilter.h @@ -16,6 +16,8 @@ #include "blackmisc/statusmessagelist.h" #include +namespace BlackMisc { class CLogPattern; } + namespace BlackGui { namespace Models @@ -30,6 +32,9 @@ namespace BlackGui //! \copydoc IModelFilter::filter virtual BlackMisc::CStatusMessageList filter(const BlackMisc::CStatusMessageList &inContainer) const override; + //! \copydoc IModelFilter::getAsValueObject + virtual BlackMisc::CVariant getAsValueObject() const override; + private: BlackMisc::CStatusMessage::StatusSeverity m_severity = BlackMisc::CStatusMessage::SeverityError; QString m_msgText; diff --git a/src/blackmisc/logpattern.h b/src/blackmisc/logpattern.h index 4f7099bfa..5c5d331a0 100644 --- a/src/blackmisc/logpattern.h +++ b/src/blackmisc/logpattern.h @@ -96,6 +96,9 @@ namespace BlackMisc //! This class acts as a SharedState filter when stored in a CVariant. bool matches(const CVariant &message) const { return match(message.to()); } + //! Technical category names matched by this pattern. + QSet getCategoryStrings() const { return m_strings; } + //! Returns true if this pattern is a proper subset of the other pattern. //! \see https://en.wikipedia.org/wiki/Proper_subset //! \details Pattern A is a proper subset of pattern B iff pattern B would match every category which pattern A matches,