16#include "calendarview_debug.h"
17#include "coloredtodoproxymodel.h"
18#include "tododelegates.h"
19#include "todoviewquickaddline.h"
20#include "todoviewquicksearch.h"
21#include "todoviewsortfilterproxymodel.h"
22#include "todoviewview.h"
24#include <Akonadi/CalendarUtils>
25#include <Akonadi/EntityMimeTypeFilterModel>
26#include <Akonadi/EntityTreeModel>
27#include <Akonadi/TagFetchJob>
29#include <Akonadi/ETMViewStateSaver>
30#include <Akonadi/IncidenceTreeModel>
31#include <Akonadi/TodoModel>
33#include <CalendarSupport/KCalPrefs>
35#include <KCalendarCore/CalFormat>
38#include <KDatePickerPopup>
39#include <KDescendantsProxyModel>
47#include <QSortFilterProxyModel>
52using namespace std::chrono_literals;
62class CalendarFilterModel :
public QSortFilterProxyModel
69 mDescendantsProxy.setDisplayAncestorData(
false);
75 mDescendantsProxy.setSourceModel(model);
78 bool filterAcceptsRow(
int source_row,
const QModelIndex &source_parent)
const override
80 const auto source_index =
sourceModel()->index(source_row, 0, source_parent);
83 if (!item.isValid()) {
86 return mEnabledCalendars.contains(item.parentCollection().id());
91 mEnabledCalendars.insert(calendar->collection().id());
97 mEnabledCalendars.remove(calendar->collection().id());
102 KDescendantsProxyModel mDescendantsProxy;
103 QSet<Akonadi::Collection::Id> mEnabledCalendars;
110 ModelStack(
const EventViews::PrefsPtr &preferences, QObject *parent_)
111 : todoModel(new Akonadi::TodoModel())
112 , coloredTodoModel(new ColoredTodoProxyModel(
preferences))
116 coloredTodoModel->setSourceModel(todoModel);
121 delete coloredTodoModel;
123 delete todoTreeModel;
124 delete todoFlatModel;
127 void registerView(TodoView *view)
132 void unregisterView(TodoView *view)
134 views.removeAll(view);
137 void setFlatView(
bool flat)
139 const QString todoMimeType = QStringLiteral(
"application/x-vnd.akonadi.calendar.todo");
141 for (TodoView *view : std::as_const(views)) {
144 view->setFlatView(flat,
false);
147 view->saveViewState();
151 delete todoFlatModel;
152 todoFlatModel =
new Akonadi::EntityMimeTypeFilterModel(parent);
153 todoFlatModel->addMimeTypeInclusionFilter(todoMimeType);
154 todoFlatModel->setSourceModel(model);
155 todoModel->setSourceModel(todoFlatModel);
157 delete todoTreeModel;
158 todoTreeModel =
nullptr;
160 delete todoTreeModel;
161 todoTreeModel =
new Akonadi::IncidenceTreeModel(QStringList() << todoMimeType, parent);
162 for (TodoView *view : std::as_const(views)) {
166 view->setFlatView(flat,
false);
168 todoTreeModel->setSourceModel(model);
169 todoModel->setSourceModel(todoTreeModel);
170 delete todoFlatModel;
171 todoFlatModel =
nullptr;
174 for (TodoView *view : std::as_const(views)) {
180 view->restoreViewState();
183 prefs->setFlatListTodo(flat);
184 prefs->writeConfig();
187 void setModel(QAbstractItemModel *model)
191 todoTreeModel->setSourceModel(this->model);
195 bool isFlatView()
const
197 return todoFlatModel !=
nullptr;
200 Akonadi::TodoModel *
const todoModel;
201 ColoredTodoProxyModel *
const coloredTodoModel;
202 QList<TodoView *> views;
203 QObject *parent =
nullptr;
205 QAbstractItemModel *model =
nullptr;
206 Akonadi::IncidenceTreeModel *todoTreeModel =
nullptr;
207 Akonadi::EntityMimeTypeFilterModel *todoFlatModel =
nullptr;
208 EventViews::PrefsPtr prefs;
213static ModelStack *sModels =
nullptr;
215TodoView::TodoView(
const EventViews::PrefsPtr &prefs,
bool sidebarView,
QWidget *parent)
217 , mCalendarFilterModel(std::make_unique<CalendarFilterModel>())
218 , mQuickSearch(nullptr)
220 , mTreeStateRestorer(nullptr)
221 , mSidebarView(sidebarView)
222 , mResizeColumnsScheduled(false)
224 mResizeColumnsTimer =
new QTimer(
this);
226 mResizeColumnsTimer->setInterval(100ms);
227 mResizeColumnsTimer->setSingleShot(
true);
229 setPreferences(prefs);
231 sModels =
new ModelStack(prefs, parent);
233 KMessageBox::information(nullptr,
234 i18n(
"Cannot move to-do to itself or a child of itself."),
235 i18nc(
"@title:window",
"Drop To-do"),
236 QStringLiteral(
"NoDropTodoOntoItself"));
239 sModels->registerView(
this);
240 sModels->setModel(mCalendarFilterModel.get());
242 mProxyModel =
new TodoViewSortFilterProxyModel(
preferences(),
this);
243 mProxyModel->setSourceModel(sModels->coloredTodoModel);
244 mProxyModel->setFilterKeyColumn(Akonadi::TodoModel::SummaryColumn);
250 mQuickSearch =
new TodoViewQuickSearch(
this);
251 mQuickSearch->setVisible(prefs->enableTodoQuickSearch());
253 &TodoViewQuickSearch::searchTextChanged,
256 connect(mQuickSearch, &TodoViewQuickSearch::searchTextChanged,
this, &TodoView::restoreViewState);
257 connect(mQuickSearch, &TodoViewQuickSearch::filterCategoryChanged, mProxyModel, &TodoViewSortFilterProxyModel::setCategoryFilter);
258 connect(mQuickSearch, &TodoViewQuickSearch::filterCategoryChanged,
this, &TodoView::restoreViewState);
259 connect(mQuickSearch, &TodoViewQuickSearch::filterPriorityChanged, mProxyModel, &TodoViewSortFilterProxyModel::setPriorityFilter);
260 connect(mQuickSearch, &TodoViewQuickSearch::filterPriorityChanged,
this, &TodoView::restoreViewState);
263 mView =
new TodoViewView(
this);
264 mView->setModel(mProxyModel);
268 mView->setSortingEnabled(
true);
270 mView->setAutoExpandDelay(250);
273 mView->setExpandsOnDoubleClick(
false);
277 connect(mView, &TodoViewView::visibleColumnCountChanged,
this, &TodoView::resizeColumns);
280 mView->setItemDelegateForColumn(Akonadi::TodoModel::SummaryColumn, richTextDelegate);
281 mView->setItemDelegateForColumn(Akonadi::TodoModel::DescriptionColumn, richTextDelegate);
284 mView->setItemDelegateForColumn(Akonadi::TodoModel::PriorityColumn, priorityDelegate);
287 mView->setItemDelegateForColumn(Akonadi::TodoModel::StartDateColumn, startDateDelegate);
290 mView->setItemDelegateForColumn(Akonadi::TodoModel::DueDateColumn, dueDateDelegate);
293 mView->setItemDelegateForColumn(Akonadi::TodoModel::PercentColumn, completeDelegate);
296 mView->setItemDelegateForColumn(Akonadi::TodoModel::CategoriesColumn, mCategoriesDelegate);
303 mQuickAdd =
new TodoViewQuickAddLine(
this);
304 mQuickAdd->setClearButtonEnabled(
true);
305 mQuickAdd->setVisible(
preferences()->enableQuickTodo());
306 connect(mQuickAdd, &TodoViewQuickAddLine::returnPressed,
this, &TodoView::addQuickTodo);
308 mFullViewButton =
nullptr;
311 mFullViewButton->setAutoRaise(
true);
312 mFullViewButton->setCheckable(
true);
314 mFullViewButton->setToolTip(
i18nc(
"@info:tooltip",
"Display to-do list in a full window"));
315 mFullViewButton->setWhatsThis(
i18nc(
"@info:whatsthis",
"Checking this option will cause the to-do view to use the full window."));
318 mFlatViewButton->setAutoRaise(
true);
319 mFlatViewButton->setCheckable(
true);
320 mFlatViewButton->setToolTip(
i18nc(
"@info:tooltip",
"Display to-dos in flat list instead of a tree"));
321 mFlatViewButton->setWhatsThis(
i18nc(
"@info:whatsthis",
322 "Checking this option will cause the to-dos to be displayed as a "
323 "flat list instead of a hierarchical tree; the parental "
324 "relationships are removed in the display."));
327 setFlatView(flatView,
true);
329 if (mFullViewButton) {
334 layout->setContentsMargins({});
336 layout->addWidget(mQuickSearch, 0, 0, 1, 2);
338 layout->addWidget(mView, 1, 0, 1, 2);
339 layout->setRowStretch(1, 1);
340 layout->addWidget(mQuickAdd, 2, 0);
345 dummyLayout->setContentsMargins(0, 0, mView->frameWidth() , 0);
347 auto f =
new QFrame(
this);
350 dummyLayout->addWidget(f);
351 dummyLayout->addWidget(mFullViewButton);
353 dummyLayout->addWidget(mFlatViewButton);
355 layout->addLayout(dummyLayout, 2, 1);
358 mItemPopupMenu =
new QMenu(
this);
360 mItemPopupMenuItemOnlyEntries << mItemPopupMenu->addAction(
QIcon::fromTheme(QStringLiteral(
"document-preview")),
361 i18nc(
"@action:inmenu show the to-do",
"&Show"),
363 &TodoView::showTodo);
366 i18nc(
"@action:inmenu edit the to-do",
"&Edit…"),
368 &TodoView::editTodo);
369 mItemPopupMenuReadWriteEntries << a;
370 mItemPopupMenuItemOnlyEntries << a;
372 a = mItemPopupMenu->addAction(
QIcon::fromTheme(QStringLiteral(
"edit-delete")),
373 i18nc(
"@action:inmenu delete the to-do",
"&Delete"),
375 &TodoView::deleteTodo);
376 mItemPopupMenuReadWriteEntries << a;
377 mItemPopupMenuItemOnlyEntries << a;
379 mItemPopupMenu->addSeparator();
381 mItemPopupMenuItemOnlyEntries << mItemPopupMenu->addAction(
QIcon::fromTheme(QStringLiteral(
"document-print")),
382 i18nc(
"@action:inmenu print the to-do",
"&Print…"),
384 &TodoView::printTodo);
386 mItemPopupMenuItemOnlyEntries << mItemPopupMenu->addAction(
QIcon::fromTheme(QStringLiteral(
"document-print-preview")),
387 i18nc(
"@action:inmenu print preview the to-do",
"Print Previe&w…"),
389 &TodoView::printPreviewTodo);
391 mItemPopupMenu->addSeparator();
393 mItemPopupMenu->addAction(
QIcon::fromTheme(QStringLiteral(
"view-calendar-tasks")),
394 i18nc(
"@action:inmenu create a new to-do",
"New &To-do…"),
398 a = mItemPopupMenu->addAction(
i18nc(
"@action:inmenu create a new sub-to-do",
"New Su&b-to-do…"),
this, &TodoView::newSubTodo);
399 mItemPopupMenuReadWriteEntries << a;
400 mItemPopupMenuItemOnlyEntries << a;
402 mMakeTodoIndependent = mItemPopupMenu->addAction(
i18nc(
"@action:inmenu",
"&Make this To-do Independent"),
this, &TodoView::unSubTodoSignal);
404 mMakeSubtodosIndependent = mItemPopupMenu->addAction(
i18nc(
"@action:inmenu",
"Make all Sub-to-dos &Independent"),
this, &TodoView::unAllSubTodoSignal);
406 mItemPopupMenuItemOnlyEntries << mMakeTodoIndependent;
407 mItemPopupMenuItemOnlyEntries << mMakeSubtodosIndependent;
409 mItemPopupMenuReadWriteEntries << mMakeTodoIndependent;
410 mItemPopupMenuReadWriteEntries << mMakeSubtodosIndependent;
412 mItemPopupMenu->addSeparator();
414 a = mItemPopupMenu->addAction(
QIcon::fromTheme(QStringLiteral(
"appointment-new")),
415 i18nc(
"@action:inmenu",
"Create Event from To-do"),
417 qOverload<>(&TodoView::createEvent));
419 mItemPopupMenuReadWriteEntries << a;
420 mItemPopupMenuItemOnlyEntries << a;
422 mItemPopupMenu->addSeparator();
425 mCopyPopupMenu->setTitle(
i18nc(
"@title:menu",
"&Copy To"));
432 mMovePopupMenu->setTitle(
i18nc(
"@title:menu",
"&Move To"));
439 mItemPopupMenu->insertMenu(
nullptr, mCopyPopupMenu);
440 mItemPopupMenu->insertMenu(
nullptr, mMovePopupMenu);
442 mItemPopupMenu->addSeparator();
443 mItemPopupMenu->addAction(
i18nc(
"@action:inmenu delete completed to-dos",
"Pur&ge Completed"),
this, &TodoView::purgeCompletedSignal);
445 mPriorityPopupMenu =
new QMenu(
this);
446 mPriority[mPriorityPopupMenu->addAction(
i18nc(
"@action:inmenu unspecified priority",
"unspecified"))] = 0;
447 mPriority[mPriorityPopupMenu->addAction(
i18nc(
"@action:inmenu highest priority",
"1 (highest)"))] = 1;
448 mPriority[mPriorityPopupMenu->addAction(
i18nc(
"@action:inmenu priority value=2",
"2"))] = 2;
449 mPriority[mPriorityPopupMenu->addAction(
i18nc(
"@action:inmenu priority value=3",
"3"))] = 3;
450 mPriority[mPriorityPopupMenu->addAction(
i18nc(
"@action:inmenu priority value=4",
"4"))] = 4;
451 mPriority[mPriorityPopupMenu->addAction(
i18nc(
"@action:inmenu medium priority",
"5 (medium)"))] = 5;
452 mPriority[mPriorityPopupMenu->addAction(
i18nc(
"@action:inmenu priority value=6",
"6"))] = 6;
453 mPriority[mPriorityPopupMenu->addAction(
i18nc(
"@action:inmenu priority value=7",
"7"))] = 7;
454 mPriority[mPriorityPopupMenu->addAction(
i18nc(
"@action:inmenu priority value=8",
"8"))] = 8;
455 mPriority[mPriorityPopupMenu->addAction(
i18nc(
"@action:inmenu lowest priority",
"9 (lowest)"))] = 9;
458 mPercentageCompletedPopupMenu =
new QMenu(
this);
459 for (
int i = 0; i <= 100; i += 10) {
461 mPercentage[mPercentageCompletedPopupMenu->addAction(label)] = i;
465 setMinimumHeight(50);
478 sModels->unregisterView(
this);
479 if (sModels->views.isEmpty()) {
485void TodoView::expandIndex(
const QModelIndex &index)
487 QModelIndex todoModelIndex = sModels->todoModel->
mapFromSource(index);
488 Q_ASSERT(todoModelIndex.
isValid());
489 const auto coloredIndex = sModels->coloredTodoModel->
mapFromSource(todoModelIndex);
490 Q_ASSERT(coloredIndex.isValid());
491 QModelIndex realIndex = mProxyModel->mapFromSource(coloredIndex);
494 mView->expand(realIndex);
495 realIndex = mProxyModel->
parent(realIndex);
499void TodoView::setModel(QAbstractItemModel *model)
501 EventView::setModel(model);
503 mCalendarFilterModel->setSourceModel(model);
509 EventView::addCalendar(calendar);
510 mCalendarFilterModel->addCalendar(calendar);
511 if (calendars().
size() == 1) {
512 mProxyModel->setCalFilter(calendar->filter());
518 mCalendarFilterModel->removeCalendar(calendar);
519 EventView::removeCalendar(calendar);
525 const QModelIndexList selection = mView->selectionModel()->selectedRows();
526 ret.
reserve(selection.count());
542void TodoView::saveLayout(
KConfig *config,
const QString &group)
const
547 QVariantList columnVisibility;
548 QVariantList columnOrder;
549 QVariantList columnWidths;
550 const int headerCount = header->
count();
551 columnVisibility.reserve(headerCount);
552 columnWidths.reserve(headerCount);
553 columnOrder.reserve(headerCount);
554 for (
int i = 0; i < headerCount; ++i) {
559 cfgGroup.
writeEntry(
"ColumnVisibility", columnVisibility);
560 cfgGroup.
writeEntry(
"ColumnOrder", columnOrder);
561 cfgGroup.
writeEntry(
"ColumnWidths", columnWidths);
571 preferences()->setFullViewTodo(mFullViewButton->isChecked());
573 preferences()->setFlatListTodo(mFlatViewButton->isChecked());
576void TodoView::restoreLayout(KConfig *config,
const QString &group,
bool minimalDefaults)
578 KConfigGroup cfgGroup = config->
group(group);
579 QHeaderView *header = mView->header();
581 QVariantList columnVisibility = cfgGroup.
readEntry(
"ColumnVisibility", QVariantList());
582 QVariantList columnOrder = cfgGroup.
readEntry(
"ColumnOrder", QVariantList());
583 QVariantList columnWidths = cfgGroup.
readEntry(
"ColumnWidths", QVariantList());
585 if (columnVisibility.isEmpty()) {
587 mView->hideColumn(Akonadi::TodoModel::RecurColumn);
588 mView->hideColumn(Akonadi::TodoModel::DescriptionColumn);
589 mView->hideColumn(Akonadi::TodoModel::CalendarColumn);
590 mView->hideColumn(Akonadi::TodoModel::CompletedDateColumn);
592 if (minimalDefaults) {
593 mView->hideColumn(Akonadi::TodoModel::PriorityColumn);
594 mView->hideColumn(Akonadi::TodoModel::PercentColumn);
595 mView->hideColumn(Akonadi::TodoModel::DescriptionColumn);
596 mView->hideColumn(Akonadi::TodoModel::CategoriesColumn);
602 for (
int i = 0; i < header->
count() && i < columnOrder.size() && i < columnWidths.size() && i < columnVisibility.size(); i++) {
603 bool visible = columnVisibility[i].toBool();
604 int width = columnWidths[i].toInt();
605 int order = columnOrder[i].toInt();
610 mView->hideColumn(i);
616 int sortColumn = cfgGroup.
readEntry(
"SortColumn", -1);
617 if (sortColumn >= 0) {
621 mFlatViewButton->setChecked(cfgGroup.
readEntry(
"FlatView",
false));
624void TodoView::setIncidenceChanger(Akonadi::IncidenceChanger *changer)
627 sModels->todoModel->setIncidenceChanger(changer);
630void TodoView::showDates(
const QDate &
start,
const QDate &end,
const QDate &)
639 Q_UNUSED(incidenceList)
643void TodoView::updateView()
645 if (calendars().empty()) {
649 auto calendar = calendars().first();
650 mProxyModel->setCalFilter(calendar->filter());
653void TodoView::changeIncidenceDisplay(
const Akonadi::Item &, Akonadi::IncidenceChanger::ChangeType)
658void TodoView::updateConfig()
660 Q_ASSERT(preferences());
661 if (!mSidebarView && mQuickSearch) {
662 mQuickSearch->setVisible(preferences()->enableTodoQuickSearch());
666 mQuickAdd->setVisible(preferences()->enableQuickTodo());
670 mProxyModel->invalidate();
676void TodoView::clearSelection()
678 mView->selectionModel()->clearSelection();
681void TodoView::addTodo(
const QString &summary,
const Akonadi::Item &parentItem,
const QStringList &categories)
683 const QString summaryTrimmed = summary.
trimmed();
684 if (!changer() || summaryTrimmed.
isEmpty()) {
691 todo->setSummary(summaryTrimmed);
692 todo->setOrganizer(Person(CalendarSupport::KCalPrefs::instance()->
fullName(), CalendarSupport::KCalPrefs::instance()->email()));
694 todo->setCategories(categories);
700 Akonadi::Collection collection;
708 changer()->createIncidence(todo, collection,
this);
715 addTodo(mQuickAdd->text(), Akonadi::Item(), mProxyModel->categories());
717 QModelIndexList selection = mView->selectionModel()->selectedRows();
718 if (selection.count() != 1) {
719 qCWarning(CALENDARVIEW_LOG) <<
"No to-do selected" << selection;
722 const QModelIndex idx = mProxyModel->mapToSource(selection[0]);
723 mView->expand(selection[0]);
725 addTodo(mQuickAdd->text(),
parent, mProxyModel->categories());
729 mQuickAdd->setText(QString());
732void TodoView::contextMenu(QPoint pos)
734 const bool hasItem = mView->indexAt(
pos).isValid();
737 for (QAction *entry : std::as_const(mItemPopupMenuItemOnlyEntries)) {
746 Akonadi::Item item = incidences.
first();
750 const bool actionIsRw = mItemPopupMenuReadWriteEntries.contains(entry);
755 enable = hasItem && (!actionIsRw || !incidenceIsRO);
761 entry->setEnabled(enable);
763 mCopyPopupMenu->setEnabled(hasItem);
764 mMovePopupMenu->setEnabled(hasItem);
768 const bool hasRecId = incidencePtr->hasRecurrenceId();
769 const bool hasSubtodos = mView->model()->hasChildren(mView->indexAt(
pos));
771 mMakeSubtodosIndependent->setEnabled(!hasRecId && hasSubtodos);
772 mMakeTodoIndependent->setEnabled(!hasRecId && !incidencePtr->relatedTo().isEmpty());
775 switch (mView->indexAt(
pos).column()) {
776 case Akonadi::TodoModel::PriorityColumn:
777 mPriorityPopupMenu->popup(mView->viewport()->mapToGlobal(
pos));
779 case Akonadi::TodoModel::PercentColumn:
780 mPercentageCompletedPopupMenu->popup(mView->viewport()->mapToGlobal(
pos));
782 case Akonadi::TodoModel::StartDateColumn:
783 mView->startPopupMenu()->popup(mView->viewport()->mapToGlobal(
pos));
785 case Akonadi::TodoModel::DueDateColumn:
786 mMovePopupMenu->popup(mView->viewport()->mapToGlobal(
pos));
788 case Akonadi::TodoModel::CategoriesColumn:
789 createCategoryPopupMenu()->popup(mView->viewport()->mapToGlobal(
pos));
792 mItemPopupMenu->popup(mView->viewport()->mapToGlobal(
pos));
796 mItemPopupMenu->popup(mView->viewport()->mapToGlobal(
pos));
800void TodoView::currentChanged(
const QModelIndex ¤t,
const QModelIndex &previous)
804 Q_EMIT incidenceSelected(Akonadi::Item(), QDate());
808 const auto todoItem = current.
data(Akonadi::TodoModel::TodoRole).
value<Akonadi::Item>();
811 Q_EMIT incidenceSelected(todoItem, QDate());
817void TodoView::showTodo()
819 QModelIndexList selection = mView->selectionModel()->selectedRows();
820 if (selection.size() != 1) {
824 const auto todoItem = selection[0].data(Akonadi::TodoModel::TodoRole).value<Akonadi::Item>();
829void TodoView::editTodo()
831 QModelIndexList selection = mView->selectionModel()->selectedRows();
832 if (selection.size() != 1) {
836 const auto todoItem = selection[0].data(Akonadi::TodoModel::TodoRole).value<Akonadi::Item>();
840void TodoView::deleteTodo()
842 QModelIndexList selection = mView->selectionModel()->selectedRows();
843 if (selection.size() == 1) {
844 const auto todoItem = selection[0].data(Akonadi::TodoModel::TodoRole).value<Akonadi::Item>();
846 if (!changer()->deletedRecently(todoItem.id())) {
852void TodoView::newTodo()
857void TodoView::newSubTodo()
859 QModelIndexList selection = mView->selectionModel()->selectedRows();
860 if (selection.size() == 1) {
861 const auto todoItem = selection[0].data(Akonadi::TodoModel::TodoRole).value<Akonadi::Item>();
863 Q_EMIT newSubTodoSignal(todoItem);
866 qCWarning(CALENDARVIEW_LOG) <<
"Selection size isn't 1";
870void TodoView::copyTodoToDate(QDate date)
876 QModelIndexList selection = mView->selectionModel()->selectedRows();
877 if (selection.size() != 1) {
881 const QModelIndex origIndex = mProxyModel->mapToSource(selection[0]);
895 QDateTime due =
todo->dtDue();
899 changer()->createIncidence(todo, Akonadi::Collection(),
this);
902void TodoView::scheduleResizeColumns()
904 mResizeColumnsScheduled =
true;
905 mResizeColumnsTimer->start();
908void TodoView::itemDoubleClicked(
const QModelIndex &index)
911 QModelIndex summary = index.
sibling(index.
row(), Akonadi::TodoModel::SummaryColumn);
920QMenu *TodoView::createCategoryPopupMenu()
922 auto tempMenu =
new QMenu(
this);
924 QModelIndexList selection = mView->selectionModel()->selectedRows();
925 if (selection.size() != 1) {
929 const auto todoItem = selection[0].data(Akonadi::TodoModel::TodoRole).value<Akonadi::Item>();
933 const QStringList checkedCategories =
todo->categories();
935 auto tagFetchJob =
new Akonadi::TagFetchJob(
this);
936 connect(tagFetchJob, &Akonadi::TagFetchJob::result,
this, &TodoView::onTagsFetched);
938 tagFetchJob->setProperty(
"checkedCategories", checkedCategories);
945void TodoView::onTagsFetched(KJob *job)
948 qCWarning(CALENDARVIEW_LOG) <<
"Failed to fetch tags " << job->
errorString();
951 auto fetchJob =
static_cast<Akonadi::TagFetchJob *
>(job);
955 const auto lst = fetchJob->tags();
956 for (
const Akonadi::Tag &tag : lst) {
957 const QString
name = tag.name();
958 QAction *action = menu->addAction(name);
961 if (checkedCategories.
contains(name)) {
968void TodoView::setNewDate(QDate date)
970 QModelIndexList selection = mView->selectionModel()->selectedRows();
971 if (selection.size() != 1) {
975 const auto todoItem = selection[0].data(Akonadi::TodoModel::TodoRole).value<Akonadi::Item>();
984 if (!
todo->allDay()) {
985 dt.setTime(
todo->dtDue().time());
988 if (
todo->hasStartDate() && dt < todo->dtStart()) {
989 todo->setDtStart(dt);
993 changer()->modifyIncidence(todoItem, oldTodo,
this);
995 qCDebug(CALENDARVIEW_LOG) <<
"Item is readOnly";
999void TodoView::setStartDate(QDate date)
1001 QModelIndexList selection = mView->selectionModel()->selectedRows();
1002 if (selection.size() != 1) {
1006 const auto todoItem = selection[0].data(Akonadi::TodoModel::TodoRole).value<Akonadi::Item>();
1015 if (!
todo->allDay()) {
1016 dt.setTime(
todo->dtStart().time());
1019 if (
todo->hasDueDate() && dt >
todo->dtDue()) {
1022 todo->setDtStart(dt);
1024 changer()->modifyIncidence(todoItem, oldTodo,
this);
1026 qCDebug(CALENDARVIEW_LOG) <<
"Item is readOnly";
1030void TodoView::setNewPercentage(QAction *action)
1032 QModelIndexList selection = mView->selectionModel()->selectedRows();
1033 if (selection.size() != 1) {
1037 const auto todoItem = selection[0].data(Akonadi::TodoModel::TodoRole).value<Akonadi::Item>();
1045 int percentage = mPercentage.value(action);
1046 if (percentage == 100) {
1048 todo->setPercentComplete(100);
1050 todo->setPercentComplete(percentage);
1052 changer()->modifyIncidence(todoItem, oldTodo,
this);
1054 qCDebug(CALENDARVIEW_LOG) <<
"Item is read only";
1058void TodoView::setNewPriority(QAction *action)
1060 const QModelIndexList selection = mView->selectionModel()->selectedRows();
1061 if (selection.size() != 1) {
1064 const auto todoItem = selection[0].data(Akonadi::TodoModel::TodoRole).value<Akonadi::Item>();
1069 todo->setPriority(mPriority[action]);
1071 changer()->modifyIncidence(todoItem, oldTodo,
this);
1075void TodoView::changedCategories(QAction *action)
1077 const QModelIndexList selection = mView->selectionModel()->selectedRows();
1078 if (selection.size() != 1) {
1082 const auto todoItem = selection[0].data(Akonadi::TodoModel::TodoRole).value<Akonadi::Item>();
1090 QStringList categories =
todo->categories();
1097 todo->setCategories(categories);
1098 changer()->modifyIncidence(todoItem, oldTodo,
this);
1100 qCDebug(CALENDARVIEW_LOG) <<
"No active item, active item is read-only, or locking failed";
1104void TodoView::setFullView(
bool fullView)
1106 if (!mFullViewButton) {
1110 mFullViewButton->setChecked(fullView);
1112 mFullViewButton->setIcon(
QIcon::fromTheme(QStringLiteral(
"view-restore")));
1114 mFullViewButton->setIcon(
QIcon::fromTheme(QStringLiteral(
"view-fullscreen")));
1117 mFullViewButton->blockSignals(
true);
1120 mFullViewButton->setChecked(fullView);
1121 mFullViewButton->blockSignals(
false);
1123 preferences()->setFullViewTodo(fullView);
1124 preferences()->writeConfig();
1126 Q_EMIT fullViewChanged(fullView);
1129void TodoView::setFlatView(
bool flatView,
bool notifyOtherViews)
1132 mFlatViewButton->setIcon(
QIcon::fromTheme(QStringLiteral(
"view-list-tree")));
1134 mFlatViewButton->setIcon(
QIcon::fromTheme(QStringLiteral(
"view-list-details")));
1137 if (notifyOtherViews) {
1138 sModels->setFlatView(flatView);
1142void TodoView::onRowsInserted(
const QModelIndex &parent,
int start,
int end)
1144 if (
start != end || !entityTreeModel()) {
1156 auto item = v.
value<Akonadi::Item>();
1161 const bool isPopulated = entityTreeModel()->isCollectionPopulated(item.
storageCollectionId());
1168 QModelIndexList selection = mView->selectionModel()->selectedRows();
1169 if (selection.size() <= 1) {
1172 int colCount =
static_cast<int>(Akonadi::TodoModel::ColumnCount);
1173 mView->selectionModel()->select(QItemSelection(idx, mView->model()->index(
start, colCount - 1)),
1180 if (sModels->isFlatView()) {
1184 QModelIndex index =
parent;
1185 mView->expand(index);
1187 mView->expand(index.
parent());
1194 highlightTodos = preferences()->highlightTodos();
1195 highlightEvents = !highlightTodos;
1196 highlightJournals =
false;
1199bool TodoView::usesFullWindow()
1201 return preferences()->fullViewTodo();
1204void TodoView::resizeColumns()
1206 mResizeColumnsScheduled =
false;
1218 const bool descriptionVisible = !mView->
isColumnHidden(Akonadi::TodoModel::DescriptionColumn);
1219 const bool categoriesVisible = !mView->
isColumnHidden(Akonadi::TodoModel::CategoriesColumn);
1223 for (
int i = 0; i < Akonadi::TodoModel::ColumnCount; ++i) {
1224 if (!mView->
isColumnHidden(i) && i != Akonadi::TodoModel::SummaryColumn && i != Akonadi::TodoModel::DescriptionColumn
1225 && i != Akonadi::TodoModel::CategoriesColumn) {
1231 int remainingSize = mView->header()->width() -
size;
1234 const int requiredSize = descriptionVisible ? 200 : 100;
1236 if (categoriesVisible) {
1237 const int categorySize = 100;
1238 mView->setColumnWidth(Akonadi::TodoModel::CategoriesColumn, categorySize);
1239 remainingSize -= categorySize;
1242 if (remainingSize < requiredSize) {
1244 mView->resizeColumnToContents(Akonadi::TodoModel::SummaryColumn);
1245 mView->resizeColumnToContents(Akonadi::TodoModel::DescriptionColumn);
1246 }
else if (descriptionVisible) {
1247 mView->setColumnWidth(Akonadi::TodoModel::SummaryColumn, remainingSize / 2);
1248 mView->setColumnWidth(Akonadi::TodoModel::DescriptionColumn, remainingSize / 2);
1250 mView->setColumnWidth(Akonadi::TodoModel::SummaryColumn, remainingSize);
1254void TodoView::restoreViewState()
1256 if (sModels->isFlatView()) {
1260 if (sModels->todoTreeModel && !sModels->todoTreeModel->
sourceModel()) {
1266 delete mTreeStateRestorer;
1267 mTreeStateRestorer =
new Akonadi::ETMViewStateSaver();
1269 KConfigGroup group(config, stateSaverGroup());
1270 mTreeStateRestorer->setView(mView);
1271 mTreeStateRestorer->restoreState(group);
1275QString TodoView::stateSaverGroup()
const
1277 QString str = QStringLiteral(
"TodoTreeViewState");
1279 str += QLatin1Char(
'S');
1285void TodoView::saveViewState()
1287 Akonadi::ETMViewStateSaver treeStateSaver;
1288 KConfigGroup group(preferences()->config(), stateSaverGroup());
1289 treeStateSaver.setView(mView);
1290 treeStateSaver.saveState(group);
1293void TodoView::resizeEvent(QResizeEvent *event)
1296 scheduleResizeColumns();
1299void TodoView::createEvent()
1301 const QModelIndexList selection = mView->selectionModel()->selectedRows();
1302 if (selection.size() != 1) {
1306 const auto todoItem = selection[0].data(Akonadi::TodoModel::TodoRole).value<Akonadi::Item>();
1308 Q_EMIT createEvent(todoItem);
1311#include "todoview.moc"
1313#include "moc_todoview.cpp"
static Collection updatedCollection(const QAbstractItemModel *model, qint64 collectionId)
void indexChangedParent(const QModelIndex &index)
void batchInsertionFinished()
Collection::Id storageCollectionId() const
void dropOnSelfRejected()
EventView is the abstract base class from which all other calendar views for event data are derived.
void showIncidenceSignal(const Akonadi::Item &)
instructs the receiver to show the incidence in read-only mode.
void deleteIncidenceSignal(const Akonadi::Item &)
instructs the receiver to delete the Incidence in some manner; some possibilities include automatical...
void editIncidenceSignal(const Akonadi::Item &)
instructs the receiver to begin editing the incidence specified in some manner.
virtual void setIncidenceChanger(Akonadi::IncidenceChanger *changer)
Assign a new incidence change helper object.
void getHighlightMode(bool &highlightEvents, bool &highlightTodos, bool &highlightJournals)
documentation in baseview.h
Akonadi::Item::List selectedIncidences() const override
KCalendarCore::DateList selectedIncidenceDates() const override
Returns a list of the dates of selected events.
QSharedPointer< Calendar > Ptr
QSharedPointer< Incidence > Ptr
QSharedPointer< Todo > Ptr
KConfigGroup group(const QString &group)
void writeEntry(const char *key, const char *value, WriteConfigFlags pFlags=Normal)
QString readEntry(const char *key, const char *aDefault=nullptr) const
virtual QString errorString() const
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
This delegate is responsible for displaying the categories of todos.
This delegate is responsible for displaying progress bars for the completion status of indivitual tod...
This delegate is responsible for displaying the due date of todos.
This delegate is responsible for displaying the priority of todos.
This delegate is responsible for displaying possible rich text elements of a todo.
Q_SCRIPTABLE Q_NOREPLY void start()
QString i18nc(const char *context, const char *text, const TYPE &arg...)
AKONADI_CALENDAR_EXPORT KCalendarCore::Incidence::Ptr incidence(const Akonadi::Item &item)
AKONADI_CALENDAR_EXPORT KCalendarCore::Todo::Ptr todo(const Akonadi::Item &item)
QString fullName(const PartType &type)
Namespace EventViews provides facilities for displaying incidences, including events,...
QString name(const QVariant &location)
QString label(StandardShortcut id)
const QList< QKeySequence > & preferences()
QAbstractItemModel(QObject *parent)
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const=0
virtual QModelIndex parent(const QModelIndex &index) const const=0
void rowsInserted(const QModelIndex &parent, int first, int last)
void doubleClicked(const QModelIndex &index)
void setDragDropMode(DragDropMode behavior)
QVariant data() const const
void setData(const QVariant &data)
QDateTime startOfDay() const const
QDateTime currentDateTime()
QIcon fromTheme(const QString &name)
virtual QModelIndex mapFromSource(const QModelIndex &sourceIndex) const const override
void currentChanged(const QModelIndex ¤t, const QModelIndex &previous)
void append(QList< T > &&value)
bool isEmpty() const const
qsizetype removeAll(const AT &t)
void reserve(qsizetype size)
QVariant data(int role) const const
Qt::ItemFlags flags() const const
bool isValid() const const
const QAbstractItemModel * model() const const
QModelIndex parent() const const
QModelIndex sibling(int row, int column) const const
bool blockSignals(bool block)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
virtual bool event(QEvent *e)
QObject * parent() const const
QVariant property(const char *name) const const
void setObjectName(QAnyStringView name)
QSortFilterProxyModel(QObject *parent)
void setFilterRegularExpression(const QRegularExpression ®ularExpression)
virtual void setSourceModel(QAbstractItemModel *sourceModel) override
QString arg(Args &&... args) const const
bool isEmpty() const const
QString trimmed() const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
void sort(Qt::CaseSensitivity cs)
typedef KeyboardModifiers
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
int columnWidth(int column) const const
bool isColumnHidden(int column) const const
void resizeColumnToContents(int column)
void setRootIsDecorated(bool show)
QVariant fromValue(T &&value)
bool isValid() const const
QString toString() const const
QStringList toStringList() const const