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