8#include "multiagendaview.h"
11#include "agenda/agenda.h"
12#include "agenda/agendaview.h"
13#include "agenda/timelabelszone.h"
14#include "calendarview_debug.h"
15#include "configdialoginterface.h"
18#include <Akonadi/CalendarUtils>
19#include <Akonadi/ETMViewStateSaver>
20#include <Akonadi/EntityTreeModel>
22#include <CalendarSupport/CollectionSelection>
24#include <KCheckableProxyModel>
25#include <KLocalizedString>
26#include <KRearrangeColumnsProxyModel>
27#include <KViewStateMaintainer>
32#include <QResizeEvent>
35#include <QSortFilterProxyModel>
76class DefaultCalendarFactory :
public MultiAgendaView::CalendarFactory
79 using Ptr = QSharedPointer<DefaultCalendarFactory>;
81 explicit DefaultCalendarFactory(MultiAgendaView *view)
92 MultiAgendaView *mView;
95static QString generateColumnLabel(
int c)
97 return i18n(
"Agenda %1", c + 1);
100class EventViews::MultiAgendaViewPrivate
103 explicit MultiAgendaViewPrivate(
const MultiAgendaView::CalendarFactory::Ptr &factory, MultiAgendaView *qq)
105 , mCalendarFactory(factory)
109 ~MultiAgendaViewPrivate()
111 qDeleteAll(mSelectionSavers);
115 void addView(KCheckableProxyModel *selectionProxy,
const QString &title);
116 AgendaView *createView(
const QString &calendar);
119 void resizeScrollView(QSize size);
120 void setActiveAgenda(AgendaView *view);
122 MultiAgendaView *
const q;
123 QList<AgendaView *> mAgendaViews;
124 QList<QWidget *> mAgendaWidgets;
125 QWidget *mTopBox =
nullptr;
126 QScrollArea *mScrollArea =
nullptr;
127 TimeLabelsZone *mTimeLabelsZone =
nullptr;
128 QSplitter *mLeftSplitter =
nullptr;
129 QSplitter *mRightSplitter =
nullptr;
130 QScrollBar *mScrollBar =
nullptr;
131 QWidget *mLeftBottomSpacer =
nullptr;
132 QWidget *mRightBottomSpacer =
nullptr;
133 QDate mStartDate, mEndDate;
134 bool mUpdateOnShow =
true;
135 bool mPendingChanges =
true;
136 bool mCustomColumnSetupUsed =
false;
137 QList<KCheckableProxyModel *> mCollectionSelectionModels;
138 QStringList mCustomColumnTitles;
139 int mCustomNumberOfColumns = 2;
140 QLabel *mLabel =
nullptr;
141 QWidget *mRightDummyWidget =
nullptr;
142 QHash<QString, KViewStateMaintainer<ETMViewStateSaver> *> mSelectionSavers;
143 QMetaObject::Connection m_selectionChangeConn;
144 MultiAgendaView::CalendarFactory::Ptr mCalendarFactory;
147MultiAgendaView::MultiAgendaView(
QWidget *parent)
152MultiAgendaView::MultiAgendaView(
const CalendarFactory::Ptr &factory,
QWidget *parent)
154 , d(new MultiAgendaViewPrivate(factory, this))
157 topLevelLayout->setSpacing(0);
158 topLevelLayout->setContentsMargins({});
169 auto sideBox =
new QWidget(
this);
171 sideBoxLayout->setSpacing(0);
172 sideBoxLayout->setContentsMargins(0, agendeHeaderHeight, 0, 0);
176 sideBoxLayout->addWidget(d->mLeftSplitter, 1);
179 d->mLabel =
new QLabel(
i18nc(
"@label:textbox",
"All Day"), d->mLeftSplitter);
181 d->mLabel->setWordWrap(
true);
183 auto timeLabelsBox =
new QWidget(d->mLeftSplitter);
184 auto timeLabelsBoxLayout =
new QVBoxLayout(timeLabelsBox);
185 timeLabelsBoxLayout->setSpacing(0);
186 timeLabelsBoxLayout->setContentsMargins({});
188 d->mTimeLabelsZone =
new TimeLabelsZone(timeLabelsBox, PrefsPtr(
new Prefs()));
189 timeLabelsBoxLayout->addWidget(d->mTimeLabelsZone);
192 d->mLeftBottomSpacer =
new QWidget(timeLabelsBox);
193 timeLabelsBoxLayout->addWidget(d->mLeftBottomSpacer);
195 topLevelLayout->addWidget(sideBox);
201 d->mScrollArea->setWidgetResizable(
true);
206 d->mTopBox =
new QWidget(d->mScrollArea->viewport());
207 auto mTopBoxHBoxLayout =
new QHBoxLayout(d->mTopBox);
208 mTopBoxHBoxLayout->setContentsMargins({});
209 d->mScrollArea->setWidget(d->mTopBox);
211 topLevelLayout->addWidget(d->mScrollArea, 100);
216 auto sideBox =
new QWidget(
this);
218 sideBoxLayout->setSpacing(0);
219 sideBoxLayout->setContentsMargins(0, agendeHeaderHeight, 0, 0);
222 sideBoxLayout->addWidget(d->mRightSplitter);
225 d->mRightDummyWidget =
new QWidget(d->mRightSplitter);
230 d->mRightBottomSpacer =
new QWidget(sideBox);
231 sideBoxLayout->addWidget(d->mRightBottomSpacer);
233 topLevelLayout->addWidget(sideBox);
237 d->mTimeLabelsZone->setContentsMargins(0, d->mScrollArea->frameWidth(), 0, d->mScrollArea->frameWidth());
245 EventView::addCalendar(calendar);
246 d->mPendingChanges =
true;
252 EventView::removeCalendar(calendar);
253 d->mPendingChanges =
true;
257void MultiAgendaView::setModel(QAbstractItemModel *model)
259 EventView::setModel(model);
271 d->mPendingChanges =
true;
277void MultiAgendaView::recreateViews()
279 if (!d->mPendingChanges) {
282 d->mPendingChanges =
false;
286 if (d->mCustomColumnSetupUsed) {
287 Q_ASSERT(d->mCollectionSelectionModels.size() == d->mCustomNumberOfColumns);
288 for (
int i = 0; i < d->mCustomNumberOfColumns; ++i) {
289 d->addView(d->mCollectionSelectionModels[i], d->mCustomColumnTitles[i]);
292 for (
const auto &calendar : calendars()) {
293 d->addView(calendar);
299 if (d->mAgendaViews.isEmpty()) {
305 d->mTimeLabelsZone->updateAll();
307 QScrollArea *timeLabel = d->mTimeLabelsZone->timeLabels().at(0);
312 if (d->mAgendaViews.size() == 1) {
313 d->mLeftSplitter->setSizes(d->mAgendaViews[0]->splitter()->sizes());
314 d->mRightSplitter->setSizes(d->mAgendaViews[0]->splitter()->sizes());
319 d->mTimeLabelsZone->updateTimeLabelsPosition();
322void MultiAgendaView::forceRecreateViews()
324 d->mPendingChanges =
true;
328void MultiAgendaViewPrivate::deleteViews()
330 for (AgendaView *
const i : std::as_const(mAgendaViews)) {
331 KCheckableProxyModel *proxy = i->takeCustomCollectionSelectionProxyModel();
332 if (proxy && !mCollectionSelectionModels.contains(proxy)) {
338 mAgendaViews.clear();
339 mTimeLabelsZone->setAgendaView(
nullptr);
340 qDeleteAll(mAgendaWidgets);
341 mAgendaWidgets.clear();
344void MultiAgendaViewPrivate::setupViews()
346 for (AgendaView *agendaView : std::as_const(mAgendaViews)) {
350 q->connect(agendaView,
359 q->connect(agendaView, &EventView::incidenceSelected, q, &EventView::incidenceSelected);
367 q->connect(agendaView, &EventView::newTodoSignal, q, &EventView::newTodoSignal);
369 q->connect(agendaView, &EventView::incidenceSelected, q, &MultiAgendaView::slotSelectionChanged);
371 q->connect(agendaView, &AgendaView::timeSpanSelectionChanged, q, &MultiAgendaView::slotClearTimeSpanSelection);
373 q->disconnect(agendaView->agenda(), &Agenda::zoomView, agendaView,
nullptr);
374 q->connect(agendaView->agenda(), &Agenda::zoomView, q, &MultiAgendaView::zoomView);
377 AgendaView *lastView = mAgendaViews.last();
378 for (AgendaView *agendaView : std::as_const(mAgendaViews)) {
379 if (agendaView != lastView) {
380 q->connect(agendaView->agenda()->verticalScrollBar(),
382 lastView->agenda()->verticalScrollBar(),
387 for (AgendaView *agenda : std::as_const(mAgendaViews)) {
388 agenda->readSettings();
392MultiAgendaView::~MultiAgendaView() =
default;
397 for (
AgendaView *agendaView : std::as_const(d->mAgendaViews)) {
398 list += agendaView->selectedIncidences();
406 for (
AgendaView *agendaView : std::as_const(d->mAgendaViews)) {
407 list += agendaView->selectedIncidenceDates();
414 for (
AgendaView *agendaView : std::as_const(d->mAgendaViews)) {
415 return agendaView->currentDateCount();
420void MultiAgendaView::showDates(
const QDate &
start,
const QDate &end,
const QDate &preferredMonth)
422 Q_UNUSED(preferredMonth)
423 d->mStartDate =
start;
425 slotResizeScrollView();
426 d->mTimeLabelsZone->updateAll();
427 for (
AgendaView *agendaView : std::as_const(d->mAgendaViews)) {
428 agendaView->showDates(
start, end);
432void MultiAgendaView::showIncidences(
const Akonadi::Item::List &incidenceList,
const QDate &date)
434 for (AgendaView *agendaView : std::as_const(d->mAgendaViews)) {
435 agendaView->showIncidences(incidenceList, date);
439void MultiAgendaView::updateView()
442 for (AgendaView *agendaView : std::as_const(d->mAgendaViews)) {
443 agendaView->updateView();
447int MultiAgendaView::maxDatesHint()
const
450 return AgendaView::MAX_DAY_COUNT;
453void MultiAgendaView::slotSelectionChanged()
455 for (AgendaView *agenda : std::as_const(d->mAgendaViews)) {
457 agenda->clearSelection();
464 for (
AgendaView *agenda : std::as_const(d->mAgendaViews)) {
465 bool valid = agenda->eventDurationHint(startDt, endDt, allDay);
474void MultiAgendaView::slotClearTimeSpanSelection()
476 for (
AgendaView *agenda : std::as_const(d->mAgendaViews)) {
478 agenda->clearTimeSpanSelection();
479 }
else if (!d->mCustomColumnSetupUsed) {
480 d->setActiveAgenda(agenda);
485void MultiAgendaViewPrivate::setActiveAgenda(
AgendaView *view)
488 if (mCustomColumnSetupUsed) {
496 auto calendars = view->calendars();
497 if (calendars.empty()) {
500 Q_ASSERT(calendars.size() == 1);
502 Q_EMIT q->activeCalendarChanged(calendars.at(0));
505AgendaView *MultiAgendaViewPrivate::createView(
const QString &title)
507 auto box =
new QWidget(mTopBox);
508 mTopBox->layout()->addWidget(box);
509 auto layout =
new QVBoxLayout(box);
510 layout->setContentsMargins({});
511 auto av =
new AgendaView(q->preferences(), q->startDateTime().date(), q->endDateTime().date(),
true,
true, q);
512 layout->addWidget(av);
513 av->setIncidenceChanger(q->changer());
516 mAgendaViews.append(av);
517 mAgendaWidgets.append(box);
519 mTimeLabelsZone->setAgendaView(av);
526 q->connect(av, &AgendaView::showIncidencePopupSignal, q, &MultiAgendaView::showIncidencePopupSignal);
528 q->connect(av, &AgendaView::showNewEventPopupSignal, q, &MultiAgendaView::showNewEventPopupSignal);
530 const QSize minHint = av->allDayAgenda()->scrollArea()->minimumSizeHint();
533 mLabel->setMinimumHeight(minHint.
height());
534 mRightDummyWidget->setMinimumHeight(minHint.
height());
543 auto *view = createView(title);
544 view->addCalendar(calendar);
547static void updateViewFromSelection(
AgendaView *view,
548 const QItemSelection &selected,
549 const QItemSelection &deselected,
550 const MultiAgendaView::CalendarFactory::Ptr &factory)
552 for (
const auto index : selected.
indexes()) {
554 const auto calendar = factory->calendarForCollection(col);
555 view->addCalendar(calendar);
558 for (
const auto index : deselected.
indexes()) {
560 if (
const auto calendar = view->calendarForCollection(col); calendar) {
561 view->removeCalendar(calendar);
567void MultiAgendaViewPrivate::addView(KCheckableProxyModel *sm,
const QString &title)
569 auto *view = createView(title);
576 [
this, view](
const QItemSelection &selected,
const QItemSelection &deselected) {
577 updateViewFromSelection(view, selected, deselected, mCalendarFactory);
581 updateViewFromSelection(view, sm->selectionModel()->selection(), QItemSelection{}, mCalendarFactory);
584void MultiAgendaView::resizeEvent(QResizeEvent *ev)
586 d->resizeScrollView(ev->
size());
591void MultiAgendaViewPrivate::resizeScrollView(QSize size)
593 const int widgetWidth = size.
width() - mTimeLabelsZone->width() - mScrollBar->width();
595 int height = size.
height();
596 if (mScrollArea->horizontalScrollBar()->isVisible()) {
597 const int sbHeight = mScrollArea->horizontalScrollBar()->height();
599 mLeftBottomSpacer->setFixedHeight(sbHeight);
600 mRightBottomSpacer->setFixedHeight(sbHeight);
602 mLeftBottomSpacer->setFixedHeight(0);
603 mRightBottomSpacer->setFixedHeight(0);
606 mTopBox->resize(widgetWidth, height);
609void MultiAgendaView::setIncidenceChanger(Akonadi::IncidenceChanger *changer)
612 for (AgendaView *agenda : std::as_const(d->mAgendaViews)) {
613 agenda->setIncidenceChanger(changer);
617void MultiAgendaView::setPreferences(
const PrefsPtr &prefs)
619 for (AgendaView *agenda : std::as_const(d->mAgendaViews)) {
620 agenda->setPreferences(prefs);
622 EventView::setPreferences(prefs);
625void MultiAgendaView::updateConfig()
628 d->mTimeLabelsZone->setPreferences(preferences());
629 d->mTimeLabelsZone->updateAll();
630 for (AgendaView *agenda : std::as_const(d->mAgendaViews)) {
631 agenda->updateConfig();
635void MultiAgendaView::resizeSplitters()
637 if (d->mAgendaViews.isEmpty()) {
642 if (!lastMovedSplitter) {
643 lastMovedSplitter = d->mLeftSplitter;
645 for (AgendaView *agenda : std::as_const(d->mAgendaViews)) {
646 if (agenda->splitter() == lastMovedSplitter) {
649 agenda->splitter()->setSizes(lastMovedSplitter->sizes());
651 if (lastMovedSplitter != d->mLeftSplitter) {
652 d->mLeftSplitter->setSizes(lastMovedSplitter->sizes());
654 if (lastMovedSplitter != d->mRightSplitter) {
655 d->mRightSplitter->setSizes(lastMovedSplitter->sizes());
659void MultiAgendaView::zoomView(
const int delta, QPoint pos,
const Qt::Orientation ori)
661 const int hourSz = preferences()->hourSize();
665 preferences()->setHourSize(hourSz - 1);
668 preferences()->setHourSize(hourSz + 1);
672 for (AgendaView *agenda : std::as_const(d->mAgendaViews)) {
673 agenda->zoomView(delta,
pos, ori);
676 d->mTimeLabelsZone->updateAll();
679void MultiAgendaView::slotResizeScrollView()
681 d->resizeScrollView(
size());
684void MultiAgendaView::showEvent(QShowEvent *event)
687 if (d->mUpdateOnShow) {
688 d->mUpdateOnShow =
false;
689 d->mPendingChanges =
true;
690 showDates(d->mStartDate, d->mEndDate);
697 for (
AgendaView *agenda : std::as_const(d->mAgendaViews)) {
702void MultiAgendaView::setupScrollBar()
704 if (!d->mAgendaViews.isEmpty() && d->mAgendaViews.constFirst()->agenda()) {
705 QScrollBar *scrollBar = d->mAgendaViews.constFirst()->agenda()->verticalScrollBar();
706 d->mScrollBar->setMinimum(scrollBar->
minimum());
707 d->mScrollBar->setMaximum(scrollBar->
maximum());
708 d->mScrollBar->setSingleStep(scrollBar->
singleStep());
709 d->mScrollBar->setPageStep(scrollBar->
pageStep());
710 d->mScrollBar->setValue(scrollBar->
value());
716 qCDebug(CALENDARVIEW_LOG);
717 d->mPendingChanges =
true;
739 d->mCustomColumnSetupUsed = configGroup.
readEntry(
"UseCustomColumnSetup",
false);
740 d->mCustomNumberOfColumns = configGroup.
readEntry(
"CustomNumberOfColumns", 2);
742 if (d->mCustomColumnTitles.size() != d->mCustomNumberOfColumns) {
743 const int orig = d->mCustomColumnTitles.size();
744 d->mCustomColumnTitles.reserve(d->mCustomNumberOfColumns);
745 for (
int i = orig; i < d->mCustomNumberOfColumns; ++i) {
746 d->mCustomColumnTitles.push_back(generateColumnLabel(i));
751 d->mCollectionSelectionModels.
clear();
753 if (d->mCustomColumnSetupUsed) {
754 d->mCollectionSelectionModels.resize(d->mCustomNumberOfColumns);
755 for (
int i = 0; i < d->mCustomNumberOfColumns; ++i) {
758 sortProxy->setSourceModel(model());
762 columnFilterProxy->setSourceColumns(
QList<int>() << 0);
763 columnFilterProxy->setSourceModel(sortProxy);
770 checkableProxy->setSourceModel(columnFilterProxy);
771 checkableProxy->setSelectionModel(qsm);
775 if (!d->mSelectionSavers.contains(groupName)) {
777 d->mSelectionSavers[groupName]->setSelectionModel(checkableProxy->selectionModel());
780 d->mSelectionSavers[groupName]->restoreState();
781 d->mCollectionSelectionModels[i] = checkableProxy;
785 d->mPendingChanges =
true;
787 qDeleteAll(oldModels);
792 configGroup.
writeEntry(
"UseCustomColumnSetup", d->mCustomColumnSetupUsed);
793 configGroup.
writeEntry(
"CustomNumberOfColumns", d->mCustomNumberOfColumns);
794 configGroup.
writeEntry(
"ColumnTitles", d->mCustomColumnTitles);
796 for (
KCheckableProxyModel *checkableProxyModel : std::as_const(d->mCollectionSelectionModels)) {
802 if (!d->mSelectionSavers.contains(groupName)) {
804 d->mSelectionSavers[groupName]->setSelectionModel(checkableProxyModel->selectionModel());
806 d->mSelectionSavers[groupName]->saveState();
810void MultiAgendaView::customCollectionsChanged(ConfigDialogInterface *dlg)
812 if (!d->mCustomColumnSetupUsed && !dlg->useCustomColumns()) {
817 d->mCustomColumnSetupUsed = dlg->useCustomColumns();
818 d->mCustomNumberOfColumns = dlg->numberOfColumns();
820 newModels.
resize(d->mCustomNumberOfColumns);
821 d->mCustomColumnTitles.clear();
822 d->mCustomColumnTitles.reserve(d->mCustomNumberOfColumns);
823 for (
int i = 0; i < d->mCustomNumberOfColumns; ++i) {
824 newModels[i] = dlg->takeSelectionModel(i);
825 d->mCustomColumnTitles.push_back(dlg->columnTitle(i));
827 d->mCollectionSelectionModels = newModels;
828 d->mPendingChanges =
true;
832bool MultiAgendaView::customColumnSetupUsed()
const
834 return d->mCustomColumnSetupUsed;
837int MultiAgendaView::customNumberOfColumns()
const
839 return d->mCustomNumberOfColumns;
842QList<KCheckableProxyModel *> MultiAgendaView::collectionSelectionModels()
const
844 return d->mCollectionSelectionModels;
847QStringList MultiAgendaView::customColumnTitles()
const
849 return d->mCustomColumnTitles;
852#include "moc_multiagendaview.cpp"
void collectionTreeFetched(const Akonadi::Collection::List &collections)
AgendaView is the agenda-like view that displays events in a single or multi-day view.
EventView is the abstract base class from which all other calendar views for event data are derived.
void dissociateOccurrencesSignal(const Akonadi::Item &, const QDate &)
Dissociate from a recurring incidence the occurrence on the given date to a new incidence or dissocia...
void showIncidenceSignal(const Akonadi::Item &)
instructs the receiver to show the incidence in read-only mode.
void toggleAlarmSignal(const Akonadi::Item &)
instructs the receiver to toggle the alarms of the Incidence.
virtual void setChanges(Changes changes)
Notifies the view that there are pending changes so a redraw is needed.
void newEventSignal()
instructs the receiver to create a new event in given collection.
void deleteIncidenceSignal(const Akonadi::Item &)
instructs the receiver to delete the Incidence in some manner; some possibilities include automatical...
void pasteIncidenceSignal()
instructs the receiver to paste the incidence
Changes changes() const
Returns if there are pending changes and a redraw is needed.
void cutIncidenceSignal(const Akonadi::Item &)
instructs the receiver to cut the Incidence
void editIncidenceSignal(const Akonadi::Item &)
instructs the receiver to begin editing the incidence specified in some manner.
void copyIncidenceSignal(const Akonadi::Item &)
instructs the receiver to copy the incidence
virtual void updateConfig()
Re-reads the configuration and picks up relevant changes which are applicable to the view.
virtual void setIncidenceChanger(Akonadi::IncidenceChanger *changer)
Assign a new incidence change helper object.
Shows one agenda for every resource side-by-side.
int currentDateCount() const override
Returns the number of currently shown dates.
bool eventDurationHint(QDateTime &startDt, QDateTime &endDt, bool &allDay) const override
Sets the default start/end date/time for new events.
Akonadi::Item::List selectedIncidences() const override
void doRestoreConfig(const KConfigGroup &configGroup) override
reimplement to read view-specific settings.
KCalendarCore::DateList selectedIncidenceDates() const override
Returns a list of the dates of selected events.
void doSaveConfig(KConfigGroup &configGroup) override
reimplement to write view-specific settings.
bool hasConfigurationDialog() const override
void setChanges(Changes changes) override
Notifies the view that there are pending changes so a redraw is needed.
void collectionSelectionChanged()
Reimplemented from KOrg::BaseView.
QSharedPointer< Calendar > 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
Q_SCRIPTABLE QString start(QString train="")
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
AKONADI_CALENDAR_EXPORT QString displayName(Akonadi::ETMCalendar *calendar, const Akonadi::Collection &collection)
Namespace EventViews provides facilities for displaying incidences, including events,...
QAction * create(GameStandardAction id, const QObject *recvr, const char *slot, QObject *parent)
void valueChanged(int value)
QModelIndexList indexes() const const
void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
void resize(qsizetype size)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
virtual bool event(QEvent *e)
T qobject_cast(QObject *object)
QObject * sender() const const
const QSize & size() const const
QSharedPointer< T > create(Args &&... args)
bool isValid() const const
void splitterMoved(int pos, int index)
QString number(double n, char format, int precision)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)