diff --git a/src/blackgui/blackgui.pro b/src/blackgui/blackgui.pro index 7be40cf86..12274611e 100644 --- a/src/blackgui/blackgui.pro +++ b/src/blackgui/blackgui.pro @@ -34,6 +34,8 @@ SOURCES += $$PWD/components/*.cpp FORMS += $$PWD/components/*.ui FORMS += $$PWD/*.ui +FORMS += $$PWD/views/*.ui + win32 { HEADERS += $$PWD/win/*.h diff --git a/src/blackgui/models/aircraftmodelfilter.cpp b/src/blackgui/models/aircraftmodelfilter.cpp new file mode 100644 index 000000000..4c48d2c79 --- /dev/null +++ b/src/blackgui/models/aircraftmodelfilter.cpp @@ -0,0 +1,50 @@ +/* Copyright (C) 2013 + * swift project Community / Contributors + * + * This file is part of swift Project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#include "aircraftmodelfilter.h" +#include "blackmisc/simulation/aircraftmodel.h" + +using namespace BlackMisc::Simulation; + +namespace BlackGui +{ + namespace Models + { + + CAircraftModelFilter::CAircraftModelFilter(const QString &modelString, const QString &description) : + m_model(modelString.trimmed()), m_description(description.trimmed()) + { } + + BlackMisc::Simulation::CAircraftModelList CAircraftModelFilter::filter(const CAircraftModelList &inContainer) const + { + if (!this->isValid()) { return inContainer; } + CAircraftModelList outContainer; + for (const CAircraftModel &model : inContainer) + { + if (!this->m_model.isEmpty()) + { + if (!this->stringMatchesFilterExpression(model.getModelString(), this->m_model)) { continue; } + } + + if (!this->m_description.isEmpty()) + { + if (!this->stringMatchesFilterExpression(model.getDescription(), this->m_description)) { continue; } + } + outContainer.push_back(model); + } + return outContainer; + } + + bool CAircraftModelFilter::isValid() const + { + return !(this->m_model.isEmpty() && this->m_description.isEmpty()); + } + + } // namespace +} // namespace diff --git a/src/blackgui/models/aircraftmodelfilter.h b/src/blackgui/models/aircraftmodelfilter.h new file mode 100644 index 000000000..e1cd684a9 --- /dev/null +++ b/src/blackgui/models/aircraftmodelfilter.h @@ -0,0 +1,46 @@ +/* Copyright (C) 2013 + * swift project Community / Contributors + * + * This file is part of swift Project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKGUI_AIRCRAFTMODELFILTER_H +#define BLACKGUI_AIRCRAFTMODELFILTER_H + +#include "listmodelfilter.h" +#include "blackmisc/simulation/aircraftmodellist.h" + +namespace BlackGui +{ + namespace Models + { + + //! Filter for aircraft models + class CAircraftModelFilter : public IModelFilter + { + public: + //! Constructor + CAircraftModelFilter(const QString &modelString, const QString &description); + + //! \copydoc IModelFilter::filter + virtual BlackMisc::Simulation::CAircraftModelList filter(const BlackMisc::Simulation::CAircraftModelList &inContainer) const override; + + //! \copydoc IModelFilter::isValid + virtual bool isValid() const override; + + private: + QString m_model; + QString m_description; + + }; + + } // namespace +} // namespace + + +#endif // guard diff --git a/src/blackgui/models/listmodelbase.cpp b/src/blackgui/models/listmodelbase.cpp index 9b96df79a..0c5f8f724 100644 --- a/src/blackgui/models/listmodelbase.cpp +++ b/src/blackgui/models/listmodelbase.cpp @@ -8,6 +8,7 @@ */ #include "listmodelbase.h" +#include "blackmisc/simulation/aircraftmodellist.h" #include "blackmisc/namevariantpairlist.h" #include "blackmisc/statusmessagelist.h" #include "blackmisc/avatcstationlist.h" @@ -16,11 +17,14 @@ #include "blackmisc/nwserverlist.h" #include "blackmisc/nwuserlist.h" #include "blackmisc/nwclientlist.h" -#include "blackmisc/nwaircraftmodellist.h" #include "blackmisc/nwaircraftmappinglist.h" #include "blackmisc/setkeyboardhotkeylist.h" +#include "blackmisc/simulation/simulatedaircraftlist.h" +#include "blackmisc/variant.h" #include "blackmisc/blackmiscfreefunctions.h" +using namespace BlackMisc; + namespace BlackGui { namespace Models @@ -28,8 +32,9 @@ namespace BlackGui /* * Column count */ - int CListModelBaseNonTemplate::columnCount(const QModelIndex & /** modelIndex **/) const + int CListModelBaseNonTemplate::columnCount(const QModelIndex &modelIndex) const { + Q_UNUSED(modelIndex); int c = this->m_columns.size(); return c; } @@ -39,20 +44,20 @@ namespace BlackGui */ QVariant CListModelBaseNonTemplate::headerData(int section, Qt::Orientation orientation, int role) const { - if (orientation == Qt::Horizontal) + if (orientation != Qt::Horizontal) { return QVariant(); } + bool handled = (role == Qt::DisplayRole || role == Qt::ToolTipRole || role == Qt::InitialSortOrderRole); + if (!handled) {return QVariant();} + if (section < 0 || section >= this->m_columns.size()) { return QVariant(); } + + if (role == Qt::DisplayRole) { - if (role == Qt::DisplayRole) - { - if (section < 0 || section >= this->m_columns.size()) { return QVariant(); } - QString header = this->m_columns.at(section).getColumnName(false); - return QVariant(header); - } - else if (role == Qt::ToolTipRole) - { - if (section < 0 || section >= this->m_columns.size()) { return QVariant(); } - QString header = this->m_columns.at(section).getColumnToolTip(false); - return header.isEmpty() ? QVariant() : QVariant(header); - } + QString header = this->m_columns.at(section).getColumnName(false); + return QVariant(header); + } + else if (role == Qt::ToolTipRole) + { + QString header = this->m_columns.at(section).getColumnToolTip(false); + return header.isEmpty() ? QVariant() : QVariant(header); } return QVariant(); } @@ -65,33 +70,83 @@ namespace BlackGui return this->m_columns.columnToPropertyIndex(column); } + /* + * To column + */ + int CListModelBaseNonTemplate::propertyIndexToColumn(const CPropertyIndex &propertyIndex) const + { + return m_columns.propertyIndexToColumn(propertyIndex); + } + + /* + * Property index + */ + BlackMisc::CPropertyIndex CListModelBaseNonTemplate::modelIndexToPropertyIndex(const QModelIndex &index) const + { + return this->columnToPropertyIndex(index.column()); + } + + /* + * Sort column + */ + void CListModelBaseNonTemplate::setSortColumnByPropertyIndex(const BlackMisc::CPropertyIndex &propertyIndex) + { + this->m_sortedColumn = this->m_columns.propertyIndexToColumn(propertyIndex); + } + /* * Sort column? */ bool CListModelBaseNonTemplate::hasValidSortColumn() const { - return this->m_sortedColumn >= 0 && this->m_sortedColumn < this->m_columns.size(); + + if (!(this->m_sortedColumn >= 0 && this->m_sortedColumn < this->m_columns.size())) { return false; } + return this->m_columns.isSortable(this->m_sortedColumn); } /* - * Make editable + * Flags */ Qt::ItemFlags CListModelBaseNonTemplate::flags(const QModelIndex &index) const { Qt::ItemFlags f = QAbstractListModel::flags(index); - if (this->m_columns.isEditable(index)) - return f | Qt::ItemIsEditable; - else - return f; + if (!index.isValid()) { return f; } + bool editable = this->m_columns.isEditable(index); + f = editable ? (f | Qt::ItemIsEditable) : (f ^ Qt::ItemIsEditable); + const CDefaultFormatter *formatter = this->m_columns.getFormatter(index); + if (formatter) + { + return formatter->flags(f, editable); + } + + // fallback behaviour with no formatter + return f; + } + + /* + * Translation context + */ + const QString &CListModelBaseNonTemplate::getTranslationContext() const + { + return m_columns.getTranslationContext(); + } + + /* + * Update + */ + int CListModelBaseNonTemplate::ps_updateContainer(const CVariant &variant, bool sort) + { + return this->performUpdateContainer(variant, sort); } /* * Row count */ template - int CListModelBase::rowCount(const QModelIndex & /** parent */) const + int CListModelBase::rowCount(const QModelIndex &parentIndex) const { - return this->m_container.size(); + Q_UNUSED(parentIndex); + return this->getContainerOrFilteredContainer().size(); } /* @@ -100,8 +155,8 @@ namespace BlackGui template bool CListModelBase::isValidIndex(const QModelIndex &index) const { - if (!index.isValid()) return false; - return (index.row() >= 0 && index.row() < this->m_container.size() && + if (!index.isValid()) { return false; } + return (index.row() >= 0 && index.row() < this->rowCount(index) && index.column() >= 0 && index.column() < this->columnCount(index)); } @@ -109,7 +164,7 @@ namespace BlackGui * Data */ template - QVariant CListModelBase::data(const QModelIndex &index, int role) const + QVariant CListModelBase::data(const QModelIndex &index, int role) const { // check / init if (!this->isValidIndex(index)) { return QVariant(); } @@ -118,11 +173,46 @@ namespace BlackGui if (!formatter) { return QVariant(); } //! Formatted data - ObjectType obj = this->m_container[index.row()]; + ObjectType obj = this->getContainerOrFilteredContainer()[index.row()]; BlackMisc::CPropertyIndex propertyIndex = this->columnToPropertyIndex(index.column()); return formatter->data(role, obj.propertyByIndex(propertyIndex)).toQVariant(); } + /* + * Set data + */ + template + bool CListModelBase::setData(const QModelIndex &index, const QVariant &value, int role) + { + Qt::ItemDataRole dataRole = static_cast(role); + if (!(dataRole == Qt::UserRole || dataRole == Qt::EditRole)) { return false; } + + // check / init + if (!this->isValidIndex(index)) { return false; } + if (!this->m_columns.isEditable(index)) { return false; } + const CDefaultFormatter *formatter = this->m_columns.getFormatter(index); + Q_ASSERT(formatter); + if (!formatter) { return false; } + + ObjectType obj = this->m_container[index.row()]; + ObjectType currentObject(obj); + BlackMisc::CPropertyIndex propertyIndex = this->columnToPropertyIndex(index.column()); + obj.setPropertyByIndex(value, propertyIndex); + + if (obj != currentObject) + { + QModelIndex topLeft = index.sibling(index.row(), 0); + QModelIndex bottomRight = index.sibling(index.row(), this->columnCount() - 1); + this->m_container[index.row()] = obj; + const CVariant co = CVariant::from(obj); + emit objectChanged(co, propertyIndex); + emit dataChanged(topLeft, bottomRight); + this->updateFilteredContainer(); + return true; + } + return false; + } + /* * Update */ @@ -135,15 +225,17 @@ namespace BlackGui bool performSort = sort && container.size() > 1 && this->hasValidSortColumn(); if (performSort) { - sortedContainer = this->sortContainerByColumn(container, this->getSortColumn(), this->m_sortOrder); + int sortColumn = this->getSortColumn(); + sortedContainer = this->sortContainerByColumn(container, sortColumn, this->m_sortOrder); } this->beginResetModel(); this->m_container = performSort ? sortedContainer : container; + this->updateFilteredContainer(); this->endResetModel(); int newSize = this->m_container.size(); - if (oldSize != newSize) { rowCountChanged(newSize); } + if (oldSize != newSize) { this->emitRowCountChanged(); } return newSize; } @@ -153,7 +245,7 @@ namespace BlackGui template void CListModelBase::update(const QModelIndex &index, const ObjectType &object) { - if (index.row() >= this->m_container.size()) return; + if (index.row() >= this->m_container.size()) { return; } this->m_container[index.row()] = object; QModelIndex i1 = index.sibling(index.row(), 0); @@ -161,6 +253,12 @@ namespace BlackGui emit this->dataChanged(i1, i2); // which range has been changed } + template + void CListModelBase::update(int rowIndex, const ObjectType &object) + { + this->update(this->index(rowIndex), object); + } + /* * Async update */ @@ -172,8 +270,8 @@ namespace BlackGui BlackMisc::CWorker *worker = BlackMisc::CWorker::fromTask(this, "ModelSort", [this, container, sort, sortColumn, sortOrder]() { ContainerType sortedContainer = this->sortContainerByColumn(container, sortColumn, sortOrder); - QMetaObject::invokeMethod(this, "updateContainer", - Q_ARG(BlackMisc::CVariant, sortedContainer.toCVariant()), Q_ARG(bool, false)); + QMetaObject::invokeMethod(this, "ps_updateContainer", + Q_ARG(BlackMisc::CVariant, sortedContainer.toCVariant()), Q_ARG(bool, false)); }); worker->then(this, &CListModelBase::asyncUpdateFinished); return worker; @@ -196,23 +294,76 @@ namespace BlackGui } } + /* + * Filter + */ + template + bool CListModelBase::hasFilter() const + { + return m_filter ? true : false; + } + + /* + * Remove filter + */ + template + void CListModelBase::removeFilter() + { + if (!this->hasFilter()) { return; } + this->m_filter.reset(nullptr); + this->beginResetModel(); + this->updateFilteredContainer(); + this->endResetModel(); + this->emitRowCountChanged(); + } + + /* + * Set filter + */ + template + void CListModelBase::setFilter(std::unique_ptr > &filter) + { + if (!filter) { this->removeFilter(); return; } // empty filter + if (filter->isValid()) + { + this->m_filter = std::move(filter); + this->beginResetModel(); + this->updateFilteredContainer(); + this->endResetModel(); + this->emitRowCountChanged(); + } + else + { + this->removeFilter(); + } + } + /* * At */ template const ObjectType &CListModelBase::at(const QModelIndex &index) const { - if (index.row() < 0 || index.row() >= this->m_container.size()) + if (index.row() < 0 || index.row() >= this->rowCount()) { static const ObjectType def {}; // default object return def; } else { - return this->m_container[index.row()]; + return this->getContainerOrFilteredContainer()[index.row()]; } } + /* + * Container + */ + template + const ContainerType &CListModelBase::getContainer() const + { + return this->m_container; + } + /* * Push back */ @@ -222,11 +373,12 @@ namespace BlackGui beginInsertRows(QModelIndex(), this->m_container.size(), this->m_container.size()); this->m_container.push_back(object); endInsertRows(); - emit rowCountChanged(this->m_container.size()); + this->updateFilteredContainer(); + this->emitRowCountChanged(); } /* - * Push back + * insert */ template void CListModelBase::insert(const ObjectType &object) @@ -234,8 +386,14 @@ namespace BlackGui beginInsertRows(QModelIndex(), 0, 0); this->m_container.insert(this->m_container.begin(), object); endInsertRows(); - int newSize = this->m_container.size(); - emit rowCountChanged(newSize); + + if (this->hasFilter()) + { + this->beginResetModel(); + this->updateFilteredContainer(); + this->endResetModel(); + } + this->emitRowCountChanged(); } /* @@ -249,42 +407,81 @@ namespace BlackGui this->m_container.remove(object); endRemoveRows(); int newSize = this->m_container.size(); - if (oldSize != newSize) { emit rowCountChanged(newSize); } + if (oldSize != newSize) + { + this->emitRowCountChanged(); + if (this->hasFilter()) + { + this->beginResetModel(); + this->updateFilteredContainer(); + this->endResetModel(); + } + } } /* * Clear */ - template void CListModelBase::clear() + template + void CListModelBase::clear() { - int oldSize = this->m_container.size(); beginResetModel(); this->m_container.clear(); + this->m_containerFiltered.clear(); endResetModel(); - if (oldSize > 0) { emit rowCountChanged(0);} + this->emitRowCountChanged(); } /* * Update on container */ - template int CListModelBase::performUpdateContainer(const BlackMisc::CVariant &variant, bool sort) + template + int CListModelBase::performUpdateContainer(const BlackMisc::CVariant &variant, bool sort) { ContainerType c; c.convertFromCVariant(variant); return this->update(c, sort); } + template + const ContainerType &CListModelBase::getContainerOrFilteredContainer() const + { + if (!this->hasFilter()) { return this->m_container; } + return m_containerFiltered; + } + + template + void CListModelBase::updateFilteredContainer() + { + if (this->hasFilter()) + { + this->m_containerFiltered = this->m_filter->filter(this->m_container); + } + else + { + this->m_containerFiltered.clear(); + } + } + + template + void CListModelBase::emitRowCountChanged() + { + int n = this->getContainerOrFilteredContainer().size(); + emit this->rowCountChanged(n, this->hasFilter()); + } + /* * Sort requested by abstract model */ - template void CListModelBase::sort(int column, Qt::SortOrder order) + template + void CListModelBase::sort(int column, Qt::SortOrder order) { if (column == this->m_sortedColumn && order == this->m_sortOrder) { return; } // new order this->m_sortedColumn = column; this->m_sortOrder = order; - if (this->m_container.size() < 2) return; // nothing to do + if (this->m_container.size() < 2) { return; } // nothing to do // sort the values this->updateContainerMaybeAsync(this->m_container, true); @@ -293,14 +490,15 @@ namespace BlackGui /* * Sort list */ - template ContainerType CListModelBase::sortContainerByColumn(const ContainerType &container, int column, Qt::SortOrder order) const + template + ContainerType CListModelBase::sortContainerByColumn(const ContainerType &container, int column, Qt::SortOrder order) const { - if (container.size() < 2) return container; // nothing to do + if (container.size() < 2 || !this->m_columns.isSortable(column)) { return container; } // nothing to do // this is the only part not really thread safe, but columns do not change so far - BlackMisc::CPropertyIndex propertyIndex = this->m_columns.columnToPropertyIndex(column); + BlackMisc::CPropertyIndex propertyIndex = this->m_columns.columnToSortPropertyIndex(column); Q_ASSERT(!propertyIndex.isEmpty()); - if (propertyIndex.isEmpty()) return container; // at release build do nothing + if (propertyIndex.isEmpty()) { return container; } // at release build do nothing // sort the values const auto p = [ = ](const ObjectType & a, const ObjectType & b) -> bool @@ -310,11 +508,7 @@ namespace BlackGui return (order == Qt::AscendingOrder) ? (aQv < bQv) : (bQv < aQv); }; - // KWB: qDebug() will be removed soon - QTime t; - t.start(); const ContainerType sorted = container.sorted(p); - qDebug() << "Sort" << this->objectName() << "column" << column << "index:" << propertyIndex.toQString() << "ms:" << t.elapsed() << "thread:" << QThread::currentThreadId(); return sorted; } @@ -328,8 +522,9 @@ namespace BlackGui template class CListModelBase; template class CListModelBase; template class CListModelBase; - template class CListModelBase; + template class CListModelBase; template class CListModelBase; + template class CListModelBase; template class CListModelBase; } // namespace diff --git a/src/blackgui/models/listmodelbase.h b/src/blackgui/models/listmodelbase.h index e125310b1..fb297d583 100644 --- a/src/blackgui/models/listmodelbase.h +++ b/src/blackgui/models/listmodelbase.h @@ -13,10 +13,13 @@ #define BLACKGUI_LISTMODELBASE_H #include "blackgui/models/columns.h" +#include "blackgui/models/listmodelfilter.h" #include "blackmisc/worker.h" #include "blackmisc/propertyindex.h" #include #include +#include +#include namespace BlackGui { @@ -35,7 +38,7 @@ namespace BlackGui virtual ~CListModelBaseNonTemplate() {} //! \copydoc QAbstractListModel::columnCount() - virtual int columnCount(const QModelIndex &modelIndex) const override; + virtual int columnCount(const QModelIndex &modelIndex = QModelIndex()) const override; //! \copydoc QAbstractItemModel::headerData() virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override; @@ -43,11 +46,11 @@ namespace BlackGui //! Column to property index virtual BlackMisc::CPropertyIndex columnToPropertyIndex(int column) const; + //! Property index to column number + virtual int propertyIndexToColumn(const BlackMisc::CPropertyIndex &propertyIndex) const; + //! Index to property index - virtual BlackMisc::CPropertyIndex modelIndexToPropertyIndex(const QModelIndex &index) const - { - return this->columnToPropertyIndex(index.column()); - } + virtual BlackMisc::CPropertyIndex modelIndexToPropertyIndex(const QModelIndex &index) const; //! Set sort column virtual void setSortColumn(int column) { this->m_sortedColumn = column; } @@ -56,10 +59,7 @@ namespace BlackGui * Set column for sorting * \param propertyIndex index of column to be sorted */ - virtual void setSortColumnByPropertyIndex(const BlackMisc::CPropertyIndex &propertyIndex) - { - this->m_sortedColumn = this->m_columns.propertyIndexToColumn(propertyIndex); - } + virtual void setSortColumnByPropertyIndex(const BlackMisc::CPropertyIndex &propertyIndex); //! Get sort column property index virtual int getSortColumn() const { return this->m_sortedColumn; } @@ -74,26 +74,23 @@ namespace BlackGui Qt::ItemFlags flags(const QModelIndex &index) const override; //! Translation context - virtual const QString &getTranslationContext() const - { - return m_columns.getTranslationContext(); - } + virtual const QString &getTranslationContext() const; signals: //! Asynchronous update finished void asyncUpdateFinished(); //! Number of elements changed - void rowCountChanged(int count); + void rowCountChanged(int count, bool withFilter); + + //! Template free information, that object changed + void objectChanged(const BlackMisc::CVariant &object, const BlackMisc::CPropertyIndex &changedIndex); protected slots: //! Helper method with template free signature //! \param variant container is transferred in variant //! \param sort - int updateContainer(const BlackMisc::CVariant &variant, bool sort) - { - return this->performUpdateContainer(variant, sort); - } + int ps_updateContainer(const BlackMisc::CVariant &variant, bool sort); protected: /*! @@ -117,8 +114,7 @@ namespace BlackGui }; //! List model - template class CListModelBase : - public CListModelBaseNonTemplate + template class CListModelBase : public CListModelBaseNonTemplate { public: @@ -129,13 +125,17 @@ namespace BlackGui virtual bool isValidIndex(const QModelIndex &index) const; //! Used container data - virtual const ContainerType &getContainer() const { return this->m_container; } + virtual const ContainerType &getContainer() const; //! \copydoc QAbstractListModel::data() virtual QVariant data(const QModelIndex &index, int role) const override; + //! \copydoc QAbstractListModel::setData() + //! \sa CListModelBaseNonTemplate::flags + virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; + //! \copydoc QAbstractListModel::rowCount() - virtual int rowCount(const QModelIndex &index = QModelIndex()) const override; + virtual int rowCount(const QModelIndex &parentIndex = QModelIndex()) const override; //! Update by new container //! \remarks a sorting is performed only if a valid sort column is set @@ -151,10 +151,7 @@ namespace BlackGui virtual void update(const QModelIndex &index, const ObjectType &object); //! Update single element - virtual void update(int rowIndex, const ObjectType &object) - { - this->update(this->index(rowIndex), object); - } + virtual void update(int rowIndex, const ObjectType &object); //! Object at row position virtual const ObjectType &at(const QModelIndex &index) const; @@ -186,15 +183,25 @@ namespace BlackGui int removeIf(K0 k0, V0 v0, KeysValues... keysValues) { int c = m_container.removeIf(BlackMisc::Predicates::MemberEqual(k0, v0, keysValues...)); - if (c > 0) { emit rowCountChanged(this->rowCount());} + if (c > 0) { this->emitRowCountChanged();} + this->updateFilteredContainer(); return c; } //! Clear the list virtual void clear(); + //! Filter available + bool hasFilter() const; + + //! Remove filter + void removeFilter(); + + //! Set the filter + void setFilter(std::unique_ptr > &filter); + protected: - ContainerType m_container; //!< used container + std::unique_ptr > m_filter; //!< Used filter /*! * Constructor @@ -207,6 +214,18 @@ namespace BlackGui //! \copydoc CModelBaseNonTemplate::performUpdateContainer virtual int performUpdateContainer(const BlackMisc::CVariant &variant, bool sort) override; + + //! Full container or cached filtered container as approproiate + const ContainerType &getContainerOrFilteredContainer() const; + + //! Update filtered container + void updateFilteredContainer(); + + //! Row count changed + void emitRowCountChanged(); + ContainerType m_container; //!< used container + ContainerType m_containerFiltered; //!< cache for filtered container data + }; } // namespace diff --git a/src/blackgui/models/listmodelfilter.cpp b/src/blackgui/models/listmodelfilter.cpp new file mode 100644 index 000000000..f06a05dfc --- /dev/null +++ b/src/blackgui/models/listmodelfilter.cpp @@ -0,0 +1,68 @@ +/* Copyright (C) 2015 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#include "listmodelfilter.h" +#include "blackmisc/simulation/aircraftmodellist.h" + +namespace BlackGui +{ + namespace Models + { + template + bool IModelFilter::stringMatchesFilterExpression(const QString &value, const QString &filter, Qt::CaseSensitivity cs) const + { + QString v = value.trimmed(); + QString f = filter.trimmed(); + + if (v.isEmpty() && f.isEmpty()) { return true; } + if (v.isEmpty()) { return false; } + + // no wildcard, just string matching + if (!filter.contains('*')) + { + return (v.indexOf(f, 0, cs) == 0) && + (v.length() == f.length()); + } + + const QString filterNoWildcard = stripWildcard(f); + + // included? + if (f.startsWith('*') && f.endsWith('*')) + { + return v.contains(filterNoWildcard, cs); + } + + // starting with + if (f.startsWith('*')) + { + return v.endsWith(filterNoWildcard, cs); + } + + if (f.endsWith('*')) + { + return v.startsWith(filterNoWildcard, cs); + } + + // should never happen + Q_ASSERT(false); + return false; + } + + template + QString IModelFilter::stripWildcard(const QString &value) const + { + QString sw(value); + return sw.remove('*'); + } + + // Forward instantiations + template class IModelFilter; + + } // namespace +} // namespace diff --git a/src/blackgui/models/listmodelfilter.h b/src/blackgui/models/listmodelfilter.h new file mode 100644 index 000000000..f1e907678 --- /dev/null +++ b/src/blackgui/models/listmodelfilter.h @@ -0,0 +1,47 @@ +/* Copyright (C) 2015 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKGUI_LISTMODELFILTER_H +#define BLACKGUI_LISTMODELFILTER_H + +#include + +namespace BlackGui +{ + namespace Models + { + //! Model filter interface + template class IModelFilter + { + + public: + //! Destructor + virtual ~IModelFilter() {} + + //! Used container data + virtual ContainerType filter(const ContainerType &container) const = 0; + + //! Anything to do? + virtual bool isValid() const = 0; + + 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; + + //! Remove the * wildcards + QString stripWildcard(const QString &value) const; + + }; + + } // namespace +} // namespace +#endif // guard diff --git a/src/blackgui/qss/filterdialog.qss b/src/blackgui/qss/filterdialog.qss new file mode 100644 index 000000000..fc952c266 --- /dev/null +++ b/src/blackgui/qss/filterdialog.qss @@ -0,0 +1,13 @@ +QDialog { + background: black; /** background is background color here **/ + background-image: url(:/textures/icons/textures/texture-inner.jpg); +} + +QPushButton { + background-color: rgba(0, 0, 255, 128); + border-style: none; + border-radius:3px; + color: yellow; + padding: 3px; + margin-right: 5px; +} diff --git a/src/blackgui/stylesheetutility.cpp b/src/blackgui/stylesheetutility.cpp index 3fd61becb..f3e144e43 100644 --- a/src/blackgui/stylesheetutility.cpp +++ b/src/blackgui/stylesheetutility.cpp @@ -233,4 +233,13 @@ namespace BlackGui QPainter p(usedWidget); usedWidget->style()->drawPrimitive(element, &opt, &p, usedWidget); } + + QString CStyleSheetUtility::styleForIconCheckBox(const QString &checkedIcon, const QString &uncheckedIcon, const QString &width, const QString &height) + { + Q_ASSERT(!checkedIcon.isEmpty()); + Q_ASSERT(!uncheckedIcon.isEmpty()); + + static const QString st = "QCheckBox::indicator { width: %1; height: %2; } QCheckBox::indicator:checked { image: url(%3); } QCheckBox::indicator:unchecked { image: url(%4); }"; + return st.arg(width).arg(height).arg(checkedIcon).arg(uncheckedIcon); + } } diff --git a/src/blackgui/stylesheetutility.h b/src/blackgui/stylesheetutility.h index ec054fdd7..958530bbe 100644 --- a/src/blackgui/stylesheetutility.h +++ b/src/blackgui/stylesheetutility.h @@ -96,6 +96,14 @@ namespace BlackGui return f; } + //! File name maininfoarea.qss + static const QString &fileNameFilterDialog() + { + static const QString f("filterdialog.qss"); + return f; + } + + //! Font weights static const QStringList &fontWeights() { @@ -133,6 +141,9 @@ namespace BlackGui //! \sa QWidget::paintEvent static void useStyleSheetInDerivedWidget(QWidget *derivedWidget, QStyle::PrimitiveElement element = QStyle::PE_Widget); + //! Stylesheet string for a checkbox displayed as 2 icons + static QString styleForIconCheckBox(const QString &checkedIcon, const QString &uncheckedIcon, const QString &width = "16px", const QString &height = "16px"); + signals: //! Sheets have been changed void styleSheetsChanged(); diff --git a/src/blackgui/views/aircraftmodelfilterform.cpp b/src/blackgui/views/aircraftmodelfilterform.cpp new file mode 100644 index 000000000..3de254a80 --- /dev/null +++ b/src/blackgui/views/aircraftmodelfilterform.cpp @@ -0,0 +1,39 @@ +/* Copyright (C) 2013 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#include "aircraftmodelfilterform.h" +#include "ui_aircraftmodelfilterform.h" + +using namespace BlackGui::Models; + +namespace BlackGui +{ + namespace Views + { + + CAircraftModelFilterForm::CAircraftModelFilterForm(QWidget *parent) : + CFilterDialog(parent), + ui(new Ui::CAircraftModelFilterForm) + { + ui->setupUi(this); + this->setWindowTitle("Filter models"); + } + + CAircraftModelFilterForm::~CAircraftModelFilterForm() + { } + + std::unique_ptr CAircraftModelFilterForm::getFilter() const + { + QString model(this->ui->le_ModelString->text()); + QString desc(this->ui->le_ModelDescription->text()); + return std::unique_ptr(new CAircraftModelFilter(model, desc)); + } + + } // namespace +} // namespace diff --git a/src/blackgui/views/aircraftmodelfilterform.h b/src/blackgui/views/aircraftmodelfilterform.h new file mode 100644 index 000000000..27291710f --- /dev/null +++ b/src/blackgui/views/aircraftmodelfilterform.h @@ -0,0 +1,50 @@ +/* Copyright (C) 2013 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKGUI_AIRCRAFTMODELFILTERFORM_H +#define BLACKGUI_AIRCRAFTMODELFILTERFORM_H + +#include "filterdialog.h" +#include "blackgui/models/aircraftmodelfilter.h" +#include +#include +#include +#include + +namespace Ui { class CAircraftModelFilterForm; } + +namespace BlackGui +{ + namespace Views + { + //! Form for a aircraft model filter + class CAircraftModelFilterForm : public CFilterDialog + { + Q_OBJECT + + public: + //! Constructor + explicit CAircraftModelFilterForm(QWidget *parent = nullptr); + + //! Destructor + ~CAircraftModelFilterForm(); + + //! Get created filter + std::unique_ptr getFilter() const; + + private: + QScopedPointer ui; + }; + + } // namespace +} // namespace + +#endif // guard diff --git a/src/blackgui/views/aircraftmodelfilterform.ui b/src/blackgui/views/aircraftmodelfilterform.ui new file mode 100644 index 000000000..637cb30f8 --- /dev/null +++ b/src/blackgui/views/aircraftmodelfilterform.ui @@ -0,0 +1,91 @@ + + + CAircraftModelFilterForm + + + + 0 + 0 + 336 + 95 + + + + Dialog + + + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + Model + + + + + + + + + + Description + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + bb_ButtonBox + accepted() + CAircraftModelFilterForm + accept() + + + 248 + 254 + + + 157 + 274 + + + + + bb_ButtonBox + rejected() + CAircraftModelFilterForm + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/blackgui/views/aircraftmodelview.cpp b/src/blackgui/views/aircraftmodelview.cpp index 2eb2b7803..ad5837b82 100644 --- a/src/blackgui/views/aircraftmodelview.cpp +++ b/src/blackgui/views/aircraftmodelview.cpp @@ -8,9 +8,13 @@ */ #include "aircraftmodelview.h" +#include "aircraftmodelfilterform.h" #include +#include +#include using namespace BlackMisc; +using namespace BlackMisc::Simulation; using namespace BlackGui::Models; namespace BlackGui @@ -22,11 +26,31 @@ namespace BlackGui this->m_withMenuItemClear = true; this->m_withMenuItemRefresh = true; this->standardInit(new CAircraftModelListModel(CAircraftModelListModel::ModelOnly, this)); + + // filter + QWidget *mainWindow = this->mainApplicationWindowWidget(); + Q_ASSERT(mainWindow); + this->setFilterDialog(new CAircraftModelFilterForm(mainWindow)); } void CAircraftModelView::setAircraftModelMode(CAircraftModelListModel::AircraftModelMode mode) { this->m_model->setAircraftModelMode(mode); } - } -} + + bool CAircraftModelView::ps_filterDialogFinished(int status) + { + if (CViewBase::ps_filterDialogFinished(status)) { return true; } + if (!this->m_filterDialog) { this->derivedModel()->removeFilter(); return true; } + std::unique_ptr> filter(this->getFilterForm()->getFilter()); + this->derivedModel()->setFilter(filter); + return true; + } + + CAircraftModelFilterForm *CAircraftModelView::getFilterForm() const + { + return static_cast(this->m_filterDialog.data()); + } + + } // namespace +} // namespace diff --git a/src/blackgui/views/aircraftmodelview.h b/src/blackgui/views/aircraftmodelview.h index a42740b2f..10e9aff8d 100644 --- a/src/blackgui/views/aircraftmodelview.h +++ b/src/blackgui/views/aircraftmodelview.h @@ -12,6 +12,7 @@ #ifndef BLACKGUI_AIRCRAFTMODELVIEW_H #define BLACKGUI_AIRCRAFTMODELVIEW_H +#include "aircraftmodelfilterform.h" #include "viewbase.h" #include "../models/aircraftmodellistmodel.h" @@ -20,7 +21,7 @@ namespace BlackGui namespace Views { //! Aircrafts view - class CAircraftModelView : public CViewBase + class CAircraftModelView : public CViewBase { public: @@ -31,6 +32,13 @@ namespace BlackGui //! Set display mode void setAircraftModelMode(Models::CAircraftModelListModel::AircraftModelMode mode); + protected slots: + //! \copydoc CViewBaseNonTemplate::ps_filterDialogFinished + virtual bool ps_filterDialogFinished(int status) override; + + private: + //! Filter form + CAircraftModelFilterForm *getFilterForm() const; }; } } diff --git a/src/blackgui/views/filterdialog.cpp b/src/blackgui/views/filterdialog.cpp new file mode 100644 index 000000000..4a2defa35 --- /dev/null +++ b/src/blackgui/views/filterdialog.cpp @@ -0,0 +1,37 @@ +/* Copyright (C) 2013 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#include "filterdialog.h" +#include "blackgui/stylesheetutility.h" + +namespace BlackGui +{ + namespace Views + { + + CFilterDialog::CFilterDialog(QWidget *parent) : QDialog(parent, Qt::Tool) + { + this->setWindowTitle("Filter dialog"); + ps_onStyleSheetChanged(); + connect(&CStyleSheetUtility::instance(), &CStyleSheetUtility::styleSheetsChanged, this, &CFilterDialog::ps_onStyleSheetChanged); + } + + CFilterDialog::~CFilterDialog() + { } + + void CFilterDialog::ps_onStyleSheetChanged() + { + const QString qss = CStyleSheetUtility::instance().style(CStyleSheetUtility::fileNameFilterDialog()); + this->setStyleSheet(qss); + } + + } // namespace +} // namespace + + diff --git a/src/blackgui/views/filterdialog.h b/src/blackgui/views/filterdialog.h new file mode 100644 index 000000000..9d0b87e52 --- /dev/null +++ b/src/blackgui/views/filterdialog.h @@ -0,0 +1,39 @@ +/* Copyright (C) 2013 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKGUI_FILTERDIALOG_H +#define BLACKGUI_FILTERDIALOG_H + +#include + +namespace BlackGui +{ + namespace Views + { + //! Base for filter dialog + class CFilterDialog : public QDialog + { + public: + //! Constructor + CFilterDialog(QWidget *parent = nullptr); + + //! Destructor + virtual ~CFilterDialog(); + + private slots: + //! Stylesheet changed + void ps_onStyleSheetChanged(); + }; + + } // namespace +} // namespace + +#endif // guard diff --git a/src/blackgui/views/viewbase.cpp b/src/blackgui/views/viewbase.cpp index a0e8c82e8..f9f79927f 100644 --- a/src/blackgui/views/viewbase.cpp +++ b/src/blackgui/views/viewbase.cpp @@ -18,14 +18,19 @@ #include "../models/serverlistmodel.h" #include "../models/userlistmodel.h" #include "../models/clientlistmodel.h" +#include "../models/simulatedaircraftlistmodel.h" #include "../models/keyboardkeylistmodel.h" +#include "../guiutility.h" #include #include #include #include +#include +#include using namespace BlackMisc; +using namespace BlackGui; using namespace BlackGui::Models; namespace BlackGui @@ -48,10 +53,36 @@ namespace BlackGui this->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); } + void CViewBaseNonTemplate::setFilterDialog(QDialog *filterDialog) + { + if (filterDialog) + { + this->m_withMenuFilter = true; + this->m_filterDialog.reset(filterDialog); + connect(filterDialog, &QDialog::finished, this, &CViewBaseNonTemplate::ps_filterDialogFinished); + } + else + { + if (!this->m_filterDialog.isNull()) { disconnect(this->m_filterDialog.data()); } + this->m_withMenuFilter = false; + this->m_filterDialog.reset(nullptr); + } + } + + QWidget *CViewBaseNonTemplate::mainApplicationWindowWidget() const + { + return CGuiUtility::mainApplicationWindowWidget(); + } + void CViewBaseNonTemplate::customMenu(QMenu &menu) const { if (this->m_withMenuItemRefresh) { menu.addAction(BlackMisc::CIcons::refresh16(), "Update", this, SIGNAL(requestUpdate())); } if (this->m_withMenuItemClear) { menu.addAction(BlackMisc::CIcons::delete16(), "Clear", this, SLOT(ps_clear())); } + if (this->m_withMenuFilter) + { + menu.addAction(BlackMisc::CIcons::tableSheet16(), "Filter", this, SLOT(ps_displayFilterDialog())); + menu.addAction(BlackMisc::CIcons::tableSheet16(), "Remove Filter", this, SLOT(ps_removeFilter())); + } if (!menu.isEmpty()) { menu.addSeparator(); } menu.addAction(BlackMisc::CIcons::resize16(), "Full resize", this, SLOT(fullResizeToContents())); @@ -91,11 +122,23 @@ namespace BlackGui int fh = qRound(1.5 * this->getHorizontalHeaderFontHeight()); this->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive); // faster mode this->horizontalHeader()->setStretchLastSection(true); - this->verticalHeader()->setDefaultSectionSize(fh); - this->verticalHeader()->setMinimumSectionSize(fh); + this->verticalHeader()->setDefaultSectionSize(fh); // for height + this->verticalHeader()->setMinimumSectionSize(fh); // for height this->initRowsResizeModeToInteractive(); } + int CViewBaseNonTemplate::ps_updateContainer(const CVariant &variant, bool sort, bool resize) + { + return this->performUpdateContainer(variant, sort, resize); + } + + void CViewBaseNonTemplate::ps_displayFilterDialog() + { + if (!this->m_withMenuFilter) { return; } + if (!this->m_filterDialog) { return; } + this->m_filterDialog->show(); + } + void CViewBaseNonTemplate::initRowsResizeModeToInteractive() { const int height = this->verticalHeader()->minimumSectionSize(); @@ -116,8 +159,8 @@ namespace BlackGui if (m_resizeMode == ResizingOnce) { return m_resizeCount < 1; } if (m_resizeMode == ResizingAuto) { - if (reachedResizeThreshold()) return false; - if (m_resizeAutoNthTime < 2) return true; + if (reachedResizeThreshold()) { return false; } + if (m_resizeAutoNthTime < 2) { return true; } return (m_resizeCount % m_resizeAutoNthTime) == 0; } return false; @@ -125,10 +168,6 @@ namespace BlackGui void CViewBaseNonTemplate::fullResizeToContents() { - // KWB remove - QTime t; - t.start(); - m_resizeCount++; this->resizeColumnsToContents(); this->resizeRowsToContents(); @@ -177,7 +216,7 @@ namespace BlackGui BlackMisc::CWorker *worker = BlackMisc::CWorker::fromTask(this, "ViewSort", [this, model, container, sort, resize, sortColumn, sortOrder]() { ContainerType sortedContainer = model->sortContainerByColumn(container, sortColumn, sortOrder); - QMetaObject::invokeMethod(this, "updateContainer", + QMetaObject::invokeMethod(this, "ps_updateContainer", Q_ARG(BlackMisc::CVariant, sortedContainer.toCVariant()), Q_ARG(bool, false), Q_ARG(bool, resize)); }); worker->then(this, &CViewBase::asyncUpdateFinished); @@ -197,6 +236,25 @@ namespace BlackGui } } + template void CViewBase::insert(const ObjectType &value, bool resize) + { + Q_ASSERT(this->m_model); + this->m_model->insert(value); + if (resize) { this->performResizeToContents(); } + } + + template const ObjectType &CViewBase::at(const QModelIndex &index) const + { + Q_ASSERT(this->m_model); + return this->m_model->at(index); + } + + template const ContainerType &CViewBase::getContainer() const + { + Q_ASSERT(this->m_model); + return this->m_model->getContainer(); + } + template ContainerType CViewBase::selectedObjects() const { if (!this->hasSelection()) { return ContainerType(); } @@ -209,25 +267,29 @@ namespace BlackGui return c; } - template int CViewBase::rowCount() const + template + int CViewBase::rowCount() const { Q_ASSERT(this->m_model); return this->m_model->rowCount(); } - template int CViewBase::columnCount() const + template + int CViewBase::columnCount() const { Q_ASSERT(this->m_model); return this->m_model->columnCount(QModelIndex()); } - template bool CViewBase::isEmpty() const + template + bool CViewBase::isEmpty() const { Q_ASSERT(this->m_model); return this->m_model->rowCount() < 1; } - template void CViewBase::setObjectName(const QString &name) + template + void CViewBase::setObjectName(const QString &name) { // then name here is mainly set for debugging purposes so each model can be identified Q_ASSERT(m_model); @@ -236,7 +298,8 @@ namespace BlackGui this->m_model->setObjectName(modelName); } - template void CViewBase::setSortIndicator() + template + void CViewBase::setSortIndicator() { if (this->m_model->hasValidSortColumn()) { @@ -247,20 +310,24 @@ namespace BlackGui } } - template void CViewBase::standardInit(ModelClass *model) + template + void CViewBase::standardInit(ModelClass *model) { Q_ASSERT(model || this->m_model); if (model) { this->m_model = model; - connect(this->m_model, &ModelClass::rowCountChanged, this, &CViewBase::countChanged); + connect(this->m_model, &ModelClass::rowCountChanged, this, &CViewBase::rowCountChanged); + connect(this->m_model, &ModelClass::objectChanged, this, &CViewBase::objectChanged); } + this->setModel(this->m_model); // via QTableView CViewBaseNonTemplate::init(); this->setSortIndicator(); } - template void CViewBase::performResizeToContents() + template + void CViewBase::performResizeToContents() { // small set or large set? if (this->performResizing()) @@ -273,13 +340,32 @@ namespace BlackGui } } - template int CViewBase::performUpdateContainer(const BlackMisc::CVariant &variant, bool sort, bool resize) + template + int CViewBase::performUpdateContainer(const BlackMisc::CVariant &variant, bool sort, bool resize) { ContainerType c; c.convertFromCVariant(variant); return this->updateContainer(c, sort, resize); } + template + bool CViewBase::ps_filterDialogFinished(int status) + { + QDialog::DialogCode statusCode = static_cast(status); + if (statusCode == QDialog::Rejected) + { + this->derivedModel()->removeFilter(); + return true; // handled + } + return false; + } + + template + void CViewBase::ps_removeFilter() + { + this->derivedModel()->removeFilter(); + } + // see here for the reason of thess forward instantiations // http://www.parashift.com/c++-faq/separate-template-class-defn-from-decl.html template class CViewBase; @@ -290,7 +376,8 @@ namespace BlackGui template class CViewBase; template class CViewBase; template class CViewBase; - template class CViewBase; + template class CViewBase; + template class CViewBase; template class CViewBase; } // namespace diff --git a/src/blackgui/views/viewbase.h b/src/blackgui/views/viewbase.h index 57f27e522..8df7f775d 100644 --- a/src/blackgui/views/viewbase.h +++ b/src/blackgui/views/viewbase.h @@ -16,6 +16,7 @@ #include "blackmisc/worker.h" #include "blackmisc/variant.h" #include +#include #include #include #include @@ -70,6 +71,12 @@ namespace BlackGui //! Selected rows if any QModelIndexList selectedRows() const; + //! Filter dialog + void setFilterDialog(QDialog *filterDialog); + + //! Main application window widget if any + QWidget *mainApplicationWindowWidget() const; + signals: //! Ask for new data void requestUpdate(); @@ -78,7 +85,10 @@ namespace BlackGui void asyncUpdateFinished(); //! Number of elements changed - void countChanged(int count); + void rowCountChanged(int count, bool withFilter); + + //! Single object was changed in model + void objectChanged(const BlackMisc::CVariant &object, const BlackMisc::CPropertyIndex &changedIndex); public slots: //! Resize to contents, strategy depends on container size @@ -127,13 +137,21 @@ namespace BlackGui bool m_forceStretchLastColumnWhenResized = false; //!< a small table might (few columns) might to fail stretching, force again bool m_withMenuItemClear = false; //!< allow clearing the view via menu bool m_withMenuItemRefresh = false; //!< allow refreshing the view via menu + bool m_withMenuFilter = false; //!< filter can be opened + QScopedPointer m_filterDialog; //!< filter dialog if any protected slots: //! Helper method with template free signature serving as callback from threaded worker - int updateContainer(const BlackMisc::CVariant &variant, bool sort, bool resize) - { - return this->performUpdateContainer(variant, sort, resize); - } + int ps_updateContainer(const BlackMisc::CVariant &variant, bool sort, bool resize); + + //! Display the filter dialog + void ps_displayFilterDialog(); + + //! Remove filter + virtual void ps_removeFilter() = 0; + + //! Filter dialog finished + virtual bool ps_filterDialogFinished(int status) = 0; private slots: //! Custom menu was requested @@ -173,19 +191,13 @@ namespace BlackGui void updateContainerMaybeAsync(const ContainerType &container, bool sort = true, bool performResizing = true); //! Insert - void insert(const ObjectType &value, bool resize = true) - { - Q_ASSERT(this->m_model); - this->m_model->insert(value); - if (resize) { this->performResizeToContents(); } - } + void insert(const ObjectType &value, bool resize = true); //! Value object at - const ObjectType &at(const QModelIndex &index) const - { - Q_ASSERT(this->m_model); - return this->m_model->at(index); - } + const ObjectType &at(const QModelIndex &index) const; + + //! Access to container + const ContainerType &getContainer() const; //! Selected objects ContainerType selectedObjects() const; @@ -227,6 +239,13 @@ namespace BlackGui //! \copydoc CViewBaseNonTemplate::performUpdateContainer virtual int performUpdateContainer(const BlackMisc::CVariant &variant, bool sort, bool performResizing) override; + //! \copydoc CViewBaseNonTemplate::ps_filterDialogFinished + //! \remarks Actually a slot, but not defined as such as the template does not support Q_OBJECT + virtual bool ps_filterDialogFinished(int status) override; + + //! \copydoc CViewBaseNonTemplate::ps_removeFilter + //! \remarks Actually a slot, but not defined as such as the template does not support Q_OBJECT + virtual void ps_removeFilter() override; }; } // namespace } // namespace