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 , mTreeStateRestorer(nullptr)
219 , mSidebarView(sidebarView)
220 , mResizeColumnsScheduled(false)
222 mResizeColumnsTimer =
new QTimer(
this);
224 mResizeColumnsTimer->setInterval(100ms);
225 mResizeColumnsTimer->setSingleShot(
true);
227 setPreferences(prefs);
229 sModels =
new ModelStack(prefs, parent);
231 KMessageBox::information(nullptr,
232 i18nc(
"@info",
"Cannot move to-do to itself or a child of itself."),
233 i18nc(
"@title:window",
"Drop To-do"),
234 QStringLiteral(
"NoDropTodoOntoItself"));
237 sModels->registerView(
this);
238 sModels->setModel(mCalendarFilterModel.get());
240 mProxyModel =
new TodoViewSortFilterProxyModel(
preferences(),
this);
241 mProxyModel->setSourceModel(sModels->coloredTodoModel);
242 mProxyModel->setFilterKeyColumn(Akonadi::TodoModel::SummaryColumn);
248 mQuickSearch =
new TodoViewQuickSearch(
this);
249 mQuickSearch->setVisible(prefs->enableTodoQuickSearch());
251 &TodoViewQuickSearch::searchTextChanged,
254 connect(mQuickSearch, &TodoViewQuickSearch::searchTextChanged,
this, &TodoView::restoreViewState);
255 connect(mQuickSearch, &TodoViewQuickSearch::filterCategoryChanged, mProxyModel, &TodoViewSortFilterProxyModel::setCategoryFilter);
256 connect(mQuickSearch, &TodoViewQuickSearch::filterCategoryChanged,
this, &TodoView::restoreViewState);
257 connect(mQuickSearch, &TodoViewQuickSearch::filterPriorityChanged, mProxyModel, &TodoViewSortFilterProxyModel::setPriorityFilter);
258 connect(mQuickSearch, &TodoViewQuickSearch::filterPriorityChanged,
this, &TodoView::restoreViewState);
261 mView =
new TodoViewView(
this);
262 mView->setModel(mProxyModel);
266 mView->setSortingEnabled(
true);
268 mView->setAutoExpandDelay(250);
271 mView->setExpandsOnDoubleClick(
false);
275 connect(mView, &TodoViewView::visibleColumnCountChanged,
this, &TodoView::resizeColumns);
278 mView->setItemDelegateForColumn(Akonadi::TodoModel::SummaryColumn, richTextDelegate);
279 mView->setItemDelegateForColumn(Akonadi::TodoModel::DescriptionColumn, richTextDelegate);
282 mView->setItemDelegateForColumn(Akonadi::TodoModel::PriorityColumn, priorityDelegate);
285 mView->setItemDelegateForColumn(Akonadi::TodoModel::StartDateColumn, startDateDelegate);
288 mView->setItemDelegateForColumn(Akonadi::TodoModel::DueDateColumn, dueDateDelegate);
291 mView->setItemDelegateForColumn(Akonadi::TodoModel::PercentColumn, completeDelegate);
294 mView->setItemDelegateForColumn(Akonadi::TodoModel::CategoriesColumn, mCategoriesDelegate);
301 mQuickAdd =
new TodoViewQuickAddLine(
this);
302 mQuickAdd->setClearButtonEnabled(
true);
303 mQuickAdd->setVisible(
preferences()->enableQuickTodo());
304 mQuickAdd->setToolTip(
i18nc(
"@info:tooltip",
"Create an open-ended to-do"));
305 mQuickAdd->setWhatsThis(
307 "Enter the summary for a new to-do. <para><note>The new to-do will be open-ended meaning that it has no start or end times nor "
308 "will it have a reminder or recurrence. Edit the newly created if you want to add more properties.</note></para>"));
309 connect(mQuickAdd, &TodoViewQuickAddLine::returnPressed,
this, &TodoView::addQuickTodo);
311 mFullViewButton =
nullptr;
314 mFullViewButton->setAutoRaise(
true);
315 mFullViewButton->setCheckable(
true);
317 mFullViewButton->setToolTip(
i18nc(
"@info:tooltip",
"Display to-do list in a full window"));
318 mFullViewButton->setWhatsThis(
i18nc(
"@info:whatsthis",
"Checking this option will cause the to-do view to use the full window."));
321 mFlatViewButton->setAutoRaise(
true);
322 mFlatViewButton->setCheckable(
true);
323 mFlatViewButton->setToolTip(
i18nc(
"@info:tooltip",
"Display to-dos in a flat list or a tree"));
324 mFlatViewButton->setWhatsThis(
i18nc(
"@info:whatsthis",
325 "Checking this button will cause the to-dos to be displayed either as a "
326 "flat list or a hierarchical tree where the parental "
327 "relationships are removed."));
330 setFlatView(flatView,
true);
332 if (mFullViewButton) {
337 layout->setContentsMargins({});
339 layout->addWidget(mQuickSearch, 0, 0, 1, 2);
341 layout->addWidget(mView, 1, 0, 1, 2);
342 layout->setRowStretch(1, 1);
343 layout->addWidget(mQuickAdd, 2, 0);
348 dummyLayout->setContentsMargins(0, 0, mView->frameWidth() , 0);
350 auto f =
new QFrame(
this);
353 dummyLayout->addWidget(f);
354 dummyLayout->addWidget(mFullViewButton);
356 dummyLayout->addWidget(mFlatViewButton);
358 layout->addLayout(dummyLayout, 2, 1);
361 mItemPopupMenu =
new QMenu(
this);
363 mItemPopupMenuItemOnlyEntries << mItemPopupMenu->addAction(
QIcon::fromTheme(QStringLiteral(
"document-preview")),
364 i18nc(
"@action:inmenu show the to-do",
"&Show"),
366 &TodoView::showTodo);
369 i18nc(
"@action:inmenu edit the to-do",
"&Edit…"),
371 &TodoView::editTodo);
372 mItemPopupMenuReadWriteEntries << a;
373 mItemPopupMenuItemOnlyEntries << a;
375 a = mItemPopupMenu->addAction(
QIcon::fromTheme(QStringLiteral(
"edit-delete")),
376 i18nc(
"@action:inmenu delete the to-do",
"&Delete"),
378 &TodoView::deleteTodo);
379 mItemPopupMenuReadWriteEntries << a;
380 mItemPopupMenuItemOnlyEntries << a;
382 mItemPopupMenu->addSeparator();
384 mItemPopupMenuItemOnlyEntries << mItemPopupMenu->addAction(
QIcon::fromTheme(QStringLiteral(
"document-print")),
385 i18nc(
"@action:inmenu print the to-do",
"&Print…"),
387 &TodoView::printTodo);
389 mItemPopupMenuItemOnlyEntries << mItemPopupMenu->addAction(
QIcon::fromTheme(QStringLiteral(
"document-print-preview")),
390 i18nc(
"@action:inmenu print preview the to-do",
"Print Previe&w…"),
392 &TodoView::printPreviewTodo);
394 mItemPopupMenu->addSeparator();
396 mItemPopupMenu->addAction(
QIcon::fromTheme(QStringLiteral(
"view-calendar-tasks")),
397 i18nc(
"@action:inmenu create a new to-do",
"New &To-do…"),
401 a = mItemPopupMenu->addAction(
i18nc(
"@action:inmenu create a new sub-to-do",
"New Su&b-to-do…"),
this, &TodoView::newSubTodo);
402 mItemPopupMenuReadWriteEntries << a;
403 mItemPopupMenuItemOnlyEntries << a;
405 mMakeTodoIndependent = mItemPopupMenu->addAction(
i18nc(
"@action:inmenu",
"&Make this To-do Independent"),
this, &TodoView::unSubTodoSignal);
407 mMakeSubtodosIndependent = mItemPopupMenu->addAction(
i18nc(
"@action:inmenu",
"Make all Sub-to-dos &Independent"),
this, &TodoView::unAllSubTodoSignal);
409 mItemPopupMenuItemOnlyEntries << mMakeTodoIndependent;
410 mItemPopupMenuItemOnlyEntries << mMakeSubtodosIndependent;
412 mItemPopupMenuReadWriteEntries << mMakeTodoIndependent;
413 mItemPopupMenuReadWriteEntries << mMakeSubtodosIndependent;
415 mItemPopupMenu->addSeparator();
417 a = mItemPopupMenu->addAction(
QIcon::fromTheme(QStringLiteral(
"appointment-new")),
418 i18nc(
"@action:inmenu",
"Create Event from To-do"),
420 qOverload<>(&TodoView::createEvent));
422 mItemPopupMenuReadWriteEntries << a;
423 mItemPopupMenuItemOnlyEntries << a;
425 mItemPopupMenu->addSeparator();
428 mCopyPopupMenu->setTitle(
i18nc(
"@title:menu",
"&Copy To"));
435 mMovePopupMenu->setTitle(
i18nc(
"@title:menu",
"&Move To"));
442 mItemPopupMenu->insertMenu(
nullptr, mCopyPopupMenu);
443 mItemPopupMenu->insertMenu(
nullptr, mMovePopupMenu);
445 mItemPopupMenu->addSeparator();
446 mItemPopupMenu->addAction(
i18nc(
"@action:inmenu delete completed to-dos",
"Pur&ge Completed"),
this, &TodoView::purgeCompletedSignal);
448 mPriorityPopupMenu =
new QMenu(
this);
449 mPriority[mPriorityPopupMenu->addAction(
i18nc(
"@action:inmenu unspecified priority",
"unspecified"))] = 0;
450 mPriority[mPriorityPopupMenu->addAction(
i18nc(
"@action:inmenu highest priority",
"1 (highest)"))] = 1;
451 mPriority[mPriorityPopupMenu->addAction(
i18nc(
"@action:inmenu priority value=2",
"2"))] = 2;
452 mPriority[mPriorityPopupMenu->addAction(
i18nc(
"@action:inmenu priority value=3",
"3"))] = 3;
453 mPriority[mPriorityPopupMenu->addAction(
i18nc(
"@action:inmenu priority value=4",
"4"))] = 4;
454 mPriority[mPriorityPopupMenu->addAction(
i18nc(
"@action:inmenu medium priority",
"5 (medium)"))] = 5;
455 mPriority[mPriorityPopupMenu->addAction(
i18nc(
"@action:inmenu priority value=6",
"6"))] = 6;
456 mPriority[mPriorityPopupMenu->addAction(
i18nc(
"@action:inmenu priority value=7",
"7"))] = 7;
457 mPriority[mPriorityPopupMenu->addAction(
i18nc(
"@action:inmenu priority value=8",
"8"))] = 8;
458 mPriority[mPriorityPopupMenu->addAction(
i18nc(
"@action:inmenu lowest priority",
"9 (lowest)"))] = 9;
461 mPercentageCompletedPopupMenu =
new QMenu(
this);
462 for (
int i = 0; i <= 100; i += 10) {
464 mPercentage[mPercentageCompletedPopupMenu->addAction(label)] = i;
468 setMinimumHeight(50);
481 sModels->unregisterView(
this);
482 if (sModels->views.isEmpty()) {
488void TodoView::expandIndex(
const QModelIndex &index)
490 QModelIndex todoModelIndex = sModels->todoModel->mapFromSource(index);
491 Q_ASSERT(todoModelIndex.
isValid());
492 const auto coloredIndex = sModels->coloredTodoModel->
mapFromSource(todoModelIndex);
493 Q_ASSERT(coloredIndex.isValid());
494 QModelIndex realIndex = mProxyModel->mapFromSource(coloredIndex);
497 mView->expand(realIndex);
498 realIndex = mProxyModel->
parent(realIndex);
502void TodoView::setModel(QAbstractItemModel *model)
504 EventView::setModel(model);
506 mCalendarFilterModel->setSourceModel(model);
512 EventView::addCalendar(calendar);
513 mCalendarFilterModel->addCalendar(calendar);
514 if (calendars().
size() == 1) {
515 mProxyModel->setCalFilter(calendar->filter());
521 mCalendarFilterModel->removeCalendar(calendar);
522 EventView::removeCalendar(calendar);
528 const QModelIndexList selection = mView->selectionModel()->selectedRows();
529 ret.
reserve(selection.count());
545void TodoView::saveLayout(
KConfig *config,
const QString &group)
const
550 QVariantList columnVisibility;
551 QVariantList columnOrder;
552 QVariantList columnWidths;
553 const int headerCount = header->
count();
554 columnVisibility.reserve(headerCount);
555 columnWidths.reserve(headerCount);
556 columnOrder.reserve(headerCount);
557 for (
int i = 0; i < headerCount; ++i) {
562 cfgGroup.
writeEntry(
"ColumnVisibility", columnVisibility);
563 cfgGroup.
writeEntry(
"ColumnOrder", columnOrder);
564 cfgGroup.
writeEntry(
"ColumnWidths", columnWidths);
574 preferences()->setFullViewTodo(mFullViewButton->isChecked());
576 preferences()->setFlatListTodo(mFlatViewButton->isChecked());
579void TodoView::restoreLayout(KConfig *config,
const QString &group,
bool minimalDefaults)
581 KConfigGroup cfgGroup = config->
group(group);
582 QHeaderView *header = mView->header();
584 QVariantList columnVisibility = cfgGroup.
readEntry(
"ColumnVisibility", QVariantList());
585 QVariantList columnOrder = cfgGroup.
readEntry(
"ColumnOrder", QVariantList());
586 QVariantList columnWidths = cfgGroup.
readEntry(
"ColumnWidths", QVariantList());
588 if (columnVisibility.isEmpty()) {
590 mView->hideColumn(Akonadi::TodoModel::RecurColumn);
591 mView->hideColumn(Akonadi::TodoModel::DescriptionColumn);
592 mView->hideColumn(Akonadi::TodoModel::CalendarColumn);
593 mView->hideColumn(Akonadi::TodoModel::CompletedDateColumn);
595 if (minimalDefaults) {
596 mView->hideColumn(Akonadi::TodoModel::PriorityColumn);
597 mView->hideColumn(Akonadi::TodoModel::PercentColumn);
598 mView->hideColumn(Akonadi::TodoModel::DescriptionColumn);
599 mView->hideColumn(Akonadi::TodoModel::CategoriesColumn);
605 for (
int i = 0; i < header->
count() && i < columnOrder.size() && i < columnWidths.size() && i < columnVisibility.size(); i++) {
606 bool visible = columnVisibility[i].toBool();
607 int width = columnWidths[i].toInt();
608 int order = columnOrder[i].toInt();
613 mView->hideColumn(i);
619 int sortColumn = cfgGroup.
readEntry(
"SortColumn", -1);
620 if (sortColumn >= 0) {
624 mFlatViewButton->setChecked(cfgGroup.
readEntry(
"FlatView",
false));
627void TodoView::setIncidenceChanger(Akonadi::IncidenceChanger *changer)
630 sModels->todoModel->setIncidenceChanger(changer);
633void TodoView::showDates(
const QDate &
start,
const QDate &end,
const QDate &)
642 Q_UNUSED(incidenceList)
646void TodoView::updateView()
648 if (calendars().empty()) {
652 auto calendar = calendars().first();
653 mProxyModel->setCalFilter(calendar->filter());
656void TodoView::changeIncidenceDisplay(
const Akonadi::Item &, Akonadi::IncidenceChanger::ChangeType)
661void TodoView::updateConfig()
663 Q_ASSERT(preferences());
664 if (!mSidebarView && mQuickSearch) {
665 mQuickSearch->setVisible(preferences()->enableTodoQuickSearch());
669 mQuickAdd->setVisible(preferences()->enableQuickTodo());
673 mProxyModel->invalidate();
679void TodoView::clearSelection()
681 mView->selectionModel()->clearSelection();
684void TodoView::addTodo(
const QString &summary,
const Akonadi::Item &parentItem,
const QStringList &categories)
686 const QString summaryTrimmed = summary.
trimmed();
687 if (!changer() || summaryTrimmed.
isEmpty()) {
694 todo->setSummary(summaryTrimmed);
695 todo->setOrganizer(Person(CalendarSupport::KCalPrefs::instance()->
fullName(), CalendarSupport::KCalPrefs::instance()->email()));
697 todo->setCategories(categories);
712 Akonadi::Collection collection;
719 changer()->createIncidence(todo, collection,
this);
726 addTodo(mQuickAdd->text(), Akonadi::Item(), mProxyModel->categories());
728 QModelIndexList selection = mView->selectionModel()->selectedRows();
729 if (selection.count() != 1) {
730 qCWarning(CALENDARVIEW_LOG) <<
"No to-do selected" << selection;
733 const QModelIndex idx = mProxyModel->mapToSource(selection[0]);
734 mView->expand(selection[0]);
736 addTodo(mQuickAdd->text(),
parent, mProxyModel->categories());
740 mQuickAdd->setText(QString());
743void TodoView::contextMenu(QPoint pos)
745 const bool hasItem = mView->indexAt(
pos).isValid();
748 for (QAction *entry : std::as_const(mItemPopupMenuItemOnlyEntries)) {
757 Akonadi::Item item = incidences.
first();
761 const bool actionIsRw = mItemPopupMenuReadWriteEntries.contains(entry);
766 enable = hasItem && (!actionIsRw || !incidenceIsRO);
772 entry->setEnabled(enable);
774 mCopyPopupMenu->setEnabled(hasItem);
775 mMovePopupMenu->setEnabled(hasItem);
779 const bool hasRecId = incidencePtr->hasRecurrenceId();
780 const bool hasSubtodos = mView->model()->hasChildren(mView->indexAt(
pos));
782 mMakeSubtodosIndependent->setEnabled(!hasRecId && hasSubtodos);
783 mMakeTodoIndependent->setEnabled(!hasRecId && !incidencePtr->relatedTo().isEmpty());
786 switch (mView->indexAt(
pos).column()) {
787 case Akonadi::TodoModel::PriorityColumn:
788 mPriorityPopupMenu->popup(mView->viewport()->mapToGlobal(
pos));
790 case Akonadi::TodoModel::PercentColumn:
791 mPercentageCompletedPopupMenu->popup(mView->viewport()->mapToGlobal(
pos));
793 case Akonadi::TodoModel::StartDateColumn:
794 mView->startPopupMenu()->popup(mView->viewport()->mapToGlobal(
pos));
796 case Akonadi::TodoModel::DueDateColumn:
797 mMovePopupMenu->popup(mView->viewport()->mapToGlobal(
pos));
799 case Akonadi::TodoModel::CategoriesColumn:
800 createCategoryPopupMenu()->popup(mView->viewport()->mapToGlobal(
pos));
803 mItemPopupMenu->popup(mView->viewport()->mapToGlobal(
pos));
807 mItemPopupMenu->popup(mView->viewport()->mapToGlobal(
pos));
811void TodoView::currentChanged(
const QModelIndex ¤t,
const QModelIndex &previous)
815 Q_EMIT incidenceSelected(Akonadi::Item(), QDate());
819 const auto todoItem = current.
data(Akonadi::TodoModel::TodoRole).
value<Akonadi::Item>();
822 Q_EMIT incidenceSelected(todoItem, QDate());
828void TodoView::showTodo()
830 QModelIndexList selection = mView->selectionModel()->selectedRows();
831 if (selection.size() != 1) {
835 const auto todoItem = selection[0].data(Akonadi::TodoModel::TodoRole).value<Akonadi::Item>();
840void TodoView::editTodo()
842 QModelIndexList selection = mView->selectionModel()->selectedRows();
843 if (selection.size() != 1) {
847 const auto todoItem = selection[0].data(Akonadi::TodoModel::TodoRole).value<Akonadi::Item>();
851void TodoView::deleteTodo()
853 QModelIndexList selection = mView->selectionModel()->selectedRows();
854 if (selection.size() == 1) {
855 const auto todoItem = selection[0].data(Akonadi::TodoModel::TodoRole).value<Akonadi::Item>();
857 if (!changer()->deletedRecently(todoItem.id())) {
863void TodoView::newTodo()
868void TodoView::newSubTodo()
870 QModelIndexList selection = mView->selectionModel()->selectedRows();
871 if (selection.size() == 1) {
872 const auto todoItem = selection[0].data(Akonadi::TodoModel::TodoRole).value<Akonadi::Item>();
874 Q_EMIT newSubTodoSignal(todoItem);
877 qCWarning(CALENDARVIEW_LOG) <<
"Selection size isn't 1";
881void TodoView::copyTodoToDate(QDate date)
887 QModelIndexList selection = mView->selectionModel()->selectedRows();
888 if (selection.size() != 1) {
892 const QModelIndex origIndex = mProxyModel->mapToSource(selection[0]);
906 QDateTime due =
todo->dtDue();
910 changer()->createIncidence(todo, Akonadi::Collection(),
this);
913void TodoView::scheduleResizeColumns()
915 mResizeColumnsScheduled =
true;
916 mResizeColumnsTimer->start();
919void TodoView::itemDoubleClicked(
const QModelIndex &index)
922 QModelIndex summary = index.
sibling(index.
row(), Akonadi::TodoModel::SummaryColumn);
931QMenu *TodoView::createCategoryPopupMenu()
933 auto tempMenu =
new QMenu(
this);
935 QModelIndexList selection = mView->selectionModel()->selectedRows();
936 if (selection.size() != 1) {
940 const auto todoItem = selection[0].data(Akonadi::TodoModel::TodoRole).value<Akonadi::Item>();
944 const QStringList checkedCategories =
todo->categories();
946 auto tagFetchJob =
new Akonadi::TagFetchJob(
this);
949 tagFetchJob->setProperty(
"checkedCategories", checkedCategories);
956void TodoView::onTagsFetched(KJob *job)
959 qCWarning(CALENDARVIEW_LOG) <<
"Failed to fetch tags " << job->
errorString();
962 auto fetchJob =
static_cast<Akonadi::TagFetchJob *
>(job);
966 const auto lst = fetchJob->tags();
967 for (
const Akonadi::Tag &tag : lst) {
968 const QString
name = tag.name();
969 QAction *action = menu->addAction(name);
972 if (checkedCategories.
contains(name)) {
979void TodoView::setNewDate(QDate date)
981 QModelIndexList selection = mView->selectionModel()->selectedRows();
982 if (selection.size() != 1) {
986 const auto todoItem = selection[0].data(Akonadi::TodoModel::TodoRole).value<Akonadi::Item>();
995 if (!
todo->allDay()) {
996 dt.setTime(
todo->dtDue().time());
999 if (
todo->hasStartDate() && dt < todo->dtStart()) {
1000 todo->setDtStart(dt);
1004 changer()->modifyIncidence(todoItem, oldTodo,
this);
1006 qCDebug(CALENDARVIEW_LOG) <<
"Item is readOnly";
1010void TodoView::setStartDate(QDate date)
1012 QModelIndexList selection = mView->selectionModel()->selectedRows();
1013 if (selection.size() != 1) {
1017 const auto todoItem = selection[0].data(Akonadi::TodoModel::TodoRole).value<Akonadi::Item>();
1026 if (!
todo->allDay()) {
1027 dt.setTime(
todo->dtStart().time());
1030 if (
todo->hasDueDate() && dt >
todo->dtDue()) {
1033 todo->setDtStart(dt);
1035 changer()->modifyIncidence(todoItem, oldTodo,
this);
1037 qCDebug(CALENDARVIEW_LOG) <<
"Item is readOnly";
1041void TodoView::setNewPercentage(QAction *action)
1043 QModelIndexList selection = mView->selectionModel()->selectedRows();
1044 if (selection.size() != 1) {
1048 const auto todoItem = selection[0].data(Akonadi::TodoModel::TodoRole).value<Akonadi::Item>();
1056 int percentage = mPercentage.value(action);
1057 if (percentage == 100) {
1059 todo->setPercentComplete(100);
1061 todo->setPercentComplete(percentage);
1063 changer()->modifyIncidence(todoItem, oldTodo,
this);
1065 qCDebug(CALENDARVIEW_LOG) <<
"Item is read only";
1069void TodoView::setNewPriority(QAction *action)
1071 const QModelIndexList selection = mView->selectionModel()->selectedRows();
1072 if (selection.size() != 1) {
1075 const auto todoItem = selection[0].data(Akonadi::TodoModel::TodoRole).value<Akonadi::Item>();
1080 todo->setPriority(mPriority[action]);
1082 changer()->modifyIncidence(todoItem, oldTodo,
this);
1086void TodoView::changedCategories(QAction *action)
1088 const QModelIndexList selection = mView->selectionModel()->selectedRows();
1089 if (selection.size() != 1) {
1093 const auto todoItem = selection[0].data(Akonadi::TodoModel::TodoRole).value<Akonadi::Item>();
1101 QStringList categories =
todo->categories();
1108 todo->setCategories(categories);
1109 changer()->modifyIncidence(todoItem, oldTodo,
this);
1111 qCDebug(CALENDARVIEW_LOG) <<
"No active item, active item is read-only, or locking failed";
1115void TodoView::setFullView(
bool fullView)
1117 if (!mFullViewButton) {
1121 mFullViewButton->setChecked(fullView);
1123 mFullViewButton->setIcon(
QIcon::fromTheme(QStringLiteral(
"view-restore")));
1125 mFullViewButton->setIcon(
QIcon::fromTheme(QStringLiteral(
"view-fullscreen")));
1128 mFullViewButton->blockSignals(
true);
1131 mFullViewButton->setChecked(fullView);
1132 mFullViewButton->blockSignals(
false);
1134 preferences()->setFullViewTodo(fullView);
1135 preferences()->writeConfig();
1137 Q_EMIT fullViewChanged(fullView);
1140void TodoView::setFlatView(
bool flatView,
bool notifyOtherViews)
1143 mFlatViewButton->setIcon(
QIcon::fromTheme(QStringLiteral(
"view-list-tree")));
1145 mFlatViewButton->setIcon(
QIcon::fromTheme(QStringLiteral(
"view-list-details")));
1148 if (notifyOtherViews) {
1149 sModels->setFlatView(flatView);
1153void TodoView::onRowsInserted(
const QModelIndex &parent,
int start,
int end)
1155 if (
start != end || !entityTreeModel()) {
1167 auto item = v.
value<Akonadi::Item>();
1172 const bool isPopulated = entityTreeModel()->isCollectionPopulated(item.
storageCollectionId());
1179 QModelIndexList selection = mView->selectionModel()->selectedRows();
1180 if (selection.size() <= 1) {
1183 int colCount =
static_cast<int>(Akonadi::TodoModel::ColumnCount);
1184 mView->selectionModel()->select(QItemSelection(idx, mView->model()->index(
start, colCount - 1)),
1191 if (sModels->isFlatView()) {
1195 QModelIndex index =
parent;
1196 mView->expand(index);
1198 mView->expand(index.
parent());
1205 highlightTodos = preferences()->highlightTodos();
1206 highlightEvents = !highlightTodos;
1207 highlightJournals =
false;
1210bool TodoView::usesFullWindow()
1212 return preferences()->fullViewTodo();
1215void TodoView::resizeColumns()
1217 mResizeColumnsScheduled =
false;
1229 const bool descriptionVisible = !mView->
isColumnHidden(Akonadi::TodoModel::DescriptionColumn);
1230 const bool categoriesVisible = !mView->
isColumnHidden(Akonadi::TodoModel::CategoriesColumn);
1234 for (
int i = 0; i < Akonadi::TodoModel::ColumnCount; ++i) {
1235 if (!mView->
isColumnHidden(i) && i != Akonadi::TodoModel::SummaryColumn && i != Akonadi::TodoModel::DescriptionColumn
1236 && i != Akonadi::TodoModel::CategoriesColumn) {
1242 int remainingSize = mView->header()->width() -
size;
1245 const int requiredSize = descriptionVisible ? 200 : 100;
1247 if (categoriesVisible) {
1248 const int categorySize = 100;
1249 mView->setColumnWidth(Akonadi::TodoModel::CategoriesColumn, categorySize);
1250 remainingSize -= categorySize;
1253 if (remainingSize < requiredSize) {
1255 mView->resizeColumnToContents(Akonadi::TodoModel::SummaryColumn);
1256 mView->resizeColumnToContents(Akonadi::TodoModel::DescriptionColumn);
1257 }
else if (descriptionVisible) {
1258 mView->setColumnWidth(Akonadi::TodoModel::SummaryColumn, remainingSize / 2);
1259 mView->setColumnWidth(Akonadi::TodoModel::DescriptionColumn, remainingSize / 2);
1261 mView->setColumnWidth(Akonadi::TodoModel::SummaryColumn, remainingSize);
1265void TodoView::restoreViewState()
1267 if (sModels->isFlatView()) {
1271 if (sModels->todoTreeModel && !sModels->todoTreeModel->
sourceModel()) {
1277 delete mTreeStateRestorer;
1278 mTreeStateRestorer =
new Akonadi::ETMViewStateSaver();
1280 KConfigGroup group(config, stateSaverGroup());
1281 mTreeStateRestorer->setView(mView);
1282 mTreeStateRestorer->restoreState(group);
1286QString TodoView::stateSaverGroup()
const
1288 QString str = QStringLiteral(
"TodoTreeViewState");
1290 str += QLatin1Char(
'S');
1296void TodoView::saveViewState()
1298 Akonadi::ETMViewStateSaver treeStateSaver;
1299 KConfigGroup group(preferences()->config(), stateSaverGroup());
1300 treeStateSaver.setView(mView);
1301 treeStateSaver.saveState(group);
1304void TodoView::resizeEvent(QResizeEvent *event)
1307 scheduleResizeColumns();
1310void TodoView::createEvent()
1312 const QModelIndexList selection = mView->selectionModel()->selectedRows();
1313 if (selection.size() != 1) {
1317 const auto todoItem = selection[0].data(Akonadi::TodoModel::TodoRole).value<Akonadi::Item>();
1319 Q_EMIT createEvent(todoItem);
1322#include "todoview.moc"
1324#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 xi18nc(const char *context, const char *text, const TYPE &arg...)
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(StandardAction id)
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)
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