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     showEventBox( p, eventBox, event, event->summary() );
00871   }
00872 }
00873 
00874 //TODO TODO TODO
00875 void CalPrintPluginBase::drawDayBox( QPainter &p, const QDate &qd,
00876     const QRect &box,
00877     bool fullDate, bool printRecurDaily, bool printRecurWeekly )
00878 {
00879   QString dayNumStr;
00880   QString ampm;
00881   const KLocale*local = KGlobal::locale();
00882 
00883 
00884   // This has to be localized
00885   if ( fullDate && mCalSys ) {
00886 
00887     dayNumStr = i18n("weekday month date", "%1 %2 %3")
00888         .arg( mCalSys->weekDayName( qd ) )
00889         .arg( mCalSys->monthName( qd ) )
00890         .arg( qd.day() );
00891 //    dayNumStr = local->formatDate(qd);
00892   } else {
00893     dayNumStr = QString::number( qd.day() );
00894   }
00895 
00896   QRect subHeaderBox( box );
00897   subHeaderBox.setHeight( mSubHeaderHeight );
00898   drawShadedBox( p, BOX_BORDER_WIDTH, p.backgroundColor(), box );
00899   drawShadedBox( p, 0, QColor( 232, 232, 232 ), subHeaderBox );
00900   drawBox( p, BOX_BORDER_WIDTH, box );
00901   QString hstring( holidayString( qd ) );
00902   QFont oldFont( p.font() );
00903 
00904   QRect headerTextBox( subHeaderBox );
00905   headerTextBox.setLeft( subHeaderBox.left()+5 );
00906   headerTextBox.setRight( subHeaderBox.right()-5 );
00907   if (!hstring.isEmpty()) {
00908     p.setFont( QFont( "sans-serif", 8, QFont::Bold, true ) );
00909 
00910     p.drawText( headerTextBox, Qt::AlignLeft | Qt::AlignVCenter, hstring );
00911   }
00912   p.setFont(QFont("sans-serif", 10, QFont::Bold));
00913   p.drawText( headerTextBox, Qt::AlignRight | Qt::AlignVCenter, dayNumStr);
00914 
00915   Event::List eventList = mCalendar->events( qd,
00916                                              EventSortStartDate,
00917                                              SortDirectionAscending );
00918   QString text;
00919   p.setFont( QFont( "sans-serif", 8 ) );
00920 
00921   int textY=mSubHeaderHeight+3; // gives the relative y-coord of the next printed entry
00922   Event::List::ConstIterator it;
00923 
00924   for( it = eventList.begin(); it != eventList.end() && textY<box.height(); ++it ) {
00925     Event *currEvent = *it;
00926     if ( ( !printRecurDaily  && currEvent->recurrenceType() == Recurrence::rDaily  ) ||
00927          ( !printRecurWeekly && currEvent->recurrenceType() == Recurrence::rWeekly ) ) {
00928       continue; }
00929     if ( currEvent->doesFloat() || currEvent->isMultiDay() )
00930       text = "";
00931     else
00932       text = local->formatTime( currEvent->dtStart().time() );
00933 
00934     drawIncidence( p, box, text, currEvent->summary(), textY );
00935   }
00936 
00937   if ( textY<box.height() ) {
00938     Todo::List todos = mCalendar->todos( qd );
00939     Todo::List::ConstIterator it2;
00940     for( it2 = todos.begin(); it2 != todos.end() && textY<box.height(); ++it2 ) {
00941       Todo *todo = *it2;
00942       if ( ( !printRecurDaily  && todo->recurrenceType() == Recurrence::rDaily  ) ||
00943            ( !printRecurWeekly && todo->recurrenceType() == Recurrence::rWeekly ) )
00944         continue;
00945       if ( todo->hasDueDate() && !todo->doesFloat() )
00946         text += KGlobal::locale()->formatTime(todo->dtDue().time()) + " ";
00947       else
00948         text = "";
00949       drawIncidence( p, box, text, i18n("To-do: %1").arg(todo->summary()), textY );
00950     }
00951   }
00952 
00953   p.setFont( oldFont );
00954 }
00955 
00956 // TODO TODO TODO
00957 void CalPrintPluginBase::drawIncidence( QPainter &p, const QRect &dayBox, const QString &time, const QString &summary, int &textY )
00958 {
00959   kdDebug(5850) << "summary = " << summary << endl;
00960 
00961   int flags = Qt::AlignLeft;
00962   QFontMetrics fm = p.fontMetrics();
00963   QRect timeBound = p.boundingRect( dayBox.x() + 5, dayBox.y() + textY,
00964                                     dayBox.width() - 10, fm.lineSpacing(),
00965                                     flags, time );
00966   p.drawText( timeBound, flags, time );
00967 
00968   int summaryWidth = time.isEmpty() ? 0 : timeBound.width() + 4;
00969   QRect summaryBound = QRect( dayBox.x() + 5 + summaryWidth, dayBox.y() + textY,
00970                               dayBox.width() - summaryWidth -5, dayBox.height() );
00971 
00972   KWordWrap *ww = KWordWrap::formatText( fm, summaryBound, flags, summary );
00973   ww->drawText( &p, dayBox.x() + 5 + summaryWidth, dayBox.y() + textY, flags );
00974 
00975   textY += ww->boundingRect().height();
00976 
00977   delete ww;
00978 }
00979 
00980 
00982 
00983 void CalPrintPluginBase::drawWeek(QPainter &p, const QDate &qd, const QRect &box )
00984 {
00985   QDate weekDate = qd;
00986   bool portrait = ( box.height() > box.width() );
00987   int cellWidth, cellHeight;
00988   int vcells;
00989   if (portrait) {
00990     cellWidth = box.width()/2;
00991     vcells=3;
00992   } else {
00993     cellWidth = box.width()/6;
00994     vcells=1;
00995   }
00996   cellHeight = box.height()/vcells;
00997 
00998   // correct begin of week
00999   int weekdayCol = weekdayColumn( qd.dayOfWeek() );
01000   weekDate = qd.addDays( -weekdayCol );
01001 
01002   for (int i = 0; i < 7; i++, weekDate = weekDate.addDays(1)) {
01003     // Saturday and sunday share a cell, so we have to special-case sunday
01004     int hpos = ((i<6)?i:(i-1)) / vcells;
01005     int vpos = ((i<6)?i:(i-1)) % vcells;
01006     QRect dayBox( box.left()+cellWidth*hpos, box.top()+cellHeight*vpos + ((i==6)?(cellHeight/2):0),
01007         cellWidth, (i<5)?(cellHeight):(cellHeight/2) );
01008     drawDayBox(p, weekDate, dayBox, true);
01009   } // for i through all weekdays
01010 }
01011 
01012 
01013 void CalPrintPluginBase::drawTimeTable(QPainter &p,
01014     const QDate &fromDate, const QDate &toDate,
01015     QTime &fromTime, QTime &toTime,
01016     const QRect &box)
01017 {
01018   // timeline is 1 hour:
01019   int alldayHeight = (int)( 3600.*box.height()/(fromTime.secsTo(toTime)+3600.) );
01020   int timelineWidth = TIMELINE_WIDTH;
01021 
01022   QRect dowBox( box );
01023   dowBox.setLeft( box.left() + timelineWidth );
01024   dowBox.setHeight( mSubHeaderHeight );
01025   drawDaysOfWeek( p, fromDate, toDate, dowBox );
01026 
01027   QRect tlBox( box );
01028   tlBox.setWidth( timelineWidth );
01029   tlBox.setTop( dowBox.bottom() + BOX_BORDER_WIDTH + alldayHeight );
01030   drawTimeLine( p, fromTime, toTime, tlBox );
01031 
01032   // draw each day
01033   QDate curDate(fromDate);
01034   int i=0;
01035   double cellWidth = double(dowBox.width()) / double(fromDate.daysTo(toDate)+1);
01036   while (curDate<=toDate) {
01037     QRect allDayBox( dowBox.left()+int(i*cellWidth), dowBox.bottom() + BOX_BORDER_WIDTH,
01038                      int((i+1)*cellWidth)-int(i*cellWidth), alldayHeight );
01039     QRect dayBox( allDayBox );
01040     dayBox.setTop( tlBox.top() );
01041     dayBox.setBottom( box.bottom() );
01042     Event::List eventList = mCalendar->events(curDate,
01043                                               EventSortStartDate,
01044                                               SortDirectionAscending);
01045     alldayHeight = drawAllDayBox( p, eventList, curDate, false, allDayBox );
01046     drawAgendaDayBox( p, eventList, curDate, false, fromTime, toTime, dayBox );
01047     i++;
01048     curDate=curDate.addDays(1);
01049   }
01050 
01051 }
01052 
01053 
01055 
01056 class MonthEventStruct
01057 {
01058   public:
01059     MonthEventStruct() : event(0) {}
01060     MonthEventStruct( const QDateTime &s, const QDateTime &e, Event *ev)
01061     {
01062       event = ev;
01063       start = s;
01064       end = e;
01065       if ( event->doesFloat() ) {
01066         start = QDateTime( start.date(), QTime(0,0,0) );
01067         end = QDateTime( end.date().addDays(1), QTime(0,0,0) ).addSecs(-1);
01068       }
01069     }
01070     bool operator<(const MonthEventStruct &mes) { return start < mes.start; }
01071     QDateTime start;
01072     QDateTime end;
01073     Event *event;
01074 };
01075 
01076 void CalPrintPluginBase::drawMonth( QPainter &p, const QDate &dt, const QRect &box, int maxdays, int subDailyFlags, int holidaysFlags )
01077 {
01078   const KCalendarSystem *calsys = calendarSystem();
01079   QRect subheaderBox( box );
01080   subheaderBox.setHeight( subHeaderHeight() );
01081   QRect borderBox( box );
01082   borderBox.setTop( subheaderBox.bottom()+1 );
01083   drawSubHeaderBox( p, calsys->monthName(dt), subheaderBox );
01084   // correct for half the border width
01085   int correction = (BOX_BORDER_WIDTH/*-1*/)/2;
01086   QRect daysBox( borderBox );
01087   daysBox.addCoords( correction, correction, -correction, -correction );
01088 
01089   int daysinmonth = calsys->daysInMonth( dt );
01090   if ( maxdays <= 0 ) maxdays = daysinmonth;
01091   
01092   int d;
01093   float dayheight = float(daysBox.height()) / float( maxdays );
01094   
01095   QColor holidayColor( 240, 240, 240 );
01096   QColor workdayColor( 255, 255, 255 );
01097   int dayNrWidth = p.fontMetrics().width( "99" );
01098   
01099   // Fill the remaining space (if a month has less days than others) with a crossed-out pattern
01100   if ( daysinmonth<maxdays ) {
01101     QRect dayBox( box.left(), daysBox.top() + round(dayheight*daysinmonth), box.width(), 0 );
01102     dayBox.setBottom( daysBox.bottom() );
01103     p.fillRect( dayBox, Qt::DiagCrossPattern );
01104   }
01105   // Backgrounded boxes for each day, plus day numbers
01106   QBrush oldbrush( p.brush() );
01107   for ( d = 0; d < daysinmonth; ++d ) {
01108     QDate day;
01109     calsys->setYMD( day, dt.year(), dt.month(), d+1 );
01110     QRect dayBox( daysBox.left()/*+rand()%50*/, daysBox.top() + round(dayheight*d), daysBox.width()/*-rand()%50*/, 0 );
01111     // 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!
01112     dayBox.setBottom( daysBox.top()+round(dayheight*(d+1)) - 1 );
01113     
01114     p.setBrush( isWorkingDay( day )?workdayColor:holidayColor );
01115     p.drawRect( dayBox );
01116     QRect dateBox( dayBox );
01117     dateBox.setWidth( dayNrWidth+3 );
01118     p.drawText( dateBox, Qt::AlignRight | Qt::AlignVCenter | Qt::SingleLine, 
01119                 QString::number(d+1) );
01120   }
01121   p.setBrush( oldbrush );
01122   int xstartcont = box.left() + dayNrWidth + 5;
01123 
01124   QDate start, end;
01125   calsys->setYMD( start, dt.year(), dt.month(), 1 );
01126   end = calsys->addMonths( start, 1 );
01127   end = calsys->addDays( end, -1 );
01128 
01129   Event::List events = mCalendar->events( start, end );
01130   QMap<int, QStringList> textEvents;
01131   QPtrList<KOrg::CellItem> timeboxItems;
01132   timeboxItems.setAutoDelete( true );
01133 
01134 
01135   // 1) For multi-day events, show boxes spanning several cells, use CellItem
01136   //    print the summary vertically
01137   // 2) For sub-day events, print the concated summaries into the remaining
01138   //    space of the box (optional, depending on the given flags)
01139   // 3) Draw some kind of timeline showing free and busy times
01140 
01141   // Holidays
01142   Event::List holidays;
01143   holidays.setAutoDelete( true );
01144   for ( QDate d(start); d <= end; d = d.addDays(1) ) {
01145     Event *e = holiday( d );
01146     if ( e ) {
01147       holidays.append( e );
01148       if ( holidaysFlags & TimeBoxes ) {
01149         timeboxItems.append( new PrintCellItem( e, QDateTime(d, QTime(0,0,0) ),
01150             QDateTime( d.addDays(1), QTime(0,0,0) ) ) );
01151       }
01152       if ( holidaysFlags & Text ) {
01153         textEvents[ d.day() ] << e->summary();
01154       }
01155     }
01156   }
01157   
01158   QValueList<MonthEventStruct> monthentries;
01159 
01160   for ( Event::List::ConstIterator evit = events.begin(); 
01161         evit != events.end(); ++evit ) {
01162     Event *e = (*evit);
01163     if (!e) continue;
01164     if ( e->doesRecur() ) {
01165       if ( e->recursOn( start ) ) {
01166         // This occurrence has possibly started before the beginning of the 
01167         // month, so obtain the start date before the beginning of the month
01168         QValueList<QDateTime> starttimes = e->startDateTimesForDate( start );
01169         QValueList<QDateTime>::ConstIterator it = starttimes.begin();
01170         for ( ; it != starttimes.end(); ++it ) {
01171           monthentries.append( MonthEventStruct( *it, e->endDateForStart( *it ), e ) );
01172         }
01173       }
01174       // Loop through all remaining days of the month and check if the event 
01175       // begins on that day (don't use Event::recursOn, as that will 
01176       // also return events that have started earlier. These start dates
01177       // however, have already been treated!
01178       Recurrence *recur = e->recurrence();
01179       QDate d1( start.addDays(1) );
01180       while ( d1 <= end ) {
01181         if ( recur->recursOn(d1) ) {
01182           TimeList times( recur->recurTimesOn( d1 ) );
01183           for ( TimeList::ConstIterator it = times.begin();
01184                 it != times.end(); ++it ) {
01185             QDateTime d1start( d1, *it );
01186             monthentries.append( MonthEventStruct( d1start, e->endDateForStart( d1start ), e ) );
01187           }
01188         }
01189         d1 = d1.addDays(1);
01190       }
01191     } else {
01192       monthentries.append( MonthEventStruct( e->dtStart(), e->dtEnd(), e ) );
01193     }
01194   }
01195   qHeapSort( monthentries );
01196 
01197   QValueList<MonthEventStruct>::ConstIterator mit = monthentries.begin();
01198   QDateTime endofmonth( end, QTime(0,0,0) );
01199   endofmonth = endofmonth.addDays(1);
01200   for ( ; mit != monthentries.end(); ++mit ) {
01201     if ( (*mit).start.date() == (*mit).end.date() ) {
01202       // Show also single-day events as time line boxes
01203       if ( subDailyFlags & TimeBoxes ) {
01204         timeboxItems.append( new PrintCellItem( (*mit).event, (*mit).start, (*mit).end ) );
01205       }
01206       // Show as text in the box
01207       if ( subDailyFlags & Text ) {
01208         textEvents[ (*mit).start.date().day() ] << (*mit).event->summary();
01209       }
01210     } else {
01211       // Multi-day events are always shown as time line boxes
01212       QDateTime thisstart( (*mit).start );
01213       QDateTime thisend( (*mit).end );
01214       if ( thisstart.date()<start ) thisstart = start;
01215       if ( thisend>endofmonth ) thisend = endofmonth;
01216       timeboxItems.append( new PrintCellItem( (*mit).event, thisstart, thisend ) );
01217     }
01218   }
01219   
01220   // For Multi-day events, line them up nicely so that the boxes don't overlap
01221   QPtrListIterator<KOrg::CellItem> it1( timeboxItems );
01222   for( it1.toFirst(); it1.current(); ++it1 ) {
01223     KOrg::CellItem *placeItem = it1.current();
01224     KOrg::CellItem::placeItem( timeboxItems, placeItem );
01225   }
01226   QDateTime starttime( start, QTime( 0, 0, 0 ) );
01227   int newxstartcont = xstartcont;
01228   
01229   QFont oldfont( p.font() );
01230   p.setFont( QFont( "sans-serif", 7 ) );
01231   for( it1.toFirst(); it1.current(); ++it1 ) {
01232     PrintCellItem *placeItem = static_cast<PrintCellItem *>( it1.current() );
01233     int minsToStart = starttime.secsTo( placeItem->start() )/60;
01234     int minsToEnd = starttime.secsTo( placeItem->end() )/60;
01235 
01236     QRect eventBox( xstartcont + placeItem->subCell()*17, 
01237            daysBox.top() + round( double( minsToStart*daysBox.height()) / double(maxdays*24*60) ), 
01238            14, 0 );
01239     eventBox.setBottom( daysBox.top() + round( double( minsToEnd*daysBox.height()) / double(maxdays*24*60) ) );
01240     drawVerticalBox( p, eventBox, placeItem->event()->summary() );
01241     newxstartcont = QMAX( newxstartcont, eventBox.right() );
01242   }
01243   xstartcont = newxstartcont;
01244 
01245   // For Single-day events, simply print their summaries into the remaining
01246   // space of the day's cell
01247   for ( int d=0; d<daysinmonth; ++d ) {
01248     QStringList dayEvents( textEvents[d+1] );
01249     QString txt = dayEvents.join(", ");
01250     QRect dayBox( xstartcont, daysBox.top()+round(dayheight*d), 0, 0 );
01251     dayBox.setRight( box.right() );
01252     dayBox.setBottom( daysBox.top()+round(dayheight*(d+1)) );
01253     printEventString(p, dayBox, txt, Qt::AlignTop | Qt::AlignLeft | Qt::BreakAnywhere );
01254   }
01255   p.setFont( oldfont );
01256 //   p.setBrush( Qt::NoBrush );
01257   drawBox( p, BOX_BORDER_WIDTH, borderBox );
01258   p.restore();
01259 }
01260 
01262 
01263 void CalPrintPluginBase::drawMonthTable(QPainter &p, const QDate &qd, bool weeknumbers,
01264                                bool recurDaily, bool recurWeekly,
01265                                const QRect &box)
01266 {
01267   int yoffset = mSubHeaderHeight;
01268   int xoffset = 0;
01269   QDate monthDate(QDate(qd.year(), qd.month(), 1));
01270   QDate monthFirst(monthDate);
01271   QDate monthLast(monthDate.addMonths(1).addDays(-1));
01272 
01273 
01274   int weekdayCol = weekdayColumn( monthDate.dayOfWeek() );
01275   monthDate = monthDate.addDays(-weekdayCol);
01276 
01277   if (weeknumbers) {
01278     xoffset += 14;
01279   }
01280 
01281   int rows=(weekdayCol + qd.daysInMonth() - 1)/7 +1;
01282   double cellHeight = ( box.height() - yoffset ) / (1.*rows);
01283   double cellWidth = ( box.width() - xoffset ) / 7.;
01284 
01285   // Precalculate the grid...
01286   // rows is at most 6, so using 8 entries in the array is fine, too!
01287   int coledges[8], rowedges[8];
01288   for ( int i = 0; i <= 7; i++ ) {
01289     rowedges[i] = int( box.top() + yoffset + i*cellHeight );
01290     coledges[i] = int( box.left() + xoffset + i*cellWidth );
01291   }
01292 
01293   if (weeknumbers) {
01294     QFont oldFont(p.font());
01295     QFont newFont(p.font());
01296     newFont.setPointSize(6);
01297     p.setFont(newFont);
01298     QDate weekDate(monthDate);
01299     for (int row = 0; row<rows; ++row ) {
01300       int calWeek = weekDate.weekNumber();
01301       QRect rc( box.left(), rowedges[row], coledges[0] - 3 - box.left(), rowedges[row+1]-rowedges[row] );
01302       p.drawText( rc, Qt::AlignRight | Qt::AlignVCenter, QString::number( calWeek ) );
01303       weekDate = weekDate.addDays( 7 );
01304     }
01305     p.setFont( oldFont );
01306   }
01307 
01308   QRect daysOfWeekBox( box );
01309   daysOfWeekBox.setHeight( mSubHeaderHeight );
01310   daysOfWeekBox.setLeft( box.left()+xoffset );
01311   drawDaysOfWeek( p, monthDate, monthDate.addDays( 6 ), daysOfWeekBox );
01312 
01313   QColor back = p.backgroundColor();
01314   bool darkbg = false;
01315   for ( int row = 0; row < rows; ++row ) {
01316     for ( int col = 0; col < 7; ++col ) {
01317       // show days from previous/next month with a grayed background
01318       if ( (monthDate < monthFirst) || (monthDate > monthLast) ) {
01319         p.setBackgroundColor( back.dark( 120 ) );
01320         darkbg = true;
01321       }
01322       QRect dayBox( coledges[col], rowedges[row], coledges[col+1]-coledges[col], rowedges[row+1]-rowedges[row] );
01323       drawDayBox(p, monthDate, dayBox, false, recurDaily, recurWeekly );
01324       if ( darkbg ) {
01325         p.setBackgroundColor( back );
01326         darkbg = false;
01327       }
01328       monthDate = monthDate.addDays(1);
01329     }
01330   }
01331 }
01332 
01333 
01335 
01336 void CalPrintPluginBase::drawTodo( int &count, Todo *todo, QPainter &p,
01337                                TodoSortField sortField, SortDirection sortDir,
01338                                bool connectSubTodos, bool strikeoutCompleted,
01339                                bool desc, int posPriority, int posSummary,
01340                                int posDueDt, int posPercentComplete,
01341                                int level, int x, int &y, int width,
01342                                int pageHeight, const Todo::List &todoList,
01343                                TodoParentStart *r )
01344 {
01345   QString outStr;
01346   const KLocale *local = KGlobal::locale();
01347   QRect rect;
01348   TodoParentStart startpt;
01349 
01350   // This list keeps all starting points of the parent to-dos so the connection
01351   // lines of the tree can easily be drawn (needed if a new page is started)
01352   static QPtrList<TodoParentStart> startPoints;
01353   if ( level < 1 ) {
01354     startPoints.clear();
01355   }
01356 
01357   // Compute the right hand side of the to-do box
01358   int rhs = posPercentComplete;
01359   if ( rhs < 0 ) rhs = posDueDt; //not printing percent completed
01360   if ( rhs < 0 ) rhs = x+width;  //not printing due dates either
01361 
01362   // size of to-do
01363   outStr=todo->summary();
01364   int left = posSummary + ( level*10 );
01365   rect = p.boundingRect( left, y, ( rhs-left-5 ), -1, Qt::WordBreak, outStr );
01366   if ( !todo->description().isEmpty() && desc ) {
01367     outStr = todo->description();
01368     rect = p.boundingRect( left+20, rect.bottom()+5, width-(left+10-x), -1,
01369                            Qt::WordBreak, outStr );
01370   }
01371   // if too big make new page
01372   if ( rect.bottom() > pageHeight ) {
01373     // first draw the connection lines from parent to-dos:
01374     if ( level > 0 && connectSubTodos ) {
01375       TodoParentStart *rct;
01376       for ( rct = startPoints.first(); rct; rct = startPoints.next() ) {
01377         int start;
01378         int center = rct->mRect.left() + (rct->mRect.width()/2);
01379         int to = p.viewport().bottom();
01380 
01381         // draw either from start point of parent or from top of the page
01382         if ( rct->mSamePage )
01383           start = rct->mRect.bottom() + 1;
01384         else
01385           start = p.viewport().top();
01386         p.moveTo( center, start );
01387         p.lineTo( center, to );
01388         rct->mSamePage = false;
01389       }
01390     }
01391     y=0;
01392     mPrinter->newPage();
01393   }
01394 
01395   // If this is a sub-to-do, r will not be 0, and we want the LH side
01396   // of the priority line up to the RH side of the parent to-do's priority
01397   bool showPriority = posPriority>=0;
01398   int lhs = posPriority;
01399   if ( r ) {
01400     lhs = r->mRect.right() + 1;
01401   }
01402 
01403   outStr.setNum( todo->priority() );
01404   rect = p.boundingRect( lhs, y + 10, 5, -1, Qt::AlignCenter, outStr );
01405   // Make it a more reasonable size
01406   rect.setWidth(18);
01407   rect.setHeight(18);
01408 
01409   // Draw a checkbox
01410   p.setBrush( QBrush( Qt::NoBrush ) );
01411   p.drawRect( rect );
01412   if ( todo->isCompleted() ) {
01413     // cross out the rectangle for completed to-dos
01414     p.drawLine( rect.topLeft(), rect.bottomRight() );
01415     p.drawLine( rect.topRight(), rect.bottomLeft() );
01416   }
01417   lhs = rect.right() + 3;
01418 
01419   // Priority
01420   if ( todo->priority() > 0 && showPriority ) {
01421     p.drawText( rect, Qt::AlignCenter, outStr );
01422   }
01423   startpt.mRect = rect; //save for later
01424 
01425   // Connect the dots
01426   if ( level > 0 && connectSubTodos ) {
01427     int bottom;
01428     int center( r->mRect.left() + (r->mRect.width()/2) );
01429     if ( r->mSamePage )
01430       bottom = r->mRect.bottom() + 1;
01431     else
01432       bottom = 0;
01433     int to( rect.top() + (rect.height()/2) );
01434     int endx( rect.left() );
01435     p.moveTo( center, bottom );
01436     p.lineTo( center, to );
01437     p.lineTo( endx, to );
01438   }
01439 
01440   // summary
01441   outStr=todo->summary();
01442   rect = p.boundingRect( lhs, rect.top(), (rhs-(left + rect.width() + 5)),
01443                          -1, Qt::WordBreak, outStr );
01444 
01445   QRect newrect;
01446   //FIXME: the following code prints underline rather than strikeout text
01447 #if 0
01448   QFont f( p.font() );
01449   if ( todo->isCompleted() && strikeoutCompleted ) {
01450     f.setStrikeOut( true );
01451     p.setFont( f );
01452   }
01453   p.drawText( rect, Qt::WordBreak, outStr, -1, &newrect );
01454   f.setStrikeOut( false );
01455   p.setFont( f );
01456 #endif
01457   //TODO: Remove this section when the code above is fixed
01458   p.drawText( rect, Qt::WordBreak, outStr, -1, &newrect );
01459   if ( todo->isCompleted() && strikeoutCompleted ) {
01460     // strike out the summary text if to-do is complete
01461     // Note: we tried to use a strike-out font and for unknown reasons the
01462     // result was underline instead of strike-out, so draw the lines ourselves.
01463     int delta = p.fontMetrics().lineSpacing();
01464     int lines = ( rect.height() / delta ) + 1;
01465     for ( int i=0; i<lines; i++ ) {
01466       p.moveTo( rect.left(),  rect.top() + ( delta/2 ) + ( i*delta ) );
01467       p.lineTo( rect.right(), rect.top() + ( delta/2 ) + ( i*delta ) );
01468     }
01469   }
01470 
01471   // due date
01472   if ( todo->hasDueDate() && posDueDt>=0 ) {
01473     outStr = local->formatDate( todo->dtDue().date(), true );
01474     rect = p.boundingRect( posDueDt, y, x + width, -1,
01475                            Qt::AlignTop | Qt::AlignLeft, outStr );
01476     p.drawText( rect, Qt::AlignTop | Qt::AlignLeft, outStr );
01477   }
01478 
01479   // percentage completed
01480   bool showPercentComplete = posPercentComplete>=0;
01481   if ( showPercentComplete ) {
01482     int lwidth = 24;
01483     int lheight = 12;
01484     //first, draw the progress bar
01485     int progress = (int)(( lwidth*todo->percentComplete())/100.0 + 0.5);
01486 
01487     p.setBrush( QBrush( Qt::NoBrush ) );
01488     p.drawRect( posPercentComplete, y+3, lwidth, lheight );
01489     if ( progress > 0 ) {
01490       p.setBrush( QColor( 128, 128, 128 ) );
01491       p.drawRect( posPercentComplete, y+3, progress, lheight );
01492     }
01493 
01494     //now, write the percentage
01495     outStr = i18n( "%1%" ).arg( todo->percentComplete() );
01496     rect = p.boundingRect( posPercentComplete+lwidth+3, y, x + width, -1,
01497                            Qt::AlignTop | Qt::AlignLeft, outStr );
01498     p.drawText( rect, Qt::AlignTop | Qt::AlignLeft, outStr );
01499   }
01500 
01501   // description
01502   if ( !todo->description().isEmpty() && desc ) {
01503     y = newrect.bottom() + 5;
01504     outStr = todo->description();
01505     rect = p.boundingRect( left+20, y, x+width-(left+10), -1,
01506                            Qt::WordBreak, outStr );
01507     p.drawText( rect, Qt::WordBreak, outStr, -1, &newrect );
01508   }
01509 
01510   // Set the new line position
01511   y = newrect.bottom() + 10; //set the line position
01512 
01513   // If the to-do has sub-to-dos, we need to call ourselves recursively
01514 #if 0
01515   Incidence::List l = todo->relations();
01516   Incidence::List::ConstIterator it;
01517   startPoints.append( &startpt );
01518   for( it = l.begin(); it != l.end(); ++it ) {
01519     count++;
01520     // In the future, to-dos might also be related to events
01521     // Manually check if the sub-to-do is in the list of to-dos to print
01522     // The problem is that relations() does not apply filters, so
01523     // we need to compare manually with the complete filtered list!
01524     Todo* subtodo = dynamic_cast<Todo *>( *it );
01525     if (subtodo && todoList.contains( subtodo ) ) {
01526       drawTodo( count, subtodo, p, connectSubTodos, strikeoutCompleted,
01527                 desc, posPriority, posSummary, posDueDt, posPercentComplete,
01528                 level+1, x, y, width, pageHeight, todoList, &startpt );
01529     }
01530   }
01531 #endif
01532   // Make a list of all the sub-to-dos related to this to-do.
01533   Todo::List t;
01534   Incidence::List l = todo->relations();
01535   Incidence::List::ConstIterator it;
01536   for( it=l.begin(); it!=l.end(); ++it ) {
01537     // In the future, to-dos might also be related to events
01538     // Manually check if the sub-to-do is in the list of to-dos to print
01539     // The problem is that relations() does not apply filters, so
01540     // we need to compare manually with the complete filtered list!
01541     Todo* subtodo = dynamic_cast<Todo *>( *it );
01542     if ( subtodo && todoList.contains( subtodo ) ) {
01543       t.append( subtodo );
01544     }
01545   }
01546 
01547   // Sort the sub-to-dos and then print them
01548   Todo::List sl = mCalendar->sortTodos( &t, sortField, sortDir );
01549   Todo::List::ConstIterator isl;
01550   startPoints.append( &startpt );
01551   for( isl = sl.begin(); isl != sl.end(); ++isl ) {
01552     count++;
01553     drawTodo( count, ( *isl ), p, sortField, sortDir,
01554               connectSubTodos, strikeoutCompleted,
01555               desc, posPriority, posSummary, posDueDt, posPercentComplete,
01556               level+1, x, y, width, pageHeight, todoList, &startpt );
01557   }
01558   startPoints.remove( &startpt );
01559 }
01560 
01561 int CalPrintPluginBase::weekdayColumn( int weekday )
01562 {
01563   return ( weekday + 7 - KGlobal::locale()->weekStartDay() ) % 7;
01564 }
01565 
01566 void CalPrintPluginBase::drawJournalField( QPainter &p, QString field, QString text,
01567                                        int x, int &y, int width, int pageHeight )
01568 {
01569   if ( text.isEmpty() ) return;
01570 
01571   QString entry( field.arg( text ) );
01572 
01573   QRect rect( p.boundingRect( x, y, width, -1, Qt::WordBreak, entry) );
01574   if ( rect.bottom() > pageHeight) {
01575     // Start new page...
01576     // FIXME: If it's a multi-line text, draw a few lines on this page, and the
01577     // remaining lines on the next page.
01578     y=0;
01579     mPrinter->newPage();
01580     rect = p.boundingRect( x, y, width, -1, Qt::WordBreak, entry);
01581   }
01582   QRect newrect;
01583   p.drawText( rect, Qt::WordBreak, entry, -1, &newrect );
01584   y = newrect.bottom() + 7;
01585 }
01586 
01587 void CalPrintPluginBase::drawJournal( Journal * journal, QPainter &p, int x, int &y,
01588                                   int width, int pageHeight )
01589 {
01590   QFont oldFont( p.font() );
01591   p.setFont( QFont( "sans-serif", 15 ) );
01592   QString headerText;
01593   QString dateText( KGlobal::locale()->
01594         formatDate( journal->dtStart().date(), false ) );
01595 
01596   if ( journal->summary().isEmpty() ) {
01597     headerText = dateText;
01598   } else {
01599     headerText = i18n("Description - date", "%1 - %2")
01600                      .arg( journal->summary() )
01601                      .arg( dateText );
01602   }
01603 
01604   QRect rect( p.boundingRect( x, y, width, -1, Qt::WordBreak, headerText) );
01605   if ( rect.bottom() > pageHeight) {
01606     // Start new page...
01607     y=0;
01608     mPrinter->newPage();
01609     rect = p.boundingRect( x, y, width, -1, Qt::WordBreak, headerText );
01610   }
01611   QRect newrect;
01612   p.drawText( rect, Qt::WordBreak, headerText, -1, &newrect );
01613   p.setFont( oldFont );
01614 
01615   y = newrect.bottom() + 4;
01616 
01617   p.drawLine( x + 3, y, x + width - 6, y );
01618   y += 5;
01619 
01620   drawJournalField( p, i18n("Person: %1"), journal->organizer().fullName(), x, y, width, pageHeight );
01621   drawJournalField( p, i18n("%1"), journal->description(), x, y, width, pageHeight );
01622   y += 10;
01623 }
01624 
01625 
01626 void CalPrintPluginBase::drawSplitHeaderRight( QPainter &p, const QDate &fd,
01627                                            const QDate &td,
01628                                            const QDate &,
01629                                            int width, int )
01630 {
01631   QFont oldFont( p.font() );
01632 
01633   QPen oldPen( p.pen() );
01634   QPen pen( Qt::black, 4 );
01635 
01636   QString title;
01637   if ( mCalSys ) {
01638     if ( fd.month() == td.month() ) {
01639       title = i18n("Date range: Month dayStart - dayEnd", "%1 %2 - %3")
01640         .arg( mCalSys->monthName( fd.month(), false ) )
01641         .arg( mCalSys->dayString( fd, false ) )
01642         .arg( mCalSys->dayString( td, false ) );
01643     } else {
01644       title = i18n("Date range: monthStart dayStart - monthEnd dayEnd", "%1 %2 - %3 %4")
01645         .arg( mCalSys->monthName( fd.month(), false ) )
01646         .arg( mCalSys->dayString( fd, false ) )
01647         .arg( mCalSys->monthName( td.month(), false ) )
01648         .arg( mCalSys->dayString( td, false ) );
01649     }
01650   }
01651 
01652   QFont serifFont("Times", 30);
01653   p.setFont(serifFont);
01654 
01655   int lineSpacing = p.fontMetrics().lineSpacing();
01656   p.drawText( 0, lineSpacing * 0, width, lineSpacing,
01657               Qt::AlignRight | Qt::AlignTop, title );
01658 
01659   title.truncate(0);
01660 
01661   p.setPen( pen );
01662   p.drawLine(300, lineSpacing * 1, width, lineSpacing * 1);
01663   p.setPen( oldPen );
01664 
01665   p.setFont(QFont("Times", 20, QFont::Bold, TRUE));
01666   int newlineSpacing = p.fontMetrics().lineSpacing();
01667   title += QString::number(fd.year());
01668   p.drawText( 0, lineSpacing * 1 + 4, width, newlineSpacing,
01669               Qt::AlignRight | Qt::AlignTop, title );
01670 
01671   p.setFont( oldFont );
01672 }
01673 
01674 #endif
KDE Home | KDE Accessibility Home | Description of Access Keys