diff --git a/framework/qml/CalendarComboBox.qml b/framework/qml/CalendarComboBox.qml index ad5b86fc..bc66c3a4 100644 --- a/framework/qml/CalendarComboBox.qml +++ b/framework/qml/CalendarComboBox.qml @@ -1,77 +1,87 @@ /* * Copyright (C) 2018 Michael Bohlender, * Copyright (C) 2019 Christian Mollekopf, * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.4 import org.kube.framework 1.0 as Kube Kube.ComboBox { id: root property alias accountId: calendarModel.accountId signal selected(var calendar) model: Kube.EntityModel { id: calendarModel type: "calendar" roles: ["name", "color"] sortRole: "name" filter: {"enabled": true} + //Set initial selection. + //onCurrentIndexChanged will not work because the as more items are added the currentIndex changes, + //but depending on the sorting it will point to a different item (Which is really a bug of the model or ComboBox). + onInitialItemsLoaded: { + if (currentIndex >= 0) { + root.selected(calendarModel.data(currentIndex).object) + } + } } textRole: "name" - onActivated: { - root.selected(calendarModel.data(index).object) + + onCurrentIndexChanged: { + if (currentIndex >= 0) { + root.selected(calendarModel.data(currentIndex).object) + } } delegate: Kube.ListDelegate { width: root.popup.width height: Kube.Units.gridUnit * 1.5 contentItem: Row { Item { width: Kube.Units.smallSpacing height: parent.height } Rectangle { anchors.verticalCenter: parent.verticalCenter width: Kube.Units.gridUnit height: Kube.Units.gridUnit radius: Kube.Units.gridUnit / 2 color: model.color } Kube.Label { padding: Kube.Units.smallSpacing text: model[root.textRole] color: root.highlightedIndex === index ? Kube.Colors.highlightedTextColor : Kube.Colors.textColor } } MouseArea { anchors.fill: parent onClicked: { root.currentIndex = calendarSelector.highlightedIndex - root.activated(calendarSelector.highlightedIndex) root.popup.close() } } } } diff --git a/framework/src/entitymodel.cpp b/framework/src/entitymodel.cpp index 86200185..9ac2beaa 100644 --- a/framework/src/entitymodel.cpp +++ b/framework/src/entitymodel.cpp @@ -1,342 +1,347 @@ /* Copyright (c) 2018 Christian Mollekopf This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "entitymodel.h" #include #include using namespace Sink; using namespace Sink::ApplicationDomain; enum Roles { IdRole = Qt::UserRole + 1, ObjectRole, LastRole }; EntityModel::EntityModel(QObject *parent) : QSortFilterProxyModel(parent) { setDynamicSortFilter(true); setFilterCaseSensitivity(Qt::CaseInsensitive); setSortCaseSensitivity(Qt::CaseInsensitive); } EntityModel::~EntityModel() { } QHash EntityModel::roleNames() const { return mRoleNames; } QVariant EntityModel::data(const QModelIndex &idx, int role) const { auto srcIdx = mapToSource(idx); auto entity = srcIdx.data(Sink::Store::DomainObjectBaseRole).value(); const auto roleName = mRoleNames.value(role); if (roleName == "identifier") { return entity->identifier(); } else if (roleName == "object") { return QVariant::fromValue(entity); } else { return entity->getProperty(roleName); } } bool EntityModel::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const { auto left = sourceLeft.data(Sink::Store::DomainObjectBaseRole).value(); auto right = sourceRight.data(Sink::Store::DomainObjectBaseRole).value(); const auto leftProperty = left->getProperty(mSortRole.toUtf8()).toString(); const auto rightProperty = right->getProperty(mSortRole.toUtf8()).toString(); return leftProperty < rightProperty; } void EntityModel::runQuery(const Query &query) { if (mType == "calendar") { mModel = Store::loadModel(query); } else if (mType == "addressbook") { mModel = Store::loadModel(query); } else if (mType == "folder") { mModel = Store::loadModel(query); } else if (mType == "todo") { mModel = Store::loadModel(query); } else { qWarning() << "Type not supported " << mType; Q_ASSERT(false); } + QObject::connect(mModel.data(), &QAbstractItemModel::dataChanged, this, [this](const QModelIndex &start, const QModelIndex &end, const QVector &roles) { + if (roles.contains(Sink::Store::ChildrenFetchedRole)) { + emit initialItemsLoaded(); + } + }); setSourceModel(mModel.data()); } void EntityModel::updateQuery() { if (mType.isEmpty() || mRoles.isEmpty()) { return; } Query query; if (!mAccountId.isEmpty()) { query.resourceFilter(mAccountId.toUtf8()); } if (!mResourceId.isEmpty()) { query.resourceFilter(mResourceId.toUtf8()); } if (!mEntityId.isEmpty()) { query.filter(mEntityId.toUtf8()); } query.setFlags(Sink::Query::LiveQuery | Sink::Query::UpdateStatus); for (const auto &property: mRoles.keys()) { query.requestedProperties << property; } for (const auto &property: mFilter.keys()) { query.filter(property.toUtf8(), {mFilter.value(property)}); } runQuery(query); } void EntityModel::setAccountId(const QString &id) { if (mAccountId != id) { mAccountId = id; updateQuery(); } } QString EntityModel::accountId() const { return mAccountId; } void EntityModel::setResourceId(const QString &id) { if (mResourceId != id) { mResourceId = id; updateQuery(); } } QString EntityModel::resourceId() const { return mResourceId; } void EntityModel::setEntityId(const QString &id) { if (mEntityId != id) { mEntityId = id; updateQuery(); } } QString EntityModel::entityId() const { return mEntityId; } void EntityModel::setType(const QString &type) { mType = type; updateQuery(); } QString EntityModel::type() const { return {}; } void EntityModel::setRoles(const QStringList &roles) { mRoleNames.clear(); mRoleNames.insert(IdRole, "identifier"); mRoleNames.insert(ObjectRole, "object"); int role = LastRole; for (int i = 0; i < roles.size(); i++) { mRoleNames.insert(role++, roles.at(i).toLatin1()); } mRoles.clear(); for (const auto &r : mRoleNames.keys()) { mRoles.insert(mRoleNames.value(r), r); } updateQuery(); } QStringList EntityModel::roles() const { // return mRoleNames.values(); return {}; } void EntityModel::setFilter(const QVariantMap &filter) { mFilter = filter; updateQuery(); } QVariantMap EntityModel::filter() const { return {}; } void EntityModel::setSortRole(const QString &sortRole) { mSortRole = sortRole; sort(0, Qt::AscendingOrder); } QString EntityModel::sortRole() const { return mSortRole; } QVariantMap EntityModel::data(int row) const { QVariantMap map; for (const auto &r : mRoleNames.keys()) { map.insert(mRoleNames.value(r), data(index(row, 0), r)); } return map; } bool EntityModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!mRoleNames.contains(role)) { return false; } const auto entity = EntityModel::data(index, ObjectRole).value(); //FIXME hardcoding calendar is not a great idea here. Sink::ApplicationDomain::Calendar modifiedEntity{*entity}; const auto propertyName = mRoleNames.value(role); modifiedEntity.setProperty(propertyName, value.toBool()); //Ignore if we didn't modify anything. if (!modifiedEntity.changedProperties().isEmpty()) { Sink::Store::modify(modifiedEntity).exec(); } return true; } EntityLoader::EntityLoader(QObject *parent) : EntityModel(parent) { QObject::connect(this, &QAbstractItemModel::rowsInserted, this, [this] (const QModelIndex &parent, int first, int last) { for (int row = first; row <= last; row++) { auto idx = index(row, 0, parent); for (const auto &r : mRoleNames.keys()) { setProperty(mRoleNames.value(r), data(idx, r)); } //We only ever use the first index (we're not expecting any more either) break; } }); } EntityLoader::~EntityLoader() { } void EntityLoader::updateQuery() { if (entityId().isEmpty()) { for (const auto &r : mRoleNames.keys()) { setProperty(mRoleNames.value(r), {}); } return; } EntityModel::updateQuery(); } CheckableEntityModel::CheckableEntityModel(QObject *parent) : EntityModel(parent) { } CheckableEntityModel::~CheckableEntityModel() { } QHash CheckableEntityModel::roleNames() const { auto roleNames = EntityModel::roleNames(); roleNames.insert(Qt::CheckStateRole, "checked"); return roleNames; } QVariant CheckableEntityModel::data(const QModelIndex &index, int role) const { if (mCheckedEntities && role == Qt::CheckStateRole) { return mCheckedEntities->contains(EntityModel::data(index, IdRole).toByteArray()); } return EntityModel::data(index, role); } bool CheckableEntityModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (mCheckedEntities && role == Qt::CheckStateRole) { auto identifier = EntityModel::data(index, IdRole).toByteArray(); if (value.toBool()) { mCheckedEntities->insert(identifier); } else { mCheckedEntities->remove(identifier); } return true; } return EntityModel::setData(index, value, role); } CheckedEntities *CheckableEntityModel::checkedEntities() const { return mCheckedEntities; } void CheckableEntityModel::setCheckedEntities(CheckedEntities *checkedEntities) { mCheckedEntities = checkedEntities; } void CheckedEntities::insert(const QByteArray &id) { mCheckedEntities.insert(id); emit checkedEntitiesChanged(); } void CheckedEntities::remove(const QByteArray &id) { mCheckedEntities.remove(id); emit checkedEntitiesChanged(); } bool CheckedEntities::contains(const QByteArray &id) const { return mCheckedEntities.contains(id); } QSet CheckedEntities::checkedEntities() const { return mCheckedEntities; } diff --git a/framework/src/entitymodel.h b/framework/src/entitymodel.h index 87df8c3a..edc581fc 100644 --- a/framework/src/entitymodel.h +++ b/framework/src/entitymodel.h @@ -1,142 +1,145 @@ /* Copyright (c) 2018 Christian Mollekopf This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #pragma once #include "kube_export.h" #include #include #include #include namespace Sink { class Query; } class KUBE_EXPORT EntityModel : public QSortFilterProxyModel { Q_OBJECT Q_PROPERTY (QString accountId READ accountId WRITE setAccountId) Q_PROPERTY (QString resourceId READ resourceId WRITE setResourceId) Q_PROPERTY (QString entityId READ entityId WRITE setEntityId) Q_PROPERTY (QString type READ type WRITE setType) Q_PROPERTY (QStringList roles READ roles WRITE setRoles) Q_PROPERTY (QString sortRole READ sortRole WRITE setSortRole) Q_PROPERTY (QVariantMap filter READ filter WRITE setFilter) public: enum Status { NoStatus, InProgressStatus, ErrorStatus, SuccessStatus, }; Q_ENUMS(Status) EntityModel(QObject *parent = Q_NULLPTR); virtual ~EntityModel(); virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; virtual QHash roleNames() const override; void setAccountId(const QString &); QString accountId() const; void setResourceId(const QString &); QString resourceId() const; void setEntityId(const QString &); QString entityId() const; void setType(const QString &); QString type() const; void setRoles(const QStringList &); QStringList roles() const; void setFilter(const QVariantMap &); QVariantMap filter() const; void setSortRole(const QString &); QString sortRole() const; Q_INVOKABLE QVariantMap data(int row) const; +signals: + void initialItemsLoaded(); + protected: bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override; virtual void updateQuery(); QHash mRoleNames; private: void runQuery(const Sink::Query &query); QSharedPointer mModel; QHash mRoles; QString mAccountId; QString mResourceId; QString mEntityId; QString mType; QString mSortRole; QVariantMap mFilter; }; class KUBE_EXPORT EntityLoader : public EntityModel { Q_OBJECT public: EntityLoader(QObject *parent = Q_NULLPTR); virtual ~EntityLoader(); protected: void updateQuery() override; }; class KUBE_EXPORT CheckedEntities : public QObject { Q_OBJECT Q_PROPERTY (QSet checkedEntities READ checkedEntities NOTIFY checkedEntitiesChanged) public: bool contains(const QByteArray &) const; void insert(const QByteArray &); void remove(const QByteArray &); QSet checkedEntities() const; signals: void checkedEntitiesChanged(); private: QSet mCheckedEntities; }; class KUBE_EXPORT CheckableEntityModel : public EntityModel { Q_OBJECT Q_PROPERTY (CheckedEntities* checkedEntities READ checkedEntities WRITE setCheckedEntities CONSTANT) public: CheckableEntityModel(QObject *parent = Q_NULLPTR); virtual ~CheckableEntityModel(); QHash roleNames() const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; CheckedEntities *checkedEntities() const; void setCheckedEntities(CheckedEntities *); private: CheckedEntities *mCheckedEntities = nullptr; };