diff --git a/framework/src/entitymodel.cpp b/framework/src/entitymodel.cpp index 9b61cb67..b38ec1d6 100644 --- a/framework/src/entitymodel.cpp +++ b/framework/src/entitymodel.cpp @@ -1,243 +1,270 @@ /* 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 { qWarning() << "Type not supported " << mType; Q_ASSERT(false); } setSourceModel(mModel.data()); } void EntityModel::updateQuery() { if (mType.isEmpty() || mRoles.isEmpty()) { return; } Query query; if (!mAccountId.isEmpty()) { query.resourceFilter(mAccountId.toUtf8()); } query.setFlags(Sink::Query::LiveQuery | Sink::Query::UpdateStatus); for (const auto &property: mRoles.keys()) { query.requestedProperties << property; } runQuery(query); } void EntityModel::setAccountId(const QString &accountId) { //Get all folders of an account mAccountId = accountId; updateQuery(); } QString EntityModel::accountId() const { return {}; } 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 &) { //TODO } 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; } 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 (role == Qt::CheckStateRole) { - return mCheckedEntities.contains(EntityModel::data(index, IdRole).toByteArray()); + 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 (role == Qt::CheckStateRole) { + if (mCheckedEntities && role == Qt::CheckStateRole) { auto identifier = EntityModel::data(index, IdRole).toByteArray(); if (value.toBool()) { - mCheckedEntities.insert(identifier); + mCheckedEntities->insert(identifier); } else { - mCheckedEntities.remove(identifier); + mCheckedEntities->remove(identifier); } - emit checkedEntitiesChanged(); return true; } return EntityModel::setData(index, value, role); } -QSet CheckableEntityModel::checkedEntities() const +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 380f5337..080e22bf 100644 --- a/framework/src/entitymodel.h +++ b/framework/src/entitymodel.h @@ -1,109 +1,121 @@ /* 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 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 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; protected: bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override; private: void runQuery(const Sink::Query &query); void updateQuery(); QSharedPointer mModel; QHash mRoleNames; QHash mRoles; QString mAccountId; QString mType; QString mSortRole; }; +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 (QSet checkedEntities READ checkedEntities NOTIFY checkedEntitiesChanged) + 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; - QSet checkedEntities() const; - -signals: - void checkedEntitiesChanged(); + CheckedEntities *checkedEntities() const; + void setCheckedEntities(CheckedEntities *); private: - QSet mCheckedEntities; + CheckedEntities *mCheckedEntities = nullptr; }; diff --git a/framework/src/frameworkplugin.cpp b/framework/src/frameworkplugin.cpp index bd723be9..0748b2dc 100644 --- a/framework/src/frameworkplugin.cpp +++ b/framework/src/frameworkplugin.cpp @@ -1,199 +1,200 @@ /* Copyright (c) 2016 Michael Bohlender Copyright (c) 2016 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 "frameworkplugin.h" #include "domain/maillistmodel.h" #include "domain/folderlistmodel.h" #include "domain/perioddayeventmodel.h" #include "domain/multidayeventmodel.h" #include "domain/eventmodel.h" #include "domain/composercontroller.h" #include "domain/mime/messageparser.h" #include "domain/retriever.h" #include "domain/outboxmodel.h" #include "domain/mouseproxy.h" #include "domain/contactcontroller.h" #include "domain/eventcontroller.h" #include "domain/peoplemodel.h" #include "domain/textdocumenthandler.h" #include "accounts/accountsmodel.h" #include "accounts/accountfactory.h" #include "settings/settings.h" #include "fabric.h" #include "kubeimage.h" #include "clipboardproxy.h" #include "startupcheck.h" #include "keyring.h" #include "controller.h" #include "domainobjectcontroller.h" #include "extensionmodel.h" #include "viewhighlighter.h" #include "file.h" #include "logmodel.h" #include "entitymodel.h" #include #include #include class KubeImageProvider : public QQuickImageProvider { public: KubeImageProvider() : QQuickImageProvider(QQuickImageProvider::Pixmap) { } QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) Q_DECL_OVERRIDE { //The platform theme plugin can overwrite our setting again once it gets loaded, //so we check on every icon load request... if (QIcon::themeName() != "kube") { QIcon::setThemeName("kube"); } const auto icon = QIcon::fromTheme(id); auto expectedSize = requestedSize; //Get the largest size that is still smaller or equal than requested //Except if we only have larger sizes, then just pick the closest one bool first = true; for (const auto s : icon.availableSizes()) { if (first && s.width() > requestedSize.width()) { expectedSize = s; break; } first = false; if (s.width() <= requestedSize.width()) { expectedSize = s; } } const auto pixmap = icon.pixmap(expectedSize); if (size) { *size = pixmap.size(); } return pixmap; } }; static QObject *fabric_singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine) { Q_UNUSED(engine) Q_UNUSED(scriptEngine) return new Kube::Fabric::Fabric; } static QObject *keyring_singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine) { Q_UNUSED(engine) Q_UNUSED(scriptEngine) auto instance = Kube::Keyring::instance(); QQmlEngine::setObjectOwnership(instance, QQmlEngine::CppOwnership); return instance; } static QString findFile(const QString file, const QStringList importPathList) { for (const auto &path : importPathList) { const QString f = path + file; if (QFileInfo::exists(f)) { return f; } } return {}; } void FrameworkPlugin::initializeEngine(QQmlEngine *engine, const char *uri) { Q_UNUSED(uri); engine->addImageProvider(QLatin1String("kube"), new KubeImageProvider); QString kubeIcons = QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("kube-icons.rcc")); //For windows if (kubeIcons.isEmpty()) { const auto locations = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation) + QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation); kubeIcons = findFile(QStringLiteral("/kube/kube-icons.rcc"), locations); } //For osx if (kubeIcons.isEmpty()) { //On Mac OS we want to include Contents/Resources/ in the bundle, and that path is in AppDataLocations. QStringList iconSearchPaths; for (const auto &p : QStandardPaths::standardLocations(QStandardPaths::AppDataLocation)) { auto iconPath = p; //I'm getting broken paths reported from standardLocations if (iconPath.contains("kube.appContents")) { iconPath.replace("kube.appContents", "kube.app/Contents"); } if (iconPath.contains("kube-kolabnow.appContents")) { iconPath.replace("kube-kolabnow.appContents", "kube-kolabnow.app/Contents"); } iconSearchPaths << iconPath; } kubeIcons = findFile(QStringLiteral("/kube/kube-icons.rcc"), iconSearchPaths); } if (!QResource::registerResource(kubeIcons, "/icons/kube")) { qWarning() << "Failed to register icon resource!" << kubeIcons; qWarning() << "Searched paths: " << QStandardPaths::standardLocations(QStandardPaths::AppDataLocation) + QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation); Q_ASSERT(false); } else { QIcon::setThemeSearchPaths(QStringList() << QStringLiteral(":/icons")); QIcon::setThemeName(QStringLiteral("kube")); } } void FrameworkPlugin::registerTypes (const char *uri) { qmlRegisterType(uri, 1, 0, "FolderListModel"); qmlRegisterType(uri, 1, 0, "MailListModel"); qmlRegisterType(uri, 1, 0, "PeriodDayEventModel"); qmlRegisterType(uri, 1, 0, "MultiDayEventModel"); qmlRegisterType(uri, 1, 0, "EventModel"); qmlRegisterType(uri, 1, 0, "EventController"); qmlRegisterType(uri, 1, 0, "ComposerController"); qmlRegisterType(uri, 1, 0, "ControllerAction"); qmlRegisterType(uri, 1, 0, "MessageParser"); qmlRegisterType(uri, 1, 0, "Retriever"); qmlRegisterType(uri, 1, 0, "OutboxModel"); qmlRegisterType(uri, 1, 0, "MouseProxy"); qmlRegisterType(uri, 1, 0,"ContactController"); qmlRegisterType(uri, 1, 0,"PeopleModel"); qmlRegisterType(uri, 1, 0, "TextDocumentHandler"); qmlRegisterType(uri, 1, 0, "LogModel"); qmlRegisterType(uri, 1, 0, "EntityModel"); + qmlRegisterType(uri, 1, 0, "CheckedEntities"); qmlRegisterType(uri, 1, 0, "CheckableEntityModel"); qmlRegisterType(uri, 1, 0, "AccountFactory"); qmlRegisterType(uri, 1, 0, "AccountsModel"); qmlRegisterType(uri, 1, 0, "ExtensionModel"); qmlRegisterType(uri, 1, 0, "File"); qmlRegisterType(uri, 1, 0, "Settings"); qmlRegisterType(uri, 1, 0, "Listener"); qmlRegisterType(uri, 1, 0, "DomainObjectController"); qmlRegisterSingletonType(uri, 1, 0, "Fabric", fabric_singletontype_provider); qmlRegisterType(uri, 1, 0, "KubeImage"); qmlRegisterType(uri, 1, 0, "Clipboard"); qmlRegisterType(uri, 1, 0, "StartupCheck"); qmlRegisterType(uri, 1, 0, "ViewHighlighter"); qmlRegisterSingletonType(uri, 1, 0, "Keyring", keyring_singletontype_provider); } diff --git a/views/calendar/main.qml b/views/calendar/main.qml index 840f2be8..8f7c66a6 100644 --- a/views/calendar/main.qml +++ b/views/calendar/main.qml @@ -1,261 +1,280 @@ /* * Copyright (C) 2018 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.7 import QtQuick.Controls 2.0 import QtQuick.Window 2.0 import org.kube.framework 1.0 as Kube import org.kube.test 1.0 import "qml" ApplicationWindow { id: app height: Screen.desktopAvailableHeight * 0.8 width: Screen.desktopAvailableWidth * 0.8 Component.onCompleted: { var initialState = { - accounts: [{ - id: "account1", - name: "Test Account" - }], + accounts: [ + { + id: "account1", + name: "Test Account" + }, + { + id: "account2", + name: "Test Account2" + }, + ], identities: [{ account: "account1", name: "Test Identity", address: "identity@example.org" }], - resources: [{ - id: "caldavresource", - account: "account1", - type: "caldav", - }], + resources: [ + { + id: "caldavresource", + account: "account1", + type: "caldav", + }, + { + id: "caldavresource2", + account: "account2", + type: "caldav", + } + ], calendars: [{ id: "calendar1", resource: "caldavresource", name: "Test Calendar", color: "#af1a6a", events: [ { resource: "caldavresource", summary: "Short event1!", starts: "2018-04-09T14:03:00", }, { resource: "caldavresource", summary: "Short event2!", starts: "2018-04-09T14:03:00", }, { resource: "caldavresource", summary: "Short event3!", starts: "2018-04-09T14:03:00", }, { resource: "caldavresource", summary: "Test Event1 with a waaaaaaaay to long summary. Why don't you just use the description you fool!", description: "This is test event #1", starts: "2018-04-10T14:03:00", ends: "2018-04-10T16:03:00", }, { resource: "caldavresource", summary: "Test Event2", description: "This is test event #2", starts: "2018-04-11T09:03:00", ends: "2018-04-11T14:03:00", }, { resource: "caldavresource", summary: "Test Event3", description: "This is test event #3", starts: "2018-04-11T10:00:00", ends: "2018-04-11T15:00:00", }, { resource: "caldavresource", summary: "Test Event4", description: "This is test event #4", starts: "2018-04-12T03:00:00", ends: "2018-04-14T22:00:00", }, { resource: "caldavresource", summary: "!!! Test Event5", description: "!!! This is test event #5", starts: "2018-04-22T22:00:00", ends: "2018-04-25T03:00:00", }, // Day-long events { resource: "caldavresource", summary: "Test day-long event1", description: "This is test day-long event #1", starts: "2018-04-10T00:00:00", ends: "2018-04-14T00:00:00", allDay: true, }, { resource: "caldavresource", summary: "Test day-long event2", description: "This is test day-long event #2", starts: "2018-04-11", allDay: true, }, { resource: "caldavresource", summary: "Test day-long event3", description: "This is test day-long event #3", starts: "2018-04-01T00:00:00", ends: "2018-04-13T00:00:00", allDay: true, }, { resource: "caldavresource", summary: "Test day-long event4", description: "This is test day-long event #4", starts: "2018-04-01T00:00:00", ends: "2018-04-25T00:00:00", allDay: true, }, { resource: "caldavresource", summary: "!!! Test day-long event5", description: "!!! This is test day-long event #5", starts: "2018-04-01T00:00:00", ends: "2018-04-05T00:00:00", allDay: true, }, { resource: "caldavresource", summary: "!!! Test day-long event6", description: "!!! This is test day-long event #6", starts: "2018-04-23T00:00:00", ends: "2018-04-25T00:00:00", allDay: true, }, { resource: "caldavresource", summary: "Test day-long event7", description: "This is test day-long event #7", starts: "2018-04-12", allDay: true, }, ], }, { id: "calendar2", resource: "caldavresource", name: "Test Calendar2", color: "#f67400" }, { id: "calendar3", resource: "caldavresource", name: "Test Calendar3 Loooooooooooooooooooooooooooooong Name", color: "#f67400" }, { id: "calendar4", resource: "caldavresource", name: "Test Calendar4", color: "#f67400" }, { id: "calendar5", resource: "caldavresource", name: "Test Calendar5", color: "#f67400" }, { id: "calendar6", resource: "caldavresource", name: "Test Calendar6", color: "#f67400" }, { id: "calendar7", resource: "caldavresource", name: "Test Calendar7", color: "#f67400" }, { id: "calendar8", resource: "caldavresource", name: "Test Calendar8", color: "#f67400" }, { id: "calendar9", resource: "caldavresource", name: "Test Calendar9", color: "#f67400" }, { id: "calendar10", resource: "caldavresource", name: "Test Calendar 10", color: "#f67400" }, { id: "calendar11", resource: "caldavresource", name: "Test Calendar11", color: "#f67400" }, { id: "calendar12", resource: "caldavresource", name: "Test Calendar12", color: "#f67400" }, { id: "calendar13", resource: "caldavresource", name: "Test Calendar13", color: "#f67400" }, { id: "calendar14", resource: "caldavresource", name: "Test Calendar14", color: "#f67400" }, { id: "calendar15", resource: "caldavresource", name: "Test Calendar15", color: "#f67400" }, { id: "calendar16", resource: "caldavresource", name: "Test Calendar16", color: "#f67400" + }, + { + id: "account2calendar", + resource: "caldavresource2", + name: "Account2Calendar", + color: "#f67400" }], } TestStore.setup(initialState) } View { anchors.fill: parent currentDate: "2018-04-11T13:00:00" autoUpdateDate: false } } diff --git a/views/calendar/qml/View.qml b/views/calendar/qml/View.qml index 4ba31f07..88114cb5 100644 --- a/views/calendar/qml/View.qml +++ b/views/calendar/qml/View.qml @@ -1,301 +1,299 @@ /* * Copyright (C) 2018 Michael Bohlender, * * 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.9 import QtQuick.Controls 2 import QtQuick.Layouts 1.2 import org.kube.framework 1.0 as Kube import "dateutils.js" as DateUtils Kube.View { id: root property date currentDate: new Date() property date selectedDate: getFirstDayOfWeek(currentDate) property bool autoUpdateDate: true + Kube.CheckedEntities { + id: calendarFilterCollector + } + onSelectedDateChanged: { console.log("Selected date changed", selectedDate) } onRefresh: { Kube.Fabric.postMessage(Kube.Messages.synchronize, {"type": "calendar"}) Kube.Fabric.postMessage(Kube.Messages.synchronize, {"type": "event"}) } function getFirstDayOfWeek(date) { var firstDay = Qt.locale().firstDayOfWeek var year = date.getFullYear() var month = date.getMonth() //Jup, getDate returns the day of the month var day = date.getDate() while (true) { if (date.getDay() === firstDay) { return date } day = day - 1 date = new Date(year, month, day) } return date } function getFirstDayOfMonth(date) { var d = date d.setDate(1) return d } RowLayout { Timer { running: autoUpdateDate interval: 2000; repeat: true onTriggered: root.currentDate = new Date() } Rectangle { anchors { top: parent.top bottom: parent.bottom } width: Kube.Units.gridUnit * 10 color: Kube.Colors.darkBackgroundColor Column { id: topLayout anchors { top: parent.top left: parent.left right: parent.right margins: Kube.Units.largeSpacing } spacing: Kube.Units.largeSpacing Kube.PositiveButton { id: newEventButton objectName: "newEventButton" anchors { left: parent.left right: parent.right } focus: true text: qsTr("New Event") onClicked: { eventPopup.open() } } Row { anchors.horizontalCenter: parent.horizontalCenter spacing: Kube.Units.smallSpacing ButtonGroup { id: viewButtonGroup } Kube.TextButton { id: weekViewButton text: qsTr("Week") textColor: Kube.Colors.highlightedTextColor checkable: true checked: true ButtonGroup.group: viewButtonGroup onCheckedChanged: { if (checked) { root.selectedDate = getFirstDayOfWeek(root.selectedDate) } } } Kube.TextButton { id: monthViewButton text: qsTr("Month") textColor: Kube.Colors.highlightedTextColor checkable: true ButtonGroup.group: viewButtonGroup onCheckedChanged: { if (checked) { root.selectedDate = getFirstDayOfMonth(root.selectedDate) } } } } /* DateView { anchors { left: parent.left right: parent.right } date: root.currentDate MouseArea { anchors.fill: parent onClicked: dateSelector.selectedDate = root.currentDate } } */ DateSelector { id: dateSelector anchors { left: parent.left right: parent.right } selectedDate: root.selectedDate onSelected: { if (weekViewButton.checked) { root.selectedDate = getFirstDayOfWeek(date) } else { root.selectedDate = getFirstDayOfMonth(date) } } onNext: { if (weekViewButton.checked) { root.selectedDate = DateUtils.nextWeek(root.selectedDate) } else { root.selectedDate = DateUtils.nextMonth(root.selectedDate) } } onPrevious: { if (weekViewButton.checked) { root.selectedDate = DateUtils.previousWeek(root.selectedDate) } else { root.selectedDate = DateUtils.previousMonth(root.selectedDate) } } } } - ColumnLayout { + Kube.InlineAccountSwitcher { //Grow from the button but don't go over topLayout anchors { bottom: parent.bottom left: topLayout.left right: parent.right bottomMargin: Kube.Units.largeSpacing rightMargin: Kube.Units.largeSpacing } - height: Math.min(implicitHeight, parent.height - (topLayout.y + topLayout.height) - Kube.Units.largeSpacing - anchors.bottomMargin) + height: parent.height - (topLayout.y + topLayout.height) - Kube.Units.largeSpacing - anchors.bottomMargin - spacing: Kube.Units.largeSpacing - Kube.ListView { + delegate: Kube.ListView { id: listView - Layout.fillHeight: true - Layout.preferredHeight: contentHeight - Layout.minimumHeight: Kube.Units.gridUnit - anchors { - left: parent.left - right: parent.right - } spacing: Kube.Units.smallSpacing model: Kube.CheckableEntityModel { id: calendarModel type: "calendar" roles: ["name", "color", "enabled"] sortRole: "name" + accountId: listView.parent.accountId + checkedEntities: calendarFilterCollector } delegate: Kube.ListDelegate { id: delegate width: listView.availableWidth height: Kube.Units.gridUnit hoverEnabled: true background: Item {} RowLayout { anchors.fill: parent spacing: Kube.Units.smallSpacing Kube.CheckBox { id: checkBox opacity: 0.9 checked: model.checked || model.enabled onCheckedChanged: { model.checked = checked model.enabled = checked } indicator: Rectangle { width: Kube.Units.gridUnit height: Kube.Units.gridUnit color: model.color Rectangle { id: highlight anchors.fill: parent visible: checkBox.hovered || checkBox.visualFocus color: Kube.Colors.highlightColor opacity: 0.4 } Kube.Icon { anchors.centerIn: parent height: Kube.Units.gridUnit width: Kube.Units.gridUnit visible: checkBox.checked iconName: Kube.Icons.checkbox_inverted } } } Kube.Label { id: label Layout.fillWidth: true text: model.name color: Kube.Colors.highlightedTextColor elide: Text.ElideLeft clip: true } ToolTip { id: toolTipItem visible: delegate.hovered && label.truncated text: label.text } } } } } } WeekView { visible: weekViewButton.checked Layout.fillHeight: true Layout.fillWidth: true currentDate: root.currentDate startDate: root.selectedDate - calendarFilter: calendarModel.checkedEntities + calendarFilter: calendarFilterCollector.checkedEntities } MonthView { visible: monthViewButton.checked Layout.fillHeight: true Layout.fillWidth: true currentDate: root.currentDate startDate: getFirstDayOfWeek(getFirstDayOfMonth(root.selectedDate)) month: root.selectedDate.getMonth() - calendarFilter: calendarModel.checkedEntities + calendarFilter: calendarFilterCollector.checkedEntities } } EventEditor { id: eventPopup x: root.width * 0.15 y: root.height * 0.15 width: root.width * 0.7 height: root.height * 0.7 } }