kaddressbook Library API Documentation

ringbinderstyle.cpp

00001 /*                                                                      
00002     This file is part of KAddressBook.
00003     Copyright (c) 2002 Jost Schenck <jost@schenck.de>
00004                                                                         
00005     This program is free software; you can redistribute it and/or modify
00006     it under the terms of the GNU General Public License as published by
00007     the Free Software Foundation; either version 2 of the License, or   
00008     (at your option) any later version.                                 
00009                                                                         
00010     This program is distributed in the hope that it will be useful,     
00011     but WITHOUT ANY WARRANTY; without even the implied warranty of      
00012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the        
00013     GNU General Public License for more details.                        
00014                                                                         
00015     You should have received a copy of the GNU General Public License   
00016     along with this program; if not, write to the Free Software         
00017     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.           
00018                                                                         
00019     As a special exception, permission is given to link this program    
00020     with any edition of Qt, and distribute the resulting executable,    
00021     without including the source code for Qt in the source distribution.
00022 */                                                                      
00023 
00024 #include "ringbinderstyle.h"
00025 
00026 #include <qcheckbox.h>
00027 #include <qlayout.h>
00028 #include <qpaintdevicemetrics.h>
00029 #include <qpainter.h>
00030 #include <qspinbox.h>
00031 #include <qstringlist.h>
00032 
00033 #include <kapplication.h>
00034 #include <kcombobox.h>
00035 #include <kconfig.h>
00036 #include <kdebug.h>
00037 #include <klistbox.h>
00038 #include <klocale.h>
00039 #include <kprinter.h>
00040 #include <kstandarddirs.h>
00041 #include <kabc/addresseelist.h>
00042 
00043 #include "printingwizard.h"
00044 #include "printprogress.h"
00045 #include "printstyle.h"
00046 
00047 #include "rbs_appearance.h"
00048 
00049 namespace KABPrinting
00050 {
00051 
00052 const char* RingBinderConfigSectionName = "RingBinderPrintStyle";
00053 const char* ShowPhoneNumbers = "ShowPhoneNumbers";
00054 const char* ShowEmailAddresses = "ShowEmailAddresses";
00055 const char* ShowStreetAddresses = "ShowStreetAddresses";
00056 const char* ShowOrganization = "ShowOrganization";
00057 const char* ShowBirthday = "ShowBirthday";
00058 const char* FillWithEmptyFields = "FillWithEmptyFields";
00059 const char* MinNumberOfEmptyFields = "MinNumberOfEmptyFields";
00060 const char* LetterGroups = "LetterGroups";
00061 
00062 RingBinderPrintStyle::RingBinderPrintStyle( PrintingWizard* parent,
00063     const char* name )
00064     : PrintStyle( parent, name ),
00065     mPageAppearance( new RingBinderStyleAppearanceForm( parent, 
00066           "AppearancePage" ) ),
00067     mPrintProgress( 0 )
00068 {
00069   KConfig * config;
00070   setPreview( "ringbinder-style.png" );
00071   // how is this done? : setPreferredSortOptions(  );
00072 
00073   addPage( mPageAppearance, i18n( "Ring Binder Printing Style - Appearance" ) );
00074 
00075   // applying previous settings
00076   config = kapp->config();
00077   config->setGroup( RingBinderConfigSectionName );
00078   mPageAppearance->cbPhoneNumbers->setChecked( config->readBoolEntry( ShowPhoneNumbers, true ) );
00079   mPageAppearance->cbEmails->setChecked( config->readBoolEntry( ShowEmailAddresses, true ) );
00080   mPageAppearance->cbStreetAddresses->setChecked( config->readBoolEntry( ShowStreetAddresses, true ) );
00081   mPageAppearance->cbOrganization->setChecked( config->readBoolEntry( ShowOrganization, true ) );
00082   mPageAppearance->cbBirthday->setChecked( config->readBoolEntry( ShowBirthday, false ) );
00083   mPageAppearance->cbFillEmpty->setChecked( config->readBoolEntry( FillWithEmptyFields, true ) );
00084   mPageAppearance->sbMinNumFill->setValue( config->readUnsignedNumEntry( MinNumberOfEmptyFields, 0 ) );
00085   QStringList tabNames = config->readListEntry( LetterGroups, ',' );
00086   if ( tabNames.isEmpty() ) {
00087     tabNames = QStringList::split( ',', QString( "AB,CD,EF,GH,IJK,LM,NO,PQR,S,TU,VW,XYZ" ) );
00088   }
00089   mPageAppearance->letterListBox->insertStringList( tabNames );
00090 }
00091 
00092 RingBinderPrintStyle::~RingBinderPrintStyle()
00093 {}
00094 
00095 void RingBinderPrintStyle::print( KABC::Addressee::List &contacts, PrintProgress *progress )
00096 {
00097   mPrintProgress = progress;
00098   progress->addMessage( i18n( "Setting up fonts and colors" ) );
00099   progress->setProgress( 0 );
00100 
00101   // first write current config settings
00102   KConfig *config = kapp->config();
00103   config->setGroup( RingBinderConfigSectionName );
00104   config->writeEntry( ShowPhoneNumbers, mPageAppearance->cbPhoneNumbers->isChecked() );
00105   config->writeEntry( ShowEmailAddresses, mPageAppearance->cbEmails->isChecked() );
00106   config->writeEntry( ShowStreetAddresses, mPageAppearance->cbStreetAddresses->isChecked() );
00107   config->writeEntry( ShowOrganization, mPageAppearance->cbOrganization->isChecked() );
00108   config->writeEntry( ShowBirthday, mPageAppearance->cbBirthday->isChecked() );
00109   config->writeEntry( FillWithEmptyFields, mPageAppearance->cbFillEmpty->isChecked() );
00110   config->writeEntry( MinNumberOfEmptyFields, mPageAppearance->sbMinNumFill->value() );
00111   QStringList tmpstrl;
00112   for ( uint i = 0; i < mPageAppearance->letterListBox->count(); i++ ) {
00113     if ( !mPageAppearance->letterListBox->text( i ).isEmpty() ) {
00114       tmpstrl.append( mPageAppearance->letterListBox->text( i ) );
00115     }
00116   }
00117   config->writeEntry( LetterGroups, tmpstrl );
00118 
00119   KPrinter *printer = wizard() ->printer();
00120   QPainter painter;
00121 
00122   // margins like in detailledprintstyle. FIXME: See how we can make this configurable.
00123   progress->addMessage( i18n( "Setting up margins and spacing" ) );
00124   int marginTop = 0,
00125       marginLeft = 64,  // to allow stapling, need refinement with two-side prints
00126       marginRight = 0,
00127       marginBottom = 0;
00128   register int left, top, width, height;
00129 
00130   // ----- we expect the printer to be set up (it is, before the wizard is started):
00131   painter.begin( printer );
00132   painter.setPen( Qt::black );
00133   printer->setFullPage( true ); // use whole page
00134   QPaintDeviceMetrics metrics( printer );
00135   kdDebug(5720) << "RingBinderPrintStyle::print: printing on a "
00136   << metrics.width() << "x" << metrics.height()
00137   << " size area," << endl << "   "
00138   << "margins are "
00139   << printer->margins().width() << " (left/right) and "
00140   << printer->margins().height() << " (top/bottom)." << endl;
00141   left = QMAX( printer->margins().width(), marginLeft ); // left margin
00142   top = QMAX( printer->margins().height(), marginTop ); // top margin
00143   width = metrics.width() - left
00144           - QMAX( printer->margins().width(), marginRight ); // page width
00145   height = metrics.height() - top
00146            - QMAX( printer->margins().height(), marginBottom ); // page height
00147 
00148   // ----- now do the printing:
00149   // this prepares for, like, two-up etc:
00150   painter.setViewport( left, top, width, height );
00151   progress->addMessage( i18n( "Printing" ) );
00152   printEntries( contacts, printer, &painter,
00153                 QRect( 0, 0, metrics.width(), metrics.height() ) );
00154   progress->addMessage( i18n( "Done" ) );
00155   painter.end();
00156   config->sync();
00157 }
00158 
00159 bool RingBinderPrintStyle::printEntries( KABC::Addressee::List &contacts,
00160     KPrinter *printer,
00161     QPainter *painter,
00162     const QRect& window )
00163 {
00164   // FIXME: handle following situations
00165   // - handle situation in which we sort descending. In this case the
00166   //   letter groups should first also be sorted descending.
00167 
00168   // FIXME: downcast should not be necessary, change printstyle interface
00169   KABC::AddresseeList * tmpl = ( KABC::AddresseeList* ) & contacts;
00170 #if KDE_VERSION >= 319
00171   KABC::Field* sfield = tmpl->sortingField();
00172 #else
00173   KABC::Field* sfield = *(KABC::Field::defaultFields().begin());
00174 #endif
00175  
00176   // we now collect the letter groups. For reverse sorted address books we
00177   // reverse the sorting of the groups:
00178   QStringList ltgroups;
00179   if ( !tmpl->reverseSorting() ) {
00180       for ( unsigned int i = 0; i < mPageAppearance->letterListBox->count(); i++ ) {
00181           ltgroups.append( mPageAppearance->letterListBox->text( i ) );
00182       }
00183   } else {
00184       for ( unsigned int i = mPageAppearance->letterListBox->count() - 1; i > 0; i-- ) {
00185           ltgroups.append( mPageAppearance->letterListBox->text( i ) );
00186       }
00187   }
00188 
00189   // the yposition of the current entry
00190   int ypos = 0;
00191   // counter variable for the progress widget
00192   int count = 0;
00193 
00194   // counter for the letter group in which we currently are:
00195   uint grpnum = 0;
00196 
00197   // iterate through the contacts
00198   printPageHeader( ltgroups[ grpnum ], window, painter );
00199   ypos = pageHeaderMetrics( window, painter ).height();
00200   for ( KABC::AddresseeList::iterator it = contacts.begin(); 
00201         it != contacts.end(); ++it ) {
00202     KABC::Addressee addressee = ( *it );
00203     if ( !addressee.isEmpty() ) {
00204       // let's see if we have to open the next group:
00205       while ( ltgroups.count() > grpnum + 1 ) {
00206         QChar nextchar;
00207         QChar nowchar;
00208         if ( !tmpl->reverseSorting() ) {
00209           nextchar = ltgroups[ grpnum + 1 ].at( 0 ).upper();
00210         } else {
00211           QString tmpstr = ltgroups[ grpnum + 1 ];
00212           nextchar = tmpstr.at( tmpstr.length() - 1 ).upper();
00213           kdDebug(5720) << "################### " << tmpstr << " last is: " <<
00214             QString(nextchar) << endl;
00215         }
00216 
00217         // determine nowchar depending on sorting criterion
00218         {
00219           QString tmpstr = sfield->value( addressee );
00220           if ( !tmpstr.isEmpty() ) {
00221             nowchar = tmpstr.at( 0 ).upper();
00222             kdDebug(5720) << "------------------ " << tmpstr << " has nowchar: "
00223               << QString(nowchar) << endl;
00224           }
00225         }
00226         if (    ( !tmpl->reverseSorting() && nowchar >= nextchar )
00227              || (  tmpl->reverseSorting() && nowchar <= nextchar ) ) {
00228           // we have reached the next letter group:
00229           //
00230           // first check if we should fill the rest of the page or even more
00231           // with empty fields:
00232           fillEmpty( window, printer, painter, ypos, grpnum );
00233 
00234           // now do change letter group
00235           grpnum++;
00236           printer->newPage();
00237           printPageHeader( ltgroups[ grpnum ], window, painter );
00238           ypos = pageHeaderMetrics( window, painter ).height();
00239           // -> next loop as there might be empty letter groups
00240         } else {
00241           break;
00242         }
00243       }
00244       // print it:
00245       kdDebug(5720) << "RingBinderPrintStyle::printEntries: printing addressee "
00246       << addressee.realName() << endl;
00247 
00248       // get the bounding rect:
00249       int entryheight = entryMetrics( addressee, window, painter, ypos ).height();
00250 
00251       // FIXME: the following conditional means that if we encounter an entry 
00252       // that does not fit on one page we just paint over the borders
00253       if (   entryheight > ( window.height() - ypos ) 
00254           && !( entryheight > window.height() ) ) { 
00255         // it does not fit on the page beginning at ypos:
00256         printer->newPage();
00257         printPageHeader( mPageAppearance->letterListBox->text( grpnum )
00258                        , window, painter );
00259         ypos = pageHeaderMetrics( window, painter ).height();
00260       }
00261       printEntry( addressee, window, painter, ypos );
00262       ypos += entryheight;
00263     } else {
00264       kdDebug(5720) << "RingBinderPrintStyle::printEntries: strange, addressee "
00265       << "with UID " << addressee.uid() << " not available." << endl;
00266     }
00267     mPrintProgress->setProgress( ( count++*100 ) / contacts.count() );
00268   }
00269 
00270   // check again if we should fill the last page with empty fields
00271   // (as the above call won't be reached for the last letter group)
00272   fillEmpty( window, printer, painter, ypos, grpnum );
00273   // ----- set progress:
00274   mPrintProgress->setProgress( 100 );
00275   kdDebug(5720) << "PLANNER STYLE: PRINT FINISHED" << endl;
00276   return true;
00277 }
00278 
00279 void RingBinderPrintStyle::fillEmpty( const QRect& window
00280                                       , KPrinter *printer
00281                                       , QPainter* painter
00282                                       , int top
00283                                       , int grpnum
00284                                     )
00285 {
00286   if ( mPageAppearance->cbFillEmpty->isChecked() ) {
00287     // print as many empty fields as fit on the page
00288     int ypos = top;
00289     int fieldscounter = 0;
00290     int entryheight = emptyEntryMetrics( window, painter, ypos ).height();
00291     do {
00292       while ( ( window.height() - ypos ) > entryheight ) {
00293         printEmptyEntry( window, painter, ypos );
00294         ypos += entryheight;
00295         fieldscounter++;
00296       }
00297       qDebug( "fieldscounter %i", fieldscounter );
00298       qDebug( "minNumFill    %i", mPageAppearance->sbMinNumFill->value() );
00299       if ( fieldscounter < mPageAppearance->sbMinNumFill->value() ) {
00300         printer->newPage();
00301         printPageHeader( mPageAppearance->letterListBox->text( grpnum )
00302                        , window, painter );
00303         ypos = pageHeaderMetrics( window, painter ).height();
00304       }
00305     } while ( fieldscounter < mPageAppearance->sbMinNumFill->value() );
00306   }
00307 }
00308 
00309 bool RingBinderPrintStyle::printEntry( const KABC::Addressee& contact
00310                                        , const QRect& window
00311                                        , QPainter* painter
00312                                        , int top
00313                                        , bool fake
00314                                        , QRect* brect
00315                                      )
00316 {
00317   QFont normfont( "Helvetica", 10, QFont::Normal );
00318   QFontMetrics fmnorm( normfont );
00319   QPen thickpen( Qt::black, 0 );
00320   QPen thinpen ( Qt::black, 0 );
00321   // store at which line we are and how many lines we have for this entry:
00322   int linenum = 0;
00323   int maxlines = 0;
00324   painter->setFont( normfont );
00325   linenum++;
00326   // FIXME: maybe we should not rely on formattedName only but make this
00327   // configurable -- if somebody sorts by familyName, but most entries have
00328   // a formatted name of "GivenName FamilyName" it might look strange.
00329   if ( !fake ) {
00330     QString namestr = contact.formattedName();
00331     if ( namestr.isEmpty() ) {
00332       namestr = contact.familyName() + ", " + contact.givenName();
00333     }
00334     if ( mPageAppearance->cbOrganization->isChecked() 
00335         && !contact.organization().isEmpty() ) {
00336       namestr += QString( " (" ) + contact.organization() + QString( ")" );
00337     }
00338     if ( mPageAppearance->cbBirthday->isChecked() && !contact.birthday().isNull() ) {
00339       namestr += QString( " *" ) + KGlobal::locale()->formatDate( 
00340           contact.birthday().date(), true );
00341     }
00342     painter->drawText( 5, top + ( linenum * fmnorm.lineSpacing() ) 
00343                               - fmnorm.leading(), namestr );
00344   }
00345   painter->setFont( normfont );
00346 
00347   // print street addresses:
00348   if ( mPageAppearance->cbStreetAddresses->isChecked() ) {
00349     KABC::Address::List addrl = contact.addresses();
00350     KABC::Address::List::iterator it;
00351     for ( it = addrl.begin(); it != addrl.end(); ++it ) {
00352       if ( !( *it ).isEmpty() ) {
00353         //FIXME:draw type label somehow
00354         // linenum++;
00355         // if(!fake) 
00356         //   painter->drawText(5, top + (linenum*fmnorm.lineSpacing()) 
00357         //                            - fmnorm.leading(), (*it).typeLabel());
00358         painter->setFont( normfont );
00359         QString formattedAddress;
00360 #if KDE_VERSION >= 319
00361         formattedAddress = (*it).formattedAddress();
00362 #else
00363         formattedAddress = (*it).label();
00364 #endif
00365         QStringList laddr = QStringList::split( QChar( '\n' ), 
00366                                                 formattedAddress );
00367         for ( QStringList::iterator it = laddr.begin(); it != laddr.end(); ++it ) {
00368           linenum++;
00369           if ( !fake ) {
00370             painter->drawText( 20, top + ( linenum * fmnorm.lineSpacing() )
00371                                - fmnorm.leading(), *it );
00372           }
00373         }
00374       }
00375     }
00376   }
00377   maxlines = linenum;
00378   linenum = 0;
00379 
00380   // print phone numbers
00381   if ( mPageAppearance->cbPhoneNumbers->isChecked() ) {
00382     KABC::PhoneNumber::List phonel = contact.phoneNumbers();
00383     KABC::PhoneNumber::List::iterator nit;
00384     for ( nit = phonel.begin(); nit != phonel.end(); ++nit ) {
00385 
00386       // don't print empty lines just reading "Home:"
00387       if ( ( *nit ).number().isEmpty() ) {
00388         continue;
00389       }
00390       linenum++;
00391 
00392       // construct phone string and draw it
00393       QString numstr = ( *nit ).typeLabel();
00394       if ( !numstr.isEmpty() ) {
00395         numstr.append( ": " );
00396       }
00397       numstr.append( ( *nit ).number() );
00398       painter->drawText( ( int ) ( window.width() * 0.5 ) + 5,
00399                          top + ( linenum * fmnorm.lineSpacing() ) 
00400                              - fmnorm.leading(), numstr );
00401     }
00402   }
00403 
00404   // print email addresses
00405   if ( mPageAppearance->cbEmails->isChecked() ) {
00406     QStringList emails = contact.emails();
00407     for ( QStringList::Iterator it = emails.begin(); it != emails.end(); ++it ) {
00408       // don't print empty lines
00409       if ( ( *it ).isEmpty() ) {
00410         continue;
00411       }
00412       linenum++;
00413       painter->drawText( ( int ) ( window.width() * 0.5 ) + 5,
00414                          top + ( linenum * fmnorm.lineSpacing() ) 
00415                              - fmnorm.leading(), *it );
00416     }
00417   }
00418 
00419   // total number of lines:
00420   if ( linenum > maxlines ) {
00421     maxlines = linenum;
00422   }
00423   if ( brect ) {
00424     brect->setRect( 0, top, window.width(), 
00425         ( maxlines * fmnorm.lineSpacing() ) + fmnorm.leading() );
00426   }
00427   if ( fake ) { // nothing to do anymore as we already have dimensions
00428     return true;
00429   }
00430   painter->setPen( thickpen );
00431   if ( !fake )
00432     painter->drawRect( 0, top, window.width(), 
00433         ( maxlines * fmnorm.lineSpacing() ) + fmnorm.leading() );
00434   if ( !fake )
00435     painter->drawLine( ( int ) ( window.width() * 0.5 ), top, 
00436         (int)( window.width() * 0.5 ), 
00437         top + ( maxlines * fmnorm.lineSpacing() ) + fmnorm.leading() );
00438   painter->setPen( thinpen );
00439   return true;
00440 }
00441 
00442 QRect RingBinderPrintStyle::entryMetrics( const KABC::Addressee& contact
00443     , const QRect& window
00444     , QPainter* painter
00445     , int top
00446                                         )
00447 {
00448   QRect ret;
00449   printEntry( contact, window, painter, top, true, &ret );
00450   return ret;
00451 }
00452 
00453 bool RingBinderPrintStyle::printEmptyEntry(
00454   const QRect& window
00455   , QPainter* painter
00456   , int top
00457 )
00458 {
00459   QFont normfont( "Helvetica", 10, QFont::Normal );
00460   QFontMetrics fmnorm( normfont );
00461   // QPen thickpen(Qt::black, 1);
00462   QPen thickpen( Qt::black, 0 );
00463   QPen thinpen ( Qt::black, 0 );
00464   painter->setFont( normfont );
00465   painter->setPen( thickpen );
00466   painter->drawRect( 0, top, window.width(), ( 3 * fmnorm.lineSpacing() ) );
00467   painter->setPen( thinpen );
00468   for ( int i = 1; i < 3; i++ ) {
00469     painter->drawLine( 0, top + i * fmnorm.lineSpacing(), window.width(), 
00470         top + i * fmnorm.lineSpacing() );
00471   }
00472   painter->drawLine( (int)( window.width() * 0.5 ), top, 
00473         (int)( window.width() * 0.5 ), top + ( 3 * fmnorm.lineSpacing() ) );
00474   // this line not as deep as we need room for the email field
00475   painter->drawLine( (int)( window.width() * 0.75 ), top, 
00476         (int)( window.width() * 0.75 ), top + ( 2 * fmnorm.lineSpacing() ) );
00477   // painter->drawText((window.width()*0.5) + 5, 
00478   //                   top+3*fmnorm.lineSpacing() -4, "email");
00479   return true;
00480 }
00481 
00482 QRect RingBinderPrintStyle::emptyEntryMetrics(
00483   const QRect& window
00484   , QPainter* /*painter*/
00485   , int top
00486 )
00487 {
00488   QFont normfont( "Helvetica", 10, QFont::Normal );
00489   QFontMetrics fmnorm( normfont );
00490   return QRect( 0, top, window.width(), ( 3 * fmnorm.lineSpacing() ) );
00491 }
00492 
00493 
00494 bool RingBinderPrintStyle::printPageHeader(
00495   const QString section
00496   , const QRect& window
00497   , QPainter* painter
00498 )
00499 {
00500   QFont sectfont( "Helvetica", 16, QFont::Normal );
00501   QFontMetrics fmsect( sectfont );
00502   painter->setFont( sectfont );
00503   painter->drawText( QRect( 0, 0, window.width(), fmsect.height() ), 
00504       Qt::AlignRight, section );
00505   return true;
00506 }
00507 
00508 QRect RingBinderPrintStyle::pageHeaderMetrics(
00509   const QRect& window
00510   , QPainter* /*painter*/
00511 )
00512 {
00513   QFont sectfont( "Helvetica", 16, QFont::Normal );
00514   QFont normfont( "Helvetica", 12, QFont::Normal );
00515   QFontMetrics fmsect( sectfont );
00516   QFontMetrics fmnorm( normfont );
00517   return QRect( 0, 0, window.width(), fmsect.height() + 10 );
00518 }
00519 
00520 
00521 RingBinderPrintStyleFactory::RingBinderPrintStyleFactory(
00522   PrintingWizard* parent,
00523   const char* name )
00524     : PrintStyleFactory( parent, name )
00525 {}
00526 
00527 
00528 PrintStyle *RingBinderPrintStyleFactory::create()
00529 {
00530   return new RingBinderPrintStyle( mParent, mName );
00531 }
00532 
00533 QString RingBinderPrintStyleFactory::description()
00534 {
00535   return i18n( "Printout for Ring Binders" );
00536 }
00537 
00538 }
00539 
00540 #include "ringbinderstyle.moc"
00541 // vim:tw=78 cin et sw=2 comments=sr\:/*,mb\:\ ,ex\:*/,\://
KDE Logo
This file is part of the documentation for kaddressbook Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat May 1 11:38:53 2004 by doxygen 1.2.15 written by Dimitri van Heesch, © 1997-2003