From 53078cc8ae4e6c2636caaae907a7e795e2a5959a Mon Sep 17 00:00:00 2001 From: Klaus Basan Date: Sun, 18 Jan 2015 23:12:52 +0100 Subject: [PATCH] refs #368, improved column handling in views * columns can be marked non-sortable * alternative column for sorting * checkable column formatters * moved functions to cpp file --- src/blackgui/models/columnformatters.cpp | 86 ++++++++++++++----- src/blackgui/models/columnformatters.h | 69 +++++++++++---- src/blackgui/models/columns.cpp | 71 ++++++++++++--- src/blackgui/models/columns.h | 52 ++++++++--- .../models/statusmessagelistmodel.cpp | 4 +- 5 files changed, 218 insertions(+), 64 deletions(-) diff --git a/src/blackgui/models/columnformatters.cpp b/src/blackgui/models/columnformatters.cpp index 3adf35230..1bc564113 100644 --- a/src/blackgui/models/columnformatters.cpp +++ b/src/blackgui/models/columnformatters.cpp @@ -20,25 +20,32 @@ namespace BlackGui namespace Models { + Qt::ItemFlags CDefaultFormatter::flags(Qt::ItemFlags flags, bool editable) const + { + return editable ? (flags | Qt::ItemIsEditable) : (flags ^ Qt::ItemIsEditable); + } + CVariant CDefaultFormatter::displayRole(const CVariant &dataCVariant) const { - // seems to be absurd, but calls the correct methods on CValueObjects - // so QVariant -> QString -> CVariant is correct - if (static_cast(dataCVariant.type()) == QMetaType::QString) return dataCVariant; // shortcut - return dataCVariant.toQString(m_useI18n); + return keepStandardTypesConvertToStringOtherwise(dataCVariant); + } + + CVariant CDefaultFormatter::editRole(const CVariant &dataCVariant) const + { + return keepStandardTypesConvertToStringOtherwise(dataCVariant); + } + + CVariant CDefaultFormatter::tooltipRole(const CVariant &value) const + { + if (static_cast(value.type()) == QMetaType::QString) { return value; } + return value.toQString(m_useI18n); } CVariant CDefaultFormatter::decorationRole(const CVariant &dataCVariant) const { // direct return if type is already correct - if (static_cast(dataCVariant.type()) == QMetaType::QPixmap) - { - return dataCVariant; - } - else if (static_cast(dataCVariant.type()) == QMetaType::QIcon) - { - return dataCVariant; - } + if (static_cast(dataCVariant.type()) == QMetaType::QPixmap) { return dataCVariant; } + if (static_cast(dataCVariant.type()) == QMetaType::QIcon) { return dataCVariant; } // convert to pixmap if (static_cast(dataCVariant.type()) == QMetaType::QImage) @@ -70,6 +77,13 @@ namespace BlackGui } } + CVariant CDefaultFormatter::checkStateRole(const CVariant &value) const + { + bool b = value.toBool(); + Qt::CheckState cs = b ? Qt::Checked : Qt::Unchecked; + return CVariant::fromValue(static_cast(cs)); + } + CVariant CDefaultFormatter::data(int role, const CVariant &inputData) const { Qt::ItemDataRole roleEnum = static_cast(role); @@ -77,26 +91,41 @@ namespace BlackGui // always supported if (roleEnum == Qt::TextAlignmentRole) return { alignmentRole() }; - // checked - if (this->m_supportedRoles.isEmpty()) return CVariant(); - if (!this->m_supportedRoles.contains(role)) return CVariant(); + // check + if (role == Qt::UserRole) { return CDefaultFormatter::displayRole(inputData); } // just as data provider + if (this->m_supportedRoles.isEmpty()) { return CVariant(); } + if (!this->m_supportedRoles.contains(role)) { return CVariant(); } switch (roleEnum) { case Qt::DisplayRole: - // formatted to string + // formatted to standard types or string return displayRole(inputData); + case Qt::EditRole: + // formatted to standard types or string + return editRole(inputData); case Qt::ToolTipRole: // formatted to string return tooltipRole(inputData); case Qt::DecorationRole: // formatted as pixmap, icon, or color return decorationRole(inputData); + case Qt::CheckStateRole: + // as Qt check state + return checkStateRole(inputData); default: break; } return CVariant(); } + CVariant CDefaultFormatter::keepStandardTypesConvertToStringOtherwise(const CVariant &inputData) const + { + if (static_cast(inputData.type()) == QMetaType::QString) { return inputData; } + if (static_cast(inputData.type()) == QMetaType::Bool) { return inputData; } + if (static_cast(inputData.type()) == QMetaType::Int) { return inputData; } + return inputData.toQString(m_useI18n); + } + CVariant CPixmapFormatter::displayRole(const CVariant &dataCVariant) const { Q_UNUSED(dataCVariant); @@ -221,6 +250,12 @@ namespace BlackGui return CVariant(); } + Qt::ItemFlags CDelegateFormatter::flags(Qt::ItemFlags flags, bool editable) const + { + flags = CDefaultFormatter::flags(flags, editable); + return flags; + } + CVariant CBoolTextFormatter::displayRole(const CVariant &dataCVariant) const { if (dataCVariant.canConvert()) @@ -232,11 +267,16 @@ namespace BlackGui return CVariant(); } + Qt::ItemFlags CBoolTextFormatter::flags(Qt::ItemFlags flags, bool editable) const + { + return CDefaultFormatter::flags(flags, editable); + } + CBoolLedFormatter::CBoolLedFormatter(int alignment) : CBoolLedFormatter("on", "off", alignment) { } CBoolLedFormatter::CBoolLedFormatter(const QString &onName, const QString &offName, int alignment) : - CBoolTextFormatter(alignment, onName, offName, roleDecorationAndToolTip()) + CBoolTextFormatter(alignment, onName, offName, rolesDecorationAndToolTip()) { CLedWidget *led = ledDefault(); led->setOn(true); @@ -273,7 +313,7 @@ namespace BlackGui { } CBoolIconFormatter::CBoolIconFormatter(const CIcon &onIcon, const CIcon &offIcon, const QString &onName, const QString &offName, int alignment) : - CBoolTextFormatter(alignment, onName, offName, roleDecorationAndToolTip()), m_iconOn(onIcon), m_iconOff(offIcon) + CBoolTextFormatter(alignment, onName, offName, rolesDecorationAndToolTip()), m_iconOn(onIcon), m_iconOff(offIcon) { this->m_iconOn.setDescriptiveText(onName); this->m_iconOff.setDescriptiveText(offName); @@ -296,5 +336,11 @@ namespace BlackGui Q_ASSERT_X(false, "CBoolIconFormatter", "no boolean value"); return CVariant(); } - } -} + + CVariant CBoolIconFormatter::tooltipRole(const CVariant &dataCVariant) const + { + return CBoolTextFormatter::displayRole(dataCVariant); + } + + } // namespace +} // namespace diff --git a/src/blackgui/models/columnformatters.h b/src/blackgui/models/columnformatters.h index cdb4c85ca..59aea1d40 100644 --- a/src/blackgui/models/columnformatters.h +++ b/src/blackgui/models/columnformatters.h @@ -36,16 +36,20 @@ namespace BlackGui //! Virtual destructor virtual ~CDefaultFormatter() {} - //! Value provided as CVariant, formatter converts to QString. + //! Flags + virtual Qt::ItemFlags flags(Qt::ItemFlags flags, bool editable) const; + + //! Value provided as CVariant, formatter converts to standard types or string. //! Used with Qt::DisplayRole displaying a text. virtual BlackMisc::CVariant displayRole(const BlackMisc::CVariant &dataCVariant) const; + //! Value provided as CVariant, formatter converts to standard types or string. + //! Used with Qt::DisplayRole displaying a text. + virtual BlackMisc::CVariant editRole(const BlackMisc::CVariant &dataCVariant) const; + //! Value provided as CVariant, formatter converts to QString. //! Used with Qt::ToolTipRole displaying a text. - virtual BlackMisc::CVariant tooltipRole(const BlackMisc::CVariant &value) const - { - return displayRole(value); - } + virtual BlackMisc::CVariant tooltipRole(const BlackMisc::CVariant &value) const; //! Value provided as CVariant, formatted as icon (Qt docu: "The data to be rendered as a decoration in the form of an icon"). //! Used with Qt::DecorationRole displaying an icon, method returns pixmap, icon, or color (see docu) @@ -54,6 +58,9 @@ namespace BlackGui //! Qt::Alignment (as CVariant) virtual BlackMisc::CVariant alignmentRole() const; + //! Value provided as CVariant (expecting a bool), returning as Qt::CheckStae + virtual BlackMisc::CVariant checkStateRole(const BlackMisc::CVariant &value) const; + //! Alignment available? virtual bool hasAlignment() const { return m_alignment >= 0; } @@ -72,14 +79,26 @@ namespace BlackGui //! Align right/vertically centered static int alignRightVCenter() { return Qt::AlignVCenter | Qt::AlignRight; } - //! Display on role + //! Display role static const QList &roleDisplay() { static const QList r({ Qt::DisplayRole}); return r; } - //! Display on role - static const QList &roleDecorationAndToolTip() { static const QList r({ Qt::DecorationRole, Qt::ToolTipRole}); return r; } + //! Display role + static const QList &rolesDisplayAndEdit() { static const QList r({ Qt::DisplayRole, Qt::EditRole}); return r; } + + //! Decoration + ToolTip role + static const QList &rolesDecorationAndToolTip() { static const QList r({ Qt::DecorationRole, Qt::ToolTipRole}); return r; } + + //! CheckState role + static const QList &roleCheckState() { static const QList r({ Qt::CheckStateRole}); return r; } + + //! No roles + static const QList &rolesNone() { static const QList r; return r; } protected: - QList m_supportedRoles = QList({ Qt::DisplayRole}); //!< supports decoration role + //! Standard conversion + virtual BlackMisc::CVariant keepStandardTypesConvertToStringOtherwise(const BlackMisc::CVariant &inputData) const; + + QList m_supportedRoles = roleDisplay(); //!< supports decoration roles int m_alignment = -1; //!< alignment horizontal/vertically / Qt::Alignment bool m_useI18n = true; //!< i18n? }; @@ -89,7 +108,7 @@ namespace BlackGui { public: //! Constructor - CPixmapFormatter(int alignment = alignDefault(), const QList &supportedRoles = roleDecorationAndToolTip()) : CDefaultFormatter(alignment, false, supportedRoles) {} + CPixmapFormatter(int alignment = alignDefault(), const QList &supportedRoles = rolesDecorationAndToolTip()) : CDefaultFormatter(alignment, false, supportedRoles) {} //! \copydoc CDefaultFormatter::displayRole virtual BlackMisc::CVariant displayRole(const BlackMisc::CVariant &dataCVariant) const override; @@ -109,6 +128,18 @@ namespace BlackGui virtual BlackMisc::CVariant displayRole(const BlackMisc::CVariant &dataCVariant) const override; }; + //! Layout will be defined by a delegate + class CDelegateFormatter : public CDefaultFormatter + { + public: + //! Constructor + CDelegateFormatter(int alignment = alignCentered(), const QList &supportedRoles = rolesDecorationAndToolTip()) : + CDefaultFormatter(alignment, false, supportedRoles) {} + + //! \copydoc CDefaultFormatter::flags + virtual Qt::ItemFlags flags(Qt::ItemFlags flags, bool editable) const override; + }; + //! Bool value, format as text class CBoolTextFormatter : public CDefaultFormatter { @@ -120,8 +151,11 @@ namespace BlackGui //! \copydoc CDefaultFormatter::displayRole virtual BlackMisc::CVariant displayRole(const BlackMisc::CVariant &dataCVariant) const override; + //! \copydoc CDefaultFormatter::flags + virtual Qt::ItemFlags flags(Qt::ItemFlags flags, bool editable) const override; + protected: - QString m_trueName = "true"; //!< displayed when true + QString m_trueName = "true"; //!< displayed when true QString m_falseName = "false"; //!< displayed when false }; @@ -180,10 +214,7 @@ namespace BlackGui virtual BlackMisc::CVariant decorationRole(const BlackMisc::CVariant &dataCVariant) const override; //! \copydoc CDefaultFormatter::tooltipRole - virtual BlackMisc::CVariant tooltipRole(const BlackMisc::CVariant &dataCVariant) const override - { - return CBoolTextFormatter::displayRole(dataCVariant); - } + virtual BlackMisc::CVariant tooltipRole(const BlackMisc::CVariant &dataCVariant) const override; protected: BlackMisc::CIcon m_iconOn; //!< Used when on @@ -195,7 +226,7 @@ namespace BlackGui { public: //! Constructor - CValueObjectFormatter(int alignment = alignDefault(), bool i18n = true, QList supportedRoles = {Qt::DisplayRole}) : CDefaultFormatter(alignment, i18n, supportedRoles) {} + CValueObjectFormatter(int alignment = alignDefault(), bool i18n = true, QList supportedRoles = roleDisplay()) : CDefaultFormatter(alignment, i18n, supportedRoles) {} //! \copydoc CDefaultFormatter::displayRole virtual BlackMisc::CVariant displayRole(const BlackMisc::CVariant &valueObject) const override; @@ -235,7 +266,7 @@ namespace BlackGui { public: //! Constructor - CPhysiqalQuantiyFormatter(MU unit = MU::defaultUnit(), int digits = 2, int alignment = alignRightVCenter(), bool withUnit = true, bool i18n = true, QList supportedRoles = { Qt::DisplayRole }) : CValueObjectFormatter(alignment, i18n, supportedRoles), m_unit(unit), m_digits(digits), m_withUnit(withUnit) {} + CPhysiqalQuantiyFormatter(MU unit = MU::defaultUnit(), int digits = 2, int alignment = alignRightVCenter(), bool withUnit = true, bool i18n = true, QList supportedRoles = roleDisplay() ) : CValueObjectFormatter(alignment, i18n, supportedRoles), m_unit(unit), m_digits(digits), m_withUnit(withUnit) {} //! \copydoc CDefaultFormatter::displayRole virtual BlackMisc::CVariant displayRole(const BlackMisc::CVariant &physicalQuantity) const override @@ -266,8 +297,8 @@ namespace BlackGui virtual void setDigits(int digits) { m_digits = digits; } protected: - MU m_unit; //!< unit - int m_digits = 2; //!< digits + MU m_unit; //!< unit + int m_digits = 2; //!< digits bool m_withUnit = true; //!< format with unit? }; diff --git a/src/blackgui/models/columns.cpp b/src/blackgui/models/columns.cpp index 985f84e82..ce14738c5 100644 --- a/src/blackgui/models/columns.cpp +++ b/src/blackgui/models/columns.cpp @@ -10,21 +10,34 @@ #include "columns.h" #include +using namespace BlackMisc; + namespace BlackGui { namespace Models { - CColumn::CColumn(const QString &headerName, const QString &toolTip, const BlackMisc::CPropertyIndex &propertyIndex, CDefaultFormatter *formatter, bool editable) : + CColumn::CColumn(const QString &headerName, const QString &toolTip, const CPropertyIndex &propertyIndex, CDefaultFormatter *formatter, bool editable) : m_columnName(headerName), m_columnToolTip(toolTip), m_formatter(formatter ? formatter : new CDefaultFormatter()), m_propertyIndex(propertyIndex), m_editable(editable) {} - CColumn::CColumn(const BlackMisc::CPropertyIndex &propertyIndex) : + CColumn::CColumn(const CPropertyIndex &propertyIndex) : m_formatter(new CPixmapFormatter()), m_propertyIndex(propertyIndex) {} - CColumn::CColumn(const QString &toolTip, const BlackMisc::CPropertyIndex &propertyIndex) : + bool CColumn::hasSortPropertyIndex() const + { + return !this->m_sortPropertyIndex.isEmpty(); + } + + void CColumn::setSortPropertyIndex(const CPropertyIndex &propertyIndex) + { + Q_ASSERT(!propertyIndex.isEmpty()); + this->m_sortPropertyIndex = propertyIndex; + } + + CColumn::CColumn(const QString &toolTip, const CPropertyIndex &propertyIndex) : m_columnToolTip(toolTip), m_formatter(new CPixmapFormatter()), m_propertyIndex(propertyIndex) {} @@ -55,22 +68,22 @@ namespace BlackGui return QCoreApplication::translate(this->getTranslationContextChar(), this->getColumnToolTipChar()); } - CColumn CColumn::standardValueObject(const QString &headerName, const BlackMisc::CPropertyIndex &propertyIndex, int alignment) + CColumn CColumn::standardValueObject(const QString &headerName, const CPropertyIndex &propertyIndex, int alignment) { return CColumn(headerName, propertyIndex, new CValueObjectFormatter(alignment)); } - CColumn CColumn::standardValueObject(const QString &headerName, const QString &toolTip, const BlackMisc::CPropertyIndex &propertyIndex, int alignment) + CColumn CColumn::standardValueObject(const QString &headerName, const QString &toolTip, const CPropertyIndex &propertyIndex, int alignment) { return CColumn(headerName, toolTip, propertyIndex, new CValueObjectFormatter(alignment)); } - CColumn CColumn::standardString(const QString &headerName, const BlackMisc::CPropertyIndex &propertyIndex, int alignment) + CColumn CColumn::standardString(const QString &headerName, const CPropertyIndex &propertyIndex, int alignment) { return CColumn(headerName, propertyIndex, new CStringFormatter(alignment)); } - CColumn CColumn::standardString(const QString &headerName, const QString &toolTip, const BlackMisc::CPropertyIndex &propertyIndex, int alignment) + CColumn CColumn::standardString(const QString &headerName, const QString &toolTip, const CPropertyIndex &propertyIndex, int alignment) { return CColumn(headerName, toolTip, propertyIndex, new CStringFormatter(alignment)); } @@ -90,7 +103,7 @@ namespace BlackGui this->m_columns.push_back(column); } - QString CColumns::propertyIndexToColumnName(const BlackMisc::CPropertyIndex &propertyIndex, bool i18n) const + QString CColumns::propertyIndexToColumnName(const CPropertyIndex &propertyIndex, bool i18n) const { int column = this->propertyIndexToColumn(propertyIndex); return this->m_columns.at(column).getColumnName(i18n); @@ -102,13 +115,23 @@ namespace BlackGui return this->m_columns.at(column).getColumnName(i18n); } - BlackMisc::CPropertyIndex CColumns::columnToPropertyIndex(int column) const + CPropertyIndex CColumns::columnToPropertyIndex(int column) const { Q_ASSERT(isValidColumn(column)); return this->m_columns.at(column).getPropertyIndex(); } - int CColumns::propertyIndexToColumn(const BlackMisc::CPropertyIndex &propertyIndex) const + CPropertyIndex CColumns::columnToSortPropertyIndex(int column) const + { + Q_ASSERT(isValidColumn(column)); + const CColumn col = this->m_columns[column]; + Q_ASSERT(col.isSortable()); + if (!col.isSortable()) { return CPropertyIndex(); } + if (col.hasSortPropertyIndex()) { return col.getSortPropertyIndex(); } + return col.getPropertyIndex(); + } + + int CColumns::propertyIndexToColumn(const CPropertyIndex &propertyIndex) const { for (int i = 0; i < this->m_columns.size(); i++) { @@ -147,6 +170,34 @@ namespace BlackGui return this->m_columns.at(index.column()).isEditable(); } + bool CColumns::isEditable(int column) const + { + if (!isValidColumn(column)) return false; + return this->m_columns.at(column).isEditable(); + } + + bool CColumns::isSortable(const QModelIndex &index) const + { + if (!isValidColumn(index)) return false; + return this->m_columns.at(index.column()).isSortable(); + } + + bool CColumns::isSortable(int column) const + { + if (!isValidColumn(column)) return false; + return this->m_columns.at(column).isSortable(); + } + + bool CColumns::isValidColumn(const QModelIndex &index) const + { + return (index.column() >= 0 && index.column() < this->m_columns.size()); + } + + bool CColumns::isValidColumn(int column) const + { + return column >= 0 && column < this->m_columns.size(); + } + const CDefaultFormatter *CColumns::getFormatter(const QModelIndex &index) const { if (!isValidColumn(index)) return nullptr; diff --git a/src/blackgui/models/columns.h b/src/blackgui/models/columns.h index 55f7abfd8..7f35b8ec7 100644 --- a/src/blackgui/models/columns.h +++ b/src/blackgui/models/columns.h @@ -49,6 +49,24 @@ namespace BlackGui //! Editable? bool isEditable() const { return this->m_editable; } + //! Set editable + void setEditable(bool editable) { this->m_editable = editable; } + + //! Sortable? + bool isSortable() const { return this->m_sortable; } + + //! Set sortable + void setSortable(bool sortable) { this->m_sortable = sortable; } + + //! Property index used when sorting, option alternative + BlackMisc::CPropertyIndex getSortPropertyIndex() const { return this->m_sortPropertyIndex; } + + //! Sort index available + bool hasSortPropertyIndex() const; + + //! Property index used when sorting, option alternative + void setSortPropertyIndex(const BlackMisc::CPropertyIndex &propertyIndex); + //! Formatter void setFormatter(CDefaultFormatter *formatter) { Q_ASSERT(formatter); m_formatter.reset(formatter); } @@ -86,9 +104,12 @@ namespace BlackGui QString m_translationContext; QString m_columnName; QString m_columnToolTip; - QSharedPointer m_formatter; - BlackMisc::CPropertyIndex m_propertyIndex; + QSharedPointer m_formatter; //!< Used formatter + BlackMisc::CPropertyIndex m_propertyIndex; //!< Property index for column + BlackMisc::CPropertyIndex m_sortPropertyIndex; //!< Property index used when sorted (optional alternative) + bool m_editable = false; + bool m_sortable = true; const char *getTranslationContextChar() const; const char *getColumnNameChar() const; const char *getColumnToolTipChar() const; @@ -119,12 +140,12 @@ namespace BlackGui //! Column to property index BlackMisc::CPropertyIndex columnToPropertyIndex(int column) const; + //! Column to property index for sort, considers \sa CColumn::getSo + BlackMisc::CPropertyIndex columnToSortPropertyIndex(int column) const; + //! Property index to column int propertyIndexToColumn(const BlackMisc::CPropertyIndex &propertyIndex) const; - //! Column index to property index - int indexToPropertyIndex(const BlackMisc::CPropertyIndex &propertyIndex) const; - //! Column index to name int nameToPropertyIndex(const QString &name) const; @@ -137,17 +158,20 @@ namespace BlackGui //! Is this column editable? bool isEditable(const QModelIndex &index) const; - //! Valid column? - bool isValidColumn(const QModelIndex &index) const - { - return (index.column() >= 0 && index.column() < this->m_columns.size()); - } + //! Is this column editable? + bool isEditable(int column) const; + + //! Sortable column? + bool isSortable(const QModelIndex &index) const; + + //! Sortable column? + bool isSortable(int column) const; //! Valid column? - bool isValidColumn(int column) const - { - return column >= 0 && column < this->m_columns.size(); - } + bool isValidColumn(const QModelIndex &index) const; + + //! Valid column? + bool isValidColumn(int column) const; //! Aligment as CVariant BlackMisc::CVariant getAlignment(const QModelIndex &index) const; diff --git a/src/blackgui/models/statusmessagelistmodel.cpp b/src/blackgui/models/statusmessagelistmodel.cpp index 5a99d24de..a2e11d3d6 100644 --- a/src/blackgui/models/statusmessagelistmodel.cpp +++ b/src/blackgui/models/statusmessagelistmodel.cpp @@ -28,7 +28,9 @@ namespace BlackGui { this->m_columns.addColumn(CColumn("time", CStatusMessage::IndexTimestamp, new CDateTimeFormatter(CDateTimeFormatter::formatHms()))); this->m_columns.addColumn(CColumn::standardString("category", CStatusMessage::IndexCategoryHumanReadable)); - this->m_columns.addColumn(CColumn("severity", CStatusMessage::IndexIcon)); + CColumn col = CColumn("severity", CStatusMessage::IndexIcon); + col.setSortPropertyIndex(CStatusMessage::IndexSeverityAsString); + this->m_columns.addColumn(col); this->m_columns.addColumn(CColumn::standardString("message", CStatusMessage::IndexMessage)); this->m_columns.addColumn(CColumn::standardString("all categories", CStatusMessage::IndexCategories));