korganizer

calprinthelper.cpp

00001 /*
00002     This file is part of KOrganizer.
00003 
00004     Copyright (c) 1998 Preston Brown <pbrown@kde.org>
00005     Copyright (c) 2003 Reinhold Kainhofer <reinhold@kainhofer.com>
00006 
00007     This program is free software; you can redistribute it and/or modify
00008     it under the terms of the GNU General Public License as published by
00009     the Free Software Foundation; either version 2 of the License, or
00010     (at your option) any later version.
00011 
00012     This program is distributed in the hope that it will be useful,
00013     but WITHOUT ANY WARRANTY; without even the implied warranty of
00014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00015     GNU General Public License for more details.
00016 
00017     You should have received a copy of the GNU General Public License
00018     along with this program; if not, write to the Free Software
00019     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020 
00021     As a special exception, permission is given to link this program
00022     with any edition of Qt, and distribute the resulting executable,
00023     without including the source code for Qt in the source distribution.
00024 */
00025 
00026 #include <qpainter.h>
00027 #include <qlayout.h>
00028 #include <qframe.h>
00029 #include <qlabel.h>
00030 #include <qptrlist.h>
00031 #include <qintdict.h>
00032 
00033 #include <kglobal.h>
00034 #include <klocale.h>
00035 #include <kdebug.h>
00036 #include <kconfig.h>
00037 #include <kcalendarsystem.h>
00038 #include <kprinter.h>
00039 #include <kwordwrap.h>
00040 
00041 #include <libkcal/calendar.h>
00042 #include <libkcal/todo.h>
00043 #include <libkcal/event.h>
00044 
00045 #include "korganizer/corehelper.h"
00046 #include "cellitem.h"
00047 
00048 #include "calprinthelper.h"
00049 
00050 #ifndef KORG_NOPRINTER
00051 
00052 
00053 
00054 class CalPrintHelper::TodoParentStart
00055 {
00056   public:
00057     TodoParentStart( QRect pt = QRect(), bool page = true )
00058       : mRect( pt ), mSamePage( page ) {}
00059 
00060     QRect mRect;
00061     bool mSamePage;
00062 };
00063 
00064 class PrintCellItem : public KOrg::CellItem
00065 {
00066   public:
00067     PrintCellItem( Event *event, const QDateTime &start, const QDateTime &end )
00068       : mEvent( event ), mStart( start), mEnd( end )
00069     {
00070     }
00071 
00072     Event *event() const { return mEvent; }
00073 
00074     QString label() const { return mEvent->summary(); }
00075 
00076     QDateTime start() const { return mStart; }
00077     QDateTime end() const { return mEnd; }
00078 
00081     bool overlaps( KOrg::CellItem *o ) const
00082     {
00083       PrintCellItem *other = static_cast<PrintCellItem *>( o );
00084 
00085 #if 0
00086       kdDebug(5850) << "PrintCellItem::overlaps() " << event()->summary()
00087                     << " <-> " << other->event()->summary() << endl;
00088       kdDebug(5850) << "  start     : " << start.toString() << endl;
00089       kdDebug(5850) << "  end       : " << end.toString() << endl;
00090       kdDebug(5850) << "  otherStart: " << otherStart.toString() << endl;
00091       kdDebug(5850) << "  otherEnd  : " << otherEnd.toString() << endl;
00092 #endif
00093 
00094       return !( other->start() >= end() || other->end() <= start() );
00095     }
00096 
00097   private:
00098     Event *mEvent;
00099     QDateTime mStart, mEnd;
00100 };
00101 
00102 CalPrintHelper::CalPrintHelper( KPrinter *pr, Calendar *cal, KConfig *cfg,
00103                                 KOrg::CoreHelper *corehelper )
00104   : mPrinter( pr ), mCalendar( cal ), mConfig( cfg ),
00105     mCoreHelper( corehelper ),
00106     mHeaderHeight( 72 ), mSubHeaderHeight( 20 ), mMargin( 36 )
00107 {
00108   if ( corehelper )
00109     setCalendarSystem( mCoreHelper->calendarSystem() );
00110 }
00111 
00112 CalPrintHelper::~CalPrintHelper()
00113 {
00114 }
00115 
00116 
00117 
00118 void CalPrintHelper::setCategoryColors( QPainter &p, Incidence *incidence )
00119 {
00120   if ( mCoreHelper ) {
00121     QColor bgColor = mCoreHelper->categoryColor( incidence->categories() );
00122     QColor textColor = mCoreHelper->textColor( bgColor );
00123     p.setPen( textColor );
00124     p.setBrush( bgColor );
00125   }
00126 }
00127 
00128 
00129 
00131 
00132 void CalPrintHelper::drawHeader( QPainter &p, QString title,
00133     const QDate &month1, const QDate &month2,
00134     int x, int y, int width, int height )
00135 {
00136   p.drawRect( x, y, width, height );
00137   p.fillRect( x + 1, y + 1, width - 2, height - 2,
00138               QBrush( Qt::Dense7Pattern ) );
00139 
00140   int right=x+width;
00141 
00142   // print previous month for month view, print current for to-do, day and week
00143   int smallMonthWidth=width/4-10;
00144   if (smallMonthWidth>100) smallMonthWidth=100;
00145   if (month2.isValid()) {
00146     right -= (10+smallMonthWidth);
00147     drawSmallMonth(p, QDate(month2.year(), month2.month(), 1),
00148                    right, y+2, smallMonthWidth, height-4);
00149     right-=10;
00150   }
00151   if (month1.isValid()) {
00152     right -= (10+smallMonthWidth);
00153     drawSmallMonth(p, QDate(month1.year(), month1.month(), 1),
00154                    right, y+2, smallMonthWidth, height-4);
00155     right-=10;
00156   }
00157 
00158   // Print the titles...
00159   QFont oldFont(p.font());
00160   p.setFont( QFont("helvetica", 18, QFont::Bold) );
00161   QRect textRect( x+5, y+5, right-10-x, height-10 );
00162   p.drawText( textRect, Qt::AlignLeft | Qt::AlignTop | Qt::WordBreak, title );
00163   p.setFont(oldFont);
00164 }
00165 
00166 
00167 void CalPrintHelper::drawSmallMonth(QPainter &p, const QDate &qd,
00168     int x, int y, int width, int height)
00169 {
00170   bool firstCol = true;
00171   QDate monthDate(QDate(qd.year(), qd.month(), 1));
00172   QDate monthDate2;
00173   int month = monthDate.month();
00174 
00175   // draw the title
00176   QFont oldFont( p.font() );
00177   p.setFont(QFont("helvetica", 8, QFont::Bold));
00178   //  int lineSpacing = p.fontMetrics().lineSpacing();
00179   if ( mCalSys )
00180     p.drawText(x, y, width, height/4, Qt::AlignCenter,
00181       mCalSys->monthName( qd ) );
00182 
00183   int cellWidth = width/7;
00184   int cellHeight = height/8;
00185   QString tmpStr;
00186 
00187   // correct begin of week
00188   int weekdayCol = weekdayColumn( qd.dayOfWeek() );
00189   monthDate2 = monthDate.addDays( -weekdayCol );
00190 
00191   // draw days of week
00192   for (int col = 0; col < 7; ++col) {
00193     // tmpStr.sprintf("%c",(const char*)monthDate2.dayName(monthDate2.dayOfWeek()));
00194     tmpStr=mCalSys->weekDayName( monthDate2 )[0].upper();
00195     p.drawText( x + col*cellWidth, y + height/4, cellWidth, cellHeight,
00196                Qt::AlignCenter, tmpStr );
00197     monthDate2 = monthDate2.addDays( 1 );
00198   }
00199 
00200   // draw separator line
00201   p.drawLine( x, y + height/4 + cellHeight, x + width,
00202       y + height/4 + cellHeight );
00203 
00204   for ( int row = 0; row < 5; row++ ) {
00205     for ( int col = 0; col < 7; col++ ) {
00206       if ( monthDate.month() != month )
00207         break;
00208       if (firstCol) {
00209         firstCol = true;
00210         col = weekdayColumn( monthDate.dayOfWeek() );
00211       }
00212       p.drawText( x+col*cellWidth,
00213                   y+height/4+cellHeight+(row*cellHeight),
00214                   cellWidth, cellHeight, Qt::AlignCenter,
00215                   tmpStr.setNum( monthDate.day() ) );
00216       monthDate = monthDate.addDays(1);
00217     }
00218   }
00219   p.setFont( oldFont );
00220 }
00221 
00222 
00224 
00225 /*
00226  * This routine draws a header box over the main part of the calendar
00227  * containing the days of the week.
00228  */
00229 void CalPrintHelper::drawDaysOfWeek(QPainter &p,
00230     const QDate &fromDate, const QDate &toDate,
00231     int x, int y, int width, int height)
00232 {
00233   int cellWidth = width/(fromDate.daysTo( toDate )+1);
00234   int currx=x;
00235   QDate cellDate(fromDate);
00236 
00237   while (cellDate<=toDate) {
00238     drawDaysOfWeekBox(p, cellDate, currx, y, cellWidth, height);
00239     currx+=cellWidth;
00240     cellDate = cellDate.addDays(1);
00241   }
00242 }
00243 
00244 
00245 void CalPrintHelper::drawDaysOfWeekBox(QPainter &p, const QDate &qd,
00246     int x, int y, int width, int height)
00247 {
00248   QFont oldFont( p.font() );
00249   p.setFont( QFont( "helvetica", 10, QFont::Bold ) );
00250   p.drawRect( x, y, width, height );
00251   p.fillRect( x+1, y+1,
00252               width-2, height-2,
00253               QBrush( Qt::Dense7Pattern ) );
00254   if ( mCalSys )
00255     p.drawText( x+5, y, width-10, height, Qt::AlignCenter | Qt::AlignVCenter,
00256              mCalSys->weekDayName( qd ) );
00257   p.setFont( oldFont );
00258 }
00259 
00260 
00261 void CalPrintHelper::drawTimeLine(QPainter &p,
00262     const QTime &fromTime, const QTime &toTime,
00263     int x, int y, int width, int height)
00264 {
00265   p.drawRect(x, y, width, height);
00266 
00267   int totalsecs=fromTime.secsTo(toTime);
00268   float minlen=(float)height*60./(float)totalsecs;
00269   float cellHeight=(60.*(float)minlen);
00270   float currY=y;
00271 
00272   QTime curTime( fromTime );
00273   QTime endTime( toTime );
00274   if ( fromTime.minute() > 30 )
00275     curTime = QTime( fromTime.hour()+1, 0, 0 );
00276   else if ( fromTime.minute() > 0 ) {
00277     curTime = QTime( fromTime.hour(), 30, 0 );
00278     float yy = currY + minlen*(float)fromTime.secsTo( curTime )/60.;
00279     p.drawLine( x+width/2, (int)yy, x+width, (int)yy );
00280     curTime = QTime( fromTime.hour()+1, 0, 0 );
00281   }
00282   currY += ( fromTime.secsTo(curTime)*minlen/60 );
00283 
00284   while ( curTime < endTime ) {
00285     p.drawLine( x, (int)currY, x+width, (int)currY );
00286     int newY=(int)(currY+cellHeight/2.);
00287     QString numStr;
00288     if (newY < y+height) {
00289       QFont oldFont( p.font() );
00290       p.drawLine(x+width/2, (int)newY, x+width, (int)newY);
00291       // draw the time:
00292       if ( !KGlobal::locale()->use12Clock() ) {
00293         numStr.setNum(curTime.hour());
00294         if (cellHeight > 30) {
00295           p.setFont(QFont("helvetica", 16, QFont::Bold));
00296         } else {
00297           p.setFont(QFont("helvetica", 12, QFont::Bold));
00298         }
00299         p.drawText(x+2, (int)currY+2, width/2-2, (int)cellHeight,
00300                   Qt::AlignTop | Qt::AlignRight, numStr);
00301         p.setFont(QFont("helvetica", 10, QFont::Normal));
00302         p.drawText(x+width/2, (int)currY+2, width/2+2, (int)(cellHeight/2)-3,
00303                   Qt::AlignTop | Qt::AlignLeft, "00");
00304       } else {
00305         QTime time( curTime.hour(), 0 );
00306         numStr = KGlobal::locale()->formatTime( time );
00307         p.setFont(QFont("helvetica", 14, QFont::Bold));
00308         p.drawText(x+2, (int)currY+2, width-4, (int)cellHeight/2-3,
00309                   Qt::AlignTop|Qt::AlignLeft, numStr);
00310       }
00311       currY+=cellHeight;
00312     p.setFont( oldFont );
00313     } // enough space for half-hour line and time
00314     if (curTime.secsTo(endTime)>3600)
00315       curTime=curTime.addSecs(3600);
00316     else curTime=endTime;
00317   } // currTime<endTime
00318 }
00319 
00320 Event *CalPrintHelper::holiday( const QDate &dt )
00321 {
00322 #ifndef KORG_NOPLUGINS
00323   if ( !mCoreHelper ) return 0;
00324   QString hstring( mCoreHelper->holidayString( dt ) );
00325   if ( !hstring.isEmpty() ) {
00326     Event*holiday=new Event();
00327     holiday->setDtStart( dt );
00328     holiday->setDtEnd( dt );
00329     holiday->setFloats( true );
00330     holiday->setCategories( i18n("Holiday") );
00331     return holiday;
00332   }
00333   return 0;
00334 #endif
00335 
00336 }
00337 
00339 
00345 void CalPrintHelper::drawAllDayBox(QPainter &p, Event::List &eventList,
00346     const QDate &qd, bool expandable,
00347     int x, int y, int width, int &height)
00348 {
00349   Event::List::Iterator it, itold;
00350 
00351   int offset=y;
00352 
00353   QPen oldPen( p.pen() );
00354   QColor oldBgColor( p.backgroundColor() );
00355   QBrush oldBrush( p.brush() );
00356   QString multiDayStr;
00357 
00358   Event*hd = holiday( qd );
00359   if ( hd ) eventList.prepend( hd );
00360 
00361   it = eventList.begin();
00362   Event *currEvent = 0;
00363   // First, print all floating events
00364   while( it!=eventList.end() ) {
00365     currEvent=*it;
00366     itold=it;
00367     ++it;
00368     if ( currEvent->doesFloat() ) {
00369       // set the colors according to the categories
00370       if ( expandable ) {
00371         if ( mUseColors )
00372           setCategoryColors( p, currEvent );
00373 
00374         p.drawRect( x, offset, width, height );
00375         p.drawText( x + 5, offset + 5, width - 10, height - 10,
00376                     Qt::AlignCenter | Qt::AlignVCenter | Qt::AlignJustify |
00377                     Qt::WordBreak,
00378                     currEvent->summary() );
00379         // reset the colors
00380         p.setBrush( oldBrush );
00381         p.setPen( oldPen );
00382         p.setBackgroundColor( oldBgColor );
00383 
00384         offset += height;
00385       } else {
00386         if ( !multiDayStr.isEmpty() ) multiDayStr += ", ";
00387         multiDayStr += currEvent->summary() + "\n";
00388       }
00389       eventList.remove( itold );
00390     }
00391   }
00392 
00393   if (!expandable) {
00394     p.drawRect(x, offset, width, height);
00395     if (!multiDayStr.isEmpty()) {
00396       p.fillRect( x + 1, offset + 1, width - 2, height - 2,
00397                   QBrush( Qt::Dense5Pattern ) );
00398       p.drawText( x + 5, offset + 5, width - 10, height - 10,
00399                   Qt::AlignCenter | Qt::AlignVCenter | Qt::AlignJustify |
00400                   Qt::WordBreak,
00401                   multiDayStr);
00402     }
00403   } else {
00404     height=offset-y;
00405   }
00406   p.setBrush( oldBrush );
00407 }
00408 
00409 
00410 void CalPrintHelper::drawAgendaDayBox( QPainter &p, Event::List &events,
00411                                      const QDate &qd, bool expandable,
00412                                      QTime &fromTime, QTime &toTime,
00413                                      int x, int y, int width, int height )
00414 {
00415   p.drawRect( x, y, width, height );
00416 
00417   Event *event;
00418 
00419   if ( expandable ) {
00420     // Adapt start/end times to include complete events
00421     Event::List::ConstIterator it;
00422     for ( it = events.begin(); it != events.end(); ++it ) {
00423       event = *it;
00424       if ( event->dtStart().time() < fromTime )
00425         fromTime = event->dtStart().time();
00426       if ( event->dtEnd().time() > toTime )
00427         toTime = event->dtEnd().time();
00428     }
00429   }
00430 
00431   // Show at least one hour
00432   if ( fromTime.secsTo( toTime ) < 3600 ) {
00433     fromTime = QTime( fromTime.hour(), 0, 0 );
00434     toTime = fromTime.addSecs( 3600 );
00435   }
00436 
00437   // calculate the height of a cell and of a minute
00438   int totalsecs = fromTime.secsTo( toTime );
00439   float minlen = height * 60. / totalsecs;
00440   float cellHeight = 60. * minlen;
00441   float currY = y;
00442 
00443   // print grid:
00444   QTime curTime( QTime( fromTime.hour(), 0, 0 ) );
00445   currY += fromTime.secsTo( curTime ) * minlen / 60;
00446 
00447   while ( curTime < toTime && curTime.isValid() ) {
00448     if ( currY > y ) p.drawLine( x, int( currY ), x + width, int( currY ) );
00449     currY += cellHeight / 2;
00450     if ( ( currY > y ) && ( currY < y + height ) ) {
00451       QPen oldPen( p.pen() );
00452       p.setPen( QColor( 192, 192, 192 ) );
00453       p.drawLine( x, int( currY ), x + width, int( currY ) );
00454       p.setPen( oldPen );
00455     } // enough space for half-hour line
00456     if ( curTime.secsTo( toTime ) > 3600 )
00457       curTime = curTime.addSecs( 3600 );
00458     else curTime = toTime;
00459     currY += cellHeight / 2;
00460   }
00461 
00462   QDateTime startPrintDate = QDateTime( qd, fromTime );
00463   QDateTime endPrintDate = QDateTime( qd, toTime );
00464 
00465   // Calculate horizontal positions and widths of events taking into account
00466   // overlapping events
00467 
00468   QPtrList<KOrg::CellItem> cells;
00469   cells.setAutoDelete( true );
00470 
00471   Event::List::ConstIterator itEvents;
00472   for( itEvents = events.begin(); itEvents != events.end(); ++itEvents ) {
00473     QValueList<QDateTime> times = (*itEvents)->startDateTimesForDate( qd );
00474     for ( QValueList<QDateTime>::ConstIterator it = times.begin();
00475           it != times.end(); ++it ) {
00476       cells.append( new PrintCellItem( *itEvents, (*it), (*itEvents)->endDateForStart( *it ) ) );
00477     }
00478   }
00479 
00480   QPtrListIterator<KOrg::CellItem> it1( cells );
00481   for( it1.toFirst(); it1.current(); ++it1 ) {
00482     KOrg::CellItem *placeItem = it1.current();
00483 
00484     KOrg::CellItem::placeItem( cells, placeItem );
00485   }
00486 
00487   QPen oldPen( p.pen() );
00488   QColor oldBgColor( p.backgroundColor() );
00489   QBrush oldBrush( p.brush() );
00490   QFont oldFont( p.font() );
00491 
00492   p.setFont( QFont( "helvetica", 10 ) );
00493   p.setBrush( QBrush( Qt::Dense7Pattern ) );
00494 
00495   for( it1.toFirst(); it1.current(); ++it1 ) {
00496     PrintCellItem *placeItem = static_cast<PrintCellItem *>( it1.current() );
00497 
00498     drawAgendaItem( placeItem, p, qd, startPrintDate, endPrintDate, minlen, x,
00499                     y, width );
00500 
00501     p.setBrush( oldBrush );
00502     p.setPen( oldPen );
00503     p.setBackgroundColor( oldBgColor );
00504   }
00505   p.setFont( oldFont );
00506 //  p.setBrush( QBrush( NoBrush ) );
00507 }
00508 
00509 
00510 void CalPrintHelper::drawAgendaItem( PrintCellItem *item, QPainter &p,
00511                                    const QDate &qd,
00512                                    const QDateTime &startPrintDate,
00513                                    const QDateTime &endPrintDate,
00514                                    float minlen, int x, int y, int width )
00515 {
00516   Event *event = item->event();
00517 
00518   // set the colors according to the categories
00519   if ( mUseColors ) setCategoryColors( p, event );
00520 
00521   // start/end of print area for event
00522   QDateTime startTime = item->start();
00523   QDateTime endTime = item->end();
00524   if ( ( startTime < endPrintDate && endTime > startPrintDate ) ||
00525        ( endTime > startPrintDate && startTime < endPrintDate ) ) {
00526     if ( startTime < startPrintDate ) startTime = startPrintDate;
00527     if ( endTime > endPrintDate ) endTime = endPrintDate;
00528     int currentHeight = int( startTime.secsTo( endTime ) / 60. * minlen );
00529     int currentYPos = int( y + startPrintDate.secsTo( startTime ) *
00530                            minlen / 60. );
00531     int currentWidth = width / item->subCells();
00532     int currentX = x + item->subCell() * currentWidth;
00533 
00534     p.drawRect( currentX, currentYPos, currentWidth, currentHeight );
00535     int offset = 4;
00536     // print the text vertically centered. If it doesn't fit inside the
00537     // box, align it at the top so the beginning is visible
00538     int flags = Qt::AlignLeft | Qt::WordBreak;
00539     QRect bound = p.boundingRect ( currentX + offset, currentYPos,
00540                                    currentWidth - 2 * offset, currentHeight,
00541                                    flags, event->summary() );
00542     flags |= Qt::AlignTop;
00543     p.drawText( currentX+offset, currentYPos+offset, currentWidth-2*offset,
00544                 currentHeight-2*offset, flags, event->summary() );
00545   }
00546 }
00547 
00548 void CalPrintHelper::drawDayBox( QPainter &p, const QDate &qd,
00549     int x, int y, int width, int height,
00550     bool fullDate, bool printRecurDaily, bool printRecurWeekly )
00551 {
00552   QString dayNumStr;
00553   QString ampm;
00554   const KLocale*local = KGlobal::locale();
00555 
00556 
00557   // This has to be localized
00558   if ( fullDate && mCalSys ) {
00559 
00560     dayNumStr = i18n("weekday month date", "%1 %2 %3")
00561         .arg( mCalSys->weekDayName( qd ) )
00562         .arg( mCalSys->monthName( qd ) )
00563         .arg( qd.day() );
00564 //    dayNumStr = local->formatDate(qd);
00565   } else {
00566     dayNumStr = QString::number( qd.day() );
00567   }
00568 
00569   p.eraseRect( x, y, width, height );
00570   QRect dayBox( x, y, width, height );
00571   p.drawRect( dayBox );
00572   p.drawRect( x, y, width, mSubHeaderHeight );
00573   p.fillRect( x + 1, y + 1, width - 2, mSubHeaderHeight - 2,
00574               QBrush( Qt::Dense7Pattern ) );
00575   QString hstring;
00576 #ifndef KORG_NOPLUGINS
00577   if ( mCoreHelper ) hstring = mCoreHelper->holidayString(qd);
00578 #endif
00579   QFont oldFont( p.font() );
00580 
00581   if (!hstring.isEmpty()) {
00582     p.setFont( QFont( "helvetica", 8, QFont::Bold, true ) );
00583 
00584     p.drawText( x+5, y, width-25, mSubHeaderHeight,
00585                 Qt::AlignLeft | Qt::AlignVCenter, hstring );
00586   }
00587   p.setFont(QFont("helvetica", 10, QFont::Bold));
00588   p.drawText( x + 5, y, width - 10, mSubHeaderHeight,
00589               Qt::AlignRight | Qt::AlignVCenter, dayNumStr);
00590 
00591   Event::List eventList = mCalendar->events( qd,
00592                                              EventSortStartDate,
00593                                              SortDirectionAscending );
00594   QString text;
00595   p.setFont( QFont( "helvetica", 8 ) );
00596 
00597   int textY=mSubHeaderHeight+3; // gives the relative y-coord of the next printed entry
00598   Event::List::ConstIterator it;
00599 
00600   for( it = eventList.begin(); it != eventList.end() && textY<height; ++it ) {
00601     Event *currEvent = *it;
00602     if ( ( !printRecurDaily  && currEvent->recurrenceType() == Recurrence::rDaily  ) ||
00603          ( !printRecurWeekly && currEvent->recurrenceType() == Recurrence::rWeekly ) ) {
00604       continue; }
00605     if ( currEvent->doesFloat() || currEvent->isMultiDay() )
00606       text = "";
00607     else
00608       text = local->formatTime( currEvent->dtStart().time() );
00609 
00610     drawIncidence( p, dayBox, text, currEvent->summary(), textY );
00611   }
00612 
00613   if ( textY<height ) {
00614     Todo::List todos = mCalendar->todos( qd );
00615     Todo::List::ConstIterator it2;
00616     for( it2 = todos.begin(); it2 != todos.end() && textY<height; ++it2 ) {
00617       Todo *todo = *it2;
00618       if ( ( !printRecurDaily  && todo->recurrenceType() == Recurrence::rDaily  ) ||
00619            ( !printRecurWeekly && todo->recurrenceType() == Recurrence::rWeekly ) )
00620         continue;
00621       if ( todo->hasDueDate() && !todo->doesFloat() )
00622         text += KGlobal::locale()->formatTime(todo->dtDue().time()) + " ";
00623       else
00624         text = "";
00625       drawIncidence( p, dayBox, text, i18n("To-do: %1").arg(todo->summary()), textY );
00626     }
00627   }
00628 
00629   p.setFont( oldFont );
00630 }
00631 
00632 void CalPrintHelper::drawIncidence( QPainter &p, QRect &dayBox, const QString &time, const QString &summary, int &textY )
00633 {
00634   kdDebug(5850) << "summary = " << summary << endl;
00635 
00636   int flags = Qt::AlignLeft;
00637   QFontMetrics fm = p.fontMetrics();
00638   QRect timeBound = p.boundingRect( dayBox.x() + 5, dayBox.y() + textY,
00639                                     dayBox.width() - 10, fm.lineSpacing(),
00640                                     flags, time );
00641   p.drawText( timeBound, flags, time );
00642 
00643   int summaryWidth = time.isEmpty() ? 0 : timeBound.width() + 4;
00644   QRect summaryBound = QRect( dayBox.x() + 5 + summaryWidth, dayBox.y() + textY,
00645                               dayBox.width() - summaryWidth -5, dayBox.height() );
00646 
00647   KWordWrap *ww = KWordWrap::formatText( fm, summaryBound, flags, summary );
00648   ww->drawText( &p, dayBox.x() + 5 + summaryWidth, dayBox.y() + textY, flags );
00649 
00650   textY += ww->boundingRect().height();
00651 
00652   delete ww;
00653 }
00654 
00655 
00657 
00658 void CalPrintHelper::drawWeek(QPainter &p, const QDate &qd,
00659     int x, int y, int width, int height)
00660 {
00661   QDate weekDate = qd;
00662   bool portrait = ( height > width );
00663   int cellWidth, cellHeight;
00664   int vcells;
00665   if (portrait) {
00666     cellWidth = width/2;
00667     vcells=3;
00668   } else {
00669     cellWidth = width/6;
00670     vcells=1;
00671   }
00672   cellHeight = height/vcells;
00673 
00674   // correct begin of week
00675   int weekdayCol = weekdayColumn( qd.dayOfWeek() );
00676   weekDate = qd.addDays( -weekdayCol );
00677 
00678   for (int i = 0; i < 7; i++, weekDate = weekDate.addDays(1)) {
00679     if (i<5) {
00680       drawDayBox(p, weekDate, x+cellWidth*(int)(i/vcells), y+cellHeight*(i%vcells),
00681         cellWidth, cellHeight, true);
00682     } else if (i==5) {
00683       drawDayBox(p, weekDate, x+cellWidth*(int)(i/vcells), y+cellHeight*(i%vcells),
00684         cellWidth, cellHeight/2, true);
00685     } else if (i==6) {
00686       drawDayBox(p, weekDate, x+cellWidth*(int)((i-1)/vcells),
00687         y+cellHeight*((i-1)%vcells)+cellHeight/2, cellWidth, cellHeight/2, true);
00688     }
00689   } // for i through all weekdays
00690 }
00691 
00692 
00693 void CalPrintHelper::drawTimeTable(QPainter &p,
00694     const QDate &fromDate, const QDate &toDate,
00695     QTime &fromTime, QTime &toTime,
00696     int x, int y, int width, int height)
00697 {
00698   // timeline is 1.5 hours:
00699   int alldayHeight = (int)( 3600.*height/(fromTime.secsTo(toTime)+3600.) );
00700   int timelineWidth = 50;
00701   int cellWidth = (int)( (width-timelineWidth)/(fromDate.daysTo(toDate)+1) );
00702   int currY=y;
00703   int currX=x;
00704 
00705   drawDaysOfWeek( p, fromDate, toDate, x+timelineWidth, currY, width-timelineWidth, mSubHeaderHeight);
00706   currY+=mSubHeaderHeight;
00707   drawTimeLine( p, fromTime, toTime, x, currY+alldayHeight,
00708     timelineWidth, height-mSubHeaderHeight-alldayHeight );
00709 
00710   currX=x+timelineWidth;
00711   // draw each day
00712   QDate curDate(fromDate);
00713   while (curDate<=toDate) {
00714     Event::List eventList = mCalendar->events(curDate,
00715                                               EventSortStartDate,
00716                                               SortDirectionAscending);
00717     drawAllDayBox( p, eventList, curDate, false, currX, currY, cellWidth, alldayHeight);
00718     drawAgendaDayBox( p, eventList, curDate, false, fromTime, toTime, currX,
00719       currY+alldayHeight, cellWidth, height-mSubHeaderHeight-alldayHeight );
00720     currX+=cellWidth;
00721     curDate=curDate.addDays(1);
00722   }
00723 
00724 }
00725 
00726 
00728 
00729 void CalPrintHelper::drawMonth(QPainter &p, const QDate &qd, bool weeknumbers,
00730                                bool recurDaily, bool recurWeekly, int x, int y,
00731                                int width, int height)
00732 {
00733   int yoffset = mSubHeaderHeight;
00734   int xoffset = 0;
00735   QDate monthDate(QDate(qd.year(), qd.month(), 1));
00736   QDate monthFirst(monthDate);
00737   QDate monthLast(monthDate.addMonths(1).addDays(-1));
00738 
00739 
00740   int weekdayCol = weekdayColumn( monthDate.dayOfWeek() );
00741   monthDate = monthDate.addDays(-weekdayCol);
00742 
00743   int rows=(weekdayCol + qd.daysInMonth() - 1)/7 +1;
00744   int cellHeight = (height-yoffset) / rows;
00745 
00746   if (weeknumbers) {
00747     QFont oldFont(p.font());
00748     QFont newFont(p.font());
00749     newFont.setPointSize(6);
00750     p.setFont(newFont);
00751     xoffset += 14;
00752     QDate weekDate(monthDate);
00753     for (int row = 0; row<rows; ++row ) {
00754       int calWeek = weekDate.weekNumber();
00755       QRect rc( x, y + yoffset + cellHeight*row, xoffset - 1, cellHeight );
00756       p.drawText( rc, Qt::AlignRight | Qt::AlignVCenter,
00757                   QString::number( calWeek ) );
00758       weekDate = weekDate.addDays( 7 );
00759     }
00760     p.setFont( oldFont );
00761   }
00762 
00763   drawDaysOfWeek( p, monthDate, monthDate.addDays( 6 ), x + xoffset,
00764                   y, width - xoffset, mSubHeaderHeight );
00765   int cellWidth = ( width - xoffset ) / 7;
00766 
00767   QColor back = p.backgroundColor();
00768   bool darkbg = false;
00769   for ( int row = 0; row < rows; ++row ) {
00770     for ( int col = 0; col < 7; ++col ) {
00771       // show days from previous/next month with a grayed background
00772       if ( (monthDate < monthFirst) || (monthDate > monthLast) ) {
00773         p.setBackgroundColor( back.dark( 120 ) );
00774         darkbg = true;
00775       }
00776       drawDayBox(p, monthDate, x+xoffset+col*cellWidth, y+yoffset+row*cellHeight,
00777                  cellWidth, cellHeight, false,  recurDaily, recurWeekly );
00778       if ( darkbg ) {
00779         p.setBackgroundColor( back );
00780         darkbg = false;
00781       }
00782       monthDate = monthDate.addDays(1);
00783     }
00784   }
00785 }
00786 
00787 
00789 
00790 void CalPrintHelper::drawTodo( int &count, Todo *todo, QPainter &p,
00791                                TodoSortField sortField, SortDirection sortDir,
00792                                bool connectSubTodos, bool strikeoutCompleted,
00793                                bool desc, int posPriority, int posSummary,
00794                                int posDueDt, int posPercentComplete,
00795                                int level, int x, int &y, int width,
00796                                int pageHeight, const Todo::List &todoList,
00797                                TodoParentStart *r )
00798 {
00799   QString outStr;
00800   const KLocale *local = KGlobal::locale();
00801   QRect rect;
00802   TodoParentStart startpt;
00803 
00804   // This list keeps all starting points of the parent to-dos so the connection
00805   // lines of the tree can easily be drawn (needed if a new page is started)
00806   static QPtrList<TodoParentStart> startPoints;
00807   if ( level < 1 ) {
00808     startPoints.clear();
00809   }
00810 
00811   // Compute the right hand side of the to-do box
00812   int rhs = posPercentComplete;
00813   if ( rhs < 0 ) rhs = posDueDt; //not printing percent completed
00814   if ( rhs < 0 ) rhs = x+width;  //not printing due dates either
00815 
00816   // size of to-do
00817   outStr=todo->summary();
00818   int left = posSummary + ( level*10 );
00819   rect = p.boundingRect( left, y, ( rhs-left-5 ), -1, Qt::WordBreak, outStr );
00820   if ( !todo->description().isEmpty() && desc ) {
00821     outStr = todo->description();
00822     rect = p.boundingRect( left+20, rect.bottom()+5, width-(left+10-x), -1,
00823                            Qt::WordBreak, outStr );
00824   }
00825   // if too big make new page
00826   if ( rect.bottom() > pageHeight ) {
00827     // first draw the connection lines from parent to-dos:
00828     if ( level > 0 && connectSubTodos ) {
00829       TodoParentStart *rct;
00830       for ( rct = startPoints.first(); rct; rct = startPoints.next() ) {
00831         int start;
00832         int center = rct->mRect.left() + (rct->mRect.width()/2);
00833         int to = p.viewport().bottom();
00834 
00835         // draw either from start point of parent or from top of the page
00836         if ( rct->mSamePage )
00837           start = rct->mRect.bottom() + 1;
00838         else
00839           start = p.viewport().top();
00840         p.moveTo( center, start );
00841         p.lineTo( center, to );
00842         rct->mSamePage = false;
00843       }
00844     }
00845     y=0;
00846     mPrinter->newPage();
00847   }
00848 
00849   // If this is a sub-to-do, r will not be 0, and we want the LH side
00850   // of the priority line up to the RH side of the parent to-do's priority
00851   bool showPriority = posPriority>=0;
00852   int lhs = posPriority;
00853   if ( r ) {
00854     lhs = r->mRect.right() + 1;
00855   }
00856 
00857   outStr.setNum( todo->priority() );
00858   rect = p.boundingRect( lhs, y + 10, 5, -1, Qt::AlignCenter, outStr );
00859   // Make it a more reasonable size
00860   rect.setWidth(18);
00861   rect.setHeight(18);
00862 
00863   // Draw a checkbox
00864   p.setBrush( QBrush( Qt::NoBrush ) );
00865   p.drawRect( rect );
00866   if ( todo->isCompleted() ) {
00867     // cross out the rectangle for completed to-dos
00868     p.drawLine( rect.topLeft(), rect.bottomRight() );
00869     p.drawLine( rect.topRight(), rect.bottomLeft() );
00870   }
00871   lhs = rect.right() + 3;
00872 
00873   // Priority
00874   if ( todo->priority() > 0 && showPriority ) {
00875     p.drawText( rect, Qt::AlignCenter, outStr );
00876   }
00877   startpt.mRect = rect; //save for later
00878 
00879   // Connect the dots
00880   if ( level > 0 && connectSubTodos ) {
00881     int bottom;
00882     int center( r->mRect.left() + (r->mRect.width()/2) );
00883     if ( r->mSamePage )
00884       bottom = r->mRect.bottom() + 1;
00885     else
00886       bottom = 0;
00887     int to( rect.top() + (rect.height()/2) );
00888     int endx( rect.left() );
00889     p.moveTo( center, bottom );
00890     p.lineTo( center, to );
00891     p.lineTo( endx, to );
00892   }
00893 
00894   // summary
00895   outStr=todo->summary();
00896   rect = p.boundingRect( lhs, rect.top(), (rhs-(left + rect.width() + 5)),
00897                          -1, Qt::WordBreak, outStr );
00898 
00899   QRect newrect;
00900   //FIXME: the following code prints underline rather than strikeout text
00901 #if 0
00902   QFont f( p.font() );
00903   if ( todo->isCompleted() && strikeoutCompleted ) {
00904     f.setStrikeOut( true );
00905     p.setFont( f );
00906   }
00907   p.drawText( rect, Qt::WordBreak, outStr, -1, &newrect );
00908   f.setStrikeOut( false );
00909   p.setFont( f );
00910 #endif
00911   //TODO: Remove this section when the code above is fixed
00912   p.drawText( rect, Qt::WordBreak, outStr, -1, &newrect );
00913   if ( todo->isCompleted() && strikeoutCompleted ) {
00914     // strike out the summary text if to-do is complete
00915     // Note: we tried to use a strike-out font and for unknown reasons the
00916     // result was underline instead of strike-out, so draw the lines ourselves.
00917     int delta = p.fontMetrics().lineSpacing();
00918     int lines = ( rect.height() / delta ) + 1;
00919     for ( int i=0; i<lines; i++ ) {
00920       p.moveTo( rect.left(),  rect.top() + ( delta/2 ) + ( i*delta ) );
00921       p.lineTo( rect.right(), rect.top() + ( delta/2 ) + ( i*delta ) );
00922     }
00923   }
00924 
00925   // due date
00926   if ( todo->hasDueDate() && posDueDt>=0 ) {
00927     outStr = local->formatDate( todo->dtDue().date(), true );
00928     rect = p.boundingRect( posDueDt, y, x + width, -1,
00929                            Qt::AlignTop | Qt::AlignLeft, outStr );
00930     p.drawText( rect, Qt::AlignTop | Qt::AlignLeft, outStr );
00931   }
00932 
00933   // percentage completed
00934   bool showPercentComplete = posPercentComplete>=0;
00935   if ( showPercentComplete ) {
00936     int lwidth = 24;
00937     int lheight = 12;
00938     //first, draw the progress bar
00939     int progress = (int)(( lwidth*todo->percentComplete())/100.0 + 0.5);
00940 
00941     p.setBrush( QBrush( Qt::NoBrush ) );
00942     p.drawRect( posPercentComplete, y+3, lwidth, lheight );
00943     if ( progress > 0 ) {
00944       p.setBrush( QBrush( Qt::Dense5Pattern ) );
00945       p.drawRect( posPercentComplete, y+3, progress, lheight );
00946     }
00947 
00948     //now, write the percentage
00949     outStr = i18n( "%1%" ).arg( todo->percentComplete() );
00950     rect = p.boundingRect( posPercentComplete+lwidth+3, y, x + width, -1,
00951                            Qt::AlignTop | Qt::AlignLeft, outStr );
00952     p.drawText( rect, Qt::AlignTop | Qt::AlignLeft, outStr );
00953   }
00954 
00955   // description
00956   if ( !todo->description().isEmpty() && desc ) {
00957     y = newrect.bottom() + 5;
00958     outStr = todo->description();
00959     rect = p.boundingRect( left+20, y, x+width-(left+10), -1,
00960                            Qt::WordBreak, outStr );
00961     p.drawText( rect, Qt::WordBreak, outStr, -1, &newrect );
00962   }
00963 
00964   // Set the new line position
00965   y = newrect.bottom() + 10; //set the line position
00966 
00967   // If the to-do has sub-to-dos, we need to call ourselves recursively
00968 #if 0
00969   Incidence::List l = todo->relations();
00970   Incidence::List::ConstIterator it;
00971   startPoints.append( &startpt );
00972   for( it = l.begin(); it != l.end(); ++it ) {
00973     count++;
00974     // In the future, to-dos might also be related to events
00975     // Manually check if the sub-to-do is in the list of to-dos to print
00976     // The problem is that relations() does not apply filters, so
00977     // we need to compare manually with the complete filtered list!
00978     Todo* subtodo = dynamic_cast<Todo *>( *it );
00979     if (subtodo && todoList.contains( subtodo ) ) {
00980       drawTodo( count, subtodo, p, connectSubTodos, strikeoutCompleted,
00981                 desc, posPriority, posSummary, posDueDt, posPercentComplete,
00982                 level+1, x, y, width, pageHeight, todoList, &startpt );
00983     }
00984   }
00985 #endif
00986   // Make a list of all the sub-to-dos related to this to-do.
00987   Todo::List t;
00988   Incidence::List l = todo->relations();
00989   Incidence::List::ConstIterator it;
00990   for( it=l.begin(); it!=l.end(); ++it ) {
00991     // In the future, to-dos might also be related to events
00992     // Manually check if the sub-to-do is in the list of to-dos to print
00993     // The problem is that relations() does not apply filters, so
00994     // we need to compare manually with the complete filtered list!
00995     Todo* subtodo = dynamic_cast<Todo *>( *it );
00996     if ( subtodo && todoList.contains( subtodo ) ) {
00997       t.append( subtodo );
00998     }
00999   }
01000 
01001   // Sort the sub-to-dos and then print them
01002   Todo::List sl = mCalendar->sortTodos( &t, sortField, sortDir );
01003   Todo::List::ConstIterator isl;
01004   startPoints.append( &startpt );
01005   for( isl = sl.begin(); isl != sl.end(); ++isl ) {
01006     count++;
01007     drawTodo( count, ( *isl ), p, sortField,  sortDir,
01008               connectSubTodos, strikeoutCompleted,
01009               desc, posPriority, posSummary, posDueDt, posPercentComplete,
01010               level+1, x, y, width, pageHeight, todoList, &startpt );
01011   }
01012   startPoints.remove( &startpt );
01013 }
01014 
01015 int CalPrintHelper::weekdayColumn( int weekday )
01016 {
01017   return ( weekday + 7 - KGlobal::locale()->weekStartDay() ) % 7;
01018 }
01019 
01020 void CalPrintHelper::drawJournalField( QPainter &p, QString field, QString text,
01021                                        int x, int &y, int width, int pageHeight )
01022 {
01023   if ( text.isEmpty() ) return;
01024 
01025   QString entry( field.arg( text ) );
01026 
01027   QRect rect( p.boundingRect( x, y, width, -1, Qt::WordBreak, entry) );
01028   if ( rect.bottom() > pageHeight) {
01029     // Start new page...
01030     // FIXME: If it's a multi-line text, draw a few lines on this page, and the
01031     // remaining lines on the next page.
01032     y=0;
01033     mPrinter->newPage();
01034     rect = p.boundingRect( x, y, width, -1, Qt::WordBreak, entry);
01035   }
01036   QRect newrect;
01037   p.drawText( rect, Qt::WordBreak, entry, -1, &newrect );
01038   y = newrect.bottom() + 7;
01039 }
01040 
01041 void CalPrintHelper::drawJournal( Journal * journal, QPainter &p, int x, int &y,
01042                                   int width, int pageHeight )
01043 {
01044   QFont oldFont( p.font() );
01045   p.setFont( QFont( "helvetica", 15 ) );
01046   QString headerText;
01047   QString dateText( KGlobal::locale()->
01048         formatDate( journal->dtStart().date(), false ) );
01049 
01050   if ( journal->summary().isEmpty() ) {
01051     headerText = dateText;
01052   } else {
01053     headerText = i18n("Description - date", "%1 - %2")
01054                      .arg( journal->summary() )
01055                      .arg( dateText );
01056   }
01057 
01058   QRect rect( p.boundingRect( x, y, width, -1, Qt::WordBreak, headerText) );
01059   if ( rect.bottom() > pageHeight) {
01060     // Start new page...
01061     y=0;
01062     mPrinter->newPage();
01063     rect = p.boundingRect( x, y, width, -1, Qt::WordBreak, headerText );
01064   }
01065   QRect newrect;
01066   p.drawText( rect, Qt::WordBreak, headerText, -1, &newrect );
01067   p.setFont( oldFont );
01068 
01069   y = newrect.bottom() + 4;
01070 
01071   p.drawLine( x + 3, y, x + width - 6, y );
01072   y += 5;
01073 
01074   drawJournalField( p, i18n("Person: %1"), journal->organizer().fullName(), x, y, width, pageHeight );
01075   drawJournalField( p, i18n("%1"), journal->description(), x, y, width, pageHeight );
01076   y += 10;
01077 }
01078 
01079 
01080 void CalPrintHelper::drawSplitHeaderRight( QPainter &p, const QDate &fd,
01081                                            const QDate &td,
01082                                            const QDate &,
01083                                            int width, int )
01084 {
01085   QFont oldFont( p.font() );
01086 
01087   QPen oldPen( p.pen() );
01088   QPen pen( Qt::black, 4 );
01089 
01090   QString title;
01091   if ( mCalSys ) {
01092     if ( fd.month() == td.month() ) {
01093       title = i18n("Date range: Month dayStart - dayEnd", "%1 %2 - %3")
01094         .arg( mCalSys->monthName( fd.month(), false ) )
01095         .arg( mCalSys->dayString( fd, false ) )
01096         .arg( mCalSys->dayString( td, false ) );
01097     } else {
01098       title = i18n("Date range: monthStart dayStart - monthEnd dayEnd", "%1 %2 - %3 %4")
01099         .arg( mCalSys->monthName( fd.month(), false ) )
01100         .arg( mCalSys->dayString( fd, false ) )
01101         .arg( mCalSys->monthName( td.month(), false ) )
01102         .arg( mCalSys->dayString( td, false ) );
01103     }
01104   }
01105 
01106   QFont serifFont("Times", 30);
01107   p.setFont(serifFont);
01108 
01109   int lineSpacing = p.fontMetrics().lineSpacing();
01110   p.drawText( 0, lineSpacing * 0, width, lineSpacing,
01111               Qt::AlignRight | Qt::AlignTop, title );
01112 
01113   title.truncate(0);
01114 
01115   p.setPen( pen );
01116   p.drawLine(300, lineSpacing * 1, width, lineSpacing * 1);
01117   p.setPen( oldPen );
01118 
01119   p.setFont(QFont("Times", 20, QFont::Bold, TRUE));
01120   int newlineSpacing = p.fontMetrics().lineSpacing();
01121   title += QString::number(fd.year());
01122   p.drawText( 0, lineSpacing * 1 + 4, width, newlineSpacing,
01123               Qt::AlignRight | Qt::AlignTop, title );
01124 
01125   p.setFont( oldFont );
01126 }
01127 
01128 #endif
KDE Home | KDE Accessibility Home | Description of Access Keys