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
86 Akonadi::CollectionCalendar::Ptr calendarForCollection(
const Akonadi::Collection &collection)
override
88 return Akonadi::CollectionCalendar::Ptr::create(mView->entityTreeModel(), collection);
95static QString generateColumnLabel(
int c)
97 return i18n(
"Agenda %1", c + 1);
100class EventViews::MultiAgendaViewPrivate
105 , mCalendarFactory(factory)
109 ~MultiAgendaViewPrivate()
111 qDeleteAll(mSelectionSavers);
114 void addView(
const Akonadi::CollectionCalendar::Ptr &calendar);
119 void resizeScrollView(
QSize size);
127 TimeLabelsZone *mTimeLabelsZone =
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;
139 int mCustomNumberOfColumns = 2;
141 QWidget *mRightDummyWidget =
nullptr;
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());
243void MultiAgendaView::addCalendar(
const Akonadi::CollectionCalendar::Ptr &calendar)
245 EventView::addCalendar(calendar);
246 d->mPendingChanges =
true;
250void MultiAgendaView::removeCalendar(
const Akonadi::CollectionCalendar::Ptr &calendar)
252 EventView::removeCalendar(calendar);
253 d->mPendingChanges =
true;
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)) {
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)) {
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);
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);
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));
507 auto box =
new QWidget(mTopBox);
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();
540void MultiAgendaViewPrivate::addView(
const Akonadi::CollectionCalendar::Ptr &calendar)
543 auto *view = createView(title);
544 view->addCalendar(calendar);
547static void updateViewFromSelection(
AgendaView *view,
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);
569 auto *view = createView(title);
577 updateViewFromSelection(view, selected, deselected, mCalendarFactory);
581 updateViewFromSelection(view, sm->selectionModel()->selection(),
QItemSelection{}, mCalendarFactory);
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();
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());
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;
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.
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 Q_NOREPLY void start()
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(StandardAction 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 append(QList< T > &&value)
bool contains(const AT &value) const const
void resize(qsizetype size)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
T qobject_cast(QObject *object)
QObject * sender() const const
const QSize & size() const const
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)