SVN_SILENT made messages (.desktop file)
[kdepim.git] / korganizer / kodaymatrix.cpp
blob0c90a9a231555da30d022c962c8d4f85d1299282
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)
453 Q_UNUSED(incidence);
454 mPendingChanges = true;
457 void KODayMatrix::setHighlightMode(bool highlightEvents,
458 bool highlightTodos,
459 bool highlightJournals)
462 // don't set mPendingChanges to true if nothing changed
463 if (highlightTodos != mHighlightTodos ||
464 highlightEvents != mHighlightEvents ||
465 highlightJournals != mHighlightJournals) {
466 mHighlightEvents = highlightEvents;
467 mHighlightTodos = highlightTodos;
468 mHighlightJournals = highlightJournals;
469 mPendingChanges = true;
473 void KODayMatrix::resourcesChanged()
475 mPendingChanges = true;
478 // ----------------------------------------------------------------------------
479 // M O U S E E V E N T H A N D L I N G
480 // ----------------------------------------------------------------------------
482 bool KODayMatrix::event(QEvent *event)
484 if (KOPrefs::instance()->mEnableToolTips && event->type() == QEvent::ToolTip) {
485 QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event);
487 // calculate which cell of the matrix the mouse is in
488 QRect sz = frameRect();
489 int dheight = sz.height() * 7 / 42;
490 int dwidth = sz.width() / 7;
491 int row = helpEvent->pos().y() / dheight;
492 int col = helpEvent->pos().x() / dwidth;
494 // show holiday names only
495 QString tipText = getHolidayLabel(col + row * 7);
496 if (!tipText.isEmpty()) {
497 QToolTip::showText(helpEvent->globalPos(), tipText);
498 } else {
499 QToolTip::hideText();
502 return QWidget::event(event);
505 void KODayMatrix::mousePressEvent(QMouseEvent *e)
507 mSelStart = getDayIndexFrom(e->x(), e->y());
508 if (e->button() == Qt::RightButton) {
509 popupMenu(mDays[mSelStart]);
510 } else if (e->button() == Qt::LeftButton) {
511 if (mSelStart > NUMDAYS - 1) {
512 mSelStart = NUMDAYS - 1;
514 mSelInit = mSelStart;
518 void KODayMatrix::popupMenu(const QDate &date)
520 QMenu popup(this);
521 popup.setTitle(date.toString());
522 QAction *newEventAction = popup.addAction(
523 QIcon::fromTheme(QStringLiteral("appointment-new")), i18n("New E&vent..."));
524 QAction *newTodoAction = popup.addAction(
525 QIcon::fromTheme(QStringLiteral("task-new")), i18n("New &To-do..."));
526 QAction *newJournalAction = popup.addAction(
527 QIcon::fromTheme(QStringLiteral("journal-new")), i18n("New &Journal..."));
528 QAction *ret = popup.exec(QCursor::pos());
529 if (ret == newEventAction) {
530 Q_EMIT newEventSignal(date);
531 } else if (ret == newTodoAction) {
532 Q_EMIT newTodoSignal(date);
533 } else if (ret == newJournalAction) {
534 Q_EMIT newJournalSignal(date);
538 void KODayMatrix::mouseReleaseEvent(QMouseEvent *e)
540 if (e->button() != Qt::LeftButton) {
541 return;
544 int tmp = getDayIndexFrom(e->x(), e->y());
545 if (tmp > NUMDAYS - 1) {
546 tmp = NUMDAYS - 1;
549 if (mSelInit > tmp) {
550 mSelEnd = mSelInit;
551 if (tmp != mSelStart) {
552 mSelStart = tmp;
553 update();
555 } else {
556 mSelStart = mSelInit;
558 //repaint only if selection has changed
559 if (tmp != mSelEnd) {
560 mSelEnd = tmp;
561 update();
565 KCalCore::DateList daylist;
566 if (mSelStart < 0) {
567 mSelStart = 0;
569 daylist.reserve(mSelEnd - mSelStart + 1);
570 for (int i = mSelStart; i <= mSelEnd; ++i) {
571 daylist.append(mDays[i]);
573 Q_EMIT selected(static_cast<const KCalCore::DateList>(daylist));
576 void KODayMatrix::mouseMoveEvent(QMouseEvent *e)
578 int tmp = getDayIndexFrom(e->x(), e->y());
579 if (tmp > NUMDAYS - 1) {
580 tmp = NUMDAYS - 1;
583 if (mSelInit > tmp) {
584 mSelEnd = mSelInit;
585 if (tmp != mSelStart) {
586 mSelStart = tmp;
587 update();
589 } else {
590 mSelStart = mSelInit;
592 //repaint only if selection has changed
593 if (tmp != mSelEnd) {
594 mSelEnd = tmp;
595 update();
600 // ----------------------------------------------------------------------------
601 // D R A G ' N D R O P H A N D L I N G
602 // ----------------------------------------------------------------------------
604 //-----------------------------------------------------------------------------
605 // Drag and Drop handling -- based on the Troll Tech dirview example
607 enum {
608 DRAG_COPY = 0,
609 DRAG_MOVE = 1,
610 DRAG_CANCEL = 2
613 void KODayMatrix::dragEnterEvent(QDragEnterEvent *e)
615 e->acceptProposedAction();
616 const QMimeData *md = e->mimeData();
617 if (!CalendarSupport::canDecode(md)) {
618 e->ignore();
619 return;
622 // some visual feedback
623 // oldPalette = palette();
624 // setPalette(my_HilitePalette);
625 // update();
628 void KODayMatrix::dragMoveEvent(QDragMoveEvent *e)
630 const QMimeData *md = e->mimeData();
631 if (!CalendarSupport::canDecode(md)) {
632 e->ignore();
633 return;
635 e->accept();
638 void KODayMatrix::dragLeaveEvent(QDragLeaveEvent *dl)
640 Q_UNUSED(dl);
641 // setPalette(oldPalette);
642 // update();
645 void KODayMatrix::dropEvent(QDropEvent *e)
647 if (!mCalendar) {
648 e->ignore();
649 return;
651 QList<QUrl> urls = (e->mimeData()->urls());
652 //qCDebug(KORGANIZER_LOG)<<" urls :"<<urls;
653 if (urls.isEmpty()) {
654 e->ignore();
655 return;
657 Akonadi::Item items;
658 //For the moment support 1 url
659 if (urls.count() >= 1) {
660 QUrl res = urls.at(0);
662 Akonadi::ItemFetchJob *job = new Akonadi::ItemFetchJob(Akonadi::Item::fromUrl(res));
663 job->fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent);
664 job->fetchScope().fetchFullPayload();
665 Akonadi::Item::List items;
666 if (job->exec()) {
667 items = job->items();
669 bool exist = items.at(0).isValid();
670 int action = DRAG_CANCEL;
672 Qt::KeyboardModifiers keyboardModifiers = e->keyboardModifiers();
674 if (keyboardModifiers & Qt::ControlModifier) {
675 action = DRAG_COPY;
676 } else if (keyboardModifiers & Qt::ShiftModifier) {
677 action = DRAG_MOVE;
678 } else {
679 QAction *copy = Q_NULLPTR, *move = Q_NULLPTR;
680 QMenu *menu = new QMenu(this);
681 if (exist) {
682 move = menu->addAction(QIcon::fromTheme(QStringLiteral("edit-paste")), i18n("&Move"));
683 if (/*existingEvent*/1) {
684 copy = menu->addAction(QIcon::fromTheme(QStringLiteral("edit-copy")), i18n("&Copy"));
686 } else {
687 move = menu->addAction(QIcon::fromTheme(QStringLiteral("list-add")), i18n("&Add"));
689 menu->addSeparator();
690 /*QAction *cancel =*/
691 menu->addAction(QIcon::fromTheme(QStringLiteral("process-stop")), i18n("&Cancel"));
692 QAction *a = menu->exec(QCursor::pos());
693 if (a == copy) {
694 action = DRAG_COPY;
695 } else if (a == move) {
696 action = DRAG_MOVE;
698 delete menu;
701 if (action == DRAG_COPY || action == DRAG_MOVE) {
702 e->accept();
703 int idx = getDayIndexFrom(e->pos().x(), e->pos().y());
705 if (action == DRAG_COPY) {
706 Q_EMIT incidenceDropped(items.at(0), mDays[idx]);
707 } else if (action == DRAG_MOVE) {
708 Q_EMIT incidenceDroppedMove(items.at(0), mDays[idx]);
714 // ----------------------------------------------------------------------------
715 // P A I N T E V E N T H A N D L I N G
716 // ----------------------------------------------------------------------------
718 void KODayMatrix::paintEvent(QPaintEvent *)
720 QPainter p;
721 const QRect rect = frameRect();
722 const int dayHeight = mDaySize.height();
723 const int dayWidth = mDaySize.width();
724 int row, column;
725 int selectionWidth, selectionHeight;
726 const bool isRTL = KOGlobals::self()->reverseLayout();
728 QPalette pal = palette();
730 p.begin(this);
732 // draw background
733 p.fillRect(0, 0, rect.width(), rect.height(), QBrush(pal.color(QPalette::Base)));
735 // draw topleft frame
736 p.setPen(pal.color(QPalette::Mid));
737 p.drawRect(0, 0, rect.width() - 1, rect.height() - 1);
738 // don't paint over borders
739 p.translate(1, 1);
741 // draw selected days with highlighted background color
742 if (mSelStart != NOSELECTION) {
744 row = mSelStart / 7;
745 // fix larger selections starting in the previous month
746 if (row < 0 && mSelEnd > 0) {
747 row = 0;
749 column = mSelStart - row * 7;
750 const QColor selectionColor = KOPrefs::instance()->agendaGridHighlightColor();
752 if (row < 6 && row >= 0) {
753 if (row == mSelEnd / 7) {
754 // Single row selection
755 p.fillRect(isRTL ?
756 (7 - (mSelEnd - mSelStart + 1) - column) * dayWidth :
757 column * dayWidth,
758 row * dayHeight,
759 (mSelEnd - mSelStart + 1) * dayWidth, dayHeight, selectionColor);
760 } else {
761 // draw first row to the right
762 p.fillRect(isRTL ? 0 : column * dayWidth, row * dayHeight,
763 (7 - column) * dayWidth, dayHeight, selectionColor);
764 // draw full block till last line
765 selectionHeight = mSelEnd / 7 - row;
766 if (selectionHeight + row >= 6) {
767 selectionHeight = 6 - row;
769 if (selectionHeight > 1) {
770 p.fillRect(0, (row + 1) * dayHeight, 7 * dayWidth,
771 (selectionHeight - 1) * dayHeight, selectionColor);
773 // draw last block from left to mSelEnd
774 if (mSelEnd / 7 < 6) {
775 selectionWidth = mSelEnd - 7 * (mSelEnd / 7) + 1;
776 p.fillRect(isRTL ?
777 (7 - selectionWidth) * dayWidth :
779 (row + selectionHeight) * dayHeight,
780 selectionWidth * dayWidth, dayHeight, selectionColor);
786 // iterate over all days in the matrix and draw the day label in appropriate colors
787 const QColor textColor = pal.color(QPalette::Text);
788 const QColor textColorShaded = getShadedColor(textColor);
789 QColor actcol = textColorShaded;
790 p.setPen(actcol);
791 QPen tmppen;
793 const QList<QDate> workDays = KOGlobals::self()->workDays(mDays[0], mDays[NUMDAYS - 1]);
794 for (int i = 0; i < NUMDAYS; ++i) {
795 row = i / 7;
796 column = isRTL ? 6 - (i - row * 7) : i - row * 7;
798 // if it is the first day of a month switch color from normal to shaded and vice versa
799 if (KOGlobals::self()->calendarSystem()->day(mDays[i]) == 1) {
800 if (actcol == textColorShaded) {
801 actcol = textColor;
802 } else {
803 actcol = textColorShaded;
805 p.setPen(actcol);
808 //Reset pen color after selected days block
809 if (i == mSelEnd + 1) {
810 p.setPen(actcol);
813 const bool holiday = !workDays.contains(mDays[i]);
815 const QColor holidayColorShaded =
816 getShadedColor(KOPrefs::instance()->agendaHolidaysBackgroundColor());
818 // if today then draw rectangle around day
819 if (mToday == i) {
820 tmppen = p.pen();
821 QPen todayPen(p.pen());
823 todayPen.setWidth(mTodayMarginWidth);
824 //draw red rectangle for holidays
825 if (holiday) {
826 if (actcol == textColor) {
827 todayPen.setColor(KOPrefs::instance()->agendaHolidaysBackgroundColor());
828 } else {
829 todayPen.setColor(holidayColorShaded);
832 //draw gray rectangle for today if in selection
833 if (i >= mSelStart && i <= mSelEnd) {
834 const QColor grey(QStringLiteral("grey"));
835 todayPen.setColor(grey);
837 p.setPen(todayPen);
838 p.drawRect(column * dayWidth, row * dayHeight, dayWidth, dayHeight);
839 p.setPen(tmppen);
842 // if any events are on that day then draw it using a bold font
843 if (mEvents.contains(mDays[i])) {
844 QFont myFont = font();
845 myFont.setBold(true);
846 p.setFont(myFont);
849 // if it is a holiday then use the default holiday color
850 if (holiday) {
851 if (actcol == textColor) {
852 p.setPen(KOPrefs::instance()->agendaHolidaysBackgroundColor());
853 } else {
854 p.setPen(holidayColorShaded);
858 // draw selected days with special color
859 if (i >= mSelStart && i <= mSelEnd && !holiday) {
860 p.setPen(Qt::white);
863 p.drawText(column * dayWidth, row * dayHeight, dayWidth, dayHeight,
864 Qt::AlignHCenter | Qt::AlignVCenter, mDayLabels[i]);
866 // reset color to actual color
867 if (holiday) {
868 p.setPen(actcol);
870 // reset bold font to plain font
871 if (mEvents.contains(mDays[i]) > 0) {
872 QFont myFont = font();
873 myFont.setBold(false);
874 p.setFont(myFont);
877 p.end();
880 // ----------------------------------------------------------------------------
881 // R E SI Z E E V E N T H A N D L I N G
882 // ----------------------------------------------------------------------------
884 void KODayMatrix::resizeEvent(QResizeEvent *)
886 QRect sz = frameRect();
887 mDaySize.setHeight(sz.height() * 7 / NUMDAYS);
888 mDaySize.setWidth(sz.width() / 7);
891 /* static */
892 QPair<QDate, QDate> KODayMatrix::matrixLimits(const QDate &month)
894 const KCalendarSystem *calSys = KOGlobals::self()->calendarSystem();
895 QDate d = month;
896 calSys->setDate(d, calSys->year(month), calSys->month(month), 1);
898 const int dayOfWeek = calSys->dayOfWeek(d);
899 const int weekstart = QLocale().firstDayOfWeek();
901 d = d.addDays(-(7 + dayOfWeek - weekstart) % 7);
903 if (dayOfWeek == weekstart) {
904 d = d.addDays(-7); // Start on the second line
907 return qMakePair(d, d.addDays(NUMDAYS - 1));