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());
577 cfgGroup.
writeEntry(
"FlatView", 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,...
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