File indexing completed on 2025-03-09 04:45:09

0001 /*
0002   SPDX-FileCopyrightText: 1999 Preston Brown <pbrown@kde.org>
0003   SPDX-FileCopyrightText: 2000, 2001 Cornelius Schumacher <schumacher@kde.org>
0004   SPDX-FileCopyrightText: 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
0005   SPDX-FileCopyrightText: 2010 Sérgio Martins <iamsergio@gmail.com>
0006   SPDX-FileCopyrightText: 2012-2013 Allen Winter <winter@kde.org>
0007 
0008   SPDX-License-Identifier: GPL-2.0-or-later WITH Qt-Commercial-exception-1.0
0009 */
0010 
0011 // TODO: put a reminder and/or recurs icon on the item?
0012 
0013 #include "listview.h"
0014 #include "helper.h"
0015 
0016 #include <CalendarSupport/Utils>
0017 
0018 #include <Akonadi/CalendarUtils>
0019 #include <Akonadi/IncidenceChanger>
0020 
0021 #include <KCalUtils/IncidenceFormatter>
0022 #include <KCalendarCore/Visitor>
0023 
0024 #include <KConfig>
0025 #include <KConfigGroup>
0026 
0027 #include "calendarview_debug.h"
0028 #include <QBoxLayout>
0029 #include <QHeaderView>
0030 #include <QIcon>
0031 #include <QLocale>
0032 #include <QTreeWidget>
0033 
0034 using namespace EventViews;
0035 using namespace KCalendarCore;
0036 using namespace KCalUtils;
0037 
0038 enum {
0039     Summary_Column = 0,
0040     StartDateTime_Column,
0041     EndDateTime_Column,
0042     Categories_Column,
0043     Dummy_EOF_Column // Dummy enum value for iteration purposes only. Always keep at the end.
0044 };
0045 
0046 static const QTime DAY_START{00, 00};
0047 static const QTime DAY_END{23, 59, 59, 999};
0048 
0049 static QString cleanSummary(const QString &summary, const QDateTime &next)
0050 {
0051     QString retStr = summary;
0052     retStr.replace(QLatin1Char('\n'), QLatin1Char(' '));
0053 
0054     if (next.isValid()) {
0055         const QString dateStr = QLocale().toString(next.date(), QLocale::ShortFormat);
0056         retStr = i18nc("%1 is an item summary. %2 is the date when this item reoccurs", "%1 (next: %2)", retStr, dateStr);
0057     }
0058     return retStr;
0059 }
0060 
0061 class ListViewItem : public QTreeWidgetItem
0062 {
0063 public:
0064     ListViewItem(const Akonadi::Item &incidence, const Akonadi::CollectionCalendar::Ptr &calendar, QTreeWidget *parent)
0065         : QTreeWidgetItem(parent)
0066         , mTreeWidget(parent)
0067         , mIncidence(incidence)
0068         , mCalendar(calendar)
0069     {
0070     }
0071 
0072     QVariant data(int column, int role) const override
0073     {
0074         if (role == Qt::ToolTipRole) {
0075             const auto incidence = mIncidence.payload<KCalendarCore::Incidence::Ptr>();
0076             return IncidenceFormatter::toolTipStr(Akonadi::CalendarUtils::displayName(mCalendar->model(), mIncidence.parentCollection()),
0077                                                   incidence,
0078                                                   start.date());
0079         }
0080 
0081         return QTreeWidgetItem::data(column, role);
0082     }
0083 
0084     bool operator<(const QTreeWidgetItem &other) const override;
0085 
0086     QTreeWidget *const mTreeWidget;
0087     const Akonadi::Item mIncidence;
0088     QDateTime start;
0089     QDateTime end;
0090     Akonadi::CollectionCalendar::Ptr mCalendar;
0091 };
0092 
0093 bool ListViewItem::operator<(const QTreeWidgetItem &other) const
0094 {
0095     const auto otheritem = static_cast<const ListViewItem *>(&other);
0096 
0097     switch (treeWidget()->sortColumn()) {
0098     case StartDateTime_Column:
0099         // Missing start times are sorted before any defined time.
0100         return otheritem->start.isValid() && (!start.isValid() || start < otheritem->start);
0101     case EndDateTime_Column:
0102         // Missing end times are sorted after any defined time.
0103         return end.isValid() && (!otheritem->end.isValid() || end < otheritem->end);
0104     default:
0105         return QTreeWidgetItem::operator<(other);
0106     }
0107 }
0108 
0109 class EventViews::ListViewPrivate
0110 {
0111 public:
0112     ListViewPrivate() = default;
0113 
0114     ~ListViewPrivate() = default;
0115 
0116     void addIncidences(const Akonadi::CollectionCalendar::Ptr &calendar, const KCalendarCore::Incidence::List &incidenceList, QDate date);
0117     void addIncidence(const Akonadi::CollectionCalendar::Ptr &calendar, const KCalendarCore::Incidence::Ptr &, QDate date);
0118     void addIncidence(const Akonadi::CollectionCalendar::Ptr &calendar, const Akonadi::Item &, QDate date);
0119     ListViewItem *getItemForIncidence(const Akonadi::Item &);
0120 
0121     QTreeWidget *mTreeWidget = nullptr;
0122     ListViewItem *mActiveItem = nullptr;
0123     QHash<Akonadi::Item::Id, Akonadi::Item> mItems;
0124     QHash<Akonadi::Item::Id, QDate> mDateList;
0125     QDate mStartDate;
0126     QDate mEndDate;
0127     DateList mSelectedDates;
0128     int mSortColumn = StartDateTime_Column;
0129     Qt::SortOrder mSortOrder = Qt::AscendingOrder;
0130 
0131     // if it's non interactive we disable context menu, and incidence editing
0132     bool mIsNonInteractive;
0133     class ListItemVisitor;
0134 };
0135 
0136 /**
0137   This class provides the initialization of a ListViewItem for calendar
0138   components using the Incidence::Visitor.
0139 */
0140 class ListViewPrivate::ListItemVisitor : public KCalendarCore::Visitor
0141 {
0142 public:
0143     ListItemVisitor(ListViewItem *item, QDate dt)
0144         : mItem(item)
0145         , mStartDate(dt)
0146     {
0147     }
0148 
0149     ~ListItemVisitor() override = default;
0150 
0151     bool visit(const Event::Ptr &) override;
0152     bool visit(const Todo::Ptr &) override;
0153     bool visit(const Journal::Ptr &) override;
0154     bool visit(const FreeBusy::Ptr &) override
0155     {
0156         // to inhibit hidden virtual compile warning
0157         return true;
0158     }
0159 
0160 private:
0161     ListViewItem *mItem = nullptr;
0162     QDate mStartDate;
0163 };
0164 
0165 bool ListViewPrivate::ListItemVisitor::visit(const Event::Ptr &e)
0166 {
0167     QIcon eventPxmp;
0168     if (e->customProperty("KABC", "ANNIVERSARY") == QLatin1StringView("YES")) {
0169         eventPxmp = QIcon::fromTheme(QStringLiteral("view-calendar-wedding-anniversary"));
0170     } else if (e->customProperty("KABC", "BIRTHDAY") == QLatin1StringView("YES")) {
0171         eventPxmp = QIcon::fromTheme(QStringLiteral("view-calendar-birthday"));
0172     } else {
0173         eventPxmp = QIcon::fromTheme(e->iconName());
0174     }
0175     mItem->setIcon(Summary_Column, eventPxmp);
0176 
0177     QDateTime next;
0178     if (e->recurs()) {
0179         const qint64 duration = e->dtStart().secsTo(e->dtEnd());
0180         QDateTime kdt(mStartDate, DAY_START);
0181         kdt = kdt.addMSecs(-1);
0182         mItem->start = e->recurrence()->getNextDateTime(kdt).toLocalTime();
0183         mItem->end = mItem->start.addSecs(duration);
0184         next = e->recurrence()->getNextDateTime(mItem->start).toLocalTime();
0185     } else {
0186         mItem->start = e->dtStart().toLocalTime();
0187         mItem->end = e->dtEnd().toLocalTime();
0188     }
0189 
0190     mItem->setText(Summary_Column, cleanSummary(e->summary(), next));
0191 
0192     if (e->allDay()) {
0193         mItem->start.setTime(DAY_START);
0194         mItem->end.setTime(DAY_END);
0195         mItem->setText(StartDateTime_Column, QLocale().toString(mItem->start.date(), QLocale::ShortFormat));
0196         mItem->setText(EndDateTime_Column, QLocale().toString(mItem->end.date(), QLocale::ShortFormat));
0197     } else {
0198         mItem->setText(StartDateTime_Column, QLocale().toString(mItem->start, QLocale::ShortFormat));
0199         mItem->setText(EndDateTime_Column, QLocale().toString(mItem->end, QLocale::ShortFormat));
0200     }
0201 
0202     mItem->setText(Categories_Column, e->categoriesStr());
0203 
0204     return true;
0205 }
0206 
0207 bool ListViewPrivate::ListItemVisitor::visit(const Todo::Ptr &t)
0208 {
0209     mItem->setIcon(Summary_Column, QIcon::fromTheme(t->iconName()));
0210 
0211     if (t->recurs()) {
0212         QDateTime kdt(mStartDate, DAY_START);
0213         kdt = kdt.addMSecs(-1);
0214         mItem->start = t->recurrence()->getNextDateTime(kdt).toLocalTime();
0215         if (t->hasDueDate()) {
0216             const qint64 duration = t->dtStart().secsTo(t->dtDue());
0217             mItem->end = mItem->start.addSecs(duration);
0218         } else {
0219             mItem->end = QDateTime();
0220         }
0221     } else {
0222         mItem->start = t->hasStartDate() ? t->dtStart().toLocalTime() : QDateTime();
0223         mItem->end = t->hasDueDate() ? t->dtDue().toLocalTime() : QDateTime();
0224     }
0225     if (t->allDay()) {
0226         mItem->start.setTime(DAY_START);
0227         mItem->end.setTime(DAY_END);
0228     }
0229 
0230     mItem->setText(Summary_Column, cleanSummary(t->summary(), QDateTime()));
0231 
0232     if (t->hasStartDate()) {
0233         if (t->allDay()) {
0234             mItem->setText(StartDateTime_Column, QLocale().toString(t->dtStart().toLocalTime().date(), QLocale::ShortFormat));
0235         } else {
0236             mItem->setText(StartDateTime_Column, QLocale().toString(t->dtStart().toLocalTime(), QLocale::ShortFormat));
0237         }
0238     } else {
0239         mItem->setText(StartDateTime_Column, QStringLiteral("---"));
0240     }
0241 
0242     if (t->hasDueDate()) {
0243         if (t->allDay()) {
0244             mItem->setText(EndDateTime_Column, QLocale().toString(t->dtDue().toLocalTime().date(), QLocale::ShortFormat));
0245         } else {
0246             mItem->setText(EndDateTime_Column, QLocale().toString(t->dtDue().toLocalTime(), QLocale::ShortFormat));
0247         }
0248     } else {
0249         mItem->setText(EndDateTime_Column, QStringLiteral("---"));
0250     }
0251     mItem->setText(Categories_Column, t->categoriesStr());
0252 
0253     return true;
0254 }
0255 
0256 bool ListViewPrivate::ListItemVisitor::visit(const Journal::Ptr &j)
0257 {
0258     mItem->setIcon(Summary_Column, QIcon::fromTheme(j->iconName()));
0259 
0260     mItem->start = j->dtStart();
0261     mItem->end = QDateTime();
0262 
0263     if (j->summary().isEmpty()) {
0264         mItem->setText(Summary_Column, cleanSummary(j->description().section(QLatin1Char('\n'), 0, 0), QDateTime()));
0265     } else {
0266         mItem->setText(Summary_Column, cleanSummary(j->summary(), QDateTime()));
0267     }
0268     if (j->allDay()) {
0269         mItem->start.setTime(DAY_START);
0270         mItem->setText(StartDateTime_Column, QLocale().toString(j->dtStart().toLocalTime().date(), QLocale::ShortFormat));
0271     } else {
0272         mItem->setText(StartDateTime_Column, QLocale().toString(j->dtStart().toLocalTime(), QLocale::ShortFormat));
0273     }
0274     mItem->setText(EndDateTime_Column, QStringLiteral("---"));
0275     mItem->setText(Categories_Column, j->categoriesStr());
0276 
0277     return true;
0278 }
0279 
0280 ListView::ListView(QWidget *parent, bool nonInteractive)
0281     : EventView(parent)
0282     , d(std::make_unique<ListViewPrivate>())
0283 {
0284     d->mActiveItem = nullptr;
0285     d->mIsNonInteractive = nonInteractive;
0286 
0287     d->mTreeWidget = new QTreeWidget(this);
0288     d->mTreeWidget->setColumnCount(4);
0289     d->mTreeWidget->setSortingEnabled(true);
0290     d->mTreeWidget->headerItem()->setText(Summary_Column, i18n("Summary"));
0291     d->mTreeWidget->headerItem()->setText(StartDateTime_Column, i18n("Start Date/Time"));
0292     d->mTreeWidget->headerItem()->setText(EndDateTime_Column, i18n("End Date/Time"));
0293     d->mTreeWidget->headerItem()->setText(Categories_Column, i18n("Tags"));
0294 
0295     d->mTreeWidget->setWordWrap(true);
0296     d->mTreeWidget->setAllColumnsShowFocus(true);
0297     d->mTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
0298     d->mTreeWidget->setRootIsDecorated(false);
0299 
0300     QBoxLayout *layoutTop = new QVBoxLayout(this);
0301     layoutTop->setContentsMargins(0, 0, 0, 0);
0302     layoutTop->addWidget(d->mTreeWidget);
0303 
0304     QObject::connect(d->mTreeWidget,
0305                      qOverload<const QModelIndex &>(&QTreeWidget::doubleClicked),
0306                      this,
0307                      qOverload<const QModelIndex &>(&ListView::defaultItemAction));
0308     QObject::connect(d->mTreeWidget, &QTreeWidget::customContextMenuRequested, this, &ListView::popupMenu);
0309     QObject::connect(d->mTreeWidget, &QTreeWidget::itemSelectionChanged, this, &ListView::processSelectionChange);
0310     QObject::connect(d->mTreeWidget->header(), &QHeaderView::sortIndicatorChanged, this, &ListView::slotSortIndicatorChanged);
0311 
0312     d->mSelectedDates.append(QDate::currentDate());
0313 
0314     updateView();
0315 }
0316 
0317 ListView::~ListView() = default;
0318 
0319 int ListView::currentDateCount() const
0320 {
0321     return d->mSelectedDates.count();
0322 }
0323 
0324 Akonadi::Item::List ListView::selectedIncidences() const
0325 {
0326     Akonadi::Item::List eventList;
0327     QTreeWidgetItem *item = d->mTreeWidget->selectedItems().isEmpty() ? nullptr : d->mTreeWidget->selectedItems().first();
0328     if (item) {
0329         auto i = static_cast<ListViewItem *>(item);
0330         eventList.append(i->mIncidence);
0331     }
0332     return eventList;
0333 }
0334 
0335 DateList ListView::selectedIncidenceDates() const
0336 {
0337     return d->mSelectedDates;
0338 }
0339 
0340 void ListView::updateView()
0341 {
0342     static int maxLen = 38;
0343 
0344     /* Set the width of the summary column to show 'maxlen' chars, at most */
0345     int width = qMin(maxLen * fontMetrics().averageCharWidth(), maxLen * 12);
0346     width += 24; // for the icon
0347 
0348     d->mTreeWidget->setColumnWidth(Summary_Column, width);
0349     for (int col = StartDateTime_Column; col < Dummy_EOF_Column; ++col) {
0350         d->mTreeWidget->resizeColumnToContents(col);
0351     }
0352     d->mTreeWidget->sortItems(d->mSortColumn, d->mSortOrder);
0353 }
0354 
0355 void ListView::showDates(const QDate &start, const QDate &end, const QDate &preferredMonth)
0356 {
0357     Q_UNUSED(preferredMonth)
0358     clear();
0359 
0360     d->mStartDate = start;
0361     d->mEndDate = end;
0362 
0363     const QString startStr = QLocale().toString(start, QLocale::ShortFormat);
0364     const QString endStr = QLocale().toString(end, QLocale::ShortFormat);
0365 
0366     d->mTreeWidget->headerItem()->setText(Summary_Column, i18n("Summary [%1 - %2]", startStr, endStr));
0367 
0368     QDate date = start;
0369     while (date <= end) {
0370         for (const auto &calendar : calendars()) {
0371             d->addIncidences(calendar, calendar->incidences(date), date);
0372         }
0373         d->mSelectedDates.append(date);
0374         date = date.addDays(1);
0375     }
0376 
0377     updateView();
0378 
0379     Q_EMIT incidenceSelected(Akonadi::Item(), QDate());
0380 }
0381 
0382 void ListView::showAll()
0383 {
0384     for (const auto &calendar : calendars()) {
0385         d->addIncidences(calendar, calendar->incidences(), QDate());
0386     }
0387 }
0388 
0389 void ListViewPrivate::addIncidences(const Akonadi::CollectionCalendar::Ptr &calendar, const KCalendarCore::Incidence::List &incidences, QDate date)
0390 {
0391     for (const KCalendarCore::Incidence::Ptr &incidence : incidences) {
0392         addIncidence(calendar, incidence, date);
0393     }
0394 }
0395 
0396 void ListViewPrivate::addIncidence(const Akonadi::CollectionCalendar::Ptr &calendar, const Akonadi::Item &item, QDate date)
0397 {
0398     Q_ASSERT(calendar);
0399     if (item.isValid() && item.hasPayload<KCalendarCore::Incidence::Ptr>()) {
0400         addIncidence(calendar, item.payload<KCalendarCore::Incidence::Ptr>(), date);
0401     }
0402 }
0403 
0404 void ListViewPrivate::addIncidence(const Akonadi::CollectionCalendar::Ptr &calendar, const KCalendarCore::Incidence::Ptr &incidence, QDate date)
0405 {
0406     if (!incidence) {
0407         return;
0408     }
0409 
0410     Akonadi::Item aitem = calendar->item(incidence);
0411 
0412     if (!aitem.isValid() || mItems.contains(aitem.id())) {
0413         return;
0414     }
0415 
0416     mDateList.insert(aitem.id(), date);
0417     mItems.insert(aitem.id(), aitem);
0418     Incidence::Ptr tinc = incidence;
0419 
0420     if (tinc->customProperty("KABC", "BIRTHDAY") == QLatin1StringView("YES") || tinc->customProperty("KABC", "ANNIVERSARY") == QLatin1StringView("YES")) {
0421         const int years = EventViews::yearDiff(tinc->dtStart().date(), mEndDate);
0422         if (years > 0) {
0423             tinc = Incidence::Ptr(incidence->clone());
0424             tinc->setReadOnly(false);
0425             tinc->setSummary(i18np("%2 (1 year)", "%2 (%1 years)", years, cleanSummary(incidence->summary(), QDateTime())));
0426             tinc->setReadOnly(true);
0427         }
0428     }
0429     auto item = new ListViewItem(aitem, calendar, mTreeWidget);
0430 
0431     ListItemVisitor v(item, mStartDate);
0432     if (!tinc->accept(v, tinc)) {
0433         delete item;
0434         return;
0435     }
0436 
0437     item->setData(0, Qt::UserRole, QVariant(aitem.id()));
0438 }
0439 
0440 void ListView::showIncidences(const Akonadi::Item::List &itemList, const QDate &date)
0441 {
0442     clear();
0443     for (const auto &item : itemList) {
0444         const auto calendar = calendar3(item);
0445         if (calendar) {
0446             d->addIncidence(calendar, Akonadi::CalendarUtils::incidence(item), date);
0447         }
0448     }
0449     updateView();
0450 
0451     // After new creation of list view no events are selected.
0452     Q_EMIT incidenceSelected(Akonadi::Item(), date);
0453 }
0454 
0455 void ListView::changeIncidenceDisplay(const Akonadi::Item &aitem, int action)
0456 {
0457     const Incidence::Ptr incidence = Akonadi::CalendarUtils::incidence(aitem);
0458     const auto calendar = calendar3(aitem);
0459 
0460     ListViewItem *item = nullptr;
0461     const QDate f = d->mSelectedDates.constFirst();
0462     const QDate l = d->mSelectedDates.constLast();
0463 
0464     QDate date;
0465     if (CalendarSupport::hasTodo(aitem)) {
0466         date = Akonadi::CalendarUtils::todo(aitem)->dtDue().toLocalTime().date();
0467     } else {
0468         date = incidence->dtStart().toLocalTime().date();
0469     }
0470 
0471     switch (action) {
0472     case Akonadi::IncidenceChanger::ChangeTypeCreate:
0473         if (date >= f && date <= l) {
0474             d->addIncidence(calendar, aitem, date);
0475         }
0476         break;
0477     case Akonadi::IncidenceChanger::ChangeTypeModify:
0478         item = d->getItemForIncidence(aitem);
0479         if (item) {
0480             delete item;
0481             d->mItems.remove(aitem.id());
0482             d->mDateList.remove(aitem.id());
0483         }
0484         if (date >= f && date <= l) {
0485             d->addIncidence(calendar, aitem, date);
0486         }
0487         break;
0488     case Akonadi::IncidenceChanger::ChangeTypeDelete:
0489         item = d->getItemForIncidence(aitem);
0490         delete item;
0491         break;
0492     default:
0493         qCDebug(CALENDARVIEW_LOG) << "Illegal action" << action;
0494     }
0495 }
0496 
0497 ListViewItem *ListViewPrivate::getItemForIncidence(const Akonadi::Item &aitem)
0498 {
0499     int index = 0;
0500     while (QTreeWidgetItem *it = mTreeWidget->topLevelItem(index)) {
0501         auto item = static_cast<ListViewItem *>(it);
0502         if (item->mIncidence.id() == aitem.id()) {
0503             return item;
0504         }
0505         ++index;
0506     }
0507 
0508     return nullptr;
0509 }
0510 
0511 void ListView::slotSortIndicatorChanged(int logicalIndex, Qt::SortOrder order)
0512 {
0513     d->mSortColumn = logicalIndex;
0514     d->mSortOrder = order;
0515 }
0516 
0517 void ListView::defaultItemAction(const QModelIndex &index)
0518 {
0519     if (!d->mIsNonInteractive) {
0520         // Get the first column, it has our Akonadi::Id
0521         const QModelIndex col0Idx = d->mTreeWidget->model()->index(index.row(), 0);
0522         Akonadi::Item::Id id = d->mTreeWidget->model()->data(col0Idx, Qt::UserRole).toLongLong();
0523         defaultAction(d->mItems.value(id));
0524     }
0525 }
0526 
0527 void ListView::defaultItemAction(const Akonadi::Item::Id id)
0528 {
0529     if (!d->mIsNonInteractive) {
0530         defaultAction(d->mItems.value(id));
0531     }
0532 }
0533 
0534 void ListView::popupMenu(const QPoint &point)
0535 {
0536     d->mActiveItem = static_cast<ListViewItem *>(d->mTreeWidget->itemAt(point));
0537 
0538     if (d->mActiveItem && !d->mIsNonInteractive) {
0539         const Akonadi::Item aitem = d->mActiveItem->mIncidence;
0540         const auto calendar = calendar3(aitem);
0541         // FIXME: For recurring incidences we don't know the date of this
0542         // occurrence, there's no reference to it at all!
0543 
0544         Q_EMIT showIncidencePopupSignal(calendar, aitem, Akonadi::CalendarUtils::incidence(aitem)->dtStart().date());
0545     } else {
0546         Q_EMIT showNewEventPopupSignal();
0547     }
0548 }
0549 
0550 void ListView::readSettings(KConfig *config)
0551 {
0552     KConfigGroup cfgGroup = config->group(QStringLiteral("ListView Layout"));
0553     readSettings(cfgGroup);
0554 }
0555 
0556 void EventViews::ListView::readSettings(const KConfigGroup &cfgGroup)
0557 {
0558     const QByteArray state = cfgGroup.readEntry("ViewState", QByteArray());
0559     d->mTreeWidget->header()->restoreState(state);
0560     d->mSortColumn = cfgGroup.readEntry("SortColumn", static_cast<int>(StartDateTime_Column));
0561     d->mSortOrder = static_cast<Qt::SortOrder>(cfgGroup.readEntry("SortOrder", static_cast<int>(Qt::AscendingOrder)));
0562 }
0563 
0564 void ListView::writeSettings(KConfig *config)
0565 {
0566     KConfigGroup cfgGroup = config->group(QStringLiteral("ListView Layout"));
0567 }
0568 
0569 void EventViews::ListView::writeSettings(KConfigGroup &cfgGroup)
0570 {
0571     const QByteArray state = d->mTreeWidget->header()->saveState();
0572     cfgGroup.writeEntry("ViewState", state);
0573     cfgGroup.writeEntry("SortColumn", d->mSortColumn);
0574     cfgGroup.writeEntry("SortOrder", static_cast<int>(d->mSortOrder));
0575 }
0576 
0577 void ListView::processSelectionChange()
0578 {
0579     if (!d->mIsNonInteractive) {
0580         ListViewItem *item;
0581         if (d->mTreeWidget->selectedItems().isEmpty()) {
0582             item = nullptr;
0583         } else {
0584             item = static_cast<ListViewItem *>(d->mTreeWidget->selectedItems().first());
0585         }
0586 
0587         if (!item) {
0588             Q_EMIT incidenceSelected(Akonadi::Item(), QDate());
0589         } else {
0590             Q_EMIT incidenceSelected(item->mIncidence, d->mDateList.value(item->mIncidence.id()));
0591         }
0592     }
0593 }
0594 
0595 void ListView::clearSelection()
0596 {
0597     d->mTreeWidget->clearSelection();
0598 }
0599 
0600 void ListView::clear()
0601 {
0602     d->mSelectedDates.clear();
0603     d->mTreeWidget->clear();
0604     d->mDateList.clear();
0605     d->mItems.clear();
0606 }
0607 
0608 QSize ListView::sizeHint() const
0609 {
0610     const QSize s = EventView::sizeHint();
0611     return {s.width() + style()->pixelMetric(QStyle::PM_ScrollBarExtent) + 1, s.height()};
0612 }
0613 
0614 #include "moc_listview.cpp"