14#include "agendaview.h"
17#include <Akonadi/CalendarUtils>
18#include <Akonadi/IncidenceChanger>
19#include <CalendarSupport/Utils>
21#include <KCalendarCore/Incidence>
23#include <KCalUtils/RecurrenceActions>
25#include "calendarview_debug.h"
28#include <KLocalizedString>
29#include <QApplication>
36#include <QResizeEvent>
44using namespace std::chrono_literals;
49class EventViews::MarcusBainsPrivate
52 MarcusBainsPrivate(
EventView *eventView, Agenda *agenda)
53 : mEventView(eventView)
58 [[nodiscard]]
int todayColumn()
const;
62 Agenda *
const mAgenda;
64 QLabel *mTimeBox =
nullptr;
66 int mOldTodayCol = -1;
69int MarcusBainsPrivate::todayColumn()
const
75 for (
const QDate &date : dateList) {
76 if (date == currentDate) {
85MarcusBains::MarcusBains(
EventView *eventView, Agenda *agenda)
87 , d(new MarcusBainsPrivate(eventView, agenda))
89 d->mTimeBox =
new QLabel(d->mAgenda);
92 d->mTimer =
new QTimer(
this);
93 d->mTimer->setSingleShot(
true);
98MarcusBains::~MarcusBains() =
default;
100void MarcusBains::updateLocation()
102 updateLocationRecalc();
105void MarcusBains::updateLocationRecalc(
bool recalculate)
107 const bool showSeconds = d->mEventView->preferences()->marcusBainsShowSeconds();
108 const QColor color = d->mEventView->preferences()->agendaMarcusBainsLineLineColor();
113 if (now.
date() != d->mOldDateTime.date()) {
116 const int todayCol = recalculate ? d->todayColumn() : d->mOldTodayCol;
119 const int minutes = time.
hour() * 60 + time.
minute();
120 const int minutesPerCell = 24 * 60 / d->mAgenda->rows();
122 d->mOldDateTime = now;
123 d->mOldTodayCol = todayCol;
125 int y = int(minutes * d->mAgenda->gridSpacingY() / minutesPerCell);
126 int x = int(d->mAgenda->gridSpacingX() * todayCol);
128 bool hideIt = !(d->mEventView->preferences()->marcusBainsEnabled());
129 if (!
isHidden() && (hideIt || (todayCol < 0))) {
142 const int fw = d->mEventView->preferences()->agendaMarcusBainsLineFont().weight();
156 d->mTimeBox->setFont(d->mEventView->preferences()->agendaMarcusBainsLineFont());
157 QPalette pal1 = d->mTimeBox->palette();
159 d->mTimeBox->setPalette(pal1);
161 d->mTimeBox->adjustSize();
162 if (
y - d->mTimeBox->height() >= 0) {
163 y -= d->mTimeBox->height();
167 if (
x - d->mTimeBox->width() + d->mAgenda->gridSpacingX() > 0) {
168 x += int(d->mAgenda->gridSpacingX() - d->mTimeBox->width() - 1);
172 d->mTimeBox->move(
x,
y);
173 d->mTimeBox->raise();
175 if (showSeconds || recalculate) {
176 d->mTimer->start(1s);
178 d->mTimer->start(1000 * (60 - time.
second()));
184class EventViews::AgendaPrivate
187 AgendaPrivate(
AgendaView *agendaView,
QScrollArea *scrollArea,
int columns,
int rows,
int rowSize,
bool isInteractive)
188 : mAgendaView(agendaView)
189 , mScrollArea(scrollArea)
194 , mGridSpacingY(rowSize)
195 , mDesiredGridSpacingY(rowSize)
197 , mResizeBorderWidth(0)
198 , mScrollBorderWidth(0)
201 , mWorkingHoursEnable(false)
202 , mHolidayMask(nullptr)
203 , mWorkingHoursYTop(0)
204 , mWorkingHoursYBottom(0)
205 , mHasSelection(false)
206 , mMarcusBains(nullptr)
207 , mActionType(Agenda::NOP)
209 , mOldLowerScrollValue(0)
210 , mOldUpperScrollValue(0)
211 , mReturnPressed(false)
212 , mIsInteractive(isInteractive)
214 if (mGridSpacingY < 4 || mGridSpacingY > 30) {
222 return mAgendaView->preferences();
225 bool isQueuedForDeletion(
const QString &uid)
const
229 return mItemsQueuedForDeletion.contains(uid) && !mAgendaItemsById.contains(uid);
248 double mGridSpacingX;
249 double mGridSpacingY;
250 double mDesiredGridSpacingY;
252 Akonadi::IncidenceChanger *mChanger =
nullptr;
255 int mResizeBorderWidth;
258 int mScrollBorderWidth;
270 bool mWorkingHoursEnable;
272 int mWorkingHoursYTop;
273 int mWorkingHoursYBottom;
277 QPoint mSelectionStartPoint;
278 QPoint mSelectionStartCell;
298 MarcusBains *mMarcusBains =
nullptr;
300 Agenda::MouseActionType mActionType;
308 int mOldLowerScrollValue;
309 int mOldUpperScrollValue;
320Agenda::Agenda(
AgendaView *agendaView,
QScrollArea *scrollArea,
int columns,
int rows,
int rowSize,
bool isInteractive)
322 , d(new AgendaPrivate(agendaView, scrollArea, columns, rows, rowSize, isInteractive))
324 setMouseTracking(
true);
335 , d(new AgendaPrivate(agendaView, scrollArea, columns, 1, 24, isInteractive))
337 d->mAllDayMode =
true;
344 delete d->mMarcusBains;
352QDate Agenda::selectedIncidenceDate()
const
354 return d->mSelectedItem ? d->mSelectedItem->occurrenceDate() :
QDate();
357QString Agenda::lastSelectedItemUid()
const
359 return d->mSelectedId;
366 d->mGridSpacingX =
static_cast<double>(d->mScrollArea->width()) / d->mColumns;
367 d->mDesiredGridSpacingY = d->preferences()->hourSize();
368 if (d->mDesiredGridSpacingY < 4 || d->mDesiredGridSpacingY > 30) {
369 d->mDesiredGridSpacingY = 10;
373 d->mGridSpacingY =
static_cast<double>(
height()) / d->mRows;
374 if (d->mGridSpacingY < d->mDesiredGridSpacingY) {
375 d->mGridSpacingY = d->mDesiredGridSpacingY;
378 d->mResizeBorderWidth = 12;
379 d->mScrollBorderWidth = 12;
380 d->mScrollDelay = 30;
381 d->mScrollOffset = 10;
390 d->mStartCell =
QPoint(0, 0);
391 d->mEndCell =
QPoint(0, 0);
393 d->mHasSelection =
false;
394 d->mSelectionStartPoint =
QPoint(0, 0);
395 d->mSelectionStartCell =
QPoint(0, 0);
396 d->mSelectionEndCell =
QPoint(0, 0);
398 d->mOldLowerScrollValue = -1;
399 d->mOldUpperScrollValue = -1;
401 d->mClickedItem =
nullptr;
403 d->mActionItem =
nullptr;
404 d->mActionType = NOP;
405 d->mItemMoved =
false;
407 d->mSelectedItem =
nullptr;
414 d->mScrollArea->viewport()->update();
418 calculateWorkingHours();
423 if (d->mAllDayMode) {
424 d->mMarcusBains =
nullptr;
426 d->mMarcusBains =
new MarcusBains(d->mAgendaView,
this);
432 qDeleteAll(d->mItems);
433 qDeleteAll(d->mItemsToDelete);
435 d->mItemsToDelete.clear();
436 d->mAgendaItemsById.clear();
437 d->mItemsQueuedForDeletion.clear();
439 d->mSelectedItem =
nullptr;
444void Agenda::clearSelection()
446 d->mHasSelection =
false;
447 d->mActionType = NOP;
451void Agenda::marcus_bains()
453 if (d->mMarcusBains) {
454 d->mMarcusBains->updateLocationRecalc(
true);
458void Agenda::changeColumns(
int columns)
461 qCDebug(CALENDARVIEW_LOG) <<
"called with argument 0";
466 d->mColumns = columns;
476int Agenda::columns()
const
481int Agenda::rows()
const
486double Agenda::gridSpacingX()
const
488 return d->mGridSpacingX;
491double Agenda::gridSpacingY()
const
493 return d->mGridSpacingY;
502 switch (
event->type()) {
508#ifndef QT_NO_WHEELEVENT
518 if (!d->mActionItem) {
523 if (
object ==
this) {
533#ifndef QT_NO_DRAGANDDROP
549#ifndef QT_NO_DRAGANDDROP
552 switch (de->
type()) {
555 if (!CalendarSupport::canDecode(md)) {
559 if (CalendarSupport::mimeDataHasIncidence(md)) {
570 if (!CalendarSupport::canDecode(md)) {
574 const QList<QUrl> incidenceUrls = CalendarSupport::incidenceItemUrls(md);
583 if (dropTarget && dropTarget !=
this) {
584 dropPosition = dropTarget->
mapTo(
this, dropPosition);
587 const QPoint gridPosition = contentsToGrid(dropPosition);
588 if (!incidenceUrls.
isEmpty()) {
589 Q_EMIT droppedIncidences(incidenceUrls, gridPosition, d->mAllDayMode);
591 Q_EMIT droppedIncidences(incidences, gridPosition, d->mAllDayMode);
604#ifndef QT_NO_WHEELEVENT
608 bool accepted =
false;
611 if (
object !=
this) {
612 viewportPos = ((
QWidget *)
object)->mapToParent(
pos);
622 if (
object !=
this) {
623 viewportPos = ((
QWidget *)
object)->mapToParent(
pos);
628 Q_EMIT mousePosSignal(gridToContents(contentsToGrid(viewportPos)));
641 return d->mAgendaView->processKeyEvent(ke);
647 if (
object !=
this) {
650 viewportPos = me->
pos();
653 switch (me->
type()) {
655 if (
object !=
this) {
658 if (d->mClickedItem) {
659 selectItem(d->mClickedItem);
660 Q_EMIT showIncidencePopupSignal(d->mClickedItem->incidence(), d->mClickedItem->occurrenceDate());
667 d->mActionItem =
nullptr;
669 d->mActionItem = item;
670 startItemAction(viewportPos);
682 QPoint gpos = contentsToGrid(viewportPos);
683 if (!ptInSelection(gpos)) {
684 d->mSelectionStartCell = gpos;
685 d->mSelectionEndCell = gpos;
686 d->mHasSelection =
true;
687 Q_EMIT newStartSelectSignal();
688 Q_EMIT newTimeSpanSignal(d->mSelectionStartCell, d->mSelectionEndCell);
691 Q_EMIT showNewEventPopupSignal();
694 d->mActionItem =
nullptr;
698 startSelectAction(viewportPos);
705 if (d->mActionItem) {
707 }
else if (d->mActionType == SELECT) {
708 endSelectAction(viewportPos);
712 Q_EMIT mousePosSignal(gridToContents(contentsToGrid(viewportPos)));
716 if (!d->mIsInteractive) {
722 QPoint indicatorPos = gridToContents(contentsToGrid(viewportPos));
723 if (
object !=
this) {
726 if (incidence && !
incidence->isReadOnly()) {
727 if (!d->mActionItem) {
728 setNoActionCursor(moveItem, viewportPos);
730 performItemAction(viewportPos);
732 if (d->mActionType == MOVE) {
736 firstItem = d->mActionItem;
738 indicatorPos = gridToContents(
QPoint(firstItem->cellXLeft(), firstItem->cellYTop()));
739 }
else if (d->mActionType == RESIZEBOTTOM) {
741 indicatorPos = gridToContents(
QPoint(d->mActionItem->cellXLeft(), d->mActionItem->cellYBottom() + 1));
746 if (d->mActionType == SELECT) {
747 performSelectAction(viewportPos);
750 if (((d->mStartCell.y() < d->mEndCell.y()) && (d->mEndCell.x() >= d->mStartCell.x())) || (d->mEndCell.x() > d->mStartCell.x())) {
751 indicatorPos = gridToContents(
QPoint(d->mEndCell.x(), d->mEndCell.y() + 1));
753 indicatorPos = gridToContents(d->mEndCell);
757 Q_EMIT mousePosSignal(indicatorPos);
762 if (
object ==
this) {
767 if (doubleClickedItem) {
768 selectItem(doubleClickedItem);
769 Q_EMIT editIncidenceSignal(doubleClickedItem->incidence());
781bool Agenda::ptInSelection(
QPoint gpos)
const
783 if (!d->mHasSelection) {
785 }
else if (gpos.
x() < d->mSelectionStartCell.x() || gpos.
x() > d->mSelectionEndCell.x()) {
787 }
else if ((gpos.
x() == d->mSelectionStartCell.x()) && (gpos.
y() < d->mSelectionStartCell.y())) {
789 }
else if ((gpos.
x() == d->mSelectionEndCell.x()) && (gpos.
y() > d->mSelectionEndCell.y())) {
795void Agenda::startSelectAction(
QPoint viewportPos)
797 Q_EMIT newStartSelectSignal();
799 d->mActionType = SELECT;
800 d->mSelectionStartPoint = viewportPos;
801 d->mHasSelection =
true;
807 d->mStartCell = gpos;
809 d->mSelectionStartCell = gpos;
810 d->mSelectionEndCell = gpos;
815void Agenda::performSelectAction(
QPoint pos)
820 if (
pos.y() - contentsY() < d->mScrollBorderWidth && contentsY() > 0) {
821 d->mScrollUpTimer.start(d->mScrollDelay);
822 }
else if (contentsY() + d->mScrollArea->viewport()->height() - d->mScrollBorderWidth <
pos.y()) {
823 d->mScrollDownTimer.start(d->mScrollDelay);
825 d->mScrollUpTimer.stop();
826 d->mScrollDownTimer.stop();
829 if (gpos != d->mEndCell) {
831 if (d->mStartCell.x() > d->mEndCell.x() || (d->mStartCell.x() == d->mEndCell.x() && d->mStartCell.y() > d->mEndCell.y())) {
833 d->mSelectionStartCell = d->mEndCell;
834 d->mSelectionEndCell = d->mStartCell;
836 d->mSelectionStartCell = d->mStartCell;
837 d->mSelectionEndCell = d->mEndCell;
844void Agenda::endSelectAction(
const QPoint ¤tPos)
846 d->mScrollUpTimer.stop();
847 d->mScrollDownTimer.stop();
849 d->mActionType = NOP;
851 Q_EMIT newTimeSpanSignal(d->mSelectionStartCell, d->mSelectionEndCell);
853 if (d->preferences()->selectionStartsEditor()) {
869 int clXLeft = item->cellXLeft();
870 int clXRight = item->cellXRight();
876 int gridDistanceX = int(
pos.x() - contpos.
x());
877 if (gridDistanceX < d->mResizeBorderWidth && clXLeft == gridpos.
x()) {
883 }
else if ((d->mGridSpacingX - gridDistanceX) < d->mResizeBorderWidth && clXRight == gridpos.
x()) {
893 int gridDistanceY = int(
pos.y() - contpos.
y());
894 if (gridDistanceY < d->mResizeBorderWidth && item->cellYTop() == gridpos.
y() && !item->firstMultiItem()) {
896 }
else if ((d->mGridSpacingY - gridDistanceY) < d->mResizeBorderWidth && item->cellYBottom() == gridpos.
y() && !item->lastMultiItem()) {
904void Agenda::startItemAction(
const QPoint &pos)
906 Q_ASSERT(d->mActionItem);
908 d->mStartCell = contentsToGrid(
pos);
909 d->mEndCell = d->mStartCell;
911 bool noResize = CalendarSupport::hasTodo(d->mActionItem->incidence());
913 d->mActionType = MOVE;
915 d->mActionType = isInResizeArea(d->mAllDayMode,
pos, d->mActionItem);
918 d->mActionItem->startMove();
919 setActionCursor(d->mActionType,
true);
922void Agenda::performItemAction(
QPoint pos)
928 if (
pos.y() < 0 ||
pos.y() >= contentsY() + d->mScrollArea->viewport()->height() ||
pos.x() < 0 ||
pos.x() >=
width()) {
929 if (d->mActionType == MOVE) {
930 d->mScrollUpTimer.stop();
931 d->mScrollDownTimer.stop();
932 d->mActionItem->resetMove();
933 placeSubCells(d->mActionItem);
934 Q_EMIT startDragSignal(d->mActionItem->incidence());
941 d->mActionItem =
nullptr;
942 d->mActionType = NOP;
943 d->mItemMoved =
false;
947 setActionCursor(d->mActionType,
true);
951 const int distanceToTop =
pos.y() - contentsY();
952 if (distanceToTop < d->mScrollBorderWidth && distanceToTop > -d->mScrollBorderWidth) {
953 d->mScrollUpTimer.start(d->mScrollDelay);
954 }
else if (contentsY() + d->mScrollArea->viewport()->height() - d->mScrollBorderWidth <
pos.y()) {
955 d->mScrollDownTimer.start(d->mScrollDelay);
957 d->mScrollUpTimer.stop();
958 d->mScrollDownTimer.stop();
962 if (d->mEndCell != gpos) {
963 if (!d->mItemMoved) {
966 i18n(
"Unable to lock item for modification. "
967 "You cannot make any changes."),
968 i18nc(
"@title:window",
"Locking Failed"),
969 QStringLiteral(
"AgendaLockingFailed"));
970 d->mScrollUpTimer.stop();
971 d->mScrollDownTimer.stop();
972 d->mActionItem->resetMove();
973 placeSubCells(d->mActionItem);
977 d->mActionItem =
nullptr;
978 d->mActionType = NOP;
979 d->mItemMoved =
false;
982 d->mItemMoved =
true;
984 d->mActionItem->raise();
985 if (d->mActionType == MOVE) {
989 firstItem = d->mActionItem;
993 lastItem = d->mActionItem;
995 QPoint deltapos = gpos - d->mEndCell;
998 bool changed =
false;
999 if (deltapos.
x() != 0) {
1000 moveItem->moveRelative(deltapos.
x(), 0);
1004 if (moveItem == firstItem && !d->mAllDayMode) {
1005 int newY = deltapos.
y() + moveItem->cellYTop();
1007 if (newY < 0 && newY > d->mScrollBorderWidth) {
1008 moveItem->expandTop(-moveItem->cellYTop());
1014 newFirst->setCellXY(moveItem->cellXLeft() - 1, rows() + newY, rows() - 1);
1015 d->mItems.append(newFirst);
1016 moveItem->resize(
int(d->mGridSpacingX * newFirst->cellWidth()),
int(d->mGridSpacingY * newFirst->cellHeight()));
1017 QPoint cpos = gridToContents(
QPoint(newFirst->cellXLeft(), newFirst->cellYTop()));
1018 newFirst->setParent(
this);
1019 newFirst->move(cpos.
x(), cpos.
y());
1021 newFirst = insertItem(moveItem->incidence(),
1022 moveItem->occurrenceDateTime(),
1023 moveItem->cellXLeft() - 1,
1026 moveItem->itemPos(),
1027 moveItem->itemCount(),
1033 moveItem->prependMoveItem(newFirst);
1034 firstItem = newFirst;
1035 }
else if (newY >= rows()) {
1038 firstItem = moveItem->nextMultiItem();
1040 d->mItems.removeAll(moveItem);
1042 d->mActionItem->removeMoveItem(moveItem);
1043 moveItem = firstItem;
1046 moveItem->expandTop(rows() - newY);
1049 moveItem->expandTop(deltapos.
y(),
true);
1053 if (moveItem && !moveItem->lastMultiItem() && !d->mAllDayMode) {
1054 int newY = deltapos.
y() + moveItem->cellYBottom();
1057 lastItem = moveItem->prevMultiItem();
1059 d->mItems.removeAll(moveItem);
1061 moveItem->removeMoveItem(moveItem);
1062 moveItem = lastItem;
1063 moveItem->expandBottom(newY + 1);
1064 }
else if (newY >= rows()) {
1065 moveItem->expandBottom(rows() - moveItem->cellYBottom() - 1);
1069 newLast->setCellXY(moveItem->cellXLeft() + 1, 0, newY - rows() - 1);
1070 d->mItems.append(newLast);
1071 moveItem->resize(
int(d->mGridSpacingX * newLast->cellWidth()),
int(d->mGridSpacingY * newLast->cellHeight()));
1072 QPoint cpos = gridToContents(
QPoint(newLast->cellXLeft(), newLast->cellYTop()));
1073 newLast->setParent(
this);
1074 newLast->move(cpos.
x(), cpos.
y());
1076 newLast = insertItem(moveItem->incidence(),
1077 moveItem->occurrenceDateTime(),
1078 moveItem->cellXLeft() + 1,
1081 moveItem->itemPos(),
1082 moveItem->itemCount(),
1085 moveItem->appendMoveItem(newLast);
1089 moveItem->expandBottom(deltapos.
y());
1094 adjustItemPosition(moveItem);
1097 moveItem = moveItem->nextMultiItem();
1100 }
else if (d->mActionType == RESIZETOP) {
1101 if (d->mEndCell.y() <= d->mActionItem->cellYBottom()) {
1102 d->mActionItem->expandTop(gpos.
y() - d->mEndCell.y());
1103 adjustItemPosition(d->mActionItem);
1105 }
else if (d->mActionType == RESIZEBOTTOM) {
1106 if (d->mEndCell.y() >= d->mActionItem->cellYTop()) {
1107 d->mActionItem->expandBottom(gpos.
y() - d->mEndCell.y());
1108 adjustItemPosition(d->mActionItem);
1110 }
else if (d->mActionType == RESIZELEFT) {
1111 if (d->mEndCell.x() <= d->mActionItem->cellXRight()) {
1112 d->mActionItem->expandLeft(gpos.
x() - d->mEndCell.x());
1113 adjustItemPosition(d->mActionItem);
1115 }
else if (d->mActionType == RESIZERIGHT) {
1116 if (d->mEndCell.x() >= d->mActionItem->cellXLeft()) {
1117 d->mActionItem->expandRight(gpos.
x() - d->mEndCell.x());
1118 adjustItemPosition(d->mActionItem);
1125void Agenda::endItemAction()
1128 d->mActionType = NOP;
1129 d->mScrollUpTimer.stop();
1130 d->mScrollDownTimer.stop();
1136 qCCritical(CALENDARVIEW_LOG) <<
"No IncidenceChanger set";
1140 bool multiModify =
false;
1143 const auto recurrenceId = d->mActionItem->occurrenceDateTime();
1145 d->mItemMoved = d->mItemMoved && !(d->mStartCell.x() == d->mEndCell.x() && d->mStartCell.y() == d->mEndCell.y());
1147 if (d->mItemMoved) {
1148 bool addIncidence =
false;
1149 bool modify =
false;
1155 if (CalendarSupport::hasEvent(incidence)) {
1156 mainIncidence = cal->event(
incidence->uid());
1157 }
else if (CalendarSupport::hasTodo(incidence)) {
1158 mainIncidence = cal->todo(
incidence->uid());
1165 const int res = d->mAgendaView->showMoveRecurDialog(incidence, recurrenceId.date());
1167 if (!d->mActionItem) {
1168 qCWarning(CALENDARVIEW_LOG) <<
"mActionItem was reset while the 'move' dialog was active";
1169 d->mItemMoved =
false;
1183 d->mChanger->startAtomicOperation(
i18n(
"Dissociate event from recurrence"));
1186 newInc->removeCustomProperty(
"VOLATILE",
"AKONADI-ID");
1189 if (newItem.
isValid() && newItem != item) {
1191 newInc->setCustomProperty(
"VOLATILE",
"AKONADI-ID",
QString::number(newItem.
id()));
1192 addIncidence =
false;
1194 addIncidence =
true;
1197 d->mAgendaView->enableAgendaUpdate(
false);
1199 d->mActionItem->setIncidence(newInc);
1200 d->mActionItem->dissociateFromMultiItem();
1202 d->mAgendaView->enableAgendaUpdate(
true);
1205 i18n(
"Unable to add the exception item to the calendar. "
1206 "No change will be done."),
1207 i18nc(
"@title:window",
"Error Occurred"));
1213 d->mActionItem->resetMove();
1214 placeSubCells(d->mActionItem);
1221 placeItem = d->mActionItem;
1236 d->mActionItem->endMove();
1242 for (it = oldconflictItems.
begin(); it != oldconflictItems.
end(); ++it) {
1248 placeSubCells(placeItem);
1249 placeItem = placeItem->nextMultiItem();
1260 d->mAgendaView->updateEventDates(modif, addIncidence, saveCollection);
1270 d->mAgendaView->updateEventDates(placeItem, addIncidence, saveCollection);
1275 d->mActionItem =
nullptr;
1276 d->mItemMoved =
false;
1279 d->mChanger->endAtomicOperation();
1283void Agenda::setActionCursor(
int actionType,
bool acting)
1286 switch (actionType) {
1312 const bool noResize = CalendarSupport::hasTodo(item);
1314 Agenda::MouseActionType resizeType = MOVE;
1316 resizeType = isInResizeArea(d->mAllDayMode,
pos, moveItem);
1318 setActionCursor(resizeType);
1327 pt = gridToContents(
QPoint(item->cellXLeft(), item->cellYTop()));
1328 pt1 = gridToContents(
QPoint(item->cellXLeft(), item->cellYTop()) +
QPoint(1, 1));
1330 int maxSubCells = item->subCells();
1331 double newSubCellWidth;
1332 if (d->mAllDayMode) {
1333 newSubCellWidth =
static_cast<double>(pt1.
y()) / maxSubCells;
1335 newSubCellWidth =
static_cast<double>(pt1.
x()) / maxSubCells;
1337 return newSubCellWidth;
1345 item->resize(
int(d->mGridSpacingX * item->cellWidth()),
int(d->mGridSpacingY * item->cellHeight()));
1346 int clXLeft = item->cellXLeft();
1348 clXLeft = item->cellXRight() + 1;
1350 QPoint cpos = gridToContents(
QPoint(clXLeft, item->cellYTop()));
1351 item->move(cpos.
x(), cpos.
y());
1354void Agenda::placeAgendaItem(
const AgendaItem::QPtr &item,
double subCellWidth)
1358 QPoint pt = gridToContents(
QPoint(item->cellXLeft(), item->cellYTop()));
1360 QPoint pt1 = gridToContents(
QPoint(item->cellXLeft() + item->cellWidth(), item->cellYBottom() + 1));
1362 double subCellPos = item->subCell() * subCellWidth;
1366 double delta = 0.01;
1367 if (subCellWidth < 0) {
1374 if (d->mAllDayMode) {
1376 height = int(subCellPos + subCellWidth + delta) - int(subCellPos);
1378 ypos = pt.
y() + int(subCellPos);
1380 width = int(subCellPos + subCellWidth + delta) - int(subCellPos);
1382 xpos = pt.
x() + int(subCellPos);
1394 item->move(xpos, ypos);
1410 qCDebug(CALENDARVIEW_LOG);
1414 qCDebug(CALENDARVIEW_LOG) <<
" event is 0";
1416 qCDebug(CALENDARVIEW_LOG) <<
" event:" <<
event->summary();
1419 qCDebug(CALENDARVIEW_LOG) <<
" placeItem is 0";
1421 qCDebug(CALENDARVIEW_LOG) <<
"Agenda::placeSubCells()...";
1425 for (CalendarSupport::CellItem *item : std::as_const(d->mItems)) {
1434 double newSubCellWidth = calcSubCellWidth(placeItem);
1436 for (it = items.
begin(); it != items.
end(); ++it) {
1439 placeAgendaItem(item, newSubCellWidth);
1440 item->addConflictItem(placeItem);
1441 placeItem->addConflictItem(item);
1445 placeAgendaItem(placeItem, newSubCellWidth);
1447 placeItem->update();
1450int Agenda::columnWidth(
int column)
const
1458 int end = gridToContents(
QPoint(column, 0)).
x();
1465 drawContents(&p, 0, -
y(), d->mGridSpacingX * d->mColumns, d->mGridSpacingY * d->mRows +
y());
1471void Agenda::drawContents(
QPainter *p,
int cx,
int cy,
int cw,
int ch)
1481 if (!d->preferences()->useSystemColor()) {
1482 dbp.fillRect(0, 0, cw, ch, d->preferences()->agendaGridBackgroundColor());
1487 dbp.translate(-cx, -cy);
1489 double lGridSpacingY = d->mGridSpacingY * 2;
1495 const QList<bool> busyDayMask = d->mAgendaView->busyDayMask();
1498 if (d->mWorkingHoursEnable && d->mHolidayMask) {
1500 if (!d->preferences()->useSystemColor()) {
1501 workColor = d->preferences()->workingHoursColor();
1506 QPoint pt1(cx, d->mWorkingHoursYTop);
1507 QPoint pt2(cx + cw, d->mWorkingHoursYBottom);
1508 if (pt2.x() >= pt1.
x() ) {
1509 int gxStart = contentsToGrid(pt1).
x();
1510 int gxEnd = contentsToGrid(pt2).
x();
1512 if (gxStart > gxEnd) {
1518 while (gxStart <= gxEnd) {
1519 int xStart = gridToContents(
QPoint(gxStart + xoffset, 0)).
x();
1520 int xWidth = columnWidth(gxStart) + 1;
1522 if (pt2.y() < pt1.
y()) {
1524 if (((gxStart == 0) && !d->mHolidayMask->at(d->mHolidayMask->count() - 1))
1525 || ((gxStart > 0) && (gxStart <
int(d->mHolidayMask->count())) && (!d->mHolidayMask->at(gxStart - 1)))) {
1527 dbp.fillRect(xStart, cy, xWidth, pt2.y() - cy + 1, workColor);
1530 if ((gxStart <
int(d->mHolidayMask->count() - 1)) && (!d->mHolidayMask->at(gxStart))) {
1531 if (pt1.
y() < cy + ch - 1) {
1532 dbp.fillRect(xStart, pt1.
y(), xWidth, cy + ch - pt1.
y() + 1, workColor);
1538 if (gxStart <
int(d->mHolidayMask->count() - 1) && !d->mHolidayMask->at(gxStart)) {
1539 dbp.fillRect(xStart, pt1.
y(), xWidth, pt2.y() - pt1.
y() + 1, workColor);
1548 if (d->preferences()->colorAgendaBusyDays() && !d->mAllDayMode) {
1549 for (
int i = 0; i < busyDayMask.
count(); ++i) {
1550 if (busyDayMask[i]) {
1551 const QPoint pt1(cx + d->mGridSpacingX * i, 0);
1554 if (!d->preferences()->useSystemColor()) {
1555 busyColor = d->preferences()->viewBgBusyColor();
1558 if ((busyColor.
blue() + busyColor.
red() + busyColor.
green()) > (256 / 2 * 3)) {
1560 busyColor = busyColor.
lighter(140);
1563 busyColor = busyColor.
darker(140);
1566 busyColor.
setAlpha(EventViews::BUSY_BACKGROUND_ALPHA);
1567 dbp.fillRect(pt1.
x(), pt1.
y(), d->mGridSpacingX, cy + ch, busyColor);
1573 if (d->mHasSelection && d->mAgendaView->dateRangeSelectionEnabled()) {
1577 if (!d->preferences()->useSystemColor()) {
1578 highlightColor = d->preferences()->agendaGridHighlightColor();
1583 if (d->mSelectionEndCell.x() > d->mSelectionStartCell.x()) {
1585 pt = gridToContents(d->mSelectionStartCell);
1586 pt1 = gridToContents(
QPoint(d->mSelectionStartCell.x() + 1, d->mRows + 1));
1587 dbp.fillRect(
QRect(pt, pt1), highlightColor);
1589 for (
int c = d->mSelectionStartCell.x() + 1; c < d->mSelectionEndCell.x(); ++c) {
1590 pt = gridToContents(
QPoint(c, 0));
1591 pt1 = gridToContents(
QPoint(c + 1, d->mRows + 1));
1592 dbp.fillRect(
QRect(pt, pt1), highlightColor);
1595 pt = gridToContents(
QPoint(d->mSelectionEndCell.x(), 0));
1596 pt1 = gridToContents(d->mSelectionEndCell +
QPoint(1, 1));
1597 dbp.fillRect(
QRect(pt, pt1), highlightColor);
1599 pt = gridToContents(d->mSelectionStartCell);
1600 pt1 = gridToContents(d->mSelectionEndCell +
QPoint(1, 1));
1601 dbp.fillRect(
QRect(pt, pt1), highlightColor);
1611 if (windowTextColor.
red() + windowTextColor.
green() + windowTextColor.
blue() < (256 / 2 * 3)) {
1613 hourPen = windowTextColor.
lighter(200);
1614 halfHourPen = windowTextColor.
lighter(500);
1617 hourPen = windowTextColor.
darker(150);
1618 halfHourPen = windowTextColor.
darker(200);
1621 dbp.setPen(hourPen);
1624 double x = (int(cx / d->mGridSpacingX)) * d->mGridSpacingX;
1625 while (
x < cx + cw) {
1626 dbp.drawLine(
int(
x), cy,
int(
x), cy + ch);
1627 x += d->mGridSpacingX;
1631 double y = (int(cy / (2 * lGridSpacingY))) * 2 * lGridSpacingY;
1632 while (
y < cy + ch) {
1633 dbp.drawLine(cx,
int(
y), cx + cw,
int(
y));
1634 y += 2 * lGridSpacingY;
1636 y = (2 * int(cy / (2 * lGridSpacingY)) + 1) * lGridSpacingY;
1637 dbp.setPen(halfHourPen);
1638 while (
y < cy + ch) {
1639 dbp.drawLine(cx,
int(
y), cx + cw,
int(
y));
1640 y += 2 * lGridSpacingY;
1651 int gy = int(
pos.y() / d->mGridSpacingY);
1661 int y = int(gpos.
y() * d->mGridSpacingY);
1669int Agenda::timeToY(
QTime time)
const
1671 int minutesPerCell = 24 * 60 / d->mRows;
1672 int timeMinutes = time.
hour() * 60 + time.
minute();
1673 int Y = (timeMinutes + (minutesPerCell / 2)) / minutesPerCell;
1682QTime Agenda::gyToTime(
int gy)
const
1684 int secondsPerCell = 24 * 60 * 60 / d->mRows;
1685 int timeSeconds = secondsPerCell * gy;
1687 QTime time(0, 0, 0);
1688 if (timeSeconds < 24 * 60 * 60) {
1689 time = time.
addSecs(timeSeconds);
1699 minArray.
fill(timeToY(
QTime(23, 59)), d->mSelectedDates.count());
1702 int ymin = item->cellYTop();
1703 int index = item->cellXLeft();
1704 if (index >= 0 && index < (
int)(d->mSelectedDates.count())) {
1705 if (ymin < minArray[index] && !d->mItemsToDelete.contains(item)) {
1706 minArray[index] = ymin;
1718 maxArray.
fill(timeToY(
QTime(0, 0)), d->mSelectedDates.count());
1721 int ymax = item->cellYBottom();
1723 int index = item->cellXLeft();
1724 if (index >= 0 && index < (
int)(d->mSelectedDates.count())) {
1725 if (ymax > maxArray[index] && !d->mItemsToDelete.contains(item)) {
1726 maxArray[index] = ymax;
1735void Agenda::setStartTime(
QTime startHour)
1737 const double startPos = (startHour.
hour() / 24. + startHour.
minute() / 1440. + startHour.
second() / 86400.) * d->mRows * gridSpacingY();
1739 verticalScrollBar()->
setValue(startPos);
1754 if (d->mAllDayMode) {
1755 qCDebug(CALENDARVIEW_LOG) <<
"using this in all-day mode is illegal.";
1759 d->mActionType = NOP;
1761 AgendaItem::QPtr agendaItem = createAgendaItem(incidence, itemPos, itemCount, recurrenceId, isSelected);
1766 if (YTop >= d->mRows) {
1767 YBottom -= YTop - (d->mRows - 1);
1768 YTop = d->mRows - 1;
1770 if (YBottom <= YTop) {
1771 qCDebug(CALENDARVIEW_LOG) <<
"Text:" << agendaItem->text() <<
" YSize<0";
1775 agendaItem->resize(
int((X + 1) * d->mGridSpacingX) -
int(X * d->mGridSpacingX),
int(YTop * d->mGridSpacingY) -
int((YBottom + 1) * d->mGridSpacingY));
1776 agendaItem->setCellXY(X, YTop, YBottom);
1777 agendaItem->setCellXRight(X);
1778 agendaItem->setResourceColor(d->mCalendar->resourceColor(incidence));
1779 agendaItem->installEventFilter(
this);
1781 agendaItem->move(
int(X * d->mGridSpacingX),
int(YTop * d->mGridSpacingY));
1783 d->mItems.append(agendaItem);
1785 placeSubCells(agendaItem);
1799 if (!d->mAllDayMode) {
1800 qCCritical(CALENDARVIEW_LOG) <<
"using this in non all-day mode is illegal.";
1804 d->mActionType = NOP;
1806 AgendaItem::QPtr agendaItem = createAgendaItem(incidence, 1, 1, recurrenceId, isSelected);
1811 agendaItem->setCellXY(XBegin, 0, 0);
1812 agendaItem->setCellXRight(XEnd);
1814 const double startIt = d->mGridSpacingX * (agendaItem->cellXLeft());
1815 const double endIt = d->mGridSpacingX * (agendaItem->cellWidth() + agendaItem->cellXLeft());
1817 agendaItem->resize(
int(endIt) -
int(startIt),
int(d->mGridSpacingY));
1819 agendaItem->installEventFilter(
this);
1820 agendaItem->setResourceColor(d->mCalendar->resourceColor(incidence));
1821 agendaItem->move(
int(XBegin * d->mGridSpacingX), 0);
1822 d->mItems.append(agendaItem);
1824 placeSubCells(agendaItem);
1835 qCWarning(CALENDARVIEW_LOG) <<
"Agenda::createAgendaItem() item is invalid.";
1839 AgendaItem::QPtr agendaItem =
new AgendaItem(d->mAgendaView, d->mCalendar, incidence, itemPos, itemCount, recurrenceId, isSelected,
this);
1841 connect(agendaItem.
data(), &AgendaItem::removeAgendaItem,
this, &Agenda::removeAgendaItem);
1842 connect(agendaItem.
data(), &AgendaItem::showAgendaItem,
this, &Agenda::showAgendaItem);
1844 d->mAgendaItemsById.insert(
incidence->uid(), agendaItem);
1859 if (d->mAllDayMode) {
1860 qCDebug(CALENDARVIEW_LOG) <<
"using this in all-day mode is illegal.";
1864 d->mActionType = NOP;
1869 int width = XEnd - XBegin + 1;
1873 int visibleCount = d->mSelectedDates.first().daysTo(d->mSelectedDates.last());
1874 for (cellX = XBegin; cellX <= XEnd; ++cellX) {
1877 if (cellX >= 0 && cellX <= visibleCount) {
1878 if (cellX == XBegin) {
1883 if (cellX == XEnd) {
1884 cellYBottom = YBottom;
1886 cellYBottom = rows() - 1;
1888 newtext = QStringLiteral(
"(%1/%2): ").
arg(count).
arg(
width);
1889 newtext.
append(ev->summary());
1891 current = insertItem(
event, recurrenceId, cellX, cellYTop, cellYBottom, count,
width, isSelected);
1893 current->setText(newtext);
1894 multiItems.
append(current);
1910 next = (it == e) ?
nullptr : (*it);
1912 item->setMultiItem((item == first) ?
nullptr : first, prev, next, (item == last) ?
nullptr : last);
1924 qCWarning(CALENDARVIEW_LOG) <<
"Agenda::removeIncidence() incidence is invalid" <<
incidence->uid();
1928 if (d->isQueuedForDeletion(
incidence->uid())) {
1940 if (
incidence->instanceIdentifier() != agendaItem->incidence()->instanceIdentifier()) {
1943 if (!removeAgendaItem(agendaItem)) {
1944 qCWarning(CALENDARVIEW_LOG) <<
"Agenda::removeIncidence() Failed to remove " <<
incidence->uid();
1953 qCCritical(CALENDARVIEW_LOG) <<
"Show what?";
1959 agendaItem->setParent(
this);
1961 if (!d->mItems.contains(agendaItem)) {
1962 d->mItems.append(agendaItem);
1964 placeSubCells(agendaItem);
1971 Q_ASSERT(agendaItem);
1976 bool taken = d->mItems.removeAll(agendaItem) > 0;
1977 d->mAgendaItemsById.remove(agendaItem->incidence()->uid(), agendaItem);
1980 for (it = conflictItems.
begin(); it != conflictItems.
end(); ++it) {
1982 (*it)->setSubCells((*it)->subCells() - 1);
1986 for (it = conflictItems.
begin(); it != conflictItems.
end(); ++it) {
1988 if (*it && *it != agendaItem) {
1992 d->mItemsToDelete.append(agendaItem);
1993 d->mItemsQueuedForDeletion.insert(agendaItem->incidence()->uid());
1994 agendaItem->setVisible(
false);
1999void Agenda::deleteItemsToDelete()
2001 qDeleteAll(d->mItemsToDelete);
2002 d->mItemsToDelete.clear();
2003 d->mItemsQueuedForDeletion.clear();
2027 if (d->mAllDayMode) {
2028 d->mGridSpacingX =
static_cast<double>(newSize.width()) / d->mColumns;
2029 d->mGridSpacingY = newSize.height();
2031 d->mGridSpacingX =
static_cast<double>(newSize.width()) / d->mColumns;
2033 d->mGridSpacingY =
static_cast<double>(newSize.height()) / d->mRows;
2034 if (d->mGridSpacingY < d->mDesiredGridSpacingY) {
2035 d->mGridSpacingY = d->mDesiredGridSpacingY;
2038 calculateWorkingHours();
2041 Q_EMIT gridSpacingYChanged(d->mGridSpacingY * 4);
2047void Agenda::resizeAllContents()
2049 double subCellWidth;
2052 subCellWidth = calcSubCellWidth(item);
2053 placeAgendaItem(item, subCellWidth);
2073 checkScrollBoundaries();
2078void Agenda::scrollUp()
2080 int currentValue = verticalScrollBar()->
value();
2081 verticalScrollBar()->
setValue(currentValue - d->mScrollOffset);
2084void Agenda::scrollDown()
2086 int currentValue = verticalScrollBar()->
value();
2087 verticalScrollBar()->
setValue(currentValue + d->mScrollOffset);
2090QSize Agenda::minimumSize()
const
2095QSize Agenda::minimumSizeHint()
const
2100int Agenda::minimumHeight()
const
2105 if (d->mAllDayMode) {
2108 return d->mGridSpacingY * d->mRows;
2112void Agenda::updateConfig()
2114 const double oldGridSpacingY = d->mGridSpacingY;
2116 if (!d->mAllDayMode) {
2117 d->mDesiredGridSpacingY = d->preferences()->hourSize();
2118 if (d->mDesiredGridSpacingY < 4 || d->mDesiredGridSpacingY > 30) {
2119 d->mDesiredGridSpacingY = 10;
2131 if (fabs(oldGridSpacingY - d->mDesiredGridSpacingY) > 0.1) {
2132 d->mGridSpacingY = d->mDesiredGridSpacingY;
2137 calculateWorkingHours();
2142void Agenda::checkScrollBoundaries()
2145 d->mOldLowerScrollValue = -1;
2146 d->mOldUpperScrollValue = -1;
2148 checkScrollBoundaries(verticalScrollBar()->value());
2151void Agenda::checkScrollBoundaries(
int v)
2153 int yMin = int((v) / d->mGridSpacingY);
2154 int yMax = int((v + d->mScrollArea->height()) / d->mGridSpacingY);
2156 if (yMin != d->mOldLowerScrollValue) {
2157 d->mOldLowerScrollValue = yMin;
2158 Q_EMIT lowerYChanged(yMin);
2160 if (yMax != d->mOldUpperScrollValue) {
2161 d->mOldUpperScrollValue = yMax;
2162 Q_EMIT upperYChanged(yMax);
2166int Agenda::visibleContentsYMin()
const
2168 int v = verticalScrollBar()->
value();
2169 return int(v / d->mGridSpacingY);
2172int Agenda::visibleContentsYMax()
const
2174 int v = verticalScrollBar()->
value();
2175 return int((v + d->mScrollArea->height()) / d->mGridSpacingY);
2178void Agenda::deselectItem()
2180 if (d->mSelectedItem.isNull()) {
2189 if (itemInc && selectedItem && itemInc->uid() == selectedItem->uid()) {
2190 item->select(
false);
2195 d->mSelectedItem =
nullptr;
2204 if (item ==
nullptr) {
2208 d->mSelectedItem = item;
2209 d->mSelectedItem->select();
2210 Q_ASSERT(d->mSelectedItem->incidence());
2211 d->mSelectedId = d->mSelectedItem->incidence()->uid();
2214 if (item && item->incidence()->uid() == d->mSelectedId) {
2218 Q_EMIT incidenceSelected(d->mSelectedItem->incidence(), d->mSelectedItem->occurrenceDate());
2221void Agenda::selectIncidenceByUid(
const QString &uid)
2224 if (item && item->incidence()->uid() == uid) {
2237void Agenda::keyPressEvent(
QKeyEvent *kev)
2239 switch (kev->
key()) {
2256void Agenda::calculateWorkingHours()
2258 d->mWorkingHoursEnable = !d->mAllDayMode;
2260 QTime tmp = d->preferences()->workingHoursStart().time();
2261 d->mWorkingHoursYTop = int(4 * d->mGridSpacingY * (tmp.
hour() + tmp.
minute() / 60. + tmp.
second() / 3600.));
2262 tmp = d->preferences()->workingHoursEnd().time();
2263 d->mWorkingHoursYBottom = int(4 * d->mGridSpacingY * (tmp.
hour() + tmp.
minute() / 60. + tmp.
second() / 3600.) - 1);
2268 d->mSelectedDates = selectedDates;
2274 return d->mSelectedDates;
2282void Agenda::setIncidenceChanger(Akonadi::IncidenceChanger *changer)
2284 d->mChanger = changer;
2289 d->mHolidayMask =
mask;
2292void Agenda::contentsMousePressEvent(
QMouseEvent *event)
2297QSize Agenda::sizeHint()
const
2299 if (d->mAllDayMode) {
2302 return {
parentWidget()->
width(),
static_cast<int>(d->mGridSpacingY * d->mRows)};
2308 return d->mScrollArea->verticalScrollBar();
2313 return d->mScrollArea;
2318 return d->mAgendaItemsById.values(uid);
2321AgendaScrollArea::AgendaScrollArea(
bool isAllDay,
AgendaView *agendaView,
bool isInteractive,
QWidget *parent)
2325 mAgenda =
new Agenda(agendaView,
this, 1, isInteractive);
2328 mAgenda =
new Agenda(agendaView,
this, 1, 96, agendaView->preferences()->hourSize(), isInteractive);
2331 setWidgetResizable(
true);
2335 mAgenda->setStartTime(agendaView->preferences()->dayBegins().time());
2338AgendaScrollArea::~AgendaScrollArea() =
default;
2340Agenda *AgendaScrollArea::agenda()
const
2345#include "moc_agenda.cpp"
Collection & parentCollection()
Collection::Id storageCollectionId() const
This class describes the widgets that represent the various calendar items in the agenda view.
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.
static Incidence::Ptr createException(const Incidence::Ptr &incidence, const QDateTime &recurrenceId, bool thisAndFuture=false)
QSharedPointer< Incidence > Ptr
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 KCalendarCore::Incidence::Ptr incidence(const Akonadi::Item &item)
char * toString(const EngineQuery &query)
Namespace EventViews provides facilities for displaying incidences, including events,...
void information(QWidget *parent, const QString &text, const QString &title=QString(), const QString &dontShowAgainName=QString(), Options options=Notify)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
const QList< QKeySequence > & next()
const QList< QKeySequence > & end()
const QList< QKeySequence > & preferences()
void triggerAction(SliderAction action)
void valueChanged(int value)
QColor darker(int factor) const const
QColor lighter(int factor) const const
bool sendEvent(QObject *receiver, QEvent *event)
QDateTime currentDateTime()
const QMimeData * mimeData() const const
QPointF position() const const
void setDropAction(Qt::DropAction action)
void setFrameStyle(int style)
void append(QList< T > &&value)
qsizetype count() const const
QList< T > & fill(parameter_type value, qsizetype size)
bool isEmpty() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
virtual bool eventFilter(QObject *watched, QEvent *event)
void installEventFilter(QObject *filterObj)
T qobject_cast(QObject *object)
void drawPixmap(const QPoint &point, const QPixmap &pixmap)
void setColor(ColorGroup group, ColorRole role, const QColor &color)
QPoint toPoint() const const
const QSize & size() const const
QPointF position() const const
QString & append(QChar ch)
QString arg(Args &&... args) const const
QString number(double n, char format, int precision)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QTime addSecs(int s) const const
bool setHMS(int h, int m, int s, int ms)
QPoint angleDelta() const const