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 i18nc(
"@info",
"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 mQuickAdd->setToolTip(
i18nc(
"@info:tooltip",
"Create an open-ended to-do"));
307 mQuickAdd->setWhatsThis(
xi18nc(
"@info:whatsthis",
308 "Enter the summary for a new to-do. <p><note>The new to-do will be open-ended meaning that it has no start or end times nor "
309 "will it have a reminder or recurrence. Edit the newly created if you want to add more properties.</note></p>"));
310 connect(mQuickAdd, &TodoViewQuickAddLine::returnPressed,
this, &TodoView::addQuickTodo);
312 mFullViewButton =
nullptr;
315 mFullViewButton->setAutoRaise(
true);
316 mFullViewButton->setCheckable(
true);
318 mFullViewButton->setToolTip(
i18nc(
"@info:tooltip",
"Display to-do list in a full window"));
319 mFullViewButton->setWhatsThis(
i18nc(
"@info:whatsthis",
"Checking this option will cause the to-do view to use the full window."));
322 mFlatViewButton->setAutoRaise(
true);
323 mFlatViewButton->setCheckable(
true);
324 mFlatViewButton->setToolTip(
i18nc(
"@info:tooltip",
"Display to-dos in a flat list or a tree"));
325 mFlatViewButton->setWhatsThis(
i18nc(
"@info:whatsthis",
326 "Checking this button will cause the to-dos to be displayed either as a "
327 "flat list or a hierarchical tree where the parental "
328 "relationships are removed."));
331 setFlatView(flatView,
true);
333 if (mFullViewButton) {
338 layout->setContentsMargins({});
340 layout->addWidget(mQuickSearch, 0, 0, 1, 2);
342 layout->addWidget(mView, 1, 0, 1, 2);
343 layout->setRowStretch(1, 1);
344 layout->addWidget(mQuickAdd, 2, 0);
349 dummyLayout->setContentsMargins(0, 0, mView->frameWidth() , 0);
351 auto f =
new QFrame(
this);
354 dummyLayout->addWidget(f);
355 dummyLayout->addWidget(mFullViewButton);
357 dummyLayout->addWidget(mFlatViewButton);
359 layout->addLayout(dummyLayout, 2, 1);
362 mItemPopupMenu =
new QMenu(
this);
364 mItemPopupMenuItemOnlyEntries << mItemPopupMenu->addAction(
QIcon::fromTheme(QStringLiteral(
"document-preview")),
365 i18nc(
"@action:inmenu show the to-do",
"&Show"),
367 &TodoView::showTodo);
370 i18nc(
"@action:inmenu edit the to-do",
"&Edit…"),
372 &TodoView::editTodo);
373 mItemPopupMenuReadWriteEntries << a;
374 mItemPopupMenuItemOnlyEntries << a;
376 a = mItemPopupMenu->addAction(
QIcon::fromTheme(QStringLiteral(
"edit-delete")),
377 i18nc(
"@action:inmenu delete the to-do",
"&Delete"),
379 &TodoView::deleteTodo);
380 mItemPopupMenuReadWriteEntries << a;
381 mItemPopupMenuItemOnlyEntries << a;
383 mItemPopupMenu->addSeparator();
385 mItemPopupMenuItemOnlyEntries << mItemPopupMenu->addAction(
QIcon::fromTheme(QStringLiteral(
"document-print")),
386 i18nc(
"@action:inmenu print the to-do",
"&Print…"),
388 &TodoView::printTodo);
390 mItemPopupMenuItemOnlyEntries << mItemPopupMenu->addAction(
QIcon::fromTheme(QStringLiteral(
"document-print-preview")),
391 i18nc(
"@action:inmenu print preview the to-do",
"Print Previe&w…"),
393 &TodoView::printPreviewTodo);
395 mItemPopupMenu->addSeparator();
397 mItemPopupMenu->addAction(
QIcon::fromTheme(QStringLiteral(
"view-calendar-tasks")),
398 i18nc(
"@action:inmenu create a new to-do",
"New &To-do…"),
402 a = mItemPopupMenu->addAction(
i18nc(
"@action:inmenu create a new sub-to-do",
"New Su&b-to-do…"),
this, &TodoView::newSubTodo);
403 mItemPopupMenuReadWriteEntries << a;
404 mItemPopupMenuItemOnlyEntries << a;
406 mMakeTodoIndependent = mItemPopupMenu->addAction(
i18nc(
"@action:inmenu",
"&Make this To-do Independent"),
this, &TodoView::unSubTodoSignal);
408 mMakeSubtodosIndependent = mItemPopupMenu->addAction(
i18nc(
"@action:inmenu",
"Make all Sub-to-dos &Independent"),
this, &TodoView::unAllSubTodoSignal);
410 mItemPopupMenuItemOnlyEntries << mMakeTodoIndependent;
411 mItemPopupMenuItemOnlyEntries << mMakeSubtodosIndependent;
413 mItemPopupMenuReadWriteEntries << mMakeTodoIndependent;
414 mItemPopupMenuReadWriteEntries << mMakeSubtodosIndependent;
416 mItemPopupMenu->addSeparator();
418 a = mItemPopupMenu->addAction(
QIcon::fromTheme(QStringLiteral(
"appointment-new")),
419 i18nc(
"@action:inmenu",
"Create Event from To-do"),
421 qOverload<>(&TodoView::createEvent));
423 mItemPopupMenuReadWriteEntries << a;
424 mItemPopupMenuItemOnlyEntries << a;
426 mItemPopupMenu->addSeparator();
429 mCopyPopupMenu->setTitle(
i18nc(
"@title:menu",
"&Copy To"));
436 mMovePopupMenu->setTitle(
i18nc(
"@title:menu",
"&Move To"));
443 mItemPopupMenu->insertMenu(
nullptr, mCopyPopupMenu);
444 mItemPopupMenu->insertMenu(
nullptr, mMovePopupMenu);
446 mItemPopupMenu->addSeparator();
447 mItemPopupMenu->addAction(
i18nc(
"@action:inmenu delete completed to-dos",
"Pur&ge Completed"),
this, &TodoView::purgeCompletedSignal);
449 mPriorityPopupMenu =
new QMenu(
this);
450 mPriority[mPriorityPopupMenu->addAction(
i18nc(
"@action:inmenu unspecified priority",
"unspecified"))] = 0;
451 mPriority[mPriorityPopupMenu->addAction(
i18nc(
"@action:inmenu highest priority",
"1 (highest)"))] = 1;
452 mPriority[mPriorityPopupMenu->addAction(
i18nc(
"@action:inmenu priority value=2",
"2"))] = 2;
453 mPriority[mPriorityPopupMenu->addAction(
i18nc(
"@action:inmenu priority value=3",
"3"))] = 3;
454 mPriority[mPriorityPopupMenu->addAction(
i18nc(
"@action:inmenu priority value=4",
"4"))] = 4;
455 mPriority[mPriorityPopupMenu->addAction(
i18nc(
"@action:inmenu medium priority",
"5 (medium)"))] = 5;
456 mPriority[mPriorityPopupMenu->addAction(
i18nc(
"@action:inmenu priority value=6",
"6"))] = 6;
457 mPriority[mPriorityPopupMenu->addAction(
i18nc(
"@action:inmenu priority value=7",
"7"))] = 7;
458 mPriority[mPriorityPopupMenu->addAction(
i18nc(
"@action:inmenu priority value=8",
"8"))] = 8;
459 mPriority[mPriorityPopupMenu->addAction(
i18nc(
"@action:inmenu lowest priority",
"9 (lowest)"))] = 9;
462 mPercentageCompletedPopupMenu =
new QMenu(
this);
463 for (
int i = 0; i <= 100; i += 10) {
465 mPercentage[mPercentageCompletedPopupMenu->addAction(label)] = i;
469 setMinimumHeight(50);
482 sModels->unregisterView(
this);
483 if (sModels->views.isEmpty()) {
489void TodoView::expandIndex(
const QModelIndex &index)
491 QModelIndex todoModelIndex = sModels->todoModel->
mapFromSource(index);
492 Q_ASSERT(todoModelIndex.
isValid());
493 const auto coloredIndex = sModels->coloredTodoModel->
mapFromSource(todoModelIndex);
494 Q_ASSERT(coloredIndex.isValid());
495 QModelIndex realIndex = mProxyModel->mapFromSource(coloredIndex);
498 mView->expand(realIndex);
499 realIndex = mProxyModel->
parent(realIndex);
503void TodoView::setModel(QAbstractItemModel *model)
505 EventView::setModel(model);
507 mCalendarFilterModel->setSourceModel(model);
513 EventView::addCalendar(calendar);
514 mCalendarFilterModel->addCalendar(calendar);
515 if (calendars().
size() == 1) {
516 mProxyModel->setCalFilter(calendar->filter());
522 mCalendarFilterModel->removeCalendar(calendar);
523 EventView::removeCalendar(calendar);
529 const QModelIndexList selection = mView->selectionModel()->selectedRows();
530 ret.
reserve(selection.count());
546void TodoView::saveLayout(
KConfig *config,
const QString &group)
const
551 QVariantList columnVisibility;
552 QVariantList columnOrder;
553 QVariantList columnWidths;
554 const int headerCount = header->
count();
555 columnVisibility.reserve(headerCount);
556 columnWidths.reserve(headerCount);
557 columnOrder.reserve(headerCount);
558 for (
int i = 0; i < headerCount; ++i) {
563 cfgGroup.
writeEntry(
"ColumnVisibility", columnVisibility);
564 cfgGroup.
writeEntry(
"ColumnOrder", columnOrder);
565 cfgGroup.
writeEntry(
"ColumnWidths", columnWidths);
575 preferences()->setFullViewTodo(mFullViewButton->isChecked());
577 preferences()->setFlatListTodo(mFlatViewButton->isChecked());
580void TodoView::restoreLayout(KConfig *config,
const QString &group,
bool minimalDefaults)
582 KConfigGroup cfgGroup = config->
group(group);
583 QHeaderView *header = mView->header();
585 QVariantList columnVisibility = cfgGroup.
readEntry(
"ColumnVisibility", QVariantList());
586 QVariantList columnOrder = cfgGroup.
readEntry(
"ColumnOrder", QVariantList());
587 QVariantList columnWidths = cfgGroup.
readEntry(
"ColumnWidths", QVariantList());
589 if (columnVisibility.isEmpty()) {
591 mView->hideColumn(Akonadi::TodoModel::RecurColumn);
592 mView->hideColumn(Akonadi::TodoModel::DescriptionColumn);
593 mView->hideColumn(Akonadi::TodoModel::CalendarColumn);
594 mView->hideColumn(Akonadi::TodoModel::CompletedDateColumn);
596 if (minimalDefaults) {
597 mView->hideColumn(Akonadi::TodoModel::PriorityColumn);
598 mView->hideColumn(Akonadi::TodoModel::PercentColumn);
599 mView->hideColumn(Akonadi::TodoModel::DescriptionColumn);
600 mView->hideColumn(Akonadi::TodoModel::CategoriesColumn);
606 for (
int i = 0; i < header->
count() && i < columnOrder.size() && i < columnWidths.size() && i < columnVisibility.size(); i++) {
607 bool visible = columnVisibility[i].toBool();
608 int width = columnWidths[i].toInt();
609 int order = columnOrder[i].toInt();
614 mView->hideColumn(i);
620 int sortColumn = cfgGroup.
readEntry(
"SortColumn", -1);
621 if (sortColumn >= 0) {
625 mFlatViewButton->setChecked(cfgGroup.
readEntry(
"FlatView",
false));
628void TodoView::setIncidenceChanger(Akonadi::IncidenceChanger *changer)
631 sModels->todoModel->setIncidenceChanger(changer);
634void TodoView::showDates(
const QDate &
start,
const QDate &end,
const QDate &)
643 Q_UNUSED(incidenceList)
647void TodoView::updateView()
649 if (calendars().empty()) {
653 auto calendar = calendars().first();
654 mProxyModel->setCalFilter(calendar->filter());
657void TodoView::changeIncidenceDisplay(
const Akonadi::Item &, Akonadi::IncidenceChanger::ChangeType)
662void TodoView::updateConfig()
664 Q_ASSERT(preferences());
665 if (!mSidebarView && mQuickSearch) {
666 mQuickSearch->setVisible(preferences()->enableTodoQuickSearch());
670 mQuickAdd->setVisible(preferences()->enableQuickTodo());
674 mProxyModel->invalidate();
680void TodoView::clearSelection()
682 mView->selectionModel()->clearSelection();
685void TodoView::addTodo(
const QString &summary,
const Akonadi::Item &parentItem,
const QStringList &categories)
687 const QString summaryTrimmed = summary.
trimmed();
688 if (!changer() || summaryTrimmed.
isEmpty()) {
695 todo->setSummary(summaryTrimmed);
696 todo->setOrganizer(Person(CalendarSupport::KCalPrefs::instance()->
fullName(), CalendarSupport::KCalPrefs::instance()->email()));
698 todo->setCategories(categories);
713 Akonadi::Collection collection;
720 changer()->createIncidence(todo, collection,
this);
727 addTodo(mQuickAdd->text(), Akonadi::Item(), mProxyModel->categories());
729 QModelIndexList selection = mView->selectionModel()->selectedRows();
730 if (selection.count() != 1) {
731 qCWarning(CALENDARVIEW_LOG) <<
"No to-do selected" << selection;
734 const QModelIndex idx = mProxyModel->mapToSource(selection[0]);
735 mView->expand(selection[0]);
737 addTodo(mQuickAdd->text(),
parent, mProxyModel->categories());
741 mQuickAdd->setText(QString());
744void TodoView::contextMenu(QPoint pos)
746 const bool hasItem = mView->indexAt(
pos).isValid();
749 for (QAction *entry : std::as_const(mItemPopupMenuItemOnlyEntries)) {
758 Akonadi::Item item = incidences.
first();
762 const bool actionIsRw = mItemPopupMenuReadWriteEntries.contains(entry);
767 enable = hasItem && (!actionIsRw || !incidenceIsRO);
773 entry->setEnabled(enable);
775 mCopyPopupMenu->setEnabled(hasItem);
776 mMovePopupMenu->setEnabled(hasItem);
780 const bool hasRecId = incidencePtr->hasRecurrenceId();
781 const bool hasSubtodos = mView->model()->hasChildren(mView->indexAt(
pos));
783 mMakeSubtodosIndependent->setEnabled(!hasRecId && hasSubtodos);
784 mMakeTodoIndependent->setEnabled(!hasRecId && !incidencePtr->relatedTo().isEmpty());
787 switch (mView->indexAt(
pos).column()) {
788 case Akonadi::TodoModel::PriorityColumn:
789 mPriorityPopupMenu->popup(mView->viewport()->mapToGlobal(
pos));
791 case Akonadi::TodoModel::PercentColumn:
792 mPercentageCompletedPopupMenu->popup(mView->viewport()->mapToGlobal(
pos));
794 case Akonadi::TodoModel::StartDateColumn:
795 mView->startPopupMenu()->popup(mView->viewport()->mapToGlobal(
pos));
797 case Akonadi::TodoModel::DueDateColumn:
798 mMovePopupMenu->popup(mView->viewport()->mapToGlobal(
pos));
800 case Akonadi::TodoModel::CategoriesColumn:
801 createCategoryPopupMenu()->popup(mView->viewport()->mapToGlobal(
pos));
804 mItemPopupMenu->popup(mView->viewport()->mapToGlobal(
pos));
808 mItemPopupMenu->popup(mView->viewport()->mapToGlobal(
pos));
812void TodoView::currentChanged(
const QModelIndex ¤t,
const QModelIndex &previous)
816 Q_EMIT incidenceSelected(Akonadi::Item(), QDate());
820 const auto todoItem = current.
data(Akonadi::TodoModel::TodoRole).
value<Akonadi::Item>();
823 Q_EMIT incidenceSelected(todoItem, QDate());
829void TodoView::showTodo()
831 QModelIndexList selection = mView->selectionModel()->selectedRows();
832 if (selection.size() != 1) {
836 const auto todoItem = selection[0].data(Akonadi::TodoModel::TodoRole).value<Akonadi::Item>();
841void TodoView::editTodo()
843 QModelIndexList selection = mView->selectionModel()->selectedRows();
844 if (selection.size() != 1) {
848 const auto todoItem = selection[0].data(Akonadi::TodoModel::TodoRole).value<Akonadi::Item>();
852void TodoView::deleteTodo()
854 QModelIndexList selection = mView->selectionModel()->selectedRows();
855 if (selection.size() == 1) {
856 const auto todoItem = selection[0].data(Akonadi::TodoModel::TodoRole).value<Akonadi::Item>();
858 if (!changer()->deletedRecently(todoItem.id())) {
864void TodoView::newTodo()
869void TodoView::newSubTodo()
871 QModelIndexList selection = mView->selectionModel()->selectedRows();
872 if (selection.size() == 1) {
873 const auto todoItem = selection[0].data(Akonadi::TodoModel::TodoRole).value<Akonadi::Item>();
875 Q_EMIT newSubTodoSignal(todoItem);
878 qCWarning(CALENDARVIEW_LOG) <<
"Selection size isn't 1";
882void TodoView::copyTodoToDate(QDate date)
888 QModelIndexList selection = mView->selectionModel()->selectedRows();
889 if (selection.size() != 1) {
893 const QModelIndex origIndex = mProxyModel->mapToSource(selection[0]);
907 QDateTime due =
todo->dtDue();
911 changer()->createIncidence(todo, Akonadi::Collection(),
this);
914void TodoView::scheduleResizeColumns()
916 mResizeColumnsScheduled =
true;
917 mResizeColumnsTimer->start();
920void TodoView::itemDoubleClicked(
const QModelIndex &index)
923 QModelIndex summary = index.
sibling(index.
row(), Akonadi::TodoModel::SummaryColumn);
932QMenu *TodoView::createCategoryPopupMenu()
934 auto tempMenu =
new QMenu(
this);
936 QModelIndexList selection = mView->selectionModel()->selectedRows();
937 if (selection.size() != 1) {
941 const auto todoItem = selection[0].data(Akonadi::TodoModel::TodoRole).value<Akonadi::Item>();
945 const QStringList checkedCategories =
todo->categories();
947 auto tagFetchJob =
new Akonadi::TagFetchJob(
this);
950 tagFetchJob->setProperty(
"checkedCategories", checkedCategories);
957void TodoView::onTagsFetched(KJob *job)
960 qCWarning(CALENDARVIEW_LOG) <<
"Failed to fetch tags " << job->
errorString();
963 auto fetchJob =
static_cast<Akonadi::TagFetchJob *
>(job);
967 const auto lst = fetchJob->tags();
968 for (
const Akonadi::Tag &tag : lst) {
969 const QString
name = tag.name();
970 QAction *action = menu->addAction(name);
973 if (checkedCategories.
contains(name)) {
980void TodoView::setNewDate(QDate date)
982 QModelIndexList selection = mView->selectionModel()->selectedRows();
983 if (selection.size() != 1) {
987 const auto todoItem = selection[0].data(Akonadi::TodoModel::TodoRole).value<Akonadi::Item>();
996 if (!
todo->allDay()) {
997 dt.setTime(
todo->dtDue().time());
1000 if (
todo->hasStartDate() && dt < todo->dtStart()) {
1001 todo->setDtStart(dt);
1005 changer()->modifyIncidence(todoItem, oldTodo,
this);
1007 qCDebug(CALENDARVIEW_LOG) <<
"Item is readOnly";
1011void TodoView::setStartDate(QDate date)
1013 QModelIndexList selection = mView->selectionModel()->selectedRows();
1014 if (selection.size() != 1) {
1018 const auto todoItem = selection[0].data(Akonadi::TodoModel::TodoRole).value<Akonadi::Item>();
1027 if (!
todo->allDay()) {
1028 dt.setTime(
todo->dtStart().time());
1031 if (
todo->hasDueDate() && dt >
todo->dtDue()) {
1034 todo->setDtStart(dt);
1036 changer()->modifyIncidence(todoItem, oldTodo,
this);
1038 qCDebug(CALENDARVIEW_LOG) <<
"Item is readOnly";
1042void TodoView::setNewPercentage(QAction *action)
1044 QModelIndexList selection = mView->selectionModel()->selectedRows();
1045 if (selection.size() != 1) {
1049 const auto todoItem = selection[0].data(Akonadi::TodoModel::TodoRole).value<Akonadi::Item>();
1057 int percentage = mPercentage.value(action);
1058 if (percentage == 100) {
1060 todo->setPercentComplete(100);
1062 todo->setPercentComplete(percentage);
1064 changer()->modifyIncidence(todoItem, oldTodo,
this);
1066 qCDebug(CALENDARVIEW_LOG) <<
"Item is read only";
1070void TodoView::setNewPriority(QAction *action)
1072 const QModelIndexList selection = mView->selectionModel()->selectedRows();
1073 if (selection.size() != 1) {
1076 const auto todoItem = selection[0].data(Akonadi::TodoModel::TodoRole).value<Akonadi::Item>();
1081 todo->setPriority(mPriority[action]);
1083 changer()->modifyIncidence(todoItem, oldTodo,
this);
1087void TodoView::changedCategories(QAction *action)
1089 const QModelIndexList selection = mView->selectionModel()->selectedRows();
1090 if (selection.size() != 1) {
1094 const auto todoItem = selection[0].data(Akonadi::TodoModel::TodoRole).value<Akonadi::Item>();
1102 QStringList categories =
todo->categories();
1109 todo->setCategories(categories);
1110 changer()->modifyIncidence(todoItem, oldTodo,
this);
1112 qCDebug(CALENDARVIEW_LOG) <<
"No active item, active item is read-only, or locking failed";
1116void TodoView::setFullView(
bool fullView)
1118 if (!mFullViewButton) {
1122 mFullViewButton->setChecked(fullView);
1124 mFullViewButton->setIcon(
QIcon::fromTheme(QStringLiteral(
"view-restore")));
1126 mFullViewButton->setIcon(
QIcon::fromTheme(QStringLiteral(
"view-fullscreen")));
1129 mFullViewButton->blockSignals(
true);
1132 mFullViewButton->setChecked(fullView);
1133 mFullViewButton->blockSignals(
false);
1135 preferences()->setFullViewTodo(fullView);
1136 preferences()->writeConfig();
1138 Q_EMIT fullViewChanged(fullView);
1141void TodoView::setFlatView(
bool flatView,
bool notifyOtherViews)
1144 mFlatViewButton->setIcon(
QIcon::fromTheme(QStringLiteral(
"view-list-tree")));
1146 mFlatViewButton->setIcon(
QIcon::fromTheme(QStringLiteral(
"view-list-details")));
1149 if (notifyOtherViews) {
1150 sModels->setFlatView(flatView);
1154void TodoView::onRowsInserted(
const QModelIndex &parent,
int start,
int end)
1156 if (
start != end || !entityTreeModel()) {
1168 auto item = v.
value<Akonadi::Item>();
1173 const bool isPopulated = entityTreeModel()->isCollectionPopulated(item.
storageCollectionId());
1180 QModelIndexList selection = mView->selectionModel()->selectedRows();
1181 if (selection.size() <= 1) {
1184 int colCount =
static_cast<int>(Akonadi::TodoModel::ColumnCount);
1185 mView->selectionModel()->select(QItemSelection(idx, mView->model()->index(
start, colCount - 1)),
1192 if (sModels->isFlatView()) {
1196 QModelIndex index =
parent;
1197 mView->expand(index);
1199 mView->expand(index.
parent());
1206 highlightTodos = preferences()->highlightTodos();
1207 highlightEvents = !highlightTodos;
1208 highlightJournals =
false;
1211bool TodoView::usesFullWindow()
1213 return preferences()->fullViewTodo();
1216void TodoView::resizeColumns()
1218 mResizeColumnsScheduled =
false;
1230 const bool descriptionVisible = !mView->
isColumnHidden(Akonadi::TodoModel::DescriptionColumn);
1231 const bool categoriesVisible = !mView->
isColumnHidden(Akonadi::TodoModel::CategoriesColumn);
1235 for (
int i = 0; i < Akonadi::TodoModel::ColumnCount; ++i) {
1236 if (!mView->
isColumnHidden(i) && i != Akonadi::TodoModel::SummaryColumn && i != Akonadi::TodoModel::DescriptionColumn
1237 && i != Akonadi::TodoModel::CategoriesColumn) {
1243 int remainingSize = mView->header()->width() -
size;
1246 const int requiredSize = descriptionVisible ? 200 : 100;
1248 if (categoriesVisible) {
1249 const int categorySize = 100;
1250 mView->setColumnWidth(Akonadi::TodoModel::CategoriesColumn, categorySize);
1251 remainingSize -= categorySize;
1254 if (remainingSize < requiredSize) {
1256 mView->resizeColumnToContents(Akonadi::TodoModel::SummaryColumn);
1257 mView->resizeColumnToContents(Akonadi::TodoModel::DescriptionColumn);
1258 }
else if (descriptionVisible) {
1259 mView->setColumnWidth(Akonadi::TodoModel::SummaryColumn, remainingSize / 2);
1260 mView->setColumnWidth(Akonadi::TodoModel::DescriptionColumn, remainingSize / 2);
1262 mView->setColumnWidth(Akonadi::TodoModel::SummaryColumn, remainingSize);
1266void TodoView::restoreViewState()
1268 if (sModels->isFlatView()) {
1272 if (sModels->todoTreeModel && !sModels->todoTreeModel->
sourceModel()) {
1278 delete mTreeStateRestorer;
1279 mTreeStateRestorer =
new Akonadi::ETMViewStateSaver();
1281 KConfigGroup group(config, stateSaverGroup());
1282 mTreeStateRestorer->setView(mView);
1283 mTreeStateRestorer->restoreState(group);
1287QString TodoView::stateSaverGroup()
const
1289 QString str = QStringLiteral(
"TodoTreeViewState");
1291 str += QLatin1Char(
'S');
1297void TodoView::saveViewState()
1299 Akonadi::ETMViewStateSaver treeStateSaver;
1300 KConfigGroup group(preferences()->config(), stateSaverGroup());
1301 treeStateSaver.setView(mView);
1302 treeStateSaver.saveState(group);
1305void TodoView::resizeEvent(QResizeEvent *event)
1308 scheduleResizeColumns();
1311void TodoView::createEvent()
1313 const QModelIndexList selection = mView->selectionModel()->selectedRows();
1314 if (selection.size() != 1) {
1318 const auto todoItem = selection[0].data(Akonadi::TodoModel::TodoRole).value<Akonadi::Item>();
1320 Q_EMIT createEvent(todoItem);
1323#include "todoview.moc"
1325#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,...
QAction * preferences(const QObject *recvr, const char *slot, QObject *parent)
QString name(StandardAction id)
QString label(StandardShortcut id)
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