File indexing completed on 2024-11-24 04:41:35
0001 /* 0002 SPDX-FileCopyrightText: 2008 Bruno Virlet <bruno.virlet@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later WITH Qt-Commercial-exception-1.0 0005 */ 0006 0007 #include "monthscene.h" 0008 #include "helper.h" 0009 #include "monthgraphicsitems.h" 0010 #include "monthitem.h" 0011 #include "monthview.h" 0012 #include "prefs.h" 0013 0014 #include <CalendarSupport/Utils> 0015 0016 #include <KColorScheme> 0017 #include <KLocalizedString> 0018 #include <QGraphicsSceneMouseEvent> 0019 #include <QIcon> 0020 #include <QResizeEvent> 0021 #include <QToolTip> 0022 0023 static const int AUTO_REPEAT_DELAY = 600; 0024 0025 using namespace EventViews; 0026 0027 MonthScene::MonthScene(MonthView *parent) 0028 : QGraphicsScene(parent) 0029 , mMonthView(parent) 0030 , mInitialized(false) 0031 , mClickedItem(nullptr) 0032 , mActionItem(nullptr) 0033 , mActionInitiated(false) 0034 , mSelectedItem(nullptr) 0035 , mStartCell(nullptr) 0036 , mPreviousCell(nullptr) 0037 , mActionType(None) 0038 , mStartHeight(0) 0039 , mCurrentIndicator(nullptr) 0040 { 0041 mBirthdayPixmap = QIcon::fromTheme(QStringLiteral("view-calendar-birthday")).pixmap(16, 16); 0042 mAnniversaryPixmap = QIcon::fromTheme(QStringLiteral("view-calendar-wedding-anniversary")).pixmap(16, 16); 0043 mAlarmPixmap = QIcon::fromTheme(QStringLiteral("appointment-reminder")).pixmap(16, 16); 0044 mRecurPixmap = QIcon::fromTheme(QStringLiteral("appointment-recurring")).pixmap(16, 16); 0045 mReadonlyPixmap = QIcon::fromTheme(QStringLiteral("object-locked")).pixmap(16, 16); 0046 mReplyPixmap = QIcon::fromTheme(QStringLiteral("mail-reply-sender")).pixmap(16, 16); 0047 mHolidayPixmap = QIcon::fromTheme(QStringLiteral("view-calendar-holiday")).pixmap(16, 16); 0048 0049 setSceneRect(0, 0, parent->width(), parent->height()); 0050 } 0051 0052 MonthScene::~MonthScene() 0053 { 0054 qDeleteAll(mMonthCellMap); 0055 qDeleteAll(mManagerList); 0056 } 0057 0058 MonthCell *MonthScene::selectedCell() const 0059 { 0060 return mMonthCellMap.value(mSelectedCellDate); 0061 } 0062 0063 MonthCell *MonthScene::previousCell() const 0064 { 0065 return mPreviousCell; 0066 } 0067 0068 int MonthScene::getRightSpan(QDate date) const 0069 { 0070 MonthCell *cell = mMonthCellMap.value(date); 0071 if (!cell) { 0072 return 0; 0073 } 0074 0075 return 7 - cell->x() - 1; 0076 } 0077 0078 int MonthScene::getLeftSpan(QDate date) const 0079 { 0080 MonthCell *cell = mMonthCellMap.value(date); 0081 if (!cell) { 0082 return 0; 0083 } 0084 0085 return cell->x(); 0086 } 0087 0088 int MonthScene::maxRowCount() 0089 { 0090 return (rowHeight() - MonthCell::topMargin()) / itemHeightIncludingSpacing(); 0091 } 0092 0093 int MonthScene::itemHeightIncludingSpacing() 0094 { 0095 return MonthCell::topMargin() + 2; 0096 } 0097 0098 int MonthScene::itemHeight() 0099 { 0100 return MonthCell::topMargin(); 0101 } 0102 0103 MonthCell *MonthScene::firstCellForMonthItem(MonthItem *manager) 0104 { 0105 for (QDate d = manager->startDate(); d <= manager->endDate(); d = d.addDays(1)) { 0106 MonthCell *monthCell = mMonthCellMap.value(d); 0107 if (monthCell) { 0108 return monthCell; 0109 } 0110 } 0111 0112 return nullptr; 0113 } 0114 0115 void MonthScene::updateGeometry() 0116 { 0117 for (MonthItem *manager : std::as_const(mManagerList)) { 0118 manager->updateGeometry(); 0119 } 0120 } 0121 0122 int MonthScene::availableWidth() const 0123 { 0124 return static_cast<int>(sceneRect().width()); 0125 } 0126 0127 int MonthScene::availableHeight() const 0128 { 0129 return static_cast<int>(sceneRect().height() - headerHeight()); 0130 } 0131 0132 int MonthScene::columnWidth() const 0133 { 0134 return static_cast<int>((availableWidth() - 1) / 7.); 0135 } 0136 0137 int MonthScene::rowHeight() const 0138 { 0139 return static_cast<int>((availableHeight() - 1) / 6.); 0140 } 0141 0142 int MonthScene::headerHeight() const 0143 { 0144 return 50; 0145 } 0146 0147 int MonthScene::cellVerticalPos(const MonthCell *cell) const 0148 { 0149 return headerHeight() + cell->y() * rowHeight(); 0150 } 0151 0152 int MonthScene::cellHorizontalPos(const MonthCell *cell) const 0153 { 0154 return cell->x() * columnWidth(); 0155 } 0156 0157 int MonthScene::sceneYToMonthGridY(int yScene) 0158 { 0159 return yScene - headerHeight(); 0160 } 0161 0162 int MonthScene::sceneXToMonthGridX(int xScene) 0163 { 0164 return xScene; 0165 } 0166 0167 void MonthGraphicsView::drawBackground(QPainter *p, const QRectF &rect) 0168 { 0169 Q_ASSERT(mScene); 0170 0171 PrefsPtr prefs = mScene->monthView()->preferences(); 0172 p->setFont(prefs->monthViewFont()); 0173 p->fillRect(rect, palette().color(QPalette::Window)); 0174 0175 /* 0176 Headers 0177 */ 0178 QFont font = prefs->monthViewFont(); 0179 font.setBold(true); 0180 font.setPointSize(15); 0181 p->setFont(font); 0182 const int dayLabelsHeight = 20; 0183 const auto dayInMonth = mMonthView->averageDate(); 0184 p->drawText(QRect(0, 0185 0, // top right 0186 static_cast<int>(mScene->sceneRect().width()), 0187 static_cast<int>(mScene->headerHeight() - dayLabelsHeight)), 0188 Qt::AlignCenter, 0189 i18nc("monthname year", "%1 %2", QLocale().standaloneMonthName(dayInMonth.month(), QLocale::LongFormat), QString::number(dayInMonth.year()))); 0190 0191 font.setPointSize(dayLabelsHeight - 10); 0192 p->setFont(font); 0193 0194 const QDate start = mMonthView->actualStartDateTime().date(); 0195 const QDate end = mMonthView->actualEndDateTime().date(); 0196 0197 for (QDate d = start; d <= start.addDays(6); d = d.addDays(1)) { 0198 const MonthCell *const cell = mScene->mMonthCellMap.value(d); 0199 0200 if (!cell) { 0201 // This means drawBackground() is being called before reloadIncidences(). Can happen with some 0202 // themes. Bug #190191 0203 return; 0204 } 0205 0206 p->drawText(QRect(mScene->cellHorizontalPos(cell), mScene->cellVerticalPos(cell) - 15, mScene->columnWidth(), 15), 0207 Qt::AlignCenter, 0208 QLocale::system().dayName(d.dayOfWeek(), QLocale::LongFormat)); 0209 } 0210 0211 /* 0212 Month grid 0213 */ 0214 int columnWidth = mScene->columnWidth(); 0215 int rowHeight = mScene->rowHeight(); 0216 QDate todayDate{QDate::currentDate()}; 0217 0218 const QList<QDate> workDays = CalendarSupport::workDays(mMonthView->actualStartDateTime().date(), mMonthView->actualEndDateTime().date()); 0219 QRect todayRect; 0220 QRect selectedRect; 0221 QColor holidayBg; 0222 QColor workdayBg; 0223 if (mMonthView->preferences()->useSystemColor()) { 0224 workdayBg = palette().color(QPalette::Base); 0225 holidayBg = palette().color(QPalette::AlternateBase); 0226 } else { 0227 workdayBg = mMonthView->preferences()->monthGridWorkHoursBackgroundColor(); 0228 holidayBg = mMonthView->preferences()->monthGridBackgroundColor(); 0229 } 0230 0231 for (QDate d = start; d <= end; d = d.addDays(1)) { 0232 const MonthCell *const cell = mScene->mMonthCellMap.value(d); 0233 0234 if (!cell) { 0235 // This means drawBackground() is being called before reloadIncidences(). Can happen with some 0236 // themes. Bug #190191 0237 return; 0238 } 0239 0240 const QRect cellRect(mScene->cellHorizontalPos(cell), mScene->cellVerticalPos(cell), columnWidth, rowHeight); 0241 if (cell == mScene->selectedCell()) { 0242 selectedRect = cellRect; 0243 } 0244 if (cell->date() == todayDate) { 0245 todayRect = cellRect; 0246 } 0247 0248 // Draw cell 0249 p->setPen(mMonthView->preferences()->monthGridBackgroundColor().darker(150)); 0250 p->setBrush(workDays.contains(d) ? workdayBg : holidayBg); 0251 p->drawRect(cellRect); 0252 if (mMonthView->isBusyDay(d)) { 0253 QColor busyColor = mMonthView->preferences()->viewBgBusyColor(); 0254 busyColor.setAlpha(EventViews::BUSY_BACKGROUND_ALPHA); 0255 p->setBrush(busyColor); 0256 p->drawRect(cellRect); 0257 } 0258 } 0259 if (!todayRect.isNull()) { 0260 KColorScheme scheme(QPalette::Normal, KColorScheme::ColorSet::View); 0261 p->setPen(scheme.foreground(KColorScheme::ForegroundRole::PositiveText).color()); 0262 p->setBrush(scheme.background(KColorScheme::BackgroundRole::PositiveBackground)); 0263 p->drawRect(todayRect); 0264 } 0265 if (!selectedRect.isNull()) { 0266 const KColorScheme scheme(QPalette::Normal, KColorScheme::ColorSet::Selection); 0267 auto color = scheme.background(KColorScheme::BackgroundRole::NormalBackground).color(); 0268 p->setPen(color); 0269 color.setAlpha(EventViews::BUSY_BACKGROUND_ALPHA); 0270 p->setBrush(color); 0271 p->drawRect(selectedRect); 0272 } 0273 0274 /* 0275 * Draw Dates 0276 */ 0277 0278 font = mMonthView->preferences()->monthViewFont(); 0279 font.setPixelSize(MonthCell::topMargin() - 4); 0280 p->setFont(font); 0281 0282 QPen oldPen; 0283 if (mMonthView->preferences()->useSystemColor()) { 0284 oldPen = palette().color(QPalette::WindowText).darker(150); 0285 } else { 0286 oldPen = mMonthView->preferences()->monthGridBackgroundColor().darker(150); 0287 } 0288 0289 for (QDate d = mMonthView->actualStartDateTime().date(); d <= mMonthView->actualEndDateTime().date(); d = d.addDays(1)) { 0290 MonthCell *const cell = mScene->mMonthCellMap.value(d); 0291 0292 // Draw cell header 0293 int cellHeaderX = mScene->cellHorizontalPos(cell) + 1; 0294 int cellHeaderY = mScene->cellVerticalPos(cell) + 1; 0295 int cellHeaderWidth = columnWidth - 2; 0296 int cellHeaderHeight = cell->topMargin() - 2; 0297 const auto brush = KColorScheme(QPalette::Normal, KColorScheme::ColorSet::Header).background(KColorScheme::BackgroundRole::NormalBackground); 0298 p->setBrush(brush); 0299 p->setPen(Qt::NoPen); 0300 p->drawRect(QRect(cellHeaderX, cellHeaderY, cellHeaderWidth, cellHeaderHeight)); 0301 0302 QFont font = p->font(); 0303 font.setBold(cell->date() == todayDate); 0304 p->setFont(font); 0305 0306 if (d.month() == mMonthView->currentMonth()) { 0307 p->setPen(palette().color(QPalette::WindowText)); 0308 } else { 0309 p->setPen(oldPen); 0310 } 0311 0312 QString dayText; 0313 // Prepend month name if d is the first or last day of month 0314 if (d.day() == 1 || // d is the first day of month 0315 d.addDays(1).day() == 1) { // d is the last day of month 0316 dayText = i18nc("'Month day' for month view cells", "%1 %2", QLocale::system().monthName(d.month(), QLocale::ShortFormat), d.day()); 0317 } else { 0318 dayText = QString::number(d.day()); 0319 } 0320 p->drawText(QRect(mScene->cellHorizontalPos(cell), // top right 0321 mScene->cellVerticalPos(cell), // of the cell 0322 mScene->columnWidth() - 2, 0323 cell->topMargin()), 0324 Qt::AlignRight, 0325 dayText); 0326 0327 /* 0328 Draw arrows if all items won't fit 0329 */ 0330 0331 // Up arrow if first item is above cell top 0332 if (mScene->startHeight() != 0 && cell->hasEventBelow(mScene->startHeight())) { 0333 cell->upArrow()->setPos(mScene->cellHorizontalPos(cell) + columnWidth / 2, 0334 mScene->cellVerticalPos(cell) + cell->upArrow()->boundingRect().height() / 2 + 2); 0335 cell->upArrow()->show(); 0336 } else { 0337 cell->upArrow()->hide(); 0338 } 0339 0340 // Down arrow if last item is below cell bottom 0341 if (!mScene->lastItemFit(cell)) { 0342 cell->downArrow()->setPos(mScene->cellHorizontalPos(cell) + columnWidth / 2, 0343 mScene->cellVerticalPos(cell) + rowHeight - cell->downArrow()->boundingRect().height() / 2 - 2); 0344 cell->downArrow()->show(); 0345 } else { 0346 cell->downArrow()->hide(); 0347 } 0348 } 0349 } 0350 0351 void MonthScene::resetAll() 0352 { 0353 qDeleteAll(mMonthCellMap); 0354 mMonthCellMap.clear(); 0355 0356 qDeleteAll(mManagerList); 0357 mManagerList.clear(); 0358 0359 mSelectedItem = nullptr; 0360 mActionItem = nullptr; 0361 mClickedItem = nullptr; 0362 } 0363 0364 Akonadi::IncidenceChanger *MonthScene::incidenceChanger() const 0365 { 0366 return mMonthView->changer(); 0367 } 0368 0369 QDate MonthScene::firstDateOnRow(int row) const 0370 { 0371 return mMonthView->actualStartDateTime().date().addDays(7 * row); 0372 } 0373 0374 bool MonthScene::lastItemFit(MonthCell *cell) 0375 { 0376 if (cell->firstFreeSpace() > maxRowCount() + startHeight()) { 0377 return false; 0378 } else { 0379 return true; 0380 } 0381 } 0382 0383 int MonthScene::totalHeight() 0384 { 0385 int max = 0; 0386 for (QDate d = mMonthView->actualStartDateTime().date(); d <= mMonthView->actualEndDateTime().date(); d = d.addDays(1)) { 0387 int c = mMonthCellMap[d]->firstFreeSpace(); 0388 if (c > max) { 0389 max = c; 0390 } 0391 } 0392 0393 return max; 0394 } 0395 0396 void MonthScene::wheelEvent(QGraphicsSceneWheelEvent *event) 0397 { 0398 Q_UNUSED(event) // until we figure out what to do in here 0399 0400 /* int numDegrees = -event->delta() / 8; 0401 int numSteps = numDegrees / 15; 0402 0403 if (startHeight() + numSteps < 0) { 0404 numSteps = -startHeight(); 0405 } 0406 0407 int cellHeight = 0; 0408 0409 MonthCell *currentCell = getCellFromPos(event->scenePos()); 0410 if (currentCell) { 0411 cellHeight = currentCell->firstFreeSpace(); 0412 } 0413 if (cellHeight == 0) { 0414 // no items in this cell, there's no point to scroll 0415 return; 0416 } 0417 0418 int newHeight; 0419 int maxStartHeight = qMax(0, cellHeight - maxRowCount()); 0420 if (numSteps > 0 && startHeight() + numSteps >= maxStartHeight) { 0421 newHeight = maxStartHeight; 0422 } else { 0423 newHeight = startHeight() + numSteps; 0424 } 0425 0426 if (newHeight == startHeight()) { 0427 return; 0428 } 0429 0430 setStartHeight(newHeight); 0431 0432 foreach (MonthItem *manager, mManagerList) { 0433 manager->updateGeometry(); 0434 } 0435 0436 invalidate(QRectF(), BackgroundLayer); 0437 0438 event->accept(); 0439 */ 0440 } 0441 0442 void MonthScene::scrollCellsDown() 0443 { 0444 int newHeight = startHeight() + 1; 0445 setStartHeight(newHeight); 0446 0447 for (MonthItem *manager : std::as_const(mManagerList)) { 0448 manager->updateGeometry(); 0449 } 0450 0451 invalidate(QRectF(), BackgroundLayer); 0452 } 0453 0454 void MonthScene::scrollCellsUp() 0455 { 0456 int newHeight = startHeight() - 1; 0457 setStartHeight(newHeight); 0458 0459 for (MonthItem *manager : std::as_const(mManagerList)) { 0460 manager->updateGeometry(); 0461 } 0462 0463 invalidate(QRectF(), BackgroundLayer); 0464 } 0465 0466 void MonthScene::clickOnScrollIndicator(ScrollIndicator *scrollItem) 0467 { 0468 if (scrollItem->direction() == ScrollIndicator::UpArrow) { 0469 scrollCellsUp(); 0470 } else if (scrollItem->direction() == ScrollIndicator::DownArrow) { 0471 scrollCellsDown(); 0472 } 0473 } 0474 0475 void MonthScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *mouseEvent) 0476 { 0477 QPointF pos = mouseEvent->scenePos(); 0478 repeatTimer.stop(); 0479 MonthGraphicsItem *iItem = dynamic_cast<MonthGraphicsItem *>(itemAt(pos, {})); 0480 if (iItem) { 0481 if (iItem->monthItem()) { 0482 auto tmp = qobject_cast<IncidenceMonthItem *>(iItem->monthItem()); 0483 if (tmp) { 0484 selectItem(iItem->monthItem()); 0485 mMonthView->defaultAction(tmp->akonadiItem()); 0486 0487 mouseEvent->accept(); 0488 } 0489 } 0490 } else { 0491 Q_EMIT newEventSignal(); 0492 } 0493 } 0494 0495 void MonthScene::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent) 0496 { 0497 QPointF pos = mouseEvent->scenePos(); 0498 0499 MonthGraphicsView *view = static_cast<MonthGraphicsView *>(views().at(0)); 0500 0501 // Change cursor depending on the part of the item it hovers to inform 0502 // the user that he can resize the item. 0503 if (mActionType == None) { 0504 MonthGraphicsItem *iItem = dynamic_cast<MonthGraphicsItem *>(itemAt(pos, {})); 0505 if (iItem) { 0506 if (iItem->monthItem()->isResizable() && iItem->isBeginItem() && iItem->mapFromScene(pos).x() <= 10) { 0507 view->setActionCursor(Resize); 0508 } else if (iItem->monthItem()->isResizable() && iItem->isEndItem() && iItem->mapFromScene(pos).x() >= iItem->boundingRect().width() - 10) { 0509 view->setActionCursor(Resize); 0510 } else { 0511 view->setActionCursor(None); 0512 } 0513 } else { 0514 view->setActionCursor(None); 0515 } 0516 mouseEvent->accept(); 0517 return; 0518 } 0519 0520 // If an item was selected during the click, we maybe have an item to move ! 0521 if (mActionItem) { 0522 // Initiate action if not already done 0523 if (!mActionInitiated && mActionType != None) { 0524 if (mActionType == Move) { 0525 mActionItem->beginMove(); 0526 } else if (mActionType == Resize) { 0527 mActionItem->beginResize(); 0528 } 0529 mActionInitiated = true; 0530 } 0531 view->setActionCursor(mActionType); 0532 0533 // Move or resize action 0534 MonthCell *const currentCell = getCellFromPos(pos); 0535 if (currentCell && currentCell != mPreviousCell) { 0536 bool ok = true; 0537 if (mActionType == Move) { 0538 if (currentCell) { 0539 mActionItem->moveTo(currentCell->date()); 0540 mActionItem->updateGeometry(); 0541 } else { 0542 mActionItem->moveTo(QDate()); 0543 mActionItem->updateGeometry(); 0544 mActionItem->endMove(); 0545 mActionItem = nullptr; 0546 mActionType = None; 0547 mStartCell = nullptr; 0548 } 0549 } else if (mActionType == Resize) { 0550 ok = mActionItem->resizeBy(mPreviousCell->date().daysTo(currentCell->date())); 0551 mActionItem->updateGeometry(); 0552 } 0553 0554 if (ok) { 0555 mPreviousCell = currentCell; 0556 } 0557 update(); 0558 } 0559 mouseEvent->accept(); 0560 } 0561 } 0562 0563 void MonthScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent) 0564 { 0565 QPointF pos = mouseEvent->scenePos(); 0566 0567 mClickedItem = nullptr; 0568 mCurrentIndicator = nullptr; 0569 0570 MonthGraphicsItem *iItem = dynamic_cast<MonthGraphicsItem *>(itemAt(pos, {})); 0571 if (iItem) { 0572 mClickedItem = iItem->monthItem(); 0573 0574 selectItem(mClickedItem); 0575 if (mouseEvent->button() == Qt::RightButton) { 0576 auto tmp = qobject_cast<IncidenceMonthItem *>(mClickedItem); 0577 if (tmp) { 0578 Q_EMIT showIncidencePopupSignal(tmp->calendar(), tmp->akonadiItem(), tmp->realStartDate()); 0579 } 0580 } 0581 0582 if (mouseEvent->button() == Qt::LeftButton) { 0583 // Basic initialization for resize and move 0584 mActionItem = mClickedItem; 0585 mStartCell = getCellFromPos(pos); 0586 mPreviousCell = mStartCell; 0587 mActionInitiated = false; 0588 0589 // Move or resize ? 0590 if (iItem->monthItem()->isResizable() && iItem->isBeginItem() && iItem->mapFromScene(pos).x() <= 10) { 0591 mActionType = Resize; 0592 mResizeType = ResizeLeft; 0593 } else if (iItem->monthItem()->isResizable() && iItem->isEndItem() && iItem->mapFromScene(pos).x() >= iItem->boundingRect().width() - 10) { 0594 mActionType = Resize; 0595 mResizeType = ResizeRight; 0596 } else if (iItem->monthItem()->isMoveable()) { 0597 mActionType = Move; 0598 } 0599 } 0600 mouseEvent->accept(); 0601 } else if (ScrollIndicator *scrollItem = dynamic_cast<ScrollIndicator *>(itemAt(pos, {}))) { 0602 clickOnScrollIndicator(scrollItem); 0603 mCurrentIndicator = scrollItem; 0604 repeatTimer.start(AUTO_REPEAT_DELAY, this); 0605 } else { 0606 // unselect items when clicking somewhere else 0607 selectItem(nullptr); 0608 0609 MonthCell *cell = getCellFromPos(pos); 0610 if (cell) { 0611 mSelectedCellDate = cell->date(); 0612 update(); 0613 if (mouseEvent->button() == Qt::RightButton) { 0614 Q_EMIT showNewEventPopupSignal(); 0615 } 0616 mouseEvent->accept(); 0617 } 0618 } 0619 } 0620 0621 void MonthScene::timerEvent(QTimerEvent *e) 0622 { 0623 if (e->timerId() == repeatTimer.timerId()) { 0624 if (mCurrentIndicator->isVisible()) { 0625 clickOnScrollIndicator(mCurrentIndicator); 0626 repeatTimer.start(AUTO_REPEAT_DELAY, this); 0627 } else { 0628 mCurrentIndicator = nullptr; 0629 repeatTimer.stop(); 0630 } 0631 } 0632 } 0633 0634 void MonthScene::helpEvent(QGraphicsSceneHelpEvent *helpEvent) 0635 { 0636 // Find the first item that does tooltips 0637 const QPointF pos = helpEvent->scenePos(); 0638 MonthGraphicsItem *toolTipItem = dynamic_cast<MonthGraphicsItem *>(itemAt(pos, {})); 0639 0640 // Show or hide the tooltip 0641 QString text; 0642 QPoint point; 0643 if (toolTipItem) { 0644 text = toolTipItem->getToolTip(); 0645 point = helpEvent->screenPos(); 0646 } 0647 QToolTip::showText(point, text, helpEvent->widget()); 0648 helpEvent->setAccepted(!text.isEmpty()); 0649 } 0650 0651 void MonthScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent) 0652 { 0653 QPointF pos = mouseEvent->scenePos(); 0654 0655 static_cast<MonthGraphicsView *>(views().at(0))->setActionCursor(None); 0656 0657 repeatTimer.stop(); 0658 mCurrentIndicator = nullptr; 0659 0660 if (mActionItem) { 0661 MonthCell *currentCell = getCellFromPos(pos); 0662 0663 const bool somethingChanged = currentCell && currentCell != mStartCell; 0664 0665 if (somethingChanged) { // We want to act if a move really happened 0666 if (mActionType == Resize) { 0667 mActionItem->endResize(); 0668 } else if (mActionType == Move) { 0669 mActionItem->endMove(); 0670 } 0671 } 0672 0673 mActionItem = nullptr; 0674 mActionType = None; 0675 mStartCell = nullptr; 0676 0677 mouseEvent->accept(); 0678 } 0679 } 0680 0681 // returns true if the point is in the monthgrid (allows to avoid selecting a cell when 0682 // a click is outside the month grid 0683 bool MonthScene::isInMonthGrid(int x, int y) const 0684 { 0685 return x >= 0 && y >= 0 && x <= availableWidth() && y <= availableHeight(); 0686 } 0687 0688 // The function converts the coordinates to the month grid coordinates to 0689 // be able to locate the cell. 0690 MonthCell *MonthScene::getCellFromPos(QPointF pos) 0691 { 0692 int y = sceneYToMonthGridY(static_cast<int>(pos.y())); 0693 int x = sceneXToMonthGridX(static_cast<int>(pos.x())); 0694 if (!isInMonthGrid(x, y)) { 0695 return nullptr; 0696 } 0697 int id = (int)(y / rowHeight()) * 7 + (int)(x / columnWidth()); 0698 0699 return mMonthCellMap.value(mMonthView->actualStartDateTime().date().addDays(id)); 0700 } 0701 0702 void MonthScene::selectItem(MonthItem *item) 0703 { 0704 /* 0705 if (mSelectedItem == item) { 0706 return; 0707 } 0708 0709 I commented the above code so it's possible to selected a selected item. 0710 korg-mobile needs that, otherwise clicking on a selected item won't bring the editor up. 0711 Another solution would be to have two Q_SIGNALS: incidenceSelected() and incidenceClicked() 0712 */ 0713 0714 auto tmp = qobject_cast<IncidenceMonthItem *>(item); 0715 0716 if (!tmp) { 0717 mSelectedItem = nullptr; 0718 Q_EMIT incidenceSelected(Akonadi::Item(), QDate()); 0719 return; 0720 } 0721 0722 mSelectedItem = item; 0723 Q_ASSERT(CalendarSupport::hasIncidence(tmp->akonadiItem())); 0724 0725 if (mMonthView->selectedIncidenceDates().isEmpty()) { 0726 Q_EMIT incidenceSelected(tmp->akonadiItem(), QDate()); 0727 } else { 0728 Q_EMIT incidenceSelected(tmp->akonadiItem(), mMonthView->selectedIncidenceDates().at(0)); 0729 } 0730 update(); 0731 } 0732 0733 void MonthScene::removeIncidence(const QString &uid) 0734 { 0735 for (MonthItem *manager : std::as_const(mManagerList)) { 0736 auto imi = qobject_cast<IncidenceMonthItem *>(manager); 0737 if (!imi) { 0738 continue; 0739 } 0740 0741 KCalendarCore::Incidence::Ptr incidence = imi->incidence(); 0742 if (!incidence) { 0743 continue; 0744 } 0745 if (incidence->uid() == uid) { 0746 const auto lst = imi->monthGraphicsItems(); 0747 for (MonthGraphicsItem *gitem : lst) { 0748 removeItem(gitem); 0749 } 0750 } 0751 } 0752 } 0753 0754 //---------------------------------------------------------- 0755 MonthGraphicsView::MonthGraphicsView(MonthView *parent) 0756 : QGraphicsView(parent) 0757 , mMonthView(parent) 0758 { 0759 setMouseTracking(true); 0760 } 0761 0762 void MonthGraphicsView::setActionCursor(MonthScene::ActionType actionType) 0763 { 0764 switch (actionType) { 0765 case MonthScene::Move: 0766 #ifndef QT_NO_CURSOR 0767 setCursor(Qt::ArrowCursor); 0768 #endif 0769 break; 0770 case MonthScene::Resize: 0771 #ifndef QT_NO_CURSOR 0772 setCursor(Qt::SizeHorCursor); 0773 #endif 0774 break; 0775 #ifndef QT_NO_CURSOR 0776 default: 0777 setCursor(Qt::ArrowCursor); 0778 #endif 0779 } 0780 } 0781 0782 void MonthGraphicsView::setScene(MonthScene *scene) 0783 { 0784 mScene = scene; 0785 QGraphicsView::setScene(scene); 0786 } 0787 0788 void MonthGraphicsView::resizeEvent(QResizeEvent *event) 0789 { 0790 mScene->setSceneRect(0, 0, event->size().width(), event->size().height()); 0791 mScene->updateGeometry(); 0792 } 0793 0794 #include "moc_monthscene.cpp"