diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 854c533021d69b2b9b5b3347e7984e93ed322d7b..9b74332697f7792625b86b064b09dfe382cbbdec 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -2,7 +2,7 @@
#
# SPDX-License-Identifier: BSD-2-Clause
-add_executable(kalendar about.cpp main.cpp agentconfiguration.cpp incidenceoccurrencemodel.cpp monthviewmodel.cpp calendarmanager.cpp multidayincidencemodel.cpp incidencewrapper.cpp remindersmodel.cpp attendeesmodel.cpp recurrenceexceptionsmodel.cpp attachmentsmodel.cpp contactsmanager.cpp todomodel.cpp incidencetreemodel.cpp todosortfilterproxymodel.cpp kalendarapplication.cpp itemtagsmodel.cpp tagmanager.cpp resources.qrc)
+add_executable(kalendar about.cpp main.cpp agentconfiguration.cpp incidenceoccurrencemodel.cpp monthviewmodel.cpp calendarmanager.cpp multidayincidencemodel.cpp incidencewrapper.cpp remindersmodel.cpp attendeesmodel.cpp recurrenceexceptionsmodel.cpp attachmentsmodel.cpp contactsmanager.cpp todomodel.cpp incidencetreemodel.cpp todosortfilterproxymodel.cpp kalendarapplication.cpp itemtagsmodel.cpp tagmanager.cpp extratodomodel.cpp resources.qrc)
target_link_libraries(kalendar
Qt5::Core
diff --git a/src/contents/ui/IncidenceInfo.qml b/src/contents/ui/IncidenceInfo.qml
index 482146bbddf61523f48afbd2831b06f33792d177..fdc09164dd1379cd76d7da159d1b7857ff8850aa 100644
--- a/src/contents/ui/IncidenceInfo.qml
+++ b/src/contents/ui/IncidenceInfo.qml
@@ -215,13 +215,19 @@ Kirigami.OverlayDrawer {
text: i18n("Tags:")
visible: incidenceInfo.incidenceWrapper.categories.length > 0
}
- QQC2.Label {
- Layout.alignment: Qt.AlignTop
+ Flow {
Layout.fillWidth: true
-
visible: incidenceInfo.incidenceWrapper.categories.length > 0
- text: incidenceInfo.incidenceWrapper.categories.join(i18nc("List separator", ", "))
- wrapMode: Text.Wrap
+ spacing: Kirigami.Units.largeSpacing
+ Repeater {
+ model: incidenceInfo.incidenceWrapper.categories
+ Tag {
+ text: modelData
+ icon.name: "edit-delete-remove"
+ actionText: i18n("Remove %1 tag", modelData)
+ showAction: false
+ }
+ }
}
QQC2.Label {
diff --git a/src/contents/ui/ScheduleView.qml b/src/contents/ui/ScheduleView.qml
index b25e41c9334990594e56b2e859c9c75dc993ac99..184fde52807ae557f8810696a12554de015d659f 100644
--- a/src/contents/ui/ScheduleView.qml
+++ b/src/contents/ui/ScheduleView.qml
@@ -365,7 +365,7 @@ Kirigami.Page {
property int dayOfMultidayIncidence: DateUtils.fullDaysBetweenDates(modelData.startTime, periodStartDate)
Component.onCompleted: {
- incidenceWrapper = Qt.createQmlObject('import org.kde.kalendar 1.0; IncidenceWrapper {id: incidence}', incidenceInfo, "incidence");
+ incidenceWrapper = Qt.createQmlObject('import org.kde.kalendar 1.0; IncidenceWrapper {id: incidence}', incidenceCard, "incidence");
incidenceWrapper.incidencePtr = modelData.incidencePtr
}
diff --git a/src/contents/ui/Tag.qml b/src/contents/ui/Tag.qml
new file mode 100644
index 0000000000000000000000000000000000000000..24db54231ef1ba5d328776313a0ebf72105c5aec
--- /dev/null
+++ b/src/contents/ui/Tag.qml
@@ -0,0 +1,114 @@
+/*
+ * SPDX-FileCopyrightText: (C) 2021 Mikel Johnson
+ *
+ * SPDX-License-Identifier: LGPL-2.0-or-later
+ */
+
+import QtQuick 2.15
+import QtQuick.Layouts 1.15
+import QtQuick.Controls 2.15 as QQC2
+import org.kde.kirigami 2.15 as Kirigami
+
+Item {
+ id: tagRoot
+ property string text
+ property alias icon: toolButton.icon
+ property alias actionText: toolButton.text
+ property bool showAction: true
+ property bool isHeading: false
+ property alias itemLayout: layout
+ property alias labelItem: label
+ property alias headingItem: heading
+ signal clicked()
+
+ implicitWidth: layout.implicitWidth
+ implicitHeight: layout.implicitHeight
+
+ Kirigami.Theme.colorSet: Kirigami.Theme.View
+ Kirigami.Theme.inherit: false
+
+ Rectangle {
+ id: mainBg
+ anchors.fill: parent
+ anchors.leftMargin: pointyBit.anchors.leftMargin + pointyBit.width / 2 - radius / 2
+ radius: 3
+ color: Kirigami.Theme.backgroundColor
+ border.color: Kirigami.ColorUtils.tintWithAlpha(Kirigami.Theme.backgroundColor,
+ Kirigami.Theme.textColor,
+ 0.3)
+ border.width: 1
+ }
+ Rectangle {
+ id: pointyBit
+ antialiasing: true
+ rotation: 45
+ y: (parent.height - height) / 2
+ anchors.left: parent.left
+ anchors.leftMargin: y + radius / 2
+ // `parent.height * Math.cos(radians)` fits a rotated square inside the parent.
+ // `rotation * (Math.PI / 180)` is rotation in radians instead of degrees.
+ // `Math.PI / 4` is 45 degrees. 180 / 4 is 45.
+ // `height + radius / 2` accounts for the rounded corners reducing visual size.
+ height: parent.height * Math.cos(Math.PI / 4) + radius / 2
+ width: height
+ color: mainBg.color
+ border.width: 1
+ border.color: mainBg.border.color
+ radius: 3
+ }
+ Rectangle {
+ id: borderCover
+ antialiasing: true
+ anchors {
+ left: parent.left
+ top: parent.top
+ bottom: parent.bottom
+ leftMargin: pointyBit.anchors.leftMargin + pointyBit.width / 2
+ margins: mainBg.border.width
+ }
+ width: height
+ color: mainBg.color
+ radius: mainBg.radius - mainBg.border.width
+ }
+
+ RowLayout {
+ id: layout
+ spacing: Math.round(label.Layout.leftMargin - (toolButton.implicitWidth - toolButton.icon.width))
+ anchors.fill: parent
+ QQC2.Label {
+ id: label
+ horizontalAlignment: Qt.AlignLeft
+ verticalAlignment: Qt.AlignVCenter
+ Layout.fillWidth: true
+ Layout.leftMargin: borderCover.anchors.leftMargin
+ Layout.rightMargin: tagRoot.showAction ? 0 : Kirigami.Units.smallSpacing
+ Layout.topMargin: tagRoot.showAction ? 0 : Kirigami.Units.smallSpacing
+ Layout.bottomMargin: tagRoot.showAction ? 0 : Kirigami.Units.smallSpacing
+ text: tagRoot.text
+ elide: Text.ElideRight
+ visible: !tagRoot.isHeading
+ }
+ Kirigami.Heading {
+ id: heading
+ horizontalAlignment: Qt.AlignLeft
+ verticalAlignment: Qt.AlignVCenter
+ Layout.fillWidth: true
+ Layout.leftMargin: borderCover.anchors.leftMargin
+ Layout.topMargin: Kirigami.Units.smallSpacing
+ Layout.bottomMargin: Kirigami.Units.smallSpacing
+ text: tagRoot.text
+ elide: Text.ElideRight
+ visible: tagRoot.isHeading
+ }
+ QQC2.ToolButton {
+ id: toolButton
+ icon.width: Kirigami.Units.iconSizes.sizeForLabels
+ icon.height: Kirigami.Units.iconSizes.sizeForLabels
+ text: i18n("Remove Tag")
+ display: QQC2.AbstractButton.IconOnly
+ onClicked: tagRoot.clicked()
+ visible: tagRoot.showAction
+ }
+ }
+}
+
diff --git a/src/contents/ui/TodoPage.qml b/src/contents/ui/TodoPage.qml
index a10da2216d9b704f8952b6970e79e1f530f0ea6e..934720af1e94c3f718e3e5235b0336e8ce2be226 100644
--- a/src/contents/ui/TodoPage.qml
+++ b/src/contents/ui/TodoPage.qml
@@ -163,7 +163,7 @@ Kirigami.Page {
Kirigami.Heading {
Layout.row: 0
Layout.column: 0
- Layout.columnSpan: root.width < Kirigami.Units.gridUnit * 30 || tagLayout.visible ? 1 : 2
+ Layout.columnSpan: root.width < Kirigami.Units.gridUnit * 30 || filterTag.visible ? 1 : 2
Layout.fillWidth: true
text: root.filterCollectionDetails && root.filterCollectionId > -1 ?
root.filterCollectionDetails.displayName : i18n("All Todos")
@@ -173,31 +173,31 @@ Kirigami.Page {
elide: Text.ElideRight
}
- RowLayout {
- id: tagLayout
+ Tag {
+ id: filterTag
Layout.row: 0
Layout.column: 1
Layout.alignment: Qt.AlignRight
visible: root.filterCategoryString
+ text: root.filterCategoryString
- Kirigami.Heading {
- Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
- text: root.filterCategoryString
- color: root.filterCollectionDetails ?
- LabelUtils.getIncidenceLabelColor(root.filterCollectionDetails.color, root.isDark) : Kirigami.Theme.textColor
- }
+ implicitWidth: itemLayout.implicitWidth > headerLayout.width - Kirigami.Units.gridUnit * 6 ?
+ headerLayout.width - Kirigami.Units.gridUnit * 6 : itemLayout.implicitWidth
+ isHeading: true
+ headingItem.color: root.filterCollectionDetails ?
+ LabelUtils.getIncidenceLabelColor(root.filterCollectionDetails.color, root.isDark) : Kirigami.Theme.textColor
+ headingItem.font.weight: Font.Bold
- QQC2.ToolButton {
- icon.name: "edit-clear"
- onClicked: root.filterCategoryString = ""
- }
+ icon.name: "edit-delete-remove"
+ onClicked: root.filterCategoryString = ""
+ actionText: i18n("Remove filtering tag")
}
Kirigami.SearchField {
id: searchField
- Layout.column: root.width < Kirigami.Units.gridUnit * 30 || tagLayout.visible ? 0 : 1
- Layout.row: root.width < Kirigami.Units.gridUnit * 30 || tagLayout.visible ? 1 : 0
- Layout.columnSpan: root.width < Kirigami.Units.gridUnit * 30 || tagLayout.visible ? 2 : 1
+ Layout.column: root.width < Kirigami.Units.gridUnit * 30 || filterTag.visible ? 0 : 1
+ Layout.row: root.width < Kirigami.Units.gridUnit * 30 || filterTag.visible ? 1 : 0
+ Layout.columnSpan: root.width < Kirigami.Units.gridUnit * 30 || filterTag.visible ? 2 : 1
Layout.fillWidth: Layout.row === 1
onTextChanged: incompleteView.model.filterTodoName(text);
}
diff --git a/src/contents/ui/TodoTreeView.qml b/src/contents/ui/TodoTreeView.qml
index 3194e8daaf3bd69250ea7ba3072c91661bfffaf9..2e875fee4bac6f242eb559382a7c8390b83c2a69 100644
--- a/src/contents/ui/TodoTreeView.qml
+++ b/src/contents/ui/TodoTreeView.qml
@@ -52,7 +52,7 @@ KirigamiAddonsTreeView.TreeListView {
Kirigami.PlaceholderMessage {
anchors.centerIn: parent
- visible: parent.count === 0 && !root.filterCollectionDetails.isFiltered
+ visible: parent.count === 0 && root.filterCollectionDetails && !root.filterCollectionDetails.isFiltered
text: root.showCompleted === Kalendar.TodoSortFilterProxyModel.ShowCompleteOnly ?
i18n("No todos completed") : i18n("No todos left to complete")
helpfulAction: Kirigami.Action {
@@ -110,6 +110,7 @@ KirigamiAddonsTreeView.TreeListView {
Layout.row: 0
Layout.column: 0
+ Layout.rowSpan: root.width < Kirigami.Units.gridUnit * 28 || recurIcon.visible || dateLabel.visible ? 1 : 2
color: model.color
radius: 100
@@ -122,6 +123,7 @@ KirigamiAddonsTreeView.TreeListView {
Layout.row: 0
Layout.column: 1
Layout.columnSpan: root.width < Kirigami.Units.gridUnit * 28 && (recurIcon.visible || dateLabel.visible) ? 2 : 1
+ Layout.rowSpan: root.width < Kirigami.Units.gridUnit * 28 || recurIcon.visible || dateLabel.visible ? 1 : 2
Layout.fillWidth: true
text: model.text
font.strikeout: model.todoCompleted
@@ -129,21 +131,32 @@ KirigamiAddonsTreeView.TreeListView {
wrapMode: Text.Wrap
}
- QQC2.Label {
- id: tagLabel
+ Flow {
+ id: tagFlow
Layout.fillWidth: true
- Layout.row: root.width < Kirigami.Units.gridUnit * 28 && (recurIcon.visible || dateLabel.visible) ? 1 : 0
+ Layout.row: root.width < Kirigami.Units.gridUnit * 28 && (recurIcon.visible || dateLabel.visible || priorityLayout.visible) ? 1 : 0
Layout.column: 2
Layout.rowSpan: root.width < Kirigami.Units.gridUnit * 28 ? 1 : 2
Layout.columnSpan: root.width < Kirigami.Units.gridUnit * 28 ? 2 : 1
- Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
Layout.rightMargin: Kirigami.Units.largeSpacing
- horizontalAlignment: Text.AlignRight
- text: model.categoriesDisplay
- elide: Text.ElideRight
+
+ layoutDirection: Qt.RightToLeft
+ spacing: Kirigami.Units.largeSpacing
+
+ Repeater {
+ id: tagsRepeater
+ model: todoModel.data(todoModel.index(index, 0), Kalendar.ExtraTodoModel.CategoriesRole) // Getting categories from the model is *very* faulty
+
+ Tag {
+ implicitWidth: itemLayout.implicitWidth > tagFlow.width ? tagFlow.width : itemLayout.implicitWidth
+ text: modelData
+ showAction: false
+ }
+ }
}
RowLayout {
+ id: priorityLayout
Layout.row: 0
Layout.column: 3
Layout.rowSpan: root.width < Kirigami.Units.gridUnit * 28 ? 1 : 2
diff --git a/src/extratodomodel.cpp b/src/extratodomodel.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8b0666afcacba251e2597478fcde16fbe8ba3fbd
--- /dev/null
+++ b/src/extratodomodel.cpp
@@ -0,0 +1,178 @@
+// SPDX-FileCopyrightText: 2021 Claudio Cambra
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
+#include
+
+ExtraTodoModel::ExtraTodoModel(QObject *parent)
+: KExtraColumnsProxyModel(parent) {
+ const QString todoMimeType = QStringLiteral("application/x-vnd.akonadi.calendar.todo");
+ m_todoTreeModel = new IncidenceTreeModel(QStringList() << todoMimeType, this);
+ const auto pref = EventViews::PrefsPtr();
+ m_baseTodoModel = new TodoModel(pref, this);
+ m_baseTodoModel->setSourceModel(m_todoTreeModel);
+ setSourceModel(m_baseTodoModel);
+
+ appendColumn(QLatin1String("StartDateTime"));
+ appendColumn(QLatin1String("EndDateTime"));
+ appendColumn(QLatin1String("PriorityInt"));
+
+ KSharedConfig::Ptr config = KSharedConfig::openConfig();
+ KConfigGroup rColorsConfig(config, "Resources Colors");
+ m_colorWatcher = KConfigWatcher::create(config);
+
+ QObject::connect(m_colorWatcher.data(), &KConfigWatcher::configChanged, this, &ExtraTodoModel::loadColors);
+
+ loadColors();
+}
+
+QVariant ExtraTodoModel::extraColumnData(const QModelIndex &parent, int row, int extraColumn, int role) const {
+ if (role != Qt::DisplayRole && role != Qt::EditRole) {
+ return QVariant();
+ }
+
+ const auto todoItem = index(row, extraColumn, parent).data(TodoModel::TodoRole).value();
+ const auto todoPtr = CalendarSupport::todo(todoItem);
+
+ switch (extraColumn) {
+ case StartTimeColumn:
+ return todoPtr->dtStart();
+ case EndTimeColumn:
+ return todoPtr->dtDue();
+ case PriorityIntColumn:
+ return todoPtr->priority();
+ default:
+ return QVariant();
+ }
+}
+
+QVariant ExtraTodoModel::data (const QModelIndex &index, int role) const {
+ if (!index.isValid()) {
+ return QVariant();
+ }
+
+ auto idx = mapToSource(index);
+ auto todoItem = idx.data(TodoModel::TodoRole).value();
+ auto collectionId = todoItem.parentCollection().id();
+ auto todoPtr = CalendarSupport::todo(todoItem);
+
+ if(role == Roles::StartTimeRole) {
+ return todoPtr->dtStart();
+ } else if (role == Roles::EndTimeRole) {
+ return todoPtr->dtDue();
+ } else if(role == Roles::LocationRole) {
+ return todoPtr->location();
+ } else if(role == Roles::AllDayRole) {
+ return todoPtr->allDay();
+ } else if(role == Roles::ColorRole) {
+ QColor nullcolor;
+ return m_colors.contains(QString::number(collectionId)) ? m_colors[QString::number(collectionId)] : nullcolor;
+ } else if(role == Roles::CompletedRole) {
+ return todoPtr->isCompleted();
+ } else if(role == Roles::PriorityRole) {
+ return todoPtr->priority();
+ } else if(role == Roles::CollectionIdRole) {
+ return collectionId;
+ } else if (role == DurationStringRole) {
+ KFormat format;
+ if (todoPtr->allDay()) {
+ return format.formatSpelloutDuration(24*60*60*1000); // format milliseconds in 1 day
+ }
+ return format.formatSpelloutDuration(todoPtr->duration().asSeconds() * 1000);
+ } else if (role == Roles::RecursRole) {
+ return todoPtr->recurs();
+ } else if (role == Roles::IsOverdueRole) {
+ return todoPtr->isOverdue();
+ } else if(role == Roles::IncidenceIdRole) {
+ return todoPtr->uid();
+ } else if(role == Roles::IncidenceTypeRole) {
+ return todoPtr->type();
+ } else if(role == Roles::IncidenceTypeStrRole) {
+ return todoPtr->typeStr();
+ } else if(role == Roles::IncidenceTypeIconRole) {
+ return todoPtr->iconName();
+ } else if(role == Roles::IncidencePtrRole) {
+ return QVariant::fromValue(CalendarSupport::incidence(todoItem));
+ } else if(role == Roles::TagsRole) {
+ return QVariant::fromValue(todoItem.tags());
+ } else if(role == Roles::ItemRole) {
+ return QVariant::fromValue(todoItem);
+ } else if(role == Roles::CategoriesRole) {
+ return todoPtr->categories();
+ } else if(role == Roles::CategoriesDisplayRole) {
+ return todoPtr->categories().join(i18nc("List separator", ", "));
+ } else if(role == Roles::TreeDepthRole) {
+ int depth = 0;
+ auto idx = index;
+ while(idx.parent().isValid()) {
+ idx = idx.parent();
+ depth++;
+ }
+ return depth;
+ }
+
+ return KExtraColumnsProxyModel::data(index, role);
+}
+
+QHash ExtraTodoModel::roleNames() const {
+ QHash roleNames = KExtraColumnsProxyModel::roleNames();
+ roleNames[TodoModel::SummaryRole] = "text";
+ roleNames[Roles::StartTimeRole] = "startTime";
+ roleNames[Roles::EndTimeRole] = "endTime";
+ roleNames[Roles::LocationRole] = "location";
+ roleNames[Roles::AllDayRole] = "allDay";
+ roleNames[Roles::ColorRole] = "color";
+ roleNames[Roles::CompletedRole] = "todoCompleted";
+ roleNames[Roles::PriorityRole] = "priority";
+ roleNames[Roles::CollectionIdRole] = "collectionId";
+ roleNames[Roles::DurationStringRole] = "durationString";
+ roleNames[Roles::RecursRole] = "recurs";
+ roleNames[Roles::IsOverdueRole] = "isOverdue";
+ roleNames[Roles::IncidenceIdRole] = "incidenceId";
+ roleNames[Roles::IncidenceTypeRole] = "incidenceType";
+ roleNames[Roles::IncidenceTypeStrRole] = "incidenceTypeStr";
+ roleNames[Roles::IncidenceTypeIconRole] = "incidenceTypeIcon";
+ roleNames[Roles::IncidencePtrRole] = "incidencePtr";
+ roleNames[Roles::TagsRole] = "tags";
+ roleNames[Roles::ItemRole] = "item";
+ roleNames[Roles::CategoriesRole] = "categories";
+ roleNames[Roles::CategoriesDisplayRole] = "categoriesDisplay";
+ roleNames[Roles::TreeDepthRole] = "treeDepth";
+
+ return roleNames;
+}
+
+Akonadi::ETMCalendar::Ptr ExtraTodoModel::calendar() {
+ return m_calendar;
+}
+
+void ExtraTodoModel::setCalendar(Akonadi::ETMCalendar::Ptr calendar) {
+ m_calendar = calendar;
+ m_todoTreeModel->setSourceModel(calendar->model());
+ m_baseTodoModel->setCalendar(calendar);
+}
+
+void ExtraTodoModel::setIncidenceChanger(Akonadi::IncidenceChanger* changer) {
+ m_baseTodoModel->setIncidenceChanger(changer);
+}
+
+QHash ExtraTodoModel::colorCache() {
+ return m_colors;
+}
+
+void ExtraTodoModel::setColorCache(QHash colorCache) {
+ m_colors = colorCache;
+}
+
+void ExtraTodoModel::loadColors() {
+ KSharedConfig::Ptr config = KSharedConfig::openConfig();
+ KConfigGroup rColorsConfig(config, "Resources Colors");
+ const QStringList colorKeyList = rColorsConfig.keyList();
+
+ for (const QString &key : colorKeyList) {
+ QColor color = rColorsConfig.readEntry(key, QColor("blue"));
+ m_colors[key] = color;
+ }
+ Q_EMIT layoutChanged();
+}
+
+Q_DECLARE_METATYPE(KCalendarCore::Incidence::Ptr)
diff --git a/src/extratodomodel.h b/src/extratodomodel.h
new file mode 100644
index 0000000000000000000000000000000000000000..bda3c8c5636a7ed711a2c55c5180949214272ed3
--- /dev/null
+++ b/src/extratodomodel.h
@@ -0,0 +1,72 @@
+// SPDX-FileCopyrightText: 2021 Claudio Cambra
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
+#pragma once
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+class ExtraTodoModel : public KExtraColumnsProxyModel
+{
+ Q_OBJECT
+
+public:
+ enum Columns {
+ StartTimeColumn = 0,
+ EndTimeColumn,
+ PriorityIntColumn
+ };
+ Q_ENUM(Columns);
+
+ enum Roles { // Remember to update roles in todosortfilterproxymodel
+ StartTimeRole = TodoModel::CalendarRole + 1,
+ EndTimeRole,
+ LocationRole,
+ AllDayRole,
+ CompletedRole,
+ PriorityRole,
+ ColorRole,
+ CollectionIdRole,
+ DurationStringRole,
+ RecursRole,
+ IsOverdueRole,
+ IncidenceIdRole,
+ IncidenceTypeRole,
+ IncidenceTypeStrRole,
+ IncidenceTypeIconRole,
+ IncidencePtrRole,
+ TagsRole,
+ ItemRole,
+ CategoriesRole,
+ CategoriesDisplayRole,
+ TreeDepthRole
+ };
+ Q_ENUM(Roles);
+
+ ExtraTodoModel(QObject *parent = nullptr);
+ ~ExtraTodoModel() = default;
+
+ QVariant extraColumnData(const QModelIndex &parent, int row, int extraColumn, int role = Qt::DisplayRole) const override;
+ QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const override;
+ QHash roleNames() const override;
+
+ Akonadi::ETMCalendar::Ptr calendar();
+ void setCalendar(Akonadi::ETMCalendar::Ptr calendar);
+ void setIncidenceChanger(Akonadi::IncidenceChanger* changer);
+
+ QHash colorCache();
+ void setColorCache(QHash colorCache);
+ void loadColors();
+
+private:
+ Akonadi::ETMCalendar::Ptr m_calendar;
+ IncidenceTreeModel *m_todoTreeModel = nullptr;
+ TodoModel *m_baseTodoModel = nullptr;
+ QHash m_colors;
+ KConfigWatcher::Ptr m_colorWatcher;
+};
+
diff --git a/src/main.cpp b/src/main.cpp
index 20dab3a0c96711926fae5b1545d0af2316413ae1..77873a598a8f06ba3cdd0c44bcfbc026c836d760 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -69,6 +69,7 @@ int main(int argc, char *argv[])
qmlRegisterType("org.kde.kalendar", 1, 0, "AttendeesModel");
qmlRegisterType("org.kde.kalendar", 1, 0, "MultiDayIncidenceModel");
qmlRegisterType("org.kde.kalendar", 1, 0, "IncidenceOccurrenceModel");
+ qmlRegisterType("org.kde.kalendar", 1, 0, "ExtraTodoModel");
qmlRegisterType("org.kde.kalendar", 1, 0, "TodoSortFilterProxyModel");
qmlRegisterType("org.kde.kalendar", 1, 0, "MonthViewModel");
qmlRegisterType("org.kde.kalendar", 1, 0, "ItemTagsModel");
diff --git a/src/resources.qrc b/src/resources.qrc
index 4f7eafb3eb6a756eb65fc47f99362d077df0a1c8..ef36c877de5e993789678cd74c727325594f54f7 100644
--- a/src/resources.qrc
+++ b/src/resources.qrc
@@ -36,5 +36,6 @@
contents/ui/NativeMenuItemFromAction.qml
contents/ui/KActionFromAction.qml
contents/ui/ColoredCheckbox.qml
+ contents/ui/Tag.qml
diff --git a/src/todosortfilterproxymodel.cpp b/src/todosortfilterproxymodel.cpp
index c39a4b824adbf5f6bd3365251196e4f950cbe6ab..2d84a1dfc64153f00ace285f6a9fc5ced19482e5 100644
--- a/src/todosortfilterproxymodel.cpp
+++ b/src/todosortfilterproxymodel.cpp
@@ -1,229 +1,8 @@
// SPDX-FileCopyrightText: 2021 Claudio Cambra
// SPDX-License-Identifier: LGPL-2.1-or-later
-#include
#include
-class ExtraTodoModel : public KExtraColumnsProxyModel
-{
- Q_OBJECT
-
-public:
- enum Columns {
- StartTimeColumn = 0,
- EndTimeColumn,
- PriorityIntColumn
- };
- Q_ENUM(Columns);
- enum Roles { // Remember to update roles in todosortfilterproxymodel
- StartTimeRole = TodoModel::CalendarRole + 1,
- EndTimeRole,
- LocationRole,
- AllDayRole,
- CompletedRole,
- PriorityRole,
- ColorRole,
- CollectionIdRole,
- DurationStringRole,
- RecursRole,
- IsOverdueRole,
- IncidenceIdRole,
- IncidenceTypeRole,
- IncidenceTypeStrRole,
- IncidenceTypeIconRole,
- IncidencePtrRole,
- TagsRole,
- ItemRole,
- CategoriesRole,
- CategoriesDisplayRole,
- TreeDepthRole
- };
- Q_ENUM(Roles);
-
- ExtraTodoModel(QObject *parent = nullptr) : KExtraColumnsProxyModel(parent) {
- const QString todoMimeType = QStringLiteral("application/x-vnd.akonadi.calendar.todo");
- m_todoTreeModel = new IncidenceTreeModel(QStringList() << todoMimeType, this);
- const auto pref = EventViews::PrefsPtr();
- m_baseTodoModel = new TodoModel(pref, this);
- m_baseTodoModel->setSourceModel(m_todoTreeModel);
- setSourceModel(m_baseTodoModel);
-
- appendColumn(QLatin1String("StartDateTime"));
- appendColumn(QLatin1String("EndDateTime"));
- appendColumn(QLatin1String("PriorityInt"));
-
- KSharedConfig::Ptr config = KSharedConfig::openConfig();
- KConfigGroup rColorsConfig(config, "Resources Colors");
- m_colorWatcher = KConfigWatcher::create(config);
-
- QObject::connect(m_colorWatcher.data(), &KConfigWatcher::configChanged, this, &ExtraTodoModel::loadColors);
-
- loadColors();
- };
- ~ExtraTodoModel() = default;
-
- QVariant extraColumnData(const QModelIndex &parent, int row, int extraColumn, int role = Qt::DisplayRole) const override {
- if (role != Qt::DisplayRole && role != Qt::EditRole) {
- return QVariant();
- }
-
- const auto todoItem = index(row, extraColumn, parent).data(TodoModel::TodoRole).value();
- const auto todoPtr = CalendarSupport::todo(todoItem);
-
- switch (extraColumn) {
- case StartTimeColumn:
- return todoPtr->dtStart();
- case EndTimeColumn:
- return todoPtr->dtDue();
- case PriorityIntColumn:
- return todoPtr->priority();
- default:
- return QVariant();
- }
- };
-
- QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const override {
- if (!index.isValid()) {
- return QVariant();
- }
-
- auto idx = mapToSource(index);
- auto todoItem = idx.data(TodoModel::TodoRole).value();
- auto collectionId = todoItem.parentCollection().id();
- auto todoPtr = CalendarSupport::todo(todoItem);
-
- if(role == Roles::StartTimeRole) {
- return todoPtr->dtStart();
- } else if (role == Roles::EndTimeRole) {
- return todoPtr->dtDue();
- } else if(role == Roles::LocationRole) {
- return todoPtr->location();
- } else if(role == Roles::AllDayRole) {
- return todoPtr->allDay();
- } else if(role == Roles::ColorRole) {
- QColor nullcolor;
- return m_colors.contains(QString::number(collectionId)) ? m_colors[QString::number(collectionId)] : nullcolor;
- } else if(role == Roles::CompletedRole) {
- return todoPtr->isCompleted();
- } else if(role == Roles::PriorityRole) {
- return todoPtr->priority();
- } else if(role == Roles::CollectionIdRole) {
- return collectionId;
- } else if (role == DurationStringRole) {
- KFormat format;
- if (todoPtr->allDay()) {
- return format.formatSpelloutDuration(24*60*60*1000); // format milliseconds in 1 day
- }
- return format.formatSpelloutDuration(todoPtr->duration().asSeconds() * 1000);
- } else if (role == Roles::RecursRole) {
- return todoPtr->recurs();
- } else if (role == Roles::IsOverdueRole) {
- return todoPtr->isOverdue();
- } else if(role == Roles::IncidenceIdRole) {
- return todoPtr->uid();
- } else if(role == Roles::IncidenceTypeRole) {
- return todoPtr->type();
- } else if(role == Roles::IncidenceTypeStrRole) {
- return todoPtr->typeStr();
- } else if(role == Roles::IncidenceTypeIconRole) {
- return todoPtr->iconName();
- } else if(role == Roles::IncidencePtrRole) {
- return QVariant::fromValue(CalendarSupport::incidence(todoItem));
- } else if(role == Roles::TagsRole) {
- return QVariant::fromValue(todoItem.tags());
- } else if(role == Roles::ItemRole) {
- return QVariant::fromValue(todoItem);
- } else if(role == Roles::CategoriesRole) {
- return todoPtr->categories();
- } else if(role == Roles::CategoriesDisplayRole) {
- return todoPtr->categories().join(i18nc("List separator", ", "));
- } else if(role == Roles::TreeDepthRole) {
- int depth = 0;
- auto idx = index;
- while(idx.parent().isValid()) {
- idx = idx.parent();
- depth++;
- }
- return depth;
- }
-
- return KExtraColumnsProxyModel::data(index, role);
- };
-
- QHash roleNames() const override {
- QHash roleNames = KExtraColumnsProxyModel::roleNames();
- roleNames[TodoModel::SummaryRole] = "text";
- roleNames[Roles::StartTimeRole] = "startTime";
- roleNames[Roles::EndTimeRole] = "endTime";
- roleNames[Roles::LocationRole] = "location";
- roleNames[Roles::AllDayRole] = "allDay";
- roleNames[Roles::ColorRole] = "color";
- roleNames[Roles::CompletedRole] = "todoCompleted";
- roleNames[Roles::PriorityRole] = "priority";
- roleNames[Roles::CollectionIdRole] = "collectionId";
- roleNames[Roles::DurationStringRole] = "durationString";
- roleNames[Roles::RecursRole] = "recurs";
- roleNames[Roles::IsOverdueRole] = "isOverdue";
- roleNames[Roles::IncidenceIdRole] = "incidenceId";
- roleNames[Roles::IncidenceTypeRole] = "incidenceType";
- roleNames[Roles::IncidenceTypeStrRole] = "incidenceTypeStr";
- roleNames[Roles::IncidenceTypeIconRole] = "incidenceTypeIcon";
- roleNames[Roles::IncidencePtrRole] = "incidencePtr";
- roleNames[Roles::TagsRole] = "tags";
- roleNames[Roles::ItemRole] = "item";
- roleNames[Roles::CategoriesRole] = "categories";
- roleNames[Roles::CategoriesDisplayRole] = "categoriesDisplay";
- roleNames[Roles::TreeDepthRole] = "treeDepth";
-
- return roleNames;
- }
-
- Akonadi::ETMCalendar::Ptr calendar() {
- return m_calendar;
- }
-
- void setCalendar(Akonadi::ETMCalendar::Ptr calendar) {
- m_calendar = calendar;
- m_todoTreeModel->setSourceModel(calendar->model());
- m_baseTodoModel->setCalendar(calendar);
- };
-
- void setIncidenceChanger(Akonadi::IncidenceChanger* changer) {
- m_baseTodoModel->setIncidenceChanger(changer);
- };
-
- QHash colorCache() {
- return m_colors;
- };
-
- void setColorCache(QHash colorCache) {
- m_colors = colorCache;
- };
-
- void loadColors() {
- KSharedConfig::Ptr config = KSharedConfig::openConfig();
- KConfigGroup rColorsConfig(config, "Resources Colors");
- const QStringList colorKeyList = rColorsConfig.keyList();
-
- for (const QString &key : colorKeyList) {
- QColor color = rColorsConfig.readEntry(key, QColor("blue"));
- m_colors[key] = color;
- }
- Q_EMIT layoutChanged();
- }
-
-private:
- Akonadi::ETMCalendar::Ptr m_calendar;
- IncidenceTreeModel *m_todoTreeModel = nullptr;
- TodoModel *m_baseTodoModel = nullptr;
- QHash m_colors;
- KConfigWatcher::Ptr m_colorWatcher;
-};
-
-
-
-
-
TodoSortFilterProxyModel::TodoSortFilterProxyModel(QObject* parent)
: QSortFilterProxyModel(parent)
{
@@ -384,8 +163,3 @@ void TodoSortFilterProxyModel::filterTodoName(QString name, int showCompleted)
invalidateFilter();
Q_EMIT layoutChanged();
}
-
-
-Q_DECLARE_METATYPE(KCalendarCore::Incidence::Ptr)
-#include "todosortfilterproxymodel.moc"
-
diff --git a/src/todosortfilterproxymodel.h b/src/todosortfilterproxymodel.h
index c84530c8761236fb605991c5f0dff8d2c90a7db5..f346047df28f1f31de8a7620ebc7ae1aca14b078 100644
--- a/src/todosortfilterproxymodel.h
+++ b/src/todosortfilterproxymodel.h
@@ -3,19 +3,14 @@
#pragma once
#include
-#include
#include
#include
-#include
-#include
-#include
-#include
#include
#include
#include
+#include
#include
-class ExtraTodoModel;
class TodoSortFilterProxyModel : public QSortFilterProxyModel
{