SVN_SILENT made messages (.desktop file) - always resolve ours
[kdepim.git] / korganizer / kodaymatrix.cpp
blob9359f530de93783e02f39b37e3ee00cdbbddc0bb
1 /*
2 This file is part of KOrganizer.
4 Copyright (c) 2001 Eitzenberger Thomas <thomas.eitzenberger@siemens.at>
5 Parts of the source code have been copied from kdpdatebutton.cpp
7 Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org>
8 Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License along
21 with this program; if not, write to the Free Software Foundation, Inc.,
22 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24 As a special exception, permission is given to link this program
25 with any edition of Qt, and distribute the resulting executable,
26 without including the source code for Qt in the source distribution.
29 #include "kodaymatrix.h"
30 #include "koglobals.h"
31 #include "prefs/koprefs.h"
33 #include <CalendarSupport/Utils>
35 #include <AkonadiCore/ItemFetchJob>
36 #include <AkonadiCore/ItemFetchScope>
38 #include <KCalendarSystem>
39 #include <QUrl>
40 #include <QIcon>
41 #include <QMenu>
43 #include <QMouseEvent>
44 #include <QPainter>
45 #include <QToolTip>
46 #include <QMimeData>
47 #include <KLocalizedString>
48 #include <QLocale>
50 // ============================================================================
51 // K O D A Y M A T R I X
52 // ============================================================================
54 const int KODayMatrix::NOSELECTION = -1000;
55 const int KODayMatrix::NUMDAYS = 42;
57 KODayMatrix::KODayMatrix(QWidget *parent)
58 : QFrame(parent), mStartDate(), mPendingChanges(false)
60 // initialize dynamic arrays
61 mDays = new QDate[NUMDAYS];
62 mDayLabels = new QString[NUMDAYS];
64 mTodayMarginWidth = 2;
65 mSelEnd = mSelStart = NOSELECTION;
67 recalculateToday();
69 mHighlightEvents = true;
70 mHighlightTodos = false;
71 mHighlightJournals = false;
74 void KODayMatrix::setCalendar(const Akonadi::ETMCalendar::Ptr &calendar)
76 if (mCalendar) {
77 mCalendar->unregisterObserver(this);
78 mCalendar->disconnect(this);
81 mCalendar = calendar;
82 mCalendar->registerObserver(this);
84 setAcceptDrops(mCalendar != Q_NULLPTR);
85 updateIncidences();
88 QColor KODayMatrix::getShadedColor(const QColor &color) const
90 QColor shaded;
91 int h = 0;
92 int s = 0;
93 int v = 0;
94 color.getHsv(&h, &s, &v);
95 s = s / 4;
96 v = 192 + v / 4;
97 shaded.setHsv(h, s, v);
99 return shaded;
102 KODayMatrix::~KODayMatrix()
104 if (mCalendar) {
105 mCalendar->unregisterObserver(this);
108 delete [] mDays;
109 delete [] mDayLabels;
112 void KODayMatrix::addSelectedDaysTo(KCalCore::DateList &selDays)
114 if (mSelStart == NOSELECTION) {
115 return;
118 // cope with selection being out of matrix limits at top (< 0)
119 int i0 = mSelStart;
120 if (i0 < 0) {
121 for (int i = i0; i < 0; ++i) {
122 selDays.append(mDays[0].addDays(i));
124 i0 = 0;
127 // cope with selection being out of matrix limits at bottom (> NUMDAYS-1)
128 if (mSelEnd > NUMDAYS - 1) {
129 for (int i = i0; i <= NUMDAYS - 1; ++i) {
130 selDays.append(mDays[i]);
132 for (int i = NUMDAYS; i < mSelEnd; ++i) {
133 selDays.append(mDays[0].addDays(i));
135 } else {
136 // apply normal routine to selection being entirely within matrix limits
137 for (int i = i0; i <= mSelEnd; ++i) {
138 selDays.append(mDays[i]);
143 void KODayMatrix::setSelectedDaysFrom(const QDate &start, const QDate &end)
145 if (mStartDate.isValid()) {
146 mSelStart = mStartDate.daysTo(start);
147 mSelEnd = mStartDate.daysTo(end);
151 void KODayMatrix::clearSelection()
153 mSelEnd = mSelStart = NOSELECTION;
156 void KODayMatrix::recalculateToday()
158 if (!mStartDate.isValid()) {
159 return;
162 mToday = -1;
163 for (int i = 0; i < NUMDAYS; ++i) {
164 mDays[i] = mStartDate.addDays(i);
165 mDayLabels[i] = QString::number(KOGlobals::self()->calendarSystem()->day(mDays[i]));
167 // if today is in the currently displayed month, hilight today
168 if (mDays[i].year() == QDate::currentDate().year() &&
169 mDays[i].month() == QDate::currentDate().month() &&
170 mDays[i].day() == QDate::currentDate().day()) {
171 mToday = i;
176 void KODayMatrix::updateView()
178 updateView(mStartDate);
181 void KODayMatrix::setUpdateNeeded()
183 mPendingChanges = true;
186 void KODayMatrix::updateView(const QDate &actdate)
188 if (!actdate.isValid() || NUMDAYS < 1) {
189 return;
191 //flag to indicate if the starting day of the matrix has changed by this call
192 bool daychanged = false;
194 // if a new startdate is to be set then apply Cornelius's calculation
195 // of the first day to be shown
196 if (actdate != mStartDate) {
197 // reset index of selection according to shift of starting date from
198 // startdate to actdate.
199 if (mSelStart != NOSELECTION) {
200 int tmp = actdate.daysTo(mStartDate);
201 // shift selection if new one would be visible at least partly !
202 if (mSelStart + tmp < NUMDAYS && mSelEnd + tmp >= 0) {
203 // nested if required for next X display pushed from a different month
204 // correction required. otherwise, for month forward and backward,
205 // it must be avoided.
206 if (mSelStart > NUMDAYS || mSelStart < 0) {
207 mSelStart = mSelStart + tmp;
209 if (mSelEnd > NUMDAYS || mSelEnd < 0) {
210 mSelEnd = mSelEnd + tmp;
215 mStartDate = actdate;
216 daychanged = true;
219 if (daychanged) {
220 recalculateToday();
223 // The calendar has not changed in the meantime and the selected range
224 // is still the same so we can save the expensive updateIncidences() call
225 if (!daychanged && !mPendingChanges) {
226 return;
229 // TODO_Recurrence: If we just change the selection, but not the data,
230 // there's no need to update the whole list of incidences... This is just a
231 // waste of computational power
232 updateIncidences();
233 QMap<QDate, QStringList> holidaysByDate = KOGlobals::self()->holiday(mDays[0], mDays[NUMDAYS - 1]);
234 for (int i = 0; i < NUMDAYS; ++i) {
235 //if it is a holy day then draw it red. Sundays are consider holidays, too
236 QStringList holidays = holidaysByDate[mDays[i]];
237 QString holiStr;
239 if ((KOGlobals::self()->calendarSystem()->dayOfWeek(mDays[i]) ==
240 KLocale::global()->weekDayOfPray()) ||
241 !holidays.isEmpty()) {
242 if (!holidays.isEmpty()) {
243 holiStr = holidays.join(i18nc("delimiter for joining holiday names", ","));
245 if (holiStr.isEmpty()) {
246 holiStr = QStringLiteral("");
249 mHolidays[i] = holiStr;
253 void KODayMatrix::updateIncidences()
255 if (!mCalendar) {
256 return;
259 mEvents.clear();
261 if (mHighlightEvents) {
262 updateEvents();
265 if (mHighlightTodos) {
266 updateTodos();
269 if (mHighlightJournals) {
270 updateJournals();
273 mPendingChanges = false;
276 void KODayMatrix::updateJournals()
278 const KCalCore::Incidence::List incidences = mCalendar->incidences();
280 foreach (const KCalCore::Incidence::Ptr &inc, incidences) {
281 Q_ASSERT(inc);
282 QDate d = inc->dtStart().toTimeSpec(mCalendar->timeSpec()).date();
283 if (inc->type() == KCalCore::Incidence::TypeJournal &&
284 d >= mDays[0] &&
285 d <= mDays[NUMDAYS - 1] &&
286 !mEvents.contains(d)) {
287 mEvents.append(d);
289 if (mEvents.count() == NUMDAYS) {
290 // No point in wasting cpu, all days are bold already
291 break;
297 * Although updateTodos() is simpler it has some similarities with updateEvent()
298 * but don't bother refactoring them so they share code, there's a bigger fish:
299 * Try to refactor updateTodos(), updateEvent(), updateJournals(), monthview,
300 * agenda view, timespent view, timeline view, event list view and todo list view
301 * all these 9 places have incidence listing code in common, maybe it could go
302 * to kcal. Ah, and then there's kontact's summary view which still uses
303 * the old CPU consuming code.
305 void KODayMatrix::updateTodos()
307 const KCalCore::Todo::List incidences = mCalendar->todos();
308 QDate d;
309 foreach (const KCalCore::Todo::Ptr &t, incidences) {
310 if (mEvents.count() == NUMDAYS) {
311 // No point in wasting cpu, all days are bold already
312 break;
314 Q_ASSERT(t);
315 if (t->hasDueDate()) {
316 ushort recurType = t->recurrenceType();
318 if (t->recurs() &&
319 !(recurType == KCalCore::Recurrence::rDaily && !KOPrefs::instance()->mDailyRecur) &&
320 !(recurType == KCalCore::Recurrence::rWeekly && !KOPrefs::instance()->mWeeklyRecur)) {
322 // It's a recurring todo, find out in which days it occurs
323 KCalCore::DateTimeList timeDateList =
324 t->recurrence()->timesInInterval(
325 KDateTime(mDays[0], mCalendar->timeSpec()),
326 KDateTime(mDays[NUMDAYS - 1], mCalendar->timeSpec()));
328 foreach (const KDateTime &dt, timeDateList) {
329 d = dt.toTimeSpec(mCalendar->timeSpec()).date();
330 if (!mEvents.contains(d)) {
331 mEvents.append(d);
335 } else {
336 d = t->dtDue().toTimeSpec(mCalendar->timeSpec()).date();
337 if (d >= mDays[0] && d <= mDays[NUMDAYS - 1] && !mEvents.contains(d)) {
338 mEvents.append(d);
345 void KODayMatrix::updateEvents()
347 if (mEvents.count() == NUMDAYS) {
348 mPendingChanges = false;
349 // No point in wasting cpu, all days are bold already
350 return;
352 KCalCore::Event::List eventlist = mCalendar->events(mDays[0], mDays[NUMDAYS - 1],
353 mCalendar->timeSpec());
355 Q_FOREACH (const KCalCore::Event::Ptr &event, eventlist) {
356 if (mEvents.count() == NUMDAYS) {
357 // No point in wasting cpu, all days are bold already
358 break;
361 Q_ASSERT(event);
362 const ushort recurType = event->recurrenceType();
363 const KDateTime dtStart = event->dtStart().toTimeSpec(mCalendar->timeSpec());
365 // timed incidences occur in
366 // [dtStart(), dtEnd()[. All-day incidences occur in [dtStart(), dtEnd()]
367 // so we subtract 1 second in the timed case
368 const int secsToAdd = event->allDay() ? 0 : -1;
369 const KDateTime dtEnd = event->dtEnd().toTimeSpec(mCalendar->timeSpec()).addSecs(secsToAdd);
371 if (!(recurType == KCalCore::Recurrence::rDaily && !KOPrefs::instance()->mDailyRecur) &&
372 !(recurType == KCalCore::Recurrence::rWeekly && !KOPrefs::instance()->mWeeklyRecur)) {
374 KCalCore::DateTimeList timeDateList;
375 const bool isRecurrent = event->recurs();
376 const int eventDuration = dtStart.daysTo(dtEnd);
378 if (isRecurrent) {
379 //Its a recurring event, find out in which days it occurs
380 timeDateList = event->recurrence()->timesInInterval(
381 KDateTime(mDays[0], mCalendar->timeSpec()),
382 KDateTime(mDays[NUMDAYS - 1], mCalendar->timeSpec()));
383 } else {
384 if (dtStart.date() >= mDays[0]) {
385 timeDateList.append(dtStart);
386 } else {
387 // The event starts in another month (not visible))
388 timeDateList.append(KDateTime(mDays[0], mCalendar->timeSpec()));
392 KCalCore::DateTimeList::iterator t;
393 for (t = timeDateList.begin(); t != timeDateList.end(); ++t) {
394 //This could be a multiday event, so iterate from dtStart() to dtEnd()
395 QDate d = t->toTimeSpec(mCalendar->timeSpec()).date();
396 int j = 0;
398 QDate occurrenceEnd;
399 if (isRecurrent) {
400 occurrenceEnd = d.addDays(eventDuration);
401 } else {
402 occurrenceEnd = dtEnd.date();
405 do {
406 mEvents.append(d);
407 ++j;
408 d = d.addDays(1);
409 } while (d <= occurrenceEnd && j < NUMDAYS);
413 mPendingChanges = false;
416 const QDate &KODayMatrix::getDate(int offset) const
418 if (offset < 0 || offset > NUMDAYS - 1) {
419 return mDays[0];
421 return mDays[offset];
424 QString KODayMatrix::getHolidayLabel(int offset) const
426 if (offset < 0 || offset > NUMDAYS - 1) {
427 return QString();
429 return mHolidays[offset];
432 int KODayMatrix::getDayIndexFrom(int x, int y) const
434 return 7 * (y / mDaySize.height()) +
435 (KOGlobals::self()->reverseLayout() ?
436 6 - x / mDaySize.width() : x / mDaySize.width());
439 void KODayMatrix::calendarIncidenceAdded(const KCalCore::Incidence::Ptr &incidence)
441 Q_UNUSED(incidence);
442 mPendingChanges = true;
445 void KODayMatrix::calendarIncidenceChanged(const KCalCore::Incidence::Ptr &incidence)
447 Q_UNUSED(incidence);
448 mPendingChanges = true;
451 void KODayMatrix::calendarIncidenceDeleted(const KCalCore::Incidence::Ptr &incidence, const KCalCore::Calendar *calendar)
453 Q_UNUSED(incidence);
454 Q_UNUSED(calendar);
455 mPendingChanges = true;
458 void KODayMatrix::setHighlightMode(bool highlightEvents,
459 bool highlightTodos,
460 bool highlightJournals)
463 // don't set mPendingChanges to true if nothing changed
464 if (highlightTodos != mHighlightTodos ||
465 highlightEvents != mHighlightEvents ||
466 highlightJournals != mHighlightJournals) {
467 mHighlightEvents = highlightEvents;
468 mHighlightTodos = highlightTodos;
469 mHighlightJournals = highlightJournals;
470 mPendingChanges = true;
474 void KODayMatrix::resourcesChanged()
476 mPendingChanges = true;
479 // ----------------------------------------------------------------------------
480 // M O U S E E V E N T H A N D L I N G
481 // ----------------------------------------------------------------------------
483 bool KODayMatrix::event(QEvent *event)
485 if (KOPrefs::instance()->mEnableToolTips && event->type() == QEvent::ToolTip) {
486 QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event);
488 // calculate which cell of the matrix the mouse is in
489 QRect sz = frameRect();
490 int dheight = sz.height() * 7 / 42;
491 int dwidth = sz.width() / 7;
492 int row = helpEvent->pos().y() / dheight;
493 int col = helpEvent->pos().x() / dwidth;
495 // show holiday names only
496 QString tipText = getHolidayLabel(col + row * 7);
497 if (!tipText.isEmpty()) {
498 QToolTip::showText(helpEvent->globalPos(), tipText);
499 } else {
500 QToolTip::hideText();
503 return QWidget::event(event);
506 void KODayMatrix::mousePressEvent(QMouseEvent *e)
508 mSelStart = getDayIndexFrom(e->x(), e->y());
509 if (e->button() == Qt::RightButton) {
510 popupMenu(mDays[mSelStart]);
511 } else if (e->button() == Qt::LeftButton) {
512 if (mSelStart > NUMDAYS - 1) {
513 mSelStart = NUMDAYS - 1;
515 mSelInit = mSelStart;
519 void KODayMatrix::popupMenu(const QDate &date)
521 QMenu popup(this);
522 popup.setTitle(date.toString());
523 QAction *newEventAction = popup.addAction(
524 QIcon::fromTheme(QStringLiteral("appointment-new")), i18n("New E&vent..."));
525 QAction *newTodoAction = popup.addAction(
526 QIcon::fromTheme(QStringLiteral("task-new")), i18n("New &To-do..."));
527 QAction *newJournalAction = popup.addAction(
528 QIcon::fromTheme(QStringLiteral("journal-new")), i18n("New &Journal..."));
529 QAction *ret = popup.exec(QCursor::pos());
530 if (ret == newEventAction) {
531 Q_EMIT newEventSignal(date);
532 } else if (ret == newTodoAction) {
533 Q_EMIT newTodoSignal(date);
534 } else if (ret == newJournalAction) {
535 Q_EMIT newJournalSignal(date);
539 void KODayMatrix::mouseReleaseEvent(QMouseEvent *e)
541 if (e->button() != Qt::LeftButton) {
542 return;
545 int tmp = getDayIndexFrom(e->x(), e->y());
546 if (tmp > NUMDAYS - 1) {
547 tmp = NUMDAYS - 1;
550 if (mSelInit > tmp) {
551 mSelEnd = mSelInit;
552 if (tmp != mSelStart) {
553 mSelStart = tmp;
554 update();
556 } else {
557 mSelStart = mSelInit;
559 //repaint only if selection has changed
560 if (tmp != mSelEnd) {
561 mSelEnd = tmp;
562 update();
566 KCalCore::DateList daylist;
567 if (mSelStart < 0) {
568 mSelStart = 0;
570 daylist.reserve(mSelEnd - mSelStart + 1);
571 for (int i = mSelStart; i <= mSelEnd; ++i) {
572 daylist.append(mDays[i]);
574 Q_EMIT selected(static_cast<const KCalCore::DateList>(daylist));
577 void KODayMatrix::mouseMoveEvent(QMouseEvent *e)
579 int tmp = getDayIndexFrom(e->x(), e->y());
580 if (tmp > NUMDAYS - 1) {
581 tmp = NUMDAYS - 1;
584 if (mSelInit > tmp) {
585 mSelEnd = mSelInit;
586 if (tmp != mSelStart) {
587 mSelStart = tmp;
588 update();
590 } else {
591 mSelStart = mSelInit;
593 //repaint only if selection has changed
594 if (tmp != mSelEnd) {
595 mSelEnd = tmp;
596 update();
601 // ----------------------------------------------------------------------------
602 // D R A G ' N D R O P H A N D L I N G
603 // ----------------------------------------------------------------------------
605 //-----------------------------------------------------------------------------
606 // Drag and Drop handling -- based on the Troll Tech dirview example
608 enum {
609 DRAG_COPY = 0,
610 DRAG_MOVE = 1,
611 DRAG_CANCEL = 2
614 void KODayMatrix::dragEnterEvent(QDragEnterEvent *e)
616 e->acceptProposedAction();
617 const QMimeData *md = e->mimeData();
618 if (!CalendarSupport::canDecode(md)) {
619 e->ignore();
620 return;
623 // some visual feedback
624 // oldPalette = palette();
625 // setPalette(my_HilitePalette);
626 // update();
629 void KODayMatrix::dragMoveEvent(QDragMoveEvent *e)
631 const QMimeData *md = e->mimeData();
632 if (!CalendarSupport::canDecode(md)) {
633 e->ignore();
634 return;
636 e->accept();
639 void KODayMatrix::dragLeaveEvent(QDragLeaveEvent *dl)
641 Q_UNUSED(dl);
642 // setPalette(oldPalette);
643 // update();
646 void KODayMatrix::dropEvent(QDropEvent *e)
648 if (!mCalendar) {
649 e->ignore();
650 return;
652 QList<QUrl> urls = (e->mimeData()->urls());
653 //qCDebug(KORGANIZER_LOG)<<" urls :"<<urls;
654 if (urls.isEmpty()) {
655 e->ignore();
656 return;
658 Akonadi::Item items;
659 //For the moment support 1 url
660 if (urls.count() >= 1) {
661 QUrl res = urls.at(0);
663 Akonadi::ItemFetchJob *job = new Akonadi::ItemFetchJob(Akonadi::Item::fromUrl(res));
664 job->fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent);
665 job->fetchScope().fetchFullPayload();
666 Akonadi::Item::List items;
667 if (job->exec()) {
668 items = job->items();
670 bool exist = items.at(0).isValid();
671 int action = DRAG_CANCEL;
673 Qt::KeyboardModifiers keyboardModifiers = e->keyboardModifiers();
675 if (keyboardModifiers & Qt::ControlModifier) {
676 action = DRAG_COPY;
677 } else if (keyboardModifiers & Qt::ShiftModifier) {
678 action = DRAG_MOVE;
679 } else {
680 QAction *copy = Q_NULLPTR, *move = Q_NULLPTR;
681 QMenu *menu = new QMenu(this);
682 if (exist) {
683 move = menu->addAction(QIcon::fromTheme(QStringLiteral("edit-paste")), i18n("&Move"));
684 if (/*existingEvent*/1) {
685 copy = menu->addAction(QIcon::fromTheme(QStringLiteral("edit-copy")), i18n("&Copy"));
687 } else {
688 move = menu->addAction(QIcon::fromTheme(QStringLiteral("list-add")), i18n("&Add"));
690 menu->addSeparator();
691 /*QAction *cancel =*/
692 menu->addAction(QIcon::fromTheme(QStringLiteral("process-stop")), i18n("&Cancel"));
693 QAction *a = menu->exec(QCursor::pos());
694 if (a == copy) {
695 action = DRAG_COPY;
696 } else if (a == move) {
697 action = DRAG_MOVE;
699 delete menu;
702 if (action == DRAG_COPY || action == DRAG_MOVE) {
703 e->accept();
704 int idx = getDayIndexFrom(e->pos().x(), e->pos().y());
706 if (action == DRAG_COPY) {
707 Q_EMIT incidenceDropped(items.at(0), mDays[idx]);
708 } else if (action == DRAG_MOVE) {
709 Q_EMIT incidenceDroppedMove(items.at(0), mDays[idx]);
715 // ----------------------------------------------------------------------------
716 // P A I N T E V E N T H A N D L I N G
717 // ----------------------------------------------------------------------------
719 void KODayMatrix::paintEvent(QPaintEvent *)
721 QPainter p;
722 const QRect rect = frameRect();
723 const int dayHeight = mDaySize.height();
724 const int dayWidth = mDaySize.width();
725 int row, column;
726 int selectionWidth, selectionHeight;
727 const bool isRTL = KOGlobals::self()->reverseLayout();
729 QPalette pal = palette();
731 p.begin(this);
733 // draw background
734 p.fillRect(0, 0, rect.width(), rect.height(), QBrush(pal.color(QPalette::Base)));
736 // draw topleft frame
737 p.setPen(pal.color(QPalette::Mid));
738 p.drawRect(0, 0, rect.width() - 1, rect.height() - 1);
739 // don't paint over borders
740 p.translate(1, 1);
742 // draw selected days with highlighted background color
743 if (mSelStart != NOSELECTION) {
745 row = mSelStart / 7;
746 // fix larger selections starting in the previous month
747 if (row < 0 && mSelEnd > 0) {
748 row = 0;
750 column = mSelStart - row * 7;
751 const QColor selectionColor = KOPrefs::instance()->agendaGridHighlightColor();
753 if (row < 6 && row >= 0) {
754 if (row == mSelEnd / 7) {
755 // Single row selection
756 p.fillRect(isRTL ?
757 (7 - (mSelEnd - mSelStart + 1) - column) * dayWidth :
758 column * dayWidth,
759 row * dayHeight,
760 (mSelEnd - mSelStart + 1) * dayWidth, dayHeight, selectionColor);
761 } else {
762 // draw first row to the right
763 p.fillRect(isRTL ? 0 : column * dayWidth, row * dayHeight,
764 (7 - column) * dayWidth, dayHeight, selectionColor);
765 // draw full block till last line
766 selectionHeight = mSelEnd / 7 - row;
767 if (selectionHeight + row >= 6) {
768 selectionHeight = 6 - row;
770 if (selectionHeight > 1) {
771 p.fillRect(0, (row + 1) * dayHeight, 7 * dayWidth,
772 (selectionHeight - 1) * dayHeight, selectionColor);
774 // draw last block from left to mSelEnd
775 if (mSelEnd / 7 < 6) {
776 selectionWidth = mSelEnd - 7 * (mSelEnd / 7) + 1;
777 p.fillRect(isRTL ?
778 (7 - selectionWidth) * dayWidth :
780 (row + selectionHeight) * dayHeight,
781 selectionWidth * dayWidth, dayHeight, selectionColor);
787 // iterate over all days in the matrix and draw the day label in appropriate colors
788 const QColor textColor = pal.color(QPalette::Text);
789 const QColor textColorShaded = getShadedColor(textColor);
790 QColor actcol = textColorShaded;
791 p.setPen(actcol);
792 QPen tmppen;
794 const QList<QDate> workDays = KOGlobals::self()->workDays(mDays[0], mDays[NUMDAYS - 1]);
795 for (int i = 0; i < NUMDAYS; ++i) {
796 row = i / 7;
797 column = isRTL ? 6 - (i - row * 7) : i - row * 7;
799 // if it is the first day of a month switch color from normal to shaded and vice versa
800 if (KOGlobals::self()->calendarSystem()->day(mDays[i]) == 1) {
801 if (actcol == textColorShaded) {
802 actcol = textColor;
803 } else {
804 actcol = textColorShaded;
806 p.setPen(actcol);
809 //Reset pen color after selected days block
810 if (i == mSelEnd + 1) {
811 p.setPen(actcol);
814 const bool holiday = !workDays.contains(mDays[i]);
816 const QColor holidayColorShaded =
817 getShadedColor(KOPrefs::instance()->agendaHolidaysBackgroundColor());
819 // if today then draw rectangle around day
820 if (mToday == i) {
821 tmppen = p.pen();
822 QPen todayPen(p.pen());
824 todayPen.setWidth(mTodayMarginWidth);
825 //draw red rectangle for holidays
826 if (holiday) {
827 if (actcol == textColor) {
828 todayPen.setColor(KOPrefs::instance()->agendaHolidaysBackgroundColor());
829 } else {
830 todayPen.setColor(holidayColorShaded);
833 //draw gray rectangle for today if in selection
834 if (i >= mSelStart && i <= mSelEnd) {
835 const QColor grey(QStringLiteral("grey"));
836 todayPen.setColor(grey);
838 p.setPen(todayPen);
839 p.drawRect(column * dayWidth, row * dayHeight, dayWidth, dayHeight);
840 p.setPen(tmppen);
843 // if any events are on that day then draw it using a bold font
844 if (mEvents.contains(mDays[i])) {
845 QFont myFont = font();
846 myFont.setBold(true);
847 p.setFont(myFont);
850 // if it is a holiday then use the default holiday color
851 if (holiday) {
852 if (actcol == textColor) {
853 p.setPen(KOPrefs::instance()->agendaHolidaysBackgroundColor());
854 } else {
855 p.setPen(holidayColorShaded);
859 // draw selected days with special color
860 if (i >= mSelStart && i <= mSelEnd && !holiday) {
861 p.setPen(Qt::white);
864 p.drawText(column * dayWidth, row * dayHeight, dayWidth, dayHeight,
865 Qt::AlignHCenter | Qt::AlignVCenter, mDayLabels[i]);
867 // reset color to actual color
868 if (holiday) {
869 p.setPen(actcol);
871 // reset bold font to plain font
872 if (mEvents.contains(mDays[i]) > 0) {
873 QFont myFont = font();
874 myFont.setBold(false);
875 p.setFont(myFont);
878 p.end();
881 // ----------------------------------------------------------------------------
882 // R E SI Z E E V E N T H A N D L I N G
883 // ----------------------------------------------------------------------------
885 void KODayMatrix::resizeEvent(QResizeEvent *)
887 QRect sz = frameRect();
888 mDaySize.setHeight(sz.height() * 7 / NUMDAYS);
889 mDaySize.setWidth(sz.width() / 7);
892 /* static */
893 QPair<QDate, QDate> KODayMatrix::matrixLimits(const QDate &month)
895 const KCalendarSystem *calSys = KOGlobals::self()->calendarSystem();
896 QDate d = month;
897 calSys->setDate(d, calSys->year(month), calSys->month(month), 1);
899 const int dayOfWeek = calSys->dayOfWeek(d);
900 const int weekstart = QLocale().firstDayOfWeek();
902 d = d.addDays(-(7 + dayOfWeek - weekstart) % 7);
904 if (dayOfWeek == weekstart) {
905 d = d.addDays(-7); // Start on the second line
908 return qMakePair(d, d.addDays(NUMDAYS - 1));