korganizer

calprintpluginbase.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 
00031 #include <kdebug.h>
00032 #include <kconfig.h>
00033 #include <kcalendarsystem.h>
00034 #include <kwordwrap.h>
00035 
00036 #include "calprintpluginbase.h"
00037 #include "cellitem.h"
00038 
00039 #ifndef KORG_NOPRINTER
00040 
00041 inline int round(const double x)
00042  {
00043  return int(x > 0.0 ? x + 0.5 : x - 0.5);
00044  }
00045 /******************************************************************
00046  **              The Todo positioning structure                  **
00047  ******************************************************************/
00048 class CalPrintPluginBase::TodoParentStart
00049 {
00050   public:
00051     TodoParentStart( QRect pt = QRect(), bool page = true )
00052       : mRect( pt ), mSamePage( page ) {}
00053 
00054     QRect mRect;
00055     bool mSamePage;
00056 };
00057 
00058 
00059 /******************************************************************
00060  **                     The Print item                           **
00061  ******************************************************************/
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 
00103 
00104 
00105 /******************************************************************
00106  **                    The Print plugin                          **
00107  ******************************************************************/
00108 
00109 
00110 CalPrintPluginBase::CalPrintPluginBase() : PrintPlugin(), mUseColors( true ),
00111      mHeaderHeight(-1), mSubHeaderHeight( SUBHEADER_HEIGHT ),
00112      mMargin( MARGIN_SIZE ), mPadding( PADDING_SIZE), mCalSys( 0 )
00113 {
00114 }
00115 CalPrintPluginBase::~CalPrintPluginBase()
00116 {
00117 }
00118 
00119 
00120 
00121 QWidget *CalPrintPluginBase::createConfigWidget( QWidget *w )
00122 {
00123   QFrame *wdg = new QFrame( w );
00124   QVBoxLayout *layout = new QVBoxLayout( wdg );
00125 
00126   QLabel *title = new QLabel( description(), wdg );
00127   QFont titleFont( title->font() );
00128   titleFont.setPointSize( 20 );
00129   titleFont.setBold( true );
00130   title->setFont( titleFont );
00131 
00132   layout->addWidget( title );
00133   layout->addWidget( new QLabel( info(), wdg ) );
00134   layout->addSpacing( 20 );
00135   layout->addWidget( new QLabel( i18n("This printing style does not "
00136                                       "have any configuration options."),
00137                                  wdg ) );
00138   layout->addStretch();
00139   return wdg;
00140 }
00141 
00142 void CalPrintPluginBase::doPrint( KPrinter *printer )
00143 {
00144   if ( !printer ) return;
00145   mPrinter = printer;
00146   QPainter p;
00147 
00148   mPrinter->setColorMode( mUseColors?(KPrinter::Color):(KPrinter::GrayScale) );
00149 
00150   p.begin( mPrinter );
00151   // TODO: Fix the margins!!!
00152   // the painter initially begins at 72 dpi per the Qt docs.
00153   // we want half-inch margins.
00154   int margins = margin();
00155   p.setViewport( margins, margins,
00156                  p.viewport().width() - 2*margins,
00157                  p.viewport().height() - 2*margins );
00158 //   QRect vp( p.viewport() );
00159 // vp.setRight( vp.right()*2 );
00160 // vp.setBottom( vp.bottom()*2 );
00161 //   p.setWindow( vp );
00162   int pageWidth = p.window().width();
00163   int pageHeight = p.window().height();
00164 //   int pageWidth = p.viewport().width();
00165 //   int pageHeight = p.viewport().height();
00166 
00167   print( p, pageWidth, pageHeight );
00168 
00169   p.end();
00170   mPrinter = 0;
00171 }
00172 
00173 void CalPrintPluginBase::doLoadConfig()
00174 {
00175   if ( mConfig ) {
00176     KConfigGroupSaver saver( mConfig, description() );
00177     mConfig->sync();
00178     QDateTime currDate( QDate::currentDate() );
00179     mFromDate = mConfig->readDateTimeEntry( "FromDate", &currDate ).date();
00180     mToDate = mConfig->readDateTimeEntry( "ToDate" ).date();
00181     mUseColors = mConfig->readBoolEntry( "UseColors", true );
00182     setUseColors( mUseColors );
00183     loadConfig();
00184   } else {
00185     kdDebug(5850) << "No config available in loadConfig!!!!" << endl;
00186   }
00187 }
00188 
00189 void CalPrintPluginBase::doSaveConfig()
00190 {
00191   if ( mConfig ) {
00192     KConfigGroupSaver saver( mConfig, description() );
00193     saveConfig();
00194     mConfig->writeEntry( "FromDate", QDateTime( mFromDate ) );
00195     mConfig->writeEntry( "ToDate", QDateTime( mToDate ) );
00196     mConfig->writeEntry( "UseColors", mUseColors );
00197     mConfig->sync();
00198   } else {
00199     kdDebug(5850) << "No config available in saveConfig!!!!" << endl;
00200   }
00201 }
00202 
00203 
00204 
00205 
00206 void CalPrintPluginBase::setKOrgCoreHelper( KOrg::CoreHelper*helper )
00207 {
00208   PrintPlugin::setKOrgCoreHelper( helper );
00209   if ( helper )
00210     setCalendarSystem( helper->calendarSystem() );
00211 }
00212 
00213 bool CalPrintPluginBase::useColors() const
00214 {
00215   return mUseColors;
00216 }
00217 void CalPrintPluginBase::setUseColors( bool useColors )
00218 {
00219   mUseColors = useColors;
00220 }
00221 
00222 KPrinter::Orientation CalPrintPluginBase::orientation() const
00223 {
00224   return (mPrinter)?(mPrinter->orientation()):(KPrinter::Portrait);
00225 }
00226 
00227 
00228 
00229 QTime CalPrintPluginBase::dayStart()
00230 {
00231   QTime start( 8,0,0 );
00232   if ( mCoreHelper ) start = mCoreHelper->dayStart();
00233   return start;
00234 }
00235 
00236 void CalPrintPluginBase::setCategoryColors( QPainter &p, Incidence *incidence )
00237 {
00238   QColor bgColor = categoryBgColor( incidence );
00239   if ( bgColor.isValid() )
00240     p.setBrush( bgColor );
00241   QColor tColor( textColor( bgColor ) );
00242   if ( tColor.isValid() )
00243     p.setPen( tColor );
00244 }
00245 
00246 QColor CalPrintPluginBase::categoryBgColor( Incidence *incidence )
00247 {
00248   if (mCoreHelper && incidence)
00249     return mCoreHelper->categoryColor( incidence->categories() );
00250   else
00251     return QColor();
00252 }
00253 
00254 QColor CalPrintPluginBase::textColor( const QColor &color )
00255 {
00256   return (mCoreHelper)?(mCoreHelper->textColor( color )):QColor();
00257 }
00258 
00259 bool CalPrintPluginBase::isWorkingDay( const QDate &dt )
00260 {
00261   return (mCoreHelper)?( mCoreHelper->isWorkingDay( dt ) ):true;
00262 }
00263 
00264 QString CalPrintPluginBase::holidayString( const QDate &dt )
00265 {
00266   return (mCoreHelper)?(mCoreHelper->holidayString(dt)):(QString::null);
00267 }
00268 
00269 
00270 Event *CalPrintPluginBase::holiday( const QDate &dt )
00271 {
00272   QString hstring( holidayString( dt ) );
00273   if ( !hstring.isEmpty() ) {
00274     Event*holiday=new Event();
00275     holiday->setSummary( hstring );
00276     holiday->setDtStart( dt );
00277     holiday->setDtEnd( dt );
00278     holiday->setFloats( true );
00279     holiday->setCategories( i18n("Holiday") );
00280     return holiday;
00281   }
00282   return 0;
00283 }
00284 
00285 const KCalendarSystem *CalPrintPluginBase::calendarSystem() const
00286 {
00287   return mCalSys;
00288 }
00289 void CalPrintPluginBase::setCalendarSystem( const KCalendarSystem *calsys )
00290 {
00291   mCalSys = calsys;
00292 }
00293 
00294 int CalPrintPluginBase::headerHeight() const
00295 {
00296   if ( mHeaderHeight >= 0 )
00297     return mHeaderHeight;
00298   else if ( orientation() == KPrinter::Portrait )
00299     return PORTRAIT_HEADER_HEIGHT;
00300   else
00301     return LANDSCAPE_HEADER_HEIGHT;
00302 }
00303 void CalPrintPluginBase::setHeaderHeight( const int height )
00304 {
00305   mHeaderHeight = height;
00306 }
00307 
00308 int CalPrintPluginBase::subHeaderHeight() const
00309 {
00310   return mSubHeaderHeight;
00311 }
00312 void CalPrintPluginBase::setSubHeaderHeight( const int height )
00313 {
00314   mSubHeaderHeight = height;
00315 }
00316 
00317 int CalPrintPluginBase::margin() const
00318 {
00319   return mMargin;
00320 }
00321 void CalPrintPluginBase::setMargin( const int margin )
00322 {
00323   mMargin = margin;
00324 }
00325 
00326 int CalPrintPluginBase::padding() const
00327 {
00328   return mPadding;
00329 }
00330 void CalPrintPluginBase::setPadding( const int padding )
00331 {
00332   mPadding = padding;
00333 }
00334 
00335 int CalPrintPluginBase::borderWidth() const
00336 {
00337   return mBorder;
00338 }
00339 void CalPrintPluginBase::setBorderWidth( const int borderwidth )
00340 {
00341   mBorder = borderwidth;
00342 }
00343 
00344 
00345 
00346 
00347 void CalPrintPluginBase::drawBox( QPainter &p, int linewidth, const QRect &rect )
00348 {
00349   QPen pen( p.pen() );
00350   QPen oldpen( pen );
00351   pen.setWidth( linewidth );
00352   p.setPen( pen );
00353   p.drawRect( rect );
00354   p.setPen( oldpen );
00355 }
00356 
00357 void CalPrintPluginBase::drawShadedBox( QPainter &p, int linewidth, const QBrush &brush, const QRect &rect )
00358 {
00359   QBrush oldbrush( p.brush() );
00360   p.setBrush( brush );
00361   drawBox( p, linewidth, rect );
00362   p.setBrush( oldbrush );
00363 }
00364 
00365 void CalPrintPluginBase::printEventString( QPainter &p, const QRect &box, const QString &str, int flags )
00366 {
00367   QRect newbox( box );
00368   newbox.addCoords( 3, 1, -1, -1 );
00369   p.drawText( newbox, (flags==-1)?(Qt::AlignTop | Qt::AlignJustify | Qt::BreakAnywhere):flags, str );
00370 }
00371 
00372 
00373 void CalPrintPluginBase::showEventBox( QPainter &p, const QRect &box, Incidence *incidence, const QString &str, int flags )
00374 {
00375   QPen oldpen( p.pen() );
00376   QBrush oldbrush( p.brush() );
00377   QColor bgColor( categoryBgColor( incidence ) );
00378   if ( mUseColors & bgColor.isValid() ) {
00379     p.setBrush( bgColor );
00380   } else {
00381     p.setBrush( QColor( 232, 232, 232 ) );
00382   }
00383   drawBox( p, EVENT_BORDER_WIDTH, box );
00384 
00385   if ( mUseColors && bgColor.isValid() ) {
00386     p.setPen( textColor( bgColor ) );
00387   }
00388   printEventString( p, box, str, flags );
00389   p.setPen( oldpen );
00390   p.setBrush( oldbrush );
00391 }
00392 
00393 
00394 void CalPrintPluginBase::drawSubHeaderBox(QPainter &p, const QString &str,
00395     const QRect &box )
00396 {
00397   drawShadedBox( p, BOX_BORDER_WIDTH, QColor( 232, 232, 232 ), box );
00398   QFont oldfont( p.font() );
00399   p.setFont( QFont( "sans-serif", 10, QFont::Bold ) );
00400   p.drawText( box, Qt::AlignCenter | Qt::AlignVCenter, str );
00401   p.setFont( oldfont );
00402 }
00403 
00404 void CalPrintPluginBase::drawVerticalBox( QPainter &p, const QRect &box, const QString &str )
00405 {
00406   p.save();
00407   p.rotate( -90 );
00408   QRect rotatedBox( -box.top()-box.height(), box.left(), box.height(), box.width() );
00409   showEventBox( p, rotatedBox, 0, str, Qt::AlignLeft | Qt::AlignVCenter | Qt::SingleLine );
00410 
00411   p.restore();
00412 }
00413 
00414 
00415 
00417 // Return value: If expand, bottom of the printed box, otherwise vertical end
00418 // of the printed contents inside the box.
00419 
00420 int CalPrintPluginBase::drawBoxWithCaption( QPainter &p, const QRect &allbox,
00421         const QString &caption, const QString &contents, bool sameLine, bool expand, const QFont &captionFont, const QFont &textFont )
00422 {
00423   QFont oldFont( p.font() );
00424 //   QFont captionFont( "sans-serif", 11, QFont::Bold );
00425 //   QFont textFont( "sans-serif", 11, QFont::Normal );
00426 //   QFont captionFont( "Tahoma", 11, QFont::Bold );
00427 //   QFont textFont( "Tahoma", 11, QFont::Normal );
00428 
00429 
00430   QRect box( allbox );
00431 
00432   // Bounding rectangle for caption, single-line, clip on the right
00433   QRect captionBox( box.left() + padding(), box.top() + padding(), 0, 0 );
00434   p.setFont( captionFont );
00435   captionBox = p.boundingRect( captionBox, Qt::AlignLeft | Qt::AlignTop | Qt::SingleLine, caption );
00436   p.setFont( oldFont );
00437   if ( captionBox.right() > box.right() )
00438     captionBox.setRight( box.right() );
00439   if ( expand && captionBox.bottom() + padding() > box.bottom() )
00440     box.setBottom( captionBox.bottom() + padding() );
00441 
00442   // Bounding rectangle for the contents (if any), word break, clip on the bottom
00443   QRect textBox( captionBox );
00444   if ( !contents.isEmpty() ) {
00445     if ( sameLine ) {
00446       textBox.setLeft( captionBox.right() + padding() );
00447     } else {
00448       textBox.setTop( captionBox.bottom() + padding() );
00449     }
00450     textBox.setRight( box.right() );
00451     textBox.setHeight( 0 );
00452     p.setFont( textFont );
00453     textBox = p.boundingRect( textBox, Qt::WordBreak | Qt::AlignTop | Qt::AlignLeft, contents );
00454     p.setFont( oldFont );
00455     if ( textBox.bottom() + padding() > box.bottom() ) {
00456       if ( expand ) {
00457         box.setBottom( textBox.bottom() + padding() );
00458       } else {
00459         textBox.setBottom( box.bottom() );
00460       }
00461     }
00462   }
00463 
00464   drawBox( p, BOX_BORDER_WIDTH, box );
00465   p.setFont( captionFont );
00466   p.drawText( captionBox, Qt::AlignLeft | Qt::AlignTop | Qt::SingleLine, caption );
00467   if ( !contents.isEmpty() ) {
00468     p.setFont( textFont );
00469     p.drawText( textBox, Qt::WordBreak | Qt::AlignTop | Qt::AlignLeft, contents );
00470   }
00471   p.setFont( oldFont );
00472 
00473   if ( expand ) {
00474     return box.bottom();
00475   } else {
00476     return textBox.bottom();
00477   }
00478 }
00479 
00480 
00482 
00483 int CalPrintPluginBase::drawHeader( QPainter &p, QString title,
00484     const QDate &month1, const QDate &month2, const QRect &allbox, bool expand )
00485 {
00486   // print previous month for month view, print current for to-do, day and week
00487   int smallMonthWidth = (allbox.width()/4) - 10;
00488   if (smallMonthWidth>100) smallMonthWidth=100;
00489 
00490   int right = allbox.right();
00491   if ( month1.isValid() ) right -= (20+smallMonthWidth);
00492   if ( month2.isValid() ) right -= (20+smallMonthWidth);
00493   QRect box( allbox );
00494   QRect textRect( allbox );
00495   textRect.addCoords( 5, 0, 0, 0 );
00496   textRect.setRight( right );
00497 
00498 
00499   QFont oldFont( p.font() );
00500   QFont newFont("sans-serif", (textRect.height()<60)?16:18, QFont::Bold);
00501   if ( expand ) {
00502     p.setFont( newFont );
00503     QRect boundingR = p.boundingRect( textRect, Qt::AlignLeft | Qt::AlignVCenter | Qt::WordBreak, title );
00504     p.setFont( oldFont );
00505     int h = boundingR.height();
00506     if ( h > allbox.height() ) {
00507       box.setHeight( h );
00508       textRect.setHeight( h );
00509     }
00510   }
00511 
00512   drawShadedBox( p, BOX_BORDER_WIDTH, QColor( 232, 232, 232 ), box );
00513 
00514   QRect monthbox( box.right()-10-smallMonthWidth, box.top(), smallMonthWidth, box.height() );
00515   if (month2.isValid()) {
00516     drawSmallMonth( p, QDate(month2.year(), month2.month(), 1), monthbox );
00517     monthbox.moveBy( -20 - smallMonthWidth, 0 );
00518   }
00519   if (month1.isValid()) {
00520     drawSmallMonth( p, QDate(month1.year(), month1.month(), 1), monthbox );
00521     monthbox.moveBy( -20 - smallMonthWidth, 0 );
00522   }
00523 
00524   // Set the margins
00525   p.setFont( newFont );
00526   p.drawText( textRect, Qt::AlignLeft | Qt::AlignVCenter | Qt::WordBreak, title );
00527   p.setFont( oldFont );
00528 
00529   return textRect.bottom();
00530 }
00531 
00532 
00533 void CalPrintPluginBase::drawSmallMonth(QPainter &p, const QDate &qd,
00534     const QRect &box )
00535 {
00536 
00537   int weekdayCol = weekdayColumn( qd.dayOfWeek() );
00538   int month = qd.month();
00539   QDate monthDate(QDate(qd.year(), qd.month(), 1));
00540   // correct begin of week
00541   QDate monthDate2( monthDate.addDays( -weekdayCol ) );
00542 
00543   double cellWidth = double(box.width())/double(7);
00544   int rownr = 3 + ( qd.daysInMonth() + weekdayCol - 1 ) / 7;
00545   // 3 Pixel after month name, 2 after day names, 1 after the calendar
00546   double cellHeight = (box.height() - 5) / rownr;
00547   QFont oldFont( p.font() );
00548   p.setFont(QFont("sans-serif", int(cellHeight-1), QFont::Normal));
00549 
00550   // draw the title
00551   if ( mCalSys ) {
00552     QRect titleBox( box );
00553     titleBox.setHeight( int(cellHeight+1) );
00554     p.drawText( titleBox, Qt::AlignTop | Qt::AlignHCenter, mCalSys->monthName( qd ) );
00555   }
00556 
00557   // draw days of week
00558   QRect wdayBox( box );
00559   wdayBox.setTop( int( box.top() + 3 + cellHeight ) );
00560   wdayBox.setHeight( int(2*cellHeight)-int(cellHeight) );
00561 
00562   if ( mCalSys ) {
00563     for (int col = 0; col < 7; ++col) {
00564       QString tmpStr = mCalSys->weekDayName( monthDate2 )[0].upper();
00565       wdayBox.setLeft( int(box.left() + col*cellWidth) );
00566       wdayBox.setRight( int(box.left() + (col+1)*cellWidth) );
00567       p.drawText( wdayBox, Qt::AlignCenter, tmpStr );
00568       monthDate2 = monthDate2.addDays( 1 );
00569     }
00570   }
00571 
00572   // draw separator line
00573   int calStartY = wdayBox.bottom() + 2;
00574   p.drawLine( box.left(), calStartY, box.right(), calStartY );
00575   monthDate = monthDate.addDays( -weekdayCol );
00576 
00577   for ( int row = 0; row < (rownr-2); row++ ) {
00578     for ( int col = 0; col < 7; col++ ) {
00579       if ( monthDate.month() == month ) {
00580         QRect dayRect( int( box.left() + col*cellWidth ), int( calStartY + row*cellHeight ), 0, 0 );
00581         dayRect.setRight( int( box.left() + (col+1)*cellWidth ) );
00582         dayRect.setBottom( int( calStartY + (row+1)*cellHeight ) );
00583         p.drawText( dayRect, Qt::AlignCenter, QString::number( monthDate.day() ) );
00584       }
00585       monthDate = monthDate.addDays(1);
00586     }
00587   }
00588   p.setFont( oldFont );
00589 }
00590 
00591 
00592 
00593 
00594 
00596 
00597 /*
00598  * This routine draws a header box over the main part of the calendar
00599  * containing the days of the week.
00600  */
00601 void CalPrintPluginBase::drawDaysOfWeek(QPainter &p,
00602     const QDate &fromDate, const QDate &toDate, const QRect &box )
00603 {
00604   double cellWidth = double(box.width()) / double(fromDate.daysTo( toDate )+1);
00605   QDate cellDate( fromDate );
00606   QRect dateBox( box );
00607   int i = 0;
00608 
00609   while ( cellDate <= toDate ) {
00610     dateBox.setLeft( box.left() + int(i*cellWidth) );
00611     dateBox.setRight( box.left() + int((i+1)*cellWidth) );
00612     drawDaysOfWeekBox(p, cellDate, dateBox );
00613     cellDate = cellDate.addDays(1);
00614     i++;
00615   }
00616 }
00617 
00618 
00619 void CalPrintPluginBase::drawDaysOfWeekBox(QPainter &p, const QDate &qd,
00620     const QRect &box )
00621 {
00622   drawSubHeaderBox( p, (mCalSys)?(mCalSys->weekDayName( qd )):(QString::null), box );
00623 }
00624 
00625 
00626 void CalPrintPluginBase::drawTimeLine(QPainter &p,
00627     const QTime &fromTime, const QTime &toTime,
00628     const QRect &box)
00629 {
00630   drawBox( p, BOX_BORDER_WIDTH, box );
00631 
00632   int totalsecs=fromTime.secsTo(toTime);
00633   float minlen=(float)box.height()*60./(float)totalsecs;
00634   float cellHeight=(60.*(float)minlen);
00635   float currY=box.top();
00636   // TODO: Don't use half of the width, but less, for the minutes!
00637   int xcenter = box.left()+box.width()/2;
00638 
00639   QTime curTime( fromTime );
00640   QTime endTime( toTime );
00641   if ( fromTime.minute() > 30 )
00642     curTime = QTime( fromTime.hour()+1, 0, 0 );
00643   else if ( fromTime.minute() > 0 ) {
00644     curTime = QTime( fromTime.hour(), 30, 0 );
00645     float yy = currY + minlen*(float)fromTime.secsTo( curTime )/60.;
00646     p.drawLine( xcenter, (int)yy, box.right(), (int)yy );
00647     curTime = QTime( fromTime.hour()+1, 0, 0 );
00648   }
00649   currY += ( float( fromTime.secsTo(curTime)*minlen ) / 60. );
00650 
00651   while ( curTime < endTime ) {
00652     p.drawLine( box.left(), (int)currY, box.right(), (int)currY );
00653     int newY=(int)(currY+cellHeight/2.);
00654     QString numStr;
00655     if ( newY < box.bottom() ) {
00656       QFont oldFont( p.font() );
00657       // draw the time:
00658       if ( !KGlobal::locale()->use12Clock() ) {
00659         p.drawLine( xcenter, (int)newY, box.right(), (int)newY);
00660         numStr.setNum(curTime.hour());
00661         if (cellHeight > 30) {
00662           p.setFont(QFont("sans-serif", 16, QFont::Bold));
00663         } else {
00664           p.setFont(QFont("sans-serif", 12, QFont::Bold));
00665         }
00666         p.drawText( box.left()+2, (int)currY+2, box.width()/2-2, (int)cellHeight,
00667                   Qt::AlignTop | Qt::AlignRight, numStr);
00668         p.setFont(QFont("sans-serif", 10, QFont::Normal));
00669         p.drawText( xcenter, (int)currY+2, box.width()/2+2, (int)(cellHeight/2)-3,
00670                   Qt::AlignTop | Qt::AlignLeft, "00");
00671       } else {
00672         p.drawLine( box.left(), (int)newY, box.right(), (int)newY);
00673         QTime time( curTime.hour(), 0 );
00674         numStr = KGlobal::locale()->formatTime( time );
00675         if ( box.width() < 60 ) {
00676           p.setFont(QFont("sans-serif", 8, QFont::Bold)); // for weekprint
00677         } else {
00678           p.setFont(QFont("sans-serif", 12, QFont::Bold)); // for dayprint
00679         }
00680         p.drawText(box.left()+2, (int)currY+2, box.width()-4, (int)cellHeight/2-3,
00681                   Qt::AlignTop|Qt::AlignLeft, numStr);
00682       }
00683       currY+=cellHeight;
00684       p.setFont( oldFont );
00685     } // enough space for half-hour line and time
00686     if (curTime.secsTo(endTime)>3600)
00687       curTime=curTime.addSecs(3600);
00688     else curTime=endTime;
00689   } // currTime<endTime
00690 }
00691 
00692 
00694 
00700 int CalPrintPluginBase::drawAllDayBox(QPainter &p, Event::List &eventList,
00701     const QDate &qd, bool expandable, const QRect &box )
00702 {
00703   Event::List::Iterator it, itold;
00704 
00705   int offset=box.top();
00706 
00707   QString multiDayStr;
00708 
00709   Event*hd = holiday( qd );
00710   if ( hd ) eventList.prepend( hd );
00711 
00712   it = eventList.begin();
00713   Event *currEvent = 0;
00714   // First, print all floating events
00715   while( it!=eventList.end() ) {
00716     currEvent=*it;
00717     itold=it;
00718     ++it;
00719     if ( currEvent && currEvent->doesFloat() ) {
00720       // set the colors according to the categories
00721       if ( expandable ) {
00722         QRect eventBox( box );
00723         eventBox.setTop( offset );
00724         showEventBox( p, eventBox, currEvent, currEvent->summary() );
00725         offset += box.height();
00726       } else {
00727         if ( !multiDayStr.isEmpty() ) multiDayStr += ", ";
00728         multiDayStr += currEvent->summary();
00729       }
00730       eventList.remove( itold );
00731     }
00732   }
00733   if ( hd ) delete hd;
00734 
00735   int ret = box.height();
00736   QRect eventBox( box );
00737   if (!expandable) {
00738     if (!multiDayStr.isEmpty()) {
00739       drawShadedBox( p, BOX_BORDER_WIDTH, QColor( 128, 128, 128 ), eventBox );
00740       printEventString( p, eventBox, multiDayStr );
00741     } else {
00742       drawBox( p, BOX_BORDER_WIDTH, eventBox );
00743     }
00744   } else {
00745     ret = offset - box.top();
00746     eventBox.setBottom( ret );
00747     drawBox( p, BOX_BORDER_WIDTH, eventBox );
00748   }
00749   return ret;
00750 }
00751 
00752 
00753 void CalPrintPluginBase::drawAgendaDayBox( QPainter &p, Event::List &events,
00754                                      const QDate &qd, bool expandable,
00755                                      QTime &fromTime, QTime &toTime,
00756                                      const QRect &oldbox )
00757 {
00758   if ( !isWorkingDay( qd ) ) {
00759     drawShadedBox( p, BOX_BORDER_WIDTH, QColor( 232, 232, 232 ), oldbox );
00760   } else {
00761     drawBox( p, BOX_BORDER_WIDTH, oldbox );
00762   }
00763   QRect box( oldbox );
00764   // Account for the border with and cut away that margin from the interior
00765 //   box.setRight( box.right()-BOX_BORDER_WIDTH );
00766 
00767   Event *event;
00768 
00769   if ( expandable ) {
00770     // Adapt start/end times to include complete events
00771     Event::List::ConstIterator it;
00772     for ( it = events.begin(); it != events.end(); ++it ) {
00773       event = *it;
00774       if ( event->dtStart().time() < fromTime )
00775         fromTime = event->dtStart().time();
00776       if ( event->dtEnd().time() > toTime )
00777         toTime = event->dtEnd().time();
00778     }
00779   }
00780 
00781   // Show at least one hour
00782 //   if ( fromTime.secsTo( toTime ) < 3600 ) {
00783 //     fromTime = QTime( fromTime.hour(), 0, 0 );
00784 //     toTime = fromTime.addSecs( 3600 );
00785 //   }
00786 
00787   // calculate the height of a cell and of a minute
00788   int totalsecs = fromTime.secsTo( toTime );
00789   float minlen = box.height() * 60. / totalsecs;
00790   float cellHeight = 60. * minlen;
00791   float currY = box.top();
00792 
00793   // print grid:
00794   QTime curTime( QTime( fromTime.hour(), 0, 0 ) );
00795   currY += fromTime.secsTo( curTime ) * minlen / 60;
00796 
00797   while ( curTime < toTime && curTime.isValid() ) {
00798     if ( currY > box.top() )
00799       p.drawLine( box.left(), int( currY ), box.right(), int( currY ) );
00800     currY += cellHeight / 2;
00801     if ( ( currY > box.top() ) && ( currY < box.bottom() ) ) {
00802       // enough space for half-hour line
00803       QPen oldPen( p.pen() );
00804       p.setPen( QColor( 192, 192, 192 ) );
00805       p.drawLine( box.left(), int( currY ), box.right(), int( currY ) );
00806       p.setPen( oldPen );
00807     }
00808     if ( curTime.secsTo( toTime ) > 3600 )
00809       curTime = curTime.addSecs( 3600 );
00810     else curTime = toTime;
00811     currY += cellHeight / 2;
00812   }
00813 
00814   QDateTime startPrintDate = QDateTime( qd, fromTime );
00815   QDateTime endPrintDate = QDateTime( qd, toTime );
00816 
00817   // Calculate horizontal positions and widths of events taking into account
00818   // overlapping events
00819 
00820   QPtrList<KOrg::CellItem> cells;
00821   cells.setAutoDelete( true );
00822 
00823   Event::List::ConstIterator itEvents;
00824   for( itEvents = events.begin(); itEvents != events.end(); ++itEvents ) {
00825     QValueList<QDateTime> times = (*itEvents)->startDateTimesForDate( qd );
00826     for ( QValueList<QDateTime>::ConstIterator it = times.begin();
00827           it != times.end(); ++it ) {
00828       cells.append( new PrintCellItem( *itEvents, (*it), (*itEvents)->endDateForStart( *it ) ) );
00829     }
00830   }
00831 
00832   QPtrListIterator<KOrg::CellItem> it1( cells );
00833   for( it1.toFirst(); it1.current(); ++it1 ) {
00834     KOrg::CellItem *placeItem = it1.current();
00835     KOrg::CellItem::placeItem( cells, placeItem );
00836   }
00837 
00838 //   p.setFont( QFont( "sans-serif", 10 ) );
00839 
00840   for( it1.toFirst(); it1.current(); ++it1 ) {
00841     PrintCellItem *placeItem = static_cast<PrintCellItem *>( it1.current() );
00842     drawAgendaItem( placeItem, p, startPrintDate, endPrintDate, minlen, box );
00843   }
00844 //   p.setFont( oldFont );
00845 }
00846 
00847 
00848 
00849 void CalPrintPluginBase::drawAgendaItem( PrintCellItem *item, QPainter &p,
00850                                    const QDateTime &startPrintDate,
00851                                    const QDateTime &endPrintDate,
00852                                    float minlen, const QRect &box )
00853 {
00854   Event *event = item->event();
00855 
00856   // start/end of print area for event
00857   QDateTime startTime = item->start();
00858   QDateTime endTime = item->end();
00859   if ( ( startTime < endPrintDate && endTime > startPrintDate ) ||
00860        ( endTime > startPrintDate && startTime < endPrintDate ) ) {
00861     if ( startTime < startPrintDate ) startTime = startPrintDate;
00862     if ( endTime > endPrintDate ) endTime = endPrintDate;
00863     int currentWidth = box.width() / item->subCells();
00864     int currentX = box.left() + item->subCell() * currentWidth;
00865     int currentYPos = int( box.top() + startPrintDate.secsTo( startTime ) *
00866                            minlen / 60. );
00867     int currentHeight = int( box.top() + startPrintDate.secsTo( endTime ) * minlen / 60. ) - currentYPos;
00868 
00869     QRect eventBox( currentX, currentYPos, currentWidth, currentHeight );
00870     QString str = i18n( "starttime - endtime summary, location",
00871                         "%1-%2 %3, %4" ).
00872                   arg( KGlobal::locale()->formatTime( startTime.time() ) ).
00873                   arg( KGlobal::locale()->formatTime( endTime.time() ) ).
00874                   arg( event->summary() ).
00875                   arg( event->location() );
00876     showEventBox( p, eventBox, event, str );
00877   }
00878 }
00879 
00880 //TODO TODO TODO
00881 void CalPrintPluginBase::drawDayBox( QPainter &p, const QDate &qd,
00882     const QRect &box,
00883     bool fullDate, bool printRecurDaily, bool printRecurWeekly )
00884 {
00885   QString dayNumStr;
00886   QString ampm;
00887   const KLocale*local = KGlobal::locale();
00888 
00889 
00890   // This has to be localized
00891   if ( fullDate && mCalSys ) {
00892 
00893     dayNumStr = i18n("weekday month date", "%1 %2 %3")
00894         .arg( mCalSys->weekDayName( qd ) )
00895         .arg( mCalSys->monthName( qd ) )
00896         .arg( qd.day() );
00897 //    dayNumStr = local->formatDate(qd);
00898   } else {
00899     dayNumStr = QString::number( qd.day() );
00900   }
00901 
00902   QRect subHeaderBox( box );
00903   subHeaderBox.setHeight( mSubHeaderHeight );
00904   drawShadedBox( p, BOX_BORDER_WIDTH, p.backgroundColor(), box );
00905   drawShadedBox( p, 0, QColor( 232, 232, 232 ), subHeaderBox );
00906   drawBox( p, BOX_BORDER_WIDTH, box );
00907   QString hstring( holidayString( qd ) );
00908   QFont oldFont( p.font() );
00909 
00910   QRect headerTextBox( subHeaderBox );
00911   headerTextBox.setLeft( subHeaderBox.left()+5 );
00912   headerTextBox.setRight( subHeaderBox.right()-5 );
00913   if (!hstring.isEmpty()) {
00914     p.setFont( QFont( "sans-serif", 8, QFont::Bold, true ) );
00915 
00916     p.drawText( headerTextBox, Qt::AlignLeft | Qt::AlignVCenter, hstring );
00917   }
00918   p.setFont(QFont("sans-serif", 10, QFont::Bold));
00919   p.drawText( headerTextBox, Qt::AlignRight | Qt::AlignVCenter, dayNumStr);
00920 
00921   Event::List eventList = mCalendar->events( qd,
00922                                              EventSortStartDate,
00923                                              SortDirectionAscending );
00924   QString text;
00925   p.setFont( QFont( "sans-serif", 8 ) );
00926 
00927   int textY=mSubHeaderHeight+3; // gives the relative y-coord of the next printed entry
00928   Event::List::ConstIterator it;
00929 
00930   for( it = eventList.begin(); it != eventList.end() && textY<box.height(); ++it ) {
00931     Event *currEvent = *it;
00932     if ( ( !printRecurDaily  && currEvent->recurrenceType() == Recurrence::rDaily  ) ||
00933          ( !printRecurWeekly && currEvent->recurrenceType() == Recurrence::rWeekly ) ) {
00934       continue; }
00935     if ( currEvent->doesFloat() || currEvent->isMultiDay() )
00936       text = "";
00937     else
00938       text = local->formatTime( currEvent->dtStart().time() );
00939 
00940     QString str;
00941     if ( !currEvent->location().isEmpty() ) {
00942       str = i18n( "summary, location", "%1, %2" ).
00943             arg( currEvent->summary() ).arg( currEvent->location() );
00944     } else {
00945       str = currEvent->summary();
00946     }
00947     drawIncidence( p, box, text, str, textY );
00948   }
00949 
00950   if ( textY<box.height() ) {
00951     Todo::List todos = mCalendar->todos( qd );
00952     Todo::List::ConstIterator it2;
00953     for( it2 = todos.begin(); it2 != todos.end() && textY<box.height(); ++it2 ) {
00954       Todo *todo = *it2;
00955       if ( ( !printRecurDaily  && todo->recurrenceType() == Recurrence::rDaily  ) ||
00956            ( !printRecurWeekly && todo->recurrenceType() == Recurrence::rWeekly ) )
00957         continue;
00958       if ( todo->hasDueDate() && !todo->doesFloat() )
00959         text += KGlobal::locale()->formatTime(todo->dtDue().time()) + " ";
00960       else
00961         text = "";
00962       QString str;
00963       if ( !todo->location().isEmpty() ) {
00964         str = i18n( "summary, location", "%1, %2" ).
00965                     arg( todo->summary() ).arg( todo->location() );
00966       } else {
00967         str = todo->summary();
00968       }
00969       drawIncidence( p, box, text, i18n("To-do: %1").arg(str), textY );
00970     }
00971   }
00972 
00973   p.setFont( oldFont );
00974 }
00975 
00976 // TODO TODO TODO
00977 void CalPrintPluginBase::drawIncidence( QPainter &p, const QRect &dayBox, const QString &time, const QString &summary, int &textY )
00978 {
00979   kdDebug(5850) << "summary = " << summary << endl;
00980 
00981   int flags = Qt::AlignLeft;
00982   QFontMetrics fm = p.fontMetrics();
00983   QRect timeBound = p.boundingRect( dayBox.x() + 5, dayBox.y() + textY,
00984                                     dayBox.width() - 10, fm.lineSpacing(),
00985                                     flags, time );
00986   p.drawText( timeBound, flags, time );
00987 
00988   int summaryWidth = time.isEmpty() ? 0 : timeBound.width() + 4;
00989   QRect summaryBound = QRect( dayBox.x() + 5 + summaryWidth, dayBox.y() + textY,
00990                               dayBox.width() - summaryWidth -5, dayBox.height() );
00991 
00992   KWordWrap *ww = KWordWrap::formatText( fm, summaryBound, flags, summary );
00993   ww->drawText( &p, dayBox.x() + 5 + summaryWidth, dayBox.y() + textY, flags );
00994 
00995   textY += ww->boundingRect().height();
00996 
00997   delete ww;
00998 }
00999 
01000 
01002 
01003 void CalPrintPluginBase::drawWeek(QPainter &p, const QDate &qd, const QRect &box )
01004 {
01005   QDate weekDate = qd;
01006   bool portrait = ( box.height() > box.width() );
01007   int cellWidth, cellHeight;
01008   int vcells;
01009   if (portrait) {
01010     cellWidth = box.width()/2;
01011     vcells=3;
01012   } else {
01013     cellWidth = box.width()/6;
01014     vcells=1;
01015   }
01016   cellHeight = box.height()/vcells;
01017 
01018   // correct begin of week
01019   int weekdayCol = weekdayColumn( qd.dayOfWeek() );
01020   weekDate = qd.addDays( -weekdayCol );
01021 
01022   for (int i = 0; i < 7; i++, weekDate = weekDate.addDays(1)) {
01023     // Saturday and sunday share a cell, so we have to special-case sunday
01024     int hpos = ((i<6)?i:(i-1)) / vcells;
01025     int vpos = ((i<6)?i:(i-1)) % vcells;
01026     QRect dayBox( box.left()+cellWidth*hpos, box.top()+cellHeight*vpos + ((i==6)?(cellHeight/2):0),
01027         cellWidth, (i<5)?(cellHeight):(cellHeight/2) );
01028     drawDayBox(p, weekDate, dayBox, true);
01029   } // for i through all weekdays
01030 }
01031 
01032 
01033 void CalPrintPluginBase::drawTimeTable(QPainter &p,
01034     const QDate &fromDate, const QDate &toDate,
01035     QTime &fromTime, QTime &toTime,
01036     const QRect &box)
01037 {
01038   // timeline is 1 hour:
01039   int alldayHeight = (int)( 3600.*box.height()/(fromTime.secsTo(toTime)+3600.) );
01040   int timelineWidth = TIMELINE_WIDTH;
01041 
01042   QRect dowBox( box );
01043   dowBox.setLeft( box.left() + timelineWidth );
01044   dowBox.setHeight( mSubHeaderHeight );
01045   drawDaysOfWeek( p, fromDate, toDate, dowBox );
01046 
01047   QRect tlBox( box );
01048   tlBox.setWidth( timelineWidth );
01049   tlBox.setTop( dowBox.bottom() + BOX_BORDER_WIDTH + alldayHeight );
01050   drawTimeLine( p, fromTime, toTime, tlBox );
01051 
01052   // draw each day
01053   QDate curDate(fromDate);
01054   int i=0;
01055   double cellWidth = double(dowBox.width()) / double(fromDate.daysTo(toDate)+1);
01056   while (curDate<=toDate) {
01057     QRect allDayBox( dowBox.left()+int(i*cellWidth), dowBox.bottom() + BOX_BORDER_WIDTH,
01058                      int((i+1)*cellWidth)-int(i*cellWidth), alldayHeight );
01059     QRect dayBox( allDayBox );
01060     dayBox.setTop( tlBox.top() );
01061     dayBox.setBottom( box.bottom() );
01062     Event::List eventList = mCalendar->events(curDate,
01063                                               EventSortStartDate,
01064                                               SortDirectionAscending);
01065     alldayHeight = drawAllDayBox( p, eventList, curDate, false, allDayBox );
01066     drawAgendaDayBox( p, eventList, curDate, false, fromTime, toTime, dayBox );
01067     i++;
01068     curDate=curDate.addDays(1);
01069   }
01070 
01071 }
01072 
01073 
01075 
01076 class MonthEventStruct
01077 {
01078   public:
01079     MonthEventStruct() : event(0) {}
01080     MonthEventStruct( const QDateTime &s, const QDateTime &e, Event *ev)
01081     {
01082       event = ev;
01083       start = s;
01084       end = e;
01085       if ( event->doesFloat() ) {
01086         start = QDateTime( start.date(), QTime(0,0,0) );
01087         end = QDateTime( end.date().addDays(1), QTime(0,0,0) ).addSecs(-1);
01088       }
01089     }
01090     bool operator<(const MonthEventStruct &mes) { return start < mes.start; }
01091     QDateTime start;
01092     QDateTime end;
01093     Event *event;
01094 };
01095 
01096 void CalPrintPluginBase::drawMonth( QPainter &p, const QDate &dt, const QRect &box, int maxdays, int subDailyFlags, int holidaysFlags )
01097 {
01098   const KCalendarSystem *calsys = calendarSystem();
01099   QRect subheaderBox( box );
01100   subheaderBox.setHeight( subHeaderHeight() );
01101   QRect borderBox( box );
01102   borderBox.setTop( subheaderBox.bottom()+1 );
01103   drawSubHeaderBox( p, calsys->monthName(dt), subheaderBox );
01104   // correct for half the border width
01105   int correction = (BOX_BORDER_WIDTH/*-1*/)/2;
01106   QRect daysBox( borderBox );
01107   daysBox.addCoords( correction, correction, -correction, -correction );
01108 
01109   int daysinmonth = calsys->daysInMonth( dt );
01110   if ( maxdays <= 0 ) maxdays = daysinmonth;
01111 
01112   int d;
01113   float dayheight = float(daysBox.height()) / float( maxdays );
01114 
01115   QColor holidayColor( 240, 240, 240 );
01116   QColor workdayColor( 255, 255, 255 );
01117   int dayNrWidth = p.fontMetrics().width( "99" );
01118 
01119   // Fill the remaining space (if a month has less days than others) with a crossed-out pattern
01120   if ( daysinmonth<maxdays ) {
01121     QRect dayBox( box.left(), daysBox.top() + round(dayheight*daysinmonth), box.width(), 0 );
01122     dayBox.setBottom( daysBox.bottom() );
01123     p.fillRect( dayBox, Qt::DiagCrossPattern );
01124   }
01125   // Backgrounded boxes for each day, plus day numbers
01126   QBrush oldbrush( p.brush() );
01127   for ( d = 0; d < daysinmonth; ++d ) {
01128     QDate day;
01129     calsys->setYMD( day, dt.year(), dt.month(), d+1 );
01130     QRect dayBox( daysBox.left()/*+rand()%50*/, daysBox.top() + round(dayheight*d), daysBox.width()/*-rand()%50*/, 0 );
01131     // FIXME: When using a border width of 0 for event boxes, don't let the rectangles overlap, i.e. subtract 1 from the top or bottom!
01132     dayBox.setBottom( daysBox.top()+round(dayheight*(d+1)) - 1 );
01133 
01134     p.setBrush( isWorkingDay( day )?workdayColor:holidayColor );
01135     p.drawRect( dayBox );
01136     QRect dateBox( dayBox );
01137     dateBox.setWidth( dayNrWidth+3 );
01138     p.drawText( dateBox, Qt::AlignRight | Qt::AlignVCenter | Qt::SingleLine,
01139                 QString::number(d+1) );
01140   }
01141   p.setBrush( oldbrush );
01142   int xstartcont = box.left() + dayNrWidth + 5;
01143 
01144   QDate start, end;
01145   calsys->setYMD( start, dt.year(), dt.month(), 1 );
01146   end = calsys->addMonths( start, 1 );
01147   end = calsys->addDays( end, -1 );
01148 
01149   Event::List events = mCalendar->events( start, end );
01150   QMap<int, QStringList> textEvents;
01151   QPtrList<KOrg::CellItem> timeboxItems;
01152   timeboxItems.setAutoDelete( true );
01153 
01154 
01155   // 1) For multi-day events, show boxes spanning several cells, use CellItem
01156   //    print the summary vertically
01157   // 2) For sub-day events, print the concated summaries into the remaining
01158   //    space of the box (optional, depending on the given flags)
01159   // 3) Draw some kind of timeline showing free and busy times
01160 
01161   // Holidays
01162   Event::List holidays;
01163   holidays.setAutoDelete( true );
01164   for ( QDate d(start); d <= end; d = d.addDays(1) ) {
01165     Event *e = holiday( d );
01166     if ( e ) {
01167       holidays.append( e );
01168       if ( holidaysFlags & TimeBoxes ) {
01169         timeboxItems.append( new PrintCellItem( e, QDateTime(d, QTime(0,0,0) ),
01170             QDateTime( d.addDays(1), QTime(0,0,0) ) ) );
01171       }
01172       if ( holidaysFlags & Text ) {
01173         textEvents[ d.day() ] << e->summary();
01174       }
01175     }
01176   }
01177 
01178   QValueList<MonthEventStruct> monthentries;
01179 
01180   for ( Event::List::ConstIterator evit = events.begin();
01181         evit != events.end(); ++evit ) {
01182     Event *e = (*evit);
01183     if (!e) continue;
01184     if ( e->doesRecur() ) {
01185       if ( e->recursOn( start ) ) {
01186         // This occurrence has possibly started before the beginning of the
01187         // month, so obtain the start date before the beginning of the month
01188         QValueList<QDateTime> starttimes = e->startDateTimesForDate( start );
01189         QValueList<QDateTime>::ConstIterator it = starttimes.begin();
01190         for ( ; it != starttimes.end(); ++it ) {
01191           monthentries.append( MonthEventStruct( *it, e->endDateForStart( *it ), e ) );
01192         }
01193       }
01194       // Loop through all remaining days of the month and check if the event
01195       // begins on that day (don't use Event::recursOn, as that will
01196       // also return events that have started earlier. These start dates
01197       // however, have already been treated!
01198       Recurrence *recur = e->recurrence();
01199       QDate d1( start.addDays(1) );
01200       while ( d1 <= end ) {
01201         if ( recur->recursOn(d1) ) {
01202           TimeList times( recur->recurTimesOn( d1 ) );
01203           for ( TimeList::ConstIterator it = times.begin();
01204                 it != times.end(); ++it ) {
01205             QDateTime d1start( d1, *it );
01206             monthentries.append( MonthEventStruct( d1start, e->endDateForStart( d1start ), e ) );
01207           }
01208         }
01209         d1 = d1.addDays(1);
01210       }
01211     } else {
01212       monthentries.append( MonthEventStruct( e->dtStart(), e->dtEnd(), e ) );
01213     }
01214   }
01215   qHeapSort( monthentries );
01216 
01217   QValueList<MonthEventStruct>::ConstIterator mit = monthentries.begin();
01218   QDateTime endofmonth( end, QTime(0,0,0) );
01219   endofmonth = endofmonth.addDays(1);
01220   for ( ; mit != monthentries.end(); ++mit ) {
01221     if ( (*mit).start.date() == (*mit).end.date() ) {
01222       // Show also single-day events as time line boxes
01223       if ( subDailyFlags & TimeBoxes ) {
01224         timeboxItems.append( new PrintCellItem( (*mit).event, (*mit).start, (*mit).end ) );
01225       }
01226       // Show as text in the box
01227       if ( subDailyFlags & Text ) {
01228         textEvents[ (*mit).start.date().day() ] << (*mit).event->summary();
01229       }
01230     } else {
01231       // Multi-day events are always shown as time line boxes
01232       QDateTime thisstart( (*mit).start );
01233       QDateTime thisend( (*mit).end );
01234       if ( thisstart.date()<start ) thisstart = start;
01235       if ( thisend>endofmonth ) thisend = endofmonth;
01236       timeboxItems.append( new PrintCellItem( (*mit).event, thisstart, thisend ) );
01237     }
01238   }
01239 
01240   // For Multi-day events, line them up nicely so that the boxes don't overlap
01241   QPtrListIterator<KOrg::CellItem> it1( timeboxItems );
01242   for( it1.toFirst(); it1.current(); ++it1 ) {
01243     KOrg::CellItem *placeItem = it1.current();
01244     KOrg::CellItem::placeItem( timeboxItems, placeItem );
01245   }
01246   QDateTime starttime( start, QTime( 0, 0, 0 ) );
01247   int newxstartcont = xstartcont;
01248 
01249   QFont oldfont( p.font() );
01250   p.setFont( QFont( "sans-serif", 7 ) );
01251   for( it1.toFirst(); it1.current(); ++it1 ) {
01252     PrintCellItem *placeItem = static_cast<PrintCellItem *>( it1.current() );
01253     int minsToStart = starttime.secsTo( placeItem->start() )/60;
01254     int minsToEnd = starttime.secsTo( placeItem->end() )/60;
01255 
01256     QRect eventBox( xstartcont + placeItem->subCell()*17,
01257            daysBox.top() + round( double( minsToStart*daysBox.height()) / double(maxdays*24*60) ),
01258            14, 0 );
01259     eventBox.setBottom( daysBox.top() + round( double( minsToEnd*daysBox.height()) / double(maxdays*24*60) ) );
01260     drawVerticalBox( p, eventBox, placeItem->event()->summary() );
01261     newxstartcont = QMAX( newxstartcont, eventBox.right() );
01262   }
01263   xstartcont = newxstartcont;
01264 
01265   // For Single-day events, simply print their summaries into the remaining
01266   // space of the day's cell
01267   for ( int d=0; d<daysinmonth; ++d ) {
01268     QStringList dayEvents( textEvents[d+1] );
01269     QString txt = dayEvents.join(", ");
01270     QRect dayBox( xstartcont, daysBox.top()+round(dayheight*d), 0, 0 );
01271     dayBox.setRight( box.right() );
01272     dayBox.setBottom( daysBox.top()+round(dayheight*(d+1)) );
01273     printEventString(p, dayBox, txt, Qt::AlignTop | Qt::AlignLeft | Qt::BreakAnywhere );
01274   }
01275   p.setFont( oldfont );
01276 //   p.setBrush( Qt::NoBrush );
01277   drawBox( p, BOX_BORDER_WIDTH, borderBox );
01278   p.restore();
01279 }
01280 
01282 
01283 void CalPrintPluginBase::drawMonthTable(QPainter &p, const QDate &qd, bool weeknumbers,
01284                                bool recurDaily, bool recurWeekly,
01285                                const QRect &box)
01286 {
01287   int yoffset = mSubHeaderHeight;
01288   int xoffset = 0;
01289   QDate monthDate(QDate(qd.year(), qd.month(), 1));
01290   QDate monthFirst(monthDate);
01291   QDate monthLast(monthDate.addMonths(1).addDays(-1));
01292 
01293 
01294   int weekdayCol = weekdayColumn( monthDate.dayOfWeek() );
01295   monthDate = monthDate.addDays(-weekdayCol);
01296 
01297   if (weeknumbers) {
01298     xoffset += 14;
01299   }
01300 
01301   int rows=(weekdayCol + qd.daysInMonth() - 1)/7 +1;
01302   double cellHeight = ( box.height() - yoffset ) / (1.*rows);
01303   double cellWidth = ( box.width() - xoffset ) / 7.;
01304 
01305   // Precalculate the grid...
01306   // rows is at most 6, so using 8 entries in the array is fine, too!
01307   int coledges[8], rowedges[8];
01308   for ( int i = 0; i <= 7; i++ ) {
01309     rowedges[i] = int( box.top() + yoffset + i*cellHeight );
01310     coledges[i] = int( box.left() + xoffset + i*cellWidth );
01311   }
01312 
01313   if (weeknumbers) {
01314     QFont oldFont(p.font());
01315     QFont newFont(p.font());
01316     newFont.setPointSize(6);
01317     p.setFont(newFont);
01318     QDate weekDate(monthDate);
01319     for (int row = 0; row<rows; ++row ) {
01320       int calWeek = weekDate.weekNumber();
01321       QRect rc( box.left(), rowedges[row], coledges[0] - 3 - box.left(), rowedges[row+1]-rowedges[row] );
01322       p.drawText( rc, Qt::AlignRight | Qt::AlignVCenter, QString::number( calWeek ) );
01323       weekDate = weekDate.addDays( 7 );
01324     }
01325     p.setFont( oldFont );
01326   }
01327 
01328   QRect daysOfWeekBox( box );
01329   daysOfWeekBox.setHeight( mSubHeaderHeight );
01330   daysOfWeekBox.setLeft( box.left()+xoffset );
01331   drawDaysOfWeek( p, monthDate, monthDate.addDays( 6 ), daysOfWeekBox );
01332 
01333   QColor back = p.backgroundColor();
01334   bool darkbg = false;
01335   for ( int row = 0; row < rows; ++row ) {
01336     for ( int col = 0; col < 7; ++col ) {
01337       // show days from previous/next month with a grayed background
01338       if ( (monthDate < monthFirst) || (monthDate > monthLast) ) {
01339         p.setBackgroundColor( back.dark( 120 ) );
01340         darkbg = true;
01341       }
01342       QRect dayBox( coledges[col], rowedges[row], coledges[col+1]-coledges[col], rowedges[row+1]-rowedges[row] );
01343       drawDayBox(p, monthDate, dayBox, false, recurDaily, recurWeekly );
01344       if ( darkbg ) {
01345         p.setBackgroundColor( back );
01346         darkbg = false;
01347       }
01348       monthDate = monthDate.addDays(1);
01349     }
01350   }
01351 }
01352 
01353 
01355 
01356 void CalPrintPluginBase::drawTodo( int &count, Todo *todo, QPainter &p,
01357                                TodoSortField sortField, SortDirection sortDir,
01358                                bool connectSubTodos, bool strikeoutCompleted,
01359                                bool desc, int posPriority, int posSummary,
01360                                int posDueDt, int posPercentComplete,
01361                                int level, int x, int &y, int width,
01362                                int pageHeight, const Todo::List &todoList,
01363                                TodoParentStart *r )
01364 {
01365   QString outStr;
01366   const KLocale *local = KGlobal::locale();
01367   QRect rect;
01368   TodoParentStart startpt;
01369 
01370   // This list keeps all starting points of the parent to-dos so the connection
01371   // lines of the tree can easily be drawn (needed if a new page is started)
01372   static QPtrList<TodoParentStart> startPoints;
01373   if ( level < 1 ) {
01374     startPoints.clear();
01375   }
01376 
01377   // Compute the right hand side of the to-do box
01378   int rhs = posPercentComplete;
01379   if ( rhs < 0 ) rhs = posDueDt; //not printing percent completed
01380   if ( rhs < 0 ) rhs = x+width;  //not printing due dates either
01381 
01382   // size of to-do
01383   outStr=todo->summary();
01384   int left = posSummary + ( level*10 );
01385   rect = p.boundingRect( left, y, ( rhs-left-5 ), -1, Qt::WordBreak, outStr );
01386   if ( !todo->description().isEmpty() && desc ) {
01387     outStr = todo->description();
01388     rect = p.boundingRect( left+20, rect.bottom()+5, width-(left+10-x), -1,
01389                            Qt::WordBreak, outStr );
01390   }
01391   // if too big make new page
01392   if ( rect.bottom() > pageHeight ) {
01393     // first draw the connection lines from parent to-dos:
01394     if ( level > 0 && connectSubTodos ) {
01395       TodoParentStart *rct;
01396       for ( rct = startPoints.first(); rct; rct = startPoints.next() ) {
01397         int start;
01398         int center = rct->mRect.left() + (rct->mRect.width()/2);
01399         int to = p.viewport().bottom();
01400 
01401         // draw either from start point of parent or from top of the page
01402         if ( rct->mSamePage )
01403           start = rct->mRect.bottom() + 1;
01404         else
01405           start = p.viewport().top();
01406         p.moveTo( center, start );
01407         p.lineTo( center, to );
01408         rct->mSamePage = false;
01409       }
01410     }
01411     y=0;
01412     mPrinter->newPage();
01413   }
01414 
01415   // If this is a sub-to-do, r will not be 0, and we want the LH side
01416   // of the priority line up to the RH side of the parent to-do's priority
01417   bool showPriority = posPriority>=0;
01418   int lhs = posPriority;
01419   if ( r ) {
01420     lhs = r->mRect.right() + 1;
01421   }
01422 
01423   outStr.setNum( todo->priority() );
01424   rect = p.boundingRect( lhs, y + 10, 5, -1, Qt::AlignCenter, outStr );
01425   // Make it a more reasonable size
01426   rect.setWidth(18);
01427   rect.setHeight(18);
01428 
01429   // Draw a checkbox
01430   p.setBrush( QBrush( Qt::NoBrush ) );
01431   p.drawRect( rect );
01432   if ( todo->isCompleted() ) {
01433     // cross out the rectangle for completed to-dos
01434     p.drawLine( rect.topLeft(), rect.bottomRight() );
01435     p.drawLine( rect.topRight(), rect.bottomLeft() );
01436   }
01437   lhs = rect.right() + 3;
01438 
01439   // Priority
01440   if ( todo->priority() > 0 && showPriority ) {
01441     p.drawText( rect, Qt::AlignCenter, outStr );
01442   }
01443   startpt.mRect = rect; //save for later
01444 
01445   // Connect the dots
01446   if ( level > 0 && connectSubTodos ) {
01447     int bottom;
01448     int center( r->mRect.left() + (r->mRect.width()/2) );
01449     if ( r->mSamePage )
01450       bottom = r->mRect.bottom() + 1;
01451     else
01452       bottom = 0;
01453     int to( rect.top() + (rect.height()/2) );
01454     int endx( rect.left() );
01455     p.moveTo( center, bottom );
01456     p.lineTo( center, to );
01457     p.lineTo( endx, to );
01458   }
01459 
01460   // summary
01461   outStr=todo->summary();
01462   rect = p.boundingRect( lhs, rect.top(), (rhs-(left + rect.width() + 5)),
01463                          -1, Qt::WordBreak, outStr );
01464 
01465   QRect newrect;
01466   //FIXME: the following code prints underline rather than strikeout text
01467 #if 0
01468   QFont f( p.font() );
01469   if ( todo->isCompleted() && strikeoutCompleted ) {
01470     f.setStrikeOut( true );
01471     p.setFont( f );
01472   }
01473   p.drawText( rect, Qt::WordBreak, outStr, -1, &newrect );
01474   f.setStrikeOut( false );
01475   p.setFont( f );
01476 #endif
01477   //TODO: Remove this section when the code above is fixed
01478   p.drawText( rect, Qt::WordBreak, outStr, -1, &newrect );
01479   if ( todo->isCompleted() && strikeoutCompleted ) {
01480     // strike out the summary text if to-do is complete
01481     // Note: we tried to use a strike-out font and for unknown reasons the
01482     // result was underline instead of strike-out, so draw the lines ourselves.
01483     int delta = p.fontMetrics().lineSpacing();
01484     int lines = ( rect.height() / delta ) + 1;
01485     for ( int i=0; i<lines; i++ ) {
01486       p.moveTo( rect.left(),  rect.top() + ( delta/2 ) + ( i*delta ) );
01487       p.lineTo( rect.right(), rect.top() + ( delta/2 ) + ( i*delta ) );
01488     }
01489   }
01490 
01491   // due date
01492   if ( todo->hasDueDate() && posDueDt>=0 ) {
01493     outStr = local->formatDate( todo->dtDue().date(), true );
01494     rect = p.boundingRect( posDueDt, y, x + width, -1,
01495                            Qt::AlignTop | Qt::AlignLeft, outStr );
01496     p.drawText( rect, Qt::AlignTop | Qt::AlignLeft, outStr );
01497   }
01498 
01499   // percentage completed
01500   bool showPercentComplete = posPercentComplete>=0;
01501   if ( showPercentComplete ) {
01502     int lwidth = 24;
01503     int lheight = 12;
01504     //first, draw the progress bar
01505     int progress = (int)(( lwidth*todo->percentComplete())/100.0 + 0.5);
01506 
01507     p.setBrush( QBrush( Qt::NoBrush ) );
01508     p.drawRect( posPercentComplete, y+3, lwidth, lheight );
01509     if ( progress > 0 ) {
01510       p.setBrush( QColor( 128, 128, 128 ) );
01511       p.drawRect( posPercentComplete, y+3, progress, lheight );
01512     }
01513 
01514     //now, write the percentage
01515     outStr = i18n( "%1%" ).arg( todo->percentComplete() );
01516     rect = p.boundingRect( posPercentComplete+lwidth+3, y, x + width, -1,
01517                            Qt::AlignTop | Qt::AlignLeft, outStr );
01518     p.drawText( rect, Qt::AlignTop | Qt::AlignLeft, outStr );
01519   }
01520 
01521   // description
01522   if ( !todo->description().isEmpty() && desc ) {
01523     y = newrect.bottom() + 5;
01524     outStr = todo->description();
01525     rect = p.boundingRect( left+20, y, x+width-(left+10), -1,
01526                            Qt::WordBreak, outStr );
01527     p.drawText( rect, Qt::WordBreak, outStr, -1, &newrect );
01528   }
01529 
01530   // Set the new line position
01531   y = newrect.bottom() + 10; //set the line position
01532 
01533   // If the to-do has sub-to-dos, we need to call ourselves recursively
01534 #if 0
01535   Incidence::List l = todo->relations();
01536   Incidence::List::ConstIterator it;
01537   startPoints.append( &startpt );
01538   for( it = l.begin(); it != l.end(); ++it ) {
01539     count++;
01540     // In the future, to-dos might also be related to events
01541     // Manually check if the sub-to-do is in the list of to-dos to print
01542     // The problem is that relations() does not apply filters, so
01543     // we need to compare manually with the complete filtered list!
01544     Todo* subtodo = dynamic_cast<Todo *>( *it );
01545     if (subtodo && todoList.contains( subtodo ) ) {
01546       drawTodo( count, subtodo, p, connectSubTodos, strikeoutCompleted,
01547                 desc, posPriority, posSummary, posDueDt, posPercentComplete,
01548                 level+1, x, y, width, pageHeight, todoList, &startpt );
01549     }
01550   }
01551 #endif
01552   // Make a list of all the sub-to-dos related to this to-do.
01553   Todo::List t;
01554   Incidence::List l = todo->relations();
01555   Incidence::List::ConstIterator it;
01556   for( it=l.begin(); it!=l.end(); ++it ) {
01557     // In the future, to-dos might also be related to events
01558     // Manually check if the sub-to-do is in the list of to-dos to print
01559     // The problem is that relations() does not apply filters, so
01560     // we need to compare manually with the complete filtered list!
01561     Todo* subtodo = dynamic_cast<Todo *>( *it );
01562     if ( subtodo && todoList.contains( subtodo ) ) {
01563       t.append( subtodo );
01564     }
01565   }
01566 
01567   // Sort the sub-to-dos and then print them
01568   Todo::List sl = mCalendar->sortTodos( &t, sortField, sortDir );
01569   Todo::List::ConstIterator isl;
01570   startPoints.append( &startpt );
01571   for( isl = sl.begin(); isl != sl.end(); ++isl ) {
01572     count++;
01573     drawTodo( count, ( *isl ), p, sortField, sortDir,
01574               connectSubTodos, strikeoutCompleted,
01575               desc, posPriority, posSummary, posDueDt, posPercentComplete,
01576               level+1, x, y, width, pageHeight, todoList, &startpt );
01577   }
01578   startPoints.remove( &startpt );
01579 }
01580 
01581 int CalPrintPluginBase::weekdayColumn( int weekday )
01582 {
01583   return ( weekday + 7 - KGlobal::locale()->weekStartDay() ) % 7;
01584 }
01585 
01586 void CalPrintPluginBase::drawJournalField( QPainter &p, QString field, QString text,
01587                                        int x, int &y, int width, int pageHeight )
01588 {
01589   if ( text.isEmpty() ) return;
01590 
01591   QString entry( field.arg( text ) );
01592 
01593   QRect rect( p.boundingRect( x, y, width, -1, Qt::WordBreak, entry) );
01594   if ( rect.bottom() > pageHeight) {
01595     // Start new page...
01596     // FIXME: If it's a multi-line text, draw a few lines on this page, and the
01597     // remaining lines on the next page.
01598     y=0;
01599     mPrinter->newPage();
01600     rect = p.boundingRect( x, y, width, -1, Qt::WordBreak, entry);
01601   }
01602   QRect newrect;
01603   p.drawText( rect, Qt::WordBreak, entry, -1, &newrect );
01604   y = newrect.bottom() + 7;
01605 }
01606 
01607 void CalPrintPluginBase::drawJournal( Journal * journal, QPainter &p, int x, int &y,
01608                                   int width, int pageHeight )
01609 {
01610   QFont oldFont( p.font() );
01611   p.setFont( QFont( "sans-serif", 15 ) );
01612   QString headerText;
01613   QString dateText( KGlobal::locale()->
01614         formatDate( journal->dtStart().date(), false ) );
01615 
01616   if ( journal->summary().isEmpty() ) {
01617     headerText = dateText;
01618   } else {
01619     headerText = i18n("Description - date", "%1 - %2")
01620                      .arg( journal->summary() )
01621                      .arg( dateText );
01622   }
01623 
01624   QRect rect( p.boundingRect( x, y, width, -1, Qt::WordBreak, headerText) );
01625   if ( rect.bottom() > pageHeight) {
01626     // Start new page...
01627     y=0;
01628     mPrinter->newPage();
01629     rect = p.boundingRect( x, y, width, -1, Qt::WordBreak, headerText );
01630   }
01631   QRect newrect;
01632   p.drawText( rect, Qt::WordBreak, headerText, -1, &newrect );
01633   p.setFont( oldFont );
01634 
01635   y = newrect.bottom() + 4;
01636 
01637   p.drawLine( x + 3, y, x + width - 6, y );
01638   y += 5;
01639 
01640   drawJournalField( p, i18n("Person: %1"), journal->organizer().fullName(), x, y, width, pageHeight );
01641   drawJournalField( p, i18n("%1"), journal->description(), x, y, width, pageHeight );
01642   y += 10;
01643 }
01644 
01645 
01646 void CalPrintPluginBase::drawSplitHeaderRight( QPainter &p, const QDate &fd,
01647                                            const QDate &td,
01648                                            const QDate &,
01649                                            int width, int )
01650 {
01651   QFont oldFont( p.font() );
01652 
01653   QPen oldPen( p.pen() );
01654   QPen pen( Qt::black, 4 );
01655 
01656   QString title;
01657   if ( mCalSys ) {
01658     if ( fd.month() == td.month() ) {
01659       title = i18n("Date range: Month dayStart - dayEnd", "%1 %2 - %3")
01660         .arg( mCalSys->monthName( fd.month(), false ) )
01661         .arg( mCalSys->dayString( fd, false ) )
01662         .arg( mCalSys->dayString( td, false ) );
01663     } else {
01664       title = i18n("Date range: monthStart dayStart - monthEnd dayEnd", "%1 %2 - %3 %4")
01665         .arg( mCalSys->monthName( fd.month(), false ) )
01666         .arg( mCalSys->dayString( fd, false ) )
01667         .arg( mCalSys->monthName( td.month(), false ) )
01668         .arg( mCalSys->dayString( td, false ) );
01669     }
01670   }
01671 
01672   QFont serifFont("Times", 30);
01673   p.setFont(serifFont);
01674 
01675   int lineSpacing = p.fontMetrics().lineSpacing();
01676   p.drawText( 0, lineSpacing * 0, width, lineSpacing,
01677               Qt::AlignRight | Qt::AlignTop, title );
01678 
01679   title.truncate(0);
01680 
01681   p.setPen( pen );
01682   p.drawLine(300, lineSpacing * 1, width, lineSpacing * 1);
01683   p.setPen( oldPen );
01684 
01685   p.setFont(QFont("Times", 20, QFont::Bold, TRUE));
01686   int newlineSpacing = p.fontMetrics().lineSpacing();
01687   title += QString::number(fd.year());
01688   p.drawText( 0, lineSpacing * 1 + 4, width, newlineSpacing,
01689               Qt::AlignRight | Qt::AlignTop, title );
01690 
01691   p.setFont( oldFont );
01692 }
01693 
01694 #endif