Better wording
[kdepim.git] / korganizer / kodaymatrix.cpp
blob6317d36f64eb8fd398d5fad1e298209d113a3bdb
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 "koprefs.h"
33 #include <calendarsupport/utils.h>
35 #include <Akonadi/ItemFetchJob>
36 #include <Akonadi/ItemFetchScope>
38 #include <KCalendarSystem>
39 #include <KIcon>
40 #include <KMenu>
42 #include <QMouseEvent>
43 #include <QPainter>
44 #include <QToolTip>
46 // ============================================================================
47 // K O D A Y M A T R I X
48 // ============================================================================
50 const int KODayMatrix::NOSELECTION = -1000;
51 const int KODayMatrix::NUMDAYS = 42;
53 KODayMatrix::KODayMatrix( QWidget *parent )
54 : QFrame( parent ), mCalendar( 0 ), mStartDate(), mPendingChanges( false )
56 // initialize dynamic arrays
57 mDays = new QDate[NUMDAYS];
58 mDayLabels = new QString[NUMDAYS];
60 mTodayMarginWidth = 2;
61 mSelEnd = mSelStart = NOSELECTION;
63 recalculateToday();
65 mHighlightEvents = true;
66 mHighlightTodos = false;
67 mHighlightJournals = false;
70 void KODayMatrix::setCalendar( CalendarSupport::Calendar *cal )
72 if ( mCalendar ) {
73 mCalendar->unregisterObserver( this );
74 mCalendar->disconnect( this );
77 mCalendar = cal;
78 mCalendar->registerObserver( this );
80 setAcceptDrops( mCalendar != 0 );
81 updateIncidences();
84 QColor KODayMatrix::getShadedColor( const QColor &color ) const
86 QColor shaded;
87 int h = 0;
88 int s = 0;
89 int v = 0;
90 color.getHsv( &h, &s, &v );
91 s = s / 4;
92 v = 192 + v / 4;
93 shaded.setHsv( h, s, v );
95 return shaded;
98 KODayMatrix::~KODayMatrix()
100 if ( mCalendar ) {
101 mCalendar->unregisterObserver( this );
104 delete [] mDays;
105 delete [] mDayLabels;
108 void KODayMatrix::addSelectedDaysTo( KCalCore::DateList &selDays )
110 if ( mSelStart == NOSELECTION ) {
111 return;
114 // cope with selection being out of matrix limits at top (< 0)
115 int i0 = mSelStart;
116 if ( i0 < 0 ) {
117 for ( int i = i0; i < 0; i++ ) {
118 selDays.append( mDays[0].addDays( i ) );
120 i0 = 0;
123 // cope with selection being out of matrix limits at bottom (> NUMDAYS-1)
124 if ( mSelEnd > NUMDAYS-1 ) {
125 for ( int i = i0; i <= NUMDAYS - 1; i++ ) {
126 selDays.append( mDays[i] );
128 for ( int i = NUMDAYS; i < mSelEnd; i++ ) {
129 selDays.append( mDays[0].addDays( i ) );
131 } else {
132 // apply normal routine to selection being entirely within matrix limits
133 for ( int i = i0; i <= mSelEnd; i++ ) {
134 selDays.append( mDays[i] );
139 void KODayMatrix::setSelectedDaysFrom( const QDate &start, const QDate &end )
141 if ( mStartDate.isValid() ) {
142 mSelStart = mStartDate.daysTo( start );
143 mSelEnd = mStartDate.daysTo( end );
147 void KODayMatrix::clearSelection()
149 mSelEnd = mSelStart = NOSELECTION;
152 void KODayMatrix::recalculateToday()
154 if ( !mStartDate.isValid() ) {
155 return;
158 mToday = -1;
159 for ( int i = 0; i < NUMDAYS; i++ ) {
160 mDays[i] = mStartDate.addDays( i );
161 mDayLabels[i] = QString::number( KOGlobals::self()->calendarSystem()->day( mDays[i] ) );
163 // if today is in the currently displayed month, hilight today
164 if ( mDays[i].year() == QDate::currentDate().year() &&
165 mDays[i].month() == QDate::currentDate().month() &&
166 mDays[i].day() == QDate::currentDate().day() ) {
167 mToday = i;
172 void KODayMatrix::updateView()
174 updateView( mStartDate );
177 void KODayMatrix::setUpdateNeeded()
179 mPendingChanges = true;
182 void KODayMatrix::updateView( const QDate &actdate )
184 if ( !actdate.isValid() || NUMDAYS < 1 ) {
185 return;
187 //flag to indicate if the starting day of the matrix has changed by this call
188 bool daychanged = false;
190 // if a new startdate is to be set then apply Cornelius's calculation
191 // of the first day to be shown
192 if ( actdate != mStartDate ) {
193 // reset index of selection according to shift of starting date from
194 // startdate to actdate.
195 if ( mSelStart != NOSELECTION ) {
196 int tmp = actdate.daysTo( mStartDate );
197 // shift selection if new one would be visible at least partly !
198 if ( mSelStart + tmp < NUMDAYS && mSelEnd + tmp >= 0 ) {
199 // nested if required for next X display pushed from a different month
200 // correction required. otherwise, for month forward and backward,
201 // it must be avoided.
202 if ( mSelStart > NUMDAYS || mSelStart < 0 ) {
203 mSelStart = mSelStart + tmp;
205 if ( mSelEnd > NUMDAYS || mSelEnd < 0 ) {
206 mSelEnd = mSelEnd + tmp;
211 mStartDate = actdate;
212 daychanged = true;
215 if ( daychanged ) {
216 recalculateToday();
219 // The calendar has not changed in the meantime and the selected range
220 // is still the same so we can save the expensive updateIncidences() call
221 if ( !daychanged && !mPendingChanges ) {
222 return;
225 // TODO_Recurrence: If we just change the selection, but not the data,
226 // there's no need to update the whole list of incidences... This is just a
227 // waste of computational power
228 updateIncidences();
229 QMap<QDate,QStringList> holidaysByDate = KOGlobals::self()->holiday( mDays[0], mDays[NUMDAYS-1] );
230 for ( int i = 0; i < NUMDAYS; i++ ) {
231 //if it is a holy day then draw it red. Sundays are consider holidays, too
232 QStringList holidays = holidaysByDate[mDays[i]];
233 QString holiStr;
235 if ( ( KOGlobals::self()->calendarSystem()->dayOfWeek( mDays[i] ) ==
236 KGlobal::locale()->weekDayOfPray() ) ||
237 !holidays.isEmpty() ) {
238 if ( !holidays.isEmpty() ) {
239 holiStr = holidays.join( i18nc( "delimiter for joining holiday names", "," ) );
241 if ( holiStr.isEmpty() ) {
242 holiStr = "";
245 mHolidays[i] = holiStr;
249 void KODayMatrix::updateIncidences()
251 if ( !mCalendar ) {
252 return;
255 mEvents.clear();
257 if ( mHighlightEvents ) {
258 updateEvents();
261 if ( mHighlightTodos ) {
262 updateTodos();
265 if ( mHighlightJournals ) {
266 updateJournals();
269 mPendingChanges = false;
272 void KODayMatrix::updateJournals()
274 const Akonadi::Item::List items = mCalendar->incidences();
276 foreach ( const Akonadi::Item & item, items ) {
277 KCalCore::Incidence::Ptr inc = CalendarSupport::incidence( item );
278 Q_ASSERT( inc );
279 QDate d = inc->dtStart().toTimeSpec( mCalendar->timeSpec() ).date();
280 if ( inc->type() == KCalCore::Incidence::TypeJournal &&
281 d >= mDays[0] &&
282 d <= mDays[NUMDAYS-1] &&
283 !mEvents.contains( d ) ) {
284 mEvents.append( d );
286 if ( mEvents.count() == NUMDAYS ) {
287 // No point in wasting cpu, all days are bold already
288 break;
294 * Although updateTodos() is simpler it has some similarities with updateEvent()
295 * but don't bother refactoring them so they share code, there's a bigger fish:
296 * Try to refactor updateTodos(), updateEvent(), updateJournals(), monthview,
297 * agenda view, timespent view, timeline view, event list view and todo list view
298 * all these 9 places have incidence listing code in common, maybe it could go
299 * to kcal. Ah, and then there's kontact's summary view which still uses
300 * the old CPU consuming code.
302 void KODayMatrix::updateTodos()
304 const Akonadi::Item::List items = mCalendar->todos();
305 QDate d;
306 foreach ( const Akonadi::Item &item, items ) {
307 if ( mEvents.count() == NUMDAYS ) {
308 // No point in wasting cpu, all days are bold already
309 break;
311 const KCalCore::Todo::Ptr t = CalendarSupport::todo( item );
312 Q_ASSERT( t );
313 if ( t->hasDueDate() ) {
314 ushort recurType = t->recurrenceType();
316 if ( t->recurs() &&
317 !( recurType == KCalCore::Recurrence::rDaily && !KOPrefs::instance()->mDailyRecur ) &&
318 !( recurType == KCalCore::Recurrence::rWeekly && !KOPrefs::instance()->mWeeklyRecur ) ) {
320 // It's a recurring todo, find out in which days it occurs
321 KCalCore::DateTimeList timeDateList =
322 t->recurrence()->timesInInterval(
323 KDateTime( mDays[0], mCalendar->timeSpec() ),
324 KDateTime( mDays[NUMDAYS-1], mCalendar->timeSpec() ) );
326 foreach ( const KDateTime &dt, timeDateList ) {
327 d = dt.toTimeSpec( mCalendar->timeSpec() ).date();
328 if ( !mEvents.contains( d ) ) {
329 mEvents.append( d );
333 } else {
334 d = t->dtDue().toTimeSpec( mCalendar->timeSpec() ).date();
335 if ( d >= mDays[0] && d <= mDays[NUMDAYS-1] && !mEvents.contains( d ) ) {
336 mEvents.append( d );
343 void KODayMatrix::updateEvents()
345 if ( mEvents.count() == NUMDAYS ) {
346 mPendingChanges = false;
347 // No point in wasting cpu, all days are bold already
348 return;
350 Akonadi::Item::List eventlist = mCalendar->events( mDays[0], mDays[NUMDAYS-1],
351 mCalendar->timeSpec() );
353 Q_FOREACH ( const Akonadi::Item & item, eventlist ) {
354 if ( mEvents.count() == NUMDAYS ) {
355 // No point in wasting cpu, all days are bold already
356 break;
358 const KCalCore::Event::Ptr event = CalendarSupport::event( item );
359 Q_ASSERT( event );
360 const ushort recurType = event->recurrenceType();
362 const KDateTime dtStart = event->dtStart().toTimeSpec( mCalendar->timeSpec() );
364 // timed incidences occur in
365 // [dtStart(), dtEnd()[. All-day incidences occur in [dtStart(), dtEnd()]
366 // so we subtract 1 second in the timed case
367 const int secsToAdd = event->allDay() ? 0 : -1;
368 const KDateTime dtEnd = event->dtEnd().toTimeSpec( mCalendar->timeSpec() ).addSecs( secsToAdd );
370 if ( !( recurType == KCalCore::Recurrence::rDaily && !KOPrefs::instance()->mDailyRecur ) &&
371 !( recurType == KCalCore::Recurrence::rWeekly && !KOPrefs::instance()->mWeeklyRecur ) ) {
373 KCalCore::DateTimeList timeDateList;
374 const bool isRecurrent = event->recurs();
375 const int eventDuration = dtStart.daysTo( dtEnd );
377 if ( isRecurrent ) {
378 //Its a recurring event, find out in which days it occurs
379 timeDateList = event->recurrence()->timesInInterval(
380 KDateTime( mDays[0], mCalendar->timeSpec() ),
381 KDateTime( mDays[NUMDAYS-1], mCalendar->timeSpec() ) );
382 } else {
383 if ( dtStart.date() >= mDays[0] ) {
384 timeDateList.append( dtStart );
385 } else {
386 // The event starts in another month (not visible))
387 timeDateList.append( KDateTime( mDays[0], mCalendar->timeSpec() ) );
391 KCalCore::DateTimeList::iterator t;
392 for ( t=timeDateList.begin(); t != timeDateList.end(); ++t ) {
393 //This could be a multiday event, so iterate from dtStart() to dtEnd()
394 QDate d = t->toTimeSpec( mCalendar->timeSpec() ).date();
395 int j = 0;
397 QDate occurrenceEnd;
398 if ( isRecurrent ) {
399 occurrenceEnd = d.addDays( eventDuration );
400 } else {
401 occurrenceEnd = dtEnd.date();
404 do {
405 mEvents.append( d );
406 ++j;
407 d = d.addDays( 1 );
408 } while ( d <= occurrenceEnd && j < NUMDAYS );
412 mPendingChanges = false;
415 const QDate &KODayMatrix::getDate( int offset ) const
417 if ( offset < 0 || offset > NUMDAYS - 1 ) {
418 return mDays[0];
420 return mDays[offset];
423 QString KODayMatrix::getHolidayLabel( int offset ) const
425 if ( offset < 0 || offset > NUMDAYS - 1 ) {
426 return 0;
428 return mHolidays[offset];
431 int KODayMatrix::getDayIndexFrom( int x, int y ) const
433 return 7 * ( y / mDaySize.height() ) +
434 ( KOGlobals::self()->reverseLayout() ?
435 6 - x / mDaySize.width() : x / mDaySize.width() );
438 void KODayMatrix::calendarIncidenceAdded( const Akonadi::Item &incidence )
440 Q_UNUSED( incidence );
441 mPendingChanges = true;
444 void KODayMatrix::calendarIncidenceChanged( const Akonadi::Item &incidence )
446 Q_UNUSED( incidence );
447 mPendingChanges = true;
450 void KODayMatrix::calendarIncidenceDeleted( const Akonadi::Item &incidence )
452 Q_UNUSED( incidence );
453 mPendingChanges = true;
456 void KODayMatrix::setHighlightMode( bool highlightEvents,
457 bool highlightTodos,
458 bool highlightJournals ) {
460 // don't set mPendingChanges to true if nothing changed
461 if ( highlightTodos != mHighlightTodos ||
462 highlightEvents != mHighlightEvents ||
463 highlightJournals != mHighlightJournals ) {
464 mHighlightEvents = highlightEvents;
465 mHighlightTodos = highlightTodos;
466 mHighlightJournals = highlightJournals;
467 mPendingChanges = true;
471 void KODayMatrix::resourcesChanged()
473 mPendingChanges = true;
476 // ----------------------------------------------------------------------------
477 // M O U S E E V E N T H A N D L I N G
478 // ----------------------------------------------------------------------------
480 bool KODayMatrix::event( QEvent *event )
482 if ( KOPrefs::instance()->mEnableToolTips && event->type() == QEvent::ToolTip ) {
483 QHelpEvent *helpEvent = static_cast<QHelpEvent*>( event );
485 // calculate which cell of the matrix the mouse is in
486 QRect sz = frameRect();
487 int dheight = sz.height() * 7 / 42;
488 int dwidth = sz.width() / 7;
489 int row = helpEvent->pos().y() / dheight;
490 int col = helpEvent->pos().x() / dwidth;
492 // show holiday names only
493 QString tipText = getHolidayLabel( col + row * 7 );
494 if ( !tipText.isEmpty() ) {
495 QToolTip::showText( helpEvent->globalPos(), tipText );
496 } else {
497 QToolTip::hideText();
500 return QWidget::event( event );
503 void KODayMatrix::mousePressEvent( QMouseEvent *e )
505 mSelStart = getDayIndexFrom( e->x(), e->y() );
506 if ( e->button() == Qt::RightButton ) {
507 popupMenu( mDays[mSelStart] ) ;
508 } else if ( e->button() == Qt::LeftButton ) {
509 if ( mSelStart > NUMDAYS - 1 ) {
510 mSelStart = NUMDAYS - 1;
512 mSelInit = mSelStart;
516 void KODayMatrix::popupMenu( const QDate &date )
518 KMenu popup( this );
519 popup.addTitle( date.toString() );
520 QAction *newEventAction = popup.addAction(
521 KIcon( "appointment-new" ), i18n( "New E&vent..." ) );
522 QAction *newTodoAction = popup.addAction(
523 KIcon( "task-new" ), i18n( "New &To-do..." ) );
524 QAction *newJournalAction = popup.addAction(
525 KIcon( "journal-new" ), i18n( "New &Journal..." ) );
526 QAction *ret = popup.exec( QCursor::pos() );
527 if ( ret == newEventAction ) {
528 emit newEventSignal( date );
529 } else if ( ret == newTodoAction ) {
530 emit newTodoSignal( date );
531 } else if ( ret == newJournalAction ) {
532 emit newJournalSignal( date );
536 void KODayMatrix::mouseReleaseEvent( QMouseEvent *e )
538 if ( e->button() != Qt::LeftButton ) {
539 return;
542 int tmp = getDayIndexFrom( e->x(), e->y() );
543 if ( tmp > NUMDAYS - 1 ) {
544 tmp = NUMDAYS - 1;
547 if ( mSelInit > tmp ) {
548 mSelEnd = mSelInit;
549 if ( tmp != mSelStart ) {
550 mSelStart = tmp;
551 update();
553 } else {
554 mSelStart = mSelInit;
556 //repaint only if selection has changed
557 if ( tmp != mSelEnd ) {
558 mSelEnd = tmp;
559 update();
563 KCalCore::DateList daylist;
564 if ( mSelStart < 0 ) {
565 mSelStart = 0;
567 for ( int i = mSelStart; i <= mSelEnd; ++i ) {
568 daylist.append( mDays[i] );
570 emit selected( static_cast<const KCalCore::DateList>(daylist) );
573 void KODayMatrix::mouseMoveEvent( QMouseEvent *e )
575 int tmp = getDayIndexFrom( e->x(), e->y() );
576 if ( tmp > NUMDAYS - 1 ) {
577 tmp = NUMDAYS - 1;
580 if ( mSelInit > tmp ) {
581 mSelEnd = mSelInit;
582 if ( tmp != mSelStart ) {
583 mSelStart = tmp;
584 update();
586 } else {
587 mSelStart = mSelInit;
589 //repaint only if selection has changed
590 if ( tmp != mSelEnd ) {
591 mSelEnd = tmp;
592 update();
597 // ----------------------------------------------------------------------------
598 // D R A G ' N D R O P H A N D L I N G
599 // ----------------------------------------------------------------------------
601 //-----------------------------------------------------------------------------
602 // Drag and Drop handling -- based on the Troll Tech dirview example
604 enum {
605 DRAG_COPY = 0,
606 DRAG_MOVE = 1,
607 DRAG_CANCEL = 2
610 void KODayMatrix::dragEnterEvent( QDragEnterEvent *e )
612 e->acceptProposedAction();
613 const QMimeData *md = e->mimeData();
614 if ( !CalendarSupport::canDecode( md ) ) {
615 e->ignore();
616 return;
619 // some visual feedback
620 // oldPalette = palette();
621 // setPalette(my_HilitePalette);
622 // update();
625 void KODayMatrix::dragMoveEvent( QDragMoveEvent *e )
627 const QMimeData *md = e->mimeData();
628 if ( !CalendarSupport::canDecode( md ) ) {
629 e->ignore();
630 return;
632 e->accept();
635 void KODayMatrix::dragLeaveEvent( QDragLeaveEvent *dl )
637 Q_UNUSED( dl );
638 // setPalette(oldPalette);
639 // update();
642 void KODayMatrix::dropEvent( QDropEvent *e )
644 if ( !mCalendar ) {
645 e->ignore();
646 return;
648 QList<QUrl> urls = ( e->mimeData()->urls() );
649 //kDebug()<<" urls :"<<urls;
650 if ( urls.isEmpty() ) {
651 e->ignore();
652 return;
654 Akonadi::Item items;
655 //For the moment support 1 url
656 if ( urls.count() >= 1 ) {
657 KUrl res = urls.at( 0 );
659 Akonadi::ItemFetchJob *job = new Akonadi::ItemFetchJob( Akonadi::Item::fromUrl( res ) );
660 job->fetchScope().setAncestorRetrieval( Akonadi::ItemFetchScope::Parent );
661 job->fetchScope().fetchFullPayload();
662 Akonadi::Item::List items;
663 if ( job->exec() ) {
664 items = job->items();
666 bool exist = items.at( 0 ).isValid();
667 int action = DRAG_CANCEL;
669 Qt::KeyboardModifiers keyboardModifiers = e->keyboardModifiers();
671 if ( keyboardModifiers & Qt::ControlModifier ) {
672 action = DRAG_COPY;
673 } else if ( keyboardModifiers & Qt::ShiftModifier ) {
674 action = DRAG_MOVE;
675 } else {
676 QAction *copy = 0, *move = 0;
677 KMenu *menu = new KMenu( this );
678 if ( exist ) {
679 move = menu->addAction( KOGlobals::self()->smallIcon( "edit-paste" ), i18n( "&Move" ) );
680 if ( /*existingEvent*/1 ) {
681 copy = menu->addAction( KOGlobals::self()->smallIcon( "edit-copy" ), i18n( "&Copy" ) );
683 } else {
684 move = menu->addAction( KOGlobals::self()->smallIcon( "list-add" ), i18n( "&Add" ) );
686 menu->addSeparator();
687 /*QAction *cancel =*/
688 menu->addAction( KOGlobals::self()->smallIcon( "process-stop" ), i18n( "&Cancel" ) );
689 QAction *a = menu->exec( QCursor::pos() );
690 if ( a == copy ) {
691 action = DRAG_COPY;
692 } else if ( a == move ) {
693 action = DRAG_MOVE;
697 if ( action == DRAG_COPY || action == DRAG_MOVE ) {
698 e->accept();
699 int idx = getDayIndexFrom( e->pos().x(), e->pos().y() );
701 if ( action == DRAG_COPY ) {
702 emit incidenceDropped( items.at( 0 ), mDays[idx] );
703 } else if ( action == DRAG_MOVE ) {
704 emit incidenceDroppedMove( items.at( 0 ), mDays[idx] );
710 // ----------------------------------------------------------------------------
711 // P A I N T E V E N T H A N D L I N G
712 // ----------------------------------------------------------------------------
714 void KODayMatrix::paintEvent( QPaintEvent * )
716 QPainter p;
717 const QRect rect = frameRect();
718 const int dayHeight = mDaySize.height();
719 const int dayWidth = mDaySize.width();
720 int row, column;
721 int selectionWidth, selectionHeight;
722 const bool isRTL = KOGlobals::self()->reverseLayout();
724 QPalette pal = palette();
726 p.begin( this );
728 // draw background
729 p.fillRect( 0, 0, rect.width(), rect.height(), QBrush( pal.color( QPalette::Base ) ) );
731 // draw topleft frame
732 p.setPen( pal.color( QPalette::Mid ) );
733 p.drawRect( 0, 0, rect.width() - 1, rect.height() - 1 );
734 // don't paint over borders
735 p.translate( 1, 1 );
737 // draw selected days with highlighted background color
738 if ( mSelStart != NOSELECTION ) {
740 row = mSelStart / 7;
741 // fix larger selections starting in the previous month
742 if ( row < 0 && mSelEnd > 0 ) {
743 row = 0;
745 column = mSelStart - row * 7;
746 const QColor selectionColor = KOPrefs::instance()->agendaGridHighlightColor();
748 if ( row < 6 && row >= 0 ) {
749 if ( row == mSelEnd / 7 ) {
750 // Single row selection
751 p.fillRect( isRTL ?
752 ( 7 - ( mSelEnd - mSelStart + 1 ) - column ) * dayWidth :
753 column * dayWidth,
754 row * dayHeight,
755 ( mSelEnd - mSelStart + 1 ) * dayWidth, dayHeight, selectionColor );
756 } else {
757 // draw first row to the right
758 p.fillRect( isRTL ? 0 : column * dayWidth, row * dayHeight,
759 ( 7 - column ) * dayWidth, dayHeight, selectionColor );
760 // draw full block till last line
761 selectionHeight = mSelEnd / 7 - row;
762 if ( selectionHeight + row >= 6 ) {
763 selectionHeight = 6 - row;
765 if ( selectionHeight > 1 ) {
766 p.fillRect( 0, ( row + 1 ) * dayHeight, 7 * dayWidth,
767 ( selectionHeight - 1 ) * dayHeight, selectionColor );
769 // draw last block from left to mSelEnd
770 if ( mSelEnd / 7 < 6 ) {
771 selectionWidth = mSelEnd - 7 * ( mSelEnd / 7 ) + 1;
772 p.fillRect( isRTL ?
773 ( 7 - selectionWidth ) * dayWidth :
775 ( row + selectionHeight ) * dayHeight,
776 selectionWidth * dayWidth, dayHeight, selectionColor );
782 // iterate over all days in the matrix and draw the day label in appropriate colors
783 const QColor textColor = pal.color( QPalette::Text );
784 const QColor textColorShaded = getShadedColor( textColor );
785 QColor actcol = textColorShaded;
786 p.setPen( actcol );
787 QPen tmppen;
789 const QList<QDate> workDays = KOGlobals::self()->workDays( mDays[0], mDays[NUMDAYS-1] );
790 for ( int i = 0; i < NUMDAYS; ++i ) {
791 row = i / 7;
792 column = isRTL ? 6 - ( i - row * 7 ) : i - row * 7;
794 // if it is the first day of a month switch color from normal to shaded and vice versa
795 if ( KOGlobals::self()->calendarSystem()->day( mDays[i] ) == 1 ) {
796 if ( actcol == textColorShaded ) {
797 actcol = textColor;
798 } else {
799 actcol = textColorShaded;
801 p.setPen( actcol );
804 //Reset pen color after selected days block
805 if ( i == mSelEnd + 1 ) {
806 p.setPen( actcol );
809 const bool holiday = !workDays.contains( mDays[i] );
811 const QColor holidayColorShaded =
812 getShadedColor( KOPrefs::instance()->agendaHolidaysBackgroundColor() );
814 // if today then draw rectangle around day
815 if ( mToday == i ) {
816 tmppen = p.pen();
817 QPen todayPen( p.pen() );
819 todayPen.setWidth( mTodayMarginWidth );
820 //draw red rectangle for holidays
821 if ( holiday ) {
822 if ( actcol == textColor ) {
823 todayPen.setColor( KOPrefs::instance()->agendaHolidaysBackgroundColor() );
824 } else {
825 todayPen.setColor( holidayColorShaded );
828 //draw gray rectangle for today if in selection
829 if ( i >= mSelStart && i <= mSelEnd ) {
830 const QColor grey( "grey" );
831 todayPen.setColor( grey );
833 p.setPen( todayPen );
834 p.drawRect( column * dayWidth, row * dayHeight, dayWidth, dayHeight );
835 p.setPen( tmppen );
838 // if any events are on that day then draw it using a bold font
839 if ( mEvents.contains( mDays[i] ) ) {
840 QFont myFont = font();
841 myFont.setBold( true );
842 p.setFont( myFont );
845 // if it is a holiday then use the default holiday color
846 if ( holiday ) {
847 if ( actcol == textColor ) {
848 p.setPen( KOPrefs::instance()->agendaHolidaysBackgroundColor() );
849 } else {
850 p.setPen( holidayColorShaded );
854 // draw selected days with special color
855 if ( i >= mSelStart && i <= mSelEnd && !holiday ) {
856 p.setPen( Qt::white );
859 p.drawText( column * dayWidth, row * dayHeight, dayWidth, dayHeight,
860 Qt::AlignHCenter | Qt::AlignVCenter, mDayLabels[i]);
862 // reset color to actual color
863 if ( holiday ) {
864 p.setPen( actcol );
866 // reset bold font to plain font
867 if ( mEvents.contains( mDays[i] ) > 0 ) {
868 QFont myFont = font();
869 myFont.setBold( false );
870 p.setFont( myFont );
873 p.end();
876 // ----------------------------------------------------------------------------
877 // R E SI Z E E V E N T H A N D L I N G
878 // ----------------------------------------------------------------------------
880 void KODayMatrix::resizeEvent( QResizeEvent * )
882 QRect sz = frameRect();
883 mDaySize.setHeight( sz.height() * 7 / NUMDAYS );
884 mDaySize.setWidth( sz.width() / 7 );
887 /* static */
888 QPair<QDate,QDate> KODayMatrix::matrixLimits( const QDate &month )
890 const KCalendarSystem *calSys = KOGlobals::self()->calendarSystem();
891 QDate d = month;
892 calSys->setDate( d, calSys->year( month ), calSys->month( month ), 1 );
894 const int dayOfWeek = calSys->dayOfWeek( d );
895 const int weekstart = KGlobal::locale()->weekStartDay();
897 d = d.addDays( -( 7 + dayOfWeek - weekstart ) % 7 );
899 if ( dayOfWeek == weekstart ) {
900 d = d.addDays( -7 ); // Start on the second line
903 return qMakePair( d, d.addDays( NUMDAYS-1 ) );
906 #include "kodaymatrix.moc"