kmail

headerstyle.cpp

00001 /*  -*- c++ -*-
00002     headerstyle.cpp
00003 
00004     This file is part of KMail, the KDE mail client.
00005     Copyright (c) 2003 Marc Mutz <mutz@kde.org>
00006 
00007     KMail is free software; you can redistribute it and/or modify it
00008     under the terms of the GNU General Public License, version 2, as
00009     published by the Free Software Foundation.
00010 
00011     KMail is distributed in the hope that it will be useful, but
00012     WITHOUT ANY WARRANTY; without even the implied warranty of
00013     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014     General Public License for more details.
00015 
00016     You should have received a copy of the GNU General Public License
00017     along with this program; if not, write to the Free Software
00018     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00019 
00020     In addition, as a special exception, the copyright holders give
00021     permission to link the code of this program with any edition of
00022     the Qt library by Trolltech AS, Norway (or with modified versions
00023     of Qt that use the same license as Qt), and distribute linked
00024     combinations including the two.  You must obey the GNU General
00025     Public License in all respects for all of the code used other than
00026     Qt.  If you modify this file, you may extend this exception to
00027     your version of the file, but you are not obligated to do so.  If
00028     you do not wish to do so, delete this exception statement from
00029     your version.
00030 */
00031 
00032 #ifdef HAVE_CONFIG_H
00033 #include <config.h>
00034 #endif
00035 
00036 #include "headerstyle.h"
00037 
00038 #include "headerstrategy.h"
00039 #include "kmkernel.h"
00040 #include "linklocator.h"
00041 #include "kmmessage.h"
00042 #include "spamheaderanalyzer.h"
00043 #include "globalsettings.h"
00044 
00045 #include <libemailfunctions/email.h>
00046 #include <libkdepim/kxface.h>
00047 using namespace KPIM;
00048 
00049 #include <mimelib/string.h>
00050 #include <mimelib/field.h>
00051 #include <mimelib/headers.h>
00052 
00053 #include <kdebug.h>
00054 #include <klocale.h>
00055 #include <kglobal.h>
00056 #include <kimproxy.h>
00057 #include <kabc/stdaddressbook.h>
00058 #include <kabc/addresseelist.h>
00059 #include <kmdcodec.h>
00060 #include <qdatetime.h>
00061 #include <qbuffer.h>
00062 #include <qbitmap.h>
00063 #include <qimage.h>
00064 #include <qapplication.h>
00065 #include <qregexp.h>
00066 
00067 #include <kstandarddirs.h>
00068 
00069 namespace KMail {
00070 
00071   //
00072   // Convenience functions:
00073   //
00074   static inline QString directionOf( const QString & str ) {
00075     return str.isRightToLeft() ? "rtl" : "ltr" ;
00076   }
00077 
00078 #if 0
00079   // Converts to html. Changes URLs into href's, escapes HTML special
00080   // chars and inserts the result into an <div> or <span> tag with
00081   // "dir" set to "rtl" or "ltr" depending on the direction of @p str.
00082   static QString convertToHtmlBlock( const QString & str, bool useSpan=false ) {
00083     QString dir = directionOf( str );
00084     QString format = "<%1 dir=\"%3\">%4</%2>";
00085     return format.arg( useSpan ? "span" : "div" )
00086                  .arg( useSpan ? "span" : "div" )
00087                  .arg( dir )
00088                  .arg( LinkLocator::convertToHtml( str ) );
00089   }
00090 #endif
00091 
00092   // ### tmp wrapper to make kmreaderwin code working:
00093   static QString strToHtml( const QString & str,
00094                             int flags = LinkLocator::PreserveSpaces ) {
00095     return LinkLocator::convertToHtml( str, flags );
00096   }
00097 
00098   //
00099   // BriefHeaderStyle
00100   //   Show everything in a single line, don't show header field names.
00101   //
00102 
00103   class BriefHeaderStyle : public HeaderStyle {
00104     friend class ::KMail::HeaderStyle;
00105   protected:
00106     BriefHeaderStyle() : HeaderStyle() {}
00107     virtual ~BriefHeaderStyle() {}
00108 
00109   public:
00110     const char * name() const { return "brief"; }
00111     const HeaderStyle * next() const { return plain(); }
00112     const HeaderStyle * prev() const { return fancy(); }
00113 
00114     QString format( const KMMessage * message, const HeaderStrategy * strategy,
00115                     const QString & vCardName, bool printing, bool topLevel ) const;
00116   };
00117 
00118   QString BriefHeaderStyle::format( const KMMessage * message,
00119                                     const HeaderStrategy * strategy,
00120                                     const QString & vCardName, bool printing, bool topLevel ) const {
00121     Q_UNUSED( topLevel );
00122     if ( !message ) return QString::null;
00123     if ( !strategy )
00124       strategy = HeaderStrategy::brief();
00125 
00126     // The direction of the header is determined according to the direction
00127     // of the application layout.
00128 
00129     QString dir = QApplication::reverseLayout() ? "rtl" : "ltr" ;
00130 
00131     // However, the direction of the message subject within the header is
00132     // determined according to the contents of the subject itself. Since
00133     // the "Re:" and "Fwd:" prefixes would always cause the subject to be
00134     // considered left-to-right, they are ignored when determining its
00135     // direction.
00136 
00137     QString subjectDir;
00138     if (!message->subject().isEmpty())
00139       subjectDir = directionOf( message->cleanSubject() );
00140     else
00141       subjectDir = directionOf( i18n("No Subject") );
00142 
00143     // Prepare the date string (when printing always use the localized date)
00144     QString dateString;
00145     if( printing ) {
00146       QDateTime dateTime;
00147       KLocale * locale = KGlobal::locale();
00148       dateTime.setTime_t( message->date() );
00149       dateString = locale->formatDateTime( dateTime );
00150     } else {
00151       dateString = message->dateStr();
00152     }
00153 
00154     QString headerStr = "<div class=\"header\" dir=\"" + dir + "\">\n";
00155 
00156     if ( strategy->showHeader( "subject" ) )
00157       headerStr += "<div dir=\"" + subjectDir + "\">\n"
00158                    "<b style=\"font-size:130%\">" +
00159                            strToHtml( message->subject() ) +
00160                            "</b></div>\n";
00161 
00162     QStringList headerParts;
00163 
00164     if ( strategy->showHeader( "from" ) ) {
00165       QString fromStr = message->from();
00166       if ( fromStr.isEmpty() ) // no valid email in from, maybe just a name
00167         fromStr = message->fromStrip(); // let's use that
00168       QString fromPart = KMMessage::emailAddrAsAnchor( fromStr, true );
00169       if ( !vCardName.isEmpty() )
00170         fromPart += "&nbsp;&nbsp;<a href=\"" + vCardName + "\">" + i18n("[vCard]") + "</a>";
00171       headerParts << fromPart;
00172     }
00173 
00174     if ( strategy->showHeader( "cc" ) && !message->cc().isEmpty() )
00175       headerParts << i18n("CC: ") + KMMessage::emailAddrAsAnchor( message->cc(), true );
00176 
00177     if ( strategy->showHeader( "bcc" ) && !message->bcc().isEmpty() )
00178       headerParts << i18n("BCC: ") + KMMessage::emailAddrAsAnchor( message->bcc(), true );
00179 
00180     if ( strategy->showHeader( "date" ) )
00181       headerParts << strToHtml(message->dateShortStr());
00182 
00183     // remove all empty (modulo whitespace) entries and joins them via ", \n"
00184     headerStr += " (" + headerParts.grep( QRegExp( "\\S" ) ).join( ",\n" ) + ')';
00185 
00186     headerStr += "</div>\n";
00187 
00188     // ### iterate over the rest of strategy->headerToDisplay() (or
00189     // ### all headers if DefaultPolicy == Display) (elsewhere, too)
00190     return headerStr;
00191   }
00192 
00193   //
00194   // PlainHeaderStyle:
00195   //   show every header field on a line by itself,
00196   //   show subject larger
00197   //
00198 
00199   class PlainHeaderStyle : public HeaderStyle {
00200     friend class ::KMail::HeaderStyle;
00201   protected:
00202     PlainHeaderStyle() : HeaderStyle() {}
00203     virtual ~PlainHeaderStyle() {}
00204 
00205   public:
00206     const char * name() const { return "plain"; }
00207     const HeaderStyle * next() const { return fancy(); }
00208     const HeaderStyle * prev() const { return brief(); }
00209 
00210     QString format( const KMMessage * message, const HeaderStrategy * strategy,
00211                     const QString & vCardName, bool printing, bool topLevel ) const;
00212 
00213   private:
00214     QString formatAllMessageHeaders( const KMMessage * message ) const;
00215   };
00216 
00217   QString PlainHeaderStyle::format( const KMMessage * message,
00218                                     const HeaderStrategy * strategy,
00219                                     const QString & vCardName, bool printing, bool topLevel ) const {
00220     Q_UNUSED( topLevel );
00221     if ( !message ) return QString::null;
00222     if ( !strategy )
00223       strategy = HeaderStrategy::rich();
00224 
00225     // The direction of the header is determined according to the direction
00226     // of the application layout.
00227 
00228     QString dir = ( QApplication::reverseLayout() ? "rtl" : "ltr" );
00229 
00230     // However, the direction of the message subject within the header is
00231     // determined according to the contents of the subject itself. Since
00232     // the "Re:" and "Fwd:" prefixes would always cause the subject to be
00233     // considered left-to-right, they are ignored when determining its
00234     // direction.
00235 
00236     QString subjectDir;
00237     if (!message->subject().isEmpty())
00238       subjectDir = directionOf( message->cleanSubject() );
00239     else
00240       subjectDir = directionOf( i18n("No Subject") );
00241 
00242     // Prepare the date string (when printing always use the localized date)
00243     QString dateString;
00244     if( printing ) {
00245       QDateTime dateTime;
00246       KLocale* locale = KGlobal::locale();
00247       dateTime.setTime_t( message->date() );
00248       dateString = locale->formatDateTime( dateTime );
00249     }
00250     else {
00251       dateString = message->dateStr();
00252     }
00253 
00254     QString headerStr;
00255 
00256     if ( strategy->headersToDisplay().isEmpty()
00257          && strategy->defaultPolicy() == HeaderStrategy::Display ) {
00258       // crude way to emulate "all" headers - Note: no strings have
00259       // i18n(), so direction should always be ltr.
00260       headerStr= QString("<div class=\"header\" dir=\"ltr\">");
00261       headerStr += formatAllMessageHeaders( message );
00262       return headerStr + "</div>";
00263     }
00264 
00265     headerStr = QString("<div class=\"header\" dir=\"%1\">").arg(dir);
00266 
00267     //case HdrLong:
00268     if ( strategy->showHeader( "subject" ) )
00269       headerStr += QString("<div dir=\"%1\"><b style=\"font-size:130%\">" +
00270                            strToHtml(message->subject()) + "</b></div>\n")
00271                         .arg(subjectDir);
00272 
00273     if ( strategy->showHeader( "date" ) )
00274       headerStr.append(i18n("Date: ") + strToHtml(dateString)+"<br>\n");
00275 
00276 #if 0
00277     // Get Instant Messaging presence
00278     QString presence;
00279     QString kabcUid;
00280     if ( strategy->showHeader( "status" ) )
00281     {
00282       KABC::AddressBook *addressBook = KABC::StdAddressBook::self( true );
00283       KABC::AddresseeList addresses = addressBook->findByEmail( KPIM::getFirstEmailAddress( message->from() ) );
00284       ::KIMProxy *imProxy = KMKernel::self()->imProxy();
00285       kabcUid = addresses[0].uid();
00286       presence = imProxy->presenceString( kabcUid );
00287     }
00288 #endif
00289 
00290     if ( strategy->showHeader( "from" ) ) {
00291       QString fromStr = message->from();
00292       if ( fromStr.isEmpty() ) // no valid email in from, maybe just a name
00293         fromStr = message->fromStrip(); // let's use that
00294       headerStr.append(i18n("From: ") +
00295           KMMessage::emailAddrAsAnchor( fromStr, false, "", true ) );
00296       if ( !vCardName.isEmpty() )
00297         headerStr.append("&nbsp;&nbsp;<a href=\"" + vCardName +
00298               "\">" + i18n("[vCard]") + "</a>" );
00299 #if 0
00300       if ( !presence.isEmpty() && strategy->showHeader( "status" ) )
00301         headerStr.append("&nbsp;&nbsp;(<span name=\"presence-" + kabcUid + "\">" + presence + "</span>)" );
00302 #endif
00303 
00304       if ( strategy->showHeader( "organization" )
00305           && !message->headerField("Organization").isEmpty())
00306         headerStr.append("&nbsp;&nbsp;(" +
00307               strToHtml(message->headerField("Organization")) + ")");
00308       headerStr.append("<br>\n");
00309     }
00310 
00311     if ( strategy->showHeader( "to" ) )
00312       headerStr.append(i18n("To: ")+
00313                        KMMessage::emailAddrAsAnchor(message->to(),false) + "<br>\n");
00314 
00315     if ( strategy->showHeader( "cc" ) && !message->cc().isEmpty() )
00316       headerStr.append(i18n("CC: ")+
00317                        KMMessage::emailAddrAsAnchor(message->cc(),false) + "<br>\n");
00318 
00319     if ( strategy->showHeader( "bcc" ) && !message->bcc().isEmpty() )
00320       headerStr.append(i18n("BCC: ")+
00321                        KMMessage::emailAddrAsAnchor(message->bcc(),false) + "<br>\n");
00322 
00323     if ( strategy->showHeader( "reply-to" ) && !message->replyTo().isEmpty())
00324       headerStr.append(i18n("Reply to: ")+
00325                      KMMessage::emailAddrAsAnchor(message->replyTo(),false) + "<br>\n");
00326 
00327     headerStr += "</div>\n";
00328 
00329     return headerStr;
00330   }
00331 
00332   QString PlainHeaderStyle::formatAllMessageHeaders( const KMMessage * message ) const {
00333     const DwHeaders & headers = message->headers();
00334     QString result;
00335 
00336     for ( const DwField * field = headers.FirstField() ; field ; field = field->Next() ) {
00337       result += ( field->FieldNameStr() + ": " ).c_str();
00338       result += strToHtml( field->FieldBodyStr().c_str() );
00339       result += "<br>\n";
00340     }
00341 
00342     return result;
00343   }
00344 
00345   //
00346   // FancyHeaderStyle:
00347   //   Like PlainHeaderStyle, but with slick frames and background colours.
00348   //
00349 
00350   class FancyHeaderStyle : public HeaderStyle {
00351     friend class ::KMail::HeaderStyle;
00352   protected:
00353     FancyHeaderStyle() : HeaderStyle() {}
00354     virtual ~FancyHeaderStyle() {}
00355 
00356   public:
00357     const char * name() const { return "fancy"; }
00358     const HeaderStyle * next() const { return enterprise(); }
00359     const HeaderStyle * prev() const { return plain(); }
00360 
00361     QString format( const KMMessage * message, const HeaderStrategy * strategy,
00362                     const QString & vCardName, bool printing, bool topLevel ) const;
00363     static QString imgToDataUrl( const QImage & image,
00364                                  const char *fmt = "PNG" );
00365 
00366   private:
00367     static QString drawSpamMeter( double percent, const QString & filterHeader );
00368 
00369   };
00370 
00371   QString FancyHeaderStyle::drawSpamMeter( double percent,
00372                                            const QString & filterHeader )
00373   {
00374     QImage meterBar( 20, 1, 8, 24 );
00375     const unsigned short gradient[20][3] = {
00376       {   0, 255,   0 },
00377       {  27, 254,   0 },
00378       {  54, 252,   0 },
00379       {  80, 250,   0 },
00380       { 107, 249,   0 },
00381       { 135, 247,   0 },
00382       { 161, 246,   0 },
00383       { 187, 244,   0 },
00384       { 214, 242,   0 },
00385       { 241, 241,   0 },
00386       { 255, 228,   0 },
00387       { 255, 202,   0 },
00388       { 255, 177,   0 },
00389       { 255, 151,   0 },
00390       { 255, 126,   0 },
00391       { 255, 101,   0 },
00392       { 255,  76,   0 },
00393       { 255,  51,   0 },
00394       { 255,  25,   0 },
00395       { 255,   0,   0 }
00396     };
00397     meterBar.setColor( 21, qRgb( 255, 255, 255 ) );
00398     meterBar.setColor( 22, qRgb( 170, 170, 170 ) );
00399     if ( percent < 0 ) // grey is for errors
00400       meterBar.fill( 22 );
00401     else {
00402       meterBar.fill( 21 );
00403       int max = QMIN( 20, static_cast<int>( percent ) / 5 );
00404       for ( int i = 0; i < max; ++i ) {
00405         meterBar.setColor( i+1, qRgb( gradient[i][0], gradient[i][1],
00406                                       gradient[i][2] ) );
00407         meterBar.setPixel( i, 0, i+1 );
00408       }
00409     }
00410     QString titleText = i18n("%1% probability of being spam.\n\nFull report:\n%2")
00411                      .arg( QString::number( percent ), filterHeader );
00412     return QString("<img src=\"%1\" width=\"%2\" height=\"%3\" style=\"border: 1px solid black;\" title=\"%4\"> &nbsp;")
00413       .arg( imgToDataUrl( meterBar, "PPM" ), QString::number( 20 ),
00414             QString::number( 5 ), titleText );
00415   }
00416 
00417 
00418   QString FancyHeaderStyle::format( const KMMessage * message,
00419                                     const HeaderStrategy * strategy,
00420                                     const QString & vCardName, bool printing, bool topLevel ) const {
00421     Q_UNUSED( topLevel );
00422     if ( !message ) return QString::null;
00423     if ( !strategy )
00424       strategy = HeaderStrategy::rich();
00425 
00426     KConfigGroup configReader( KMKernel::config(), "Reader" );
00427 
00428     // ### from kmreaderwin begin
00429     // The direction of the header is determined according to the direction
00430     // of the application layout.
00431 
00432     QString dir = ( QApplication::reverseLayout() ? "rtl" : "ltr" );
00433     QString headerStr = QString("<div class=\"fancy header\" dir=\"%1\">\n").arg(dir);
00434 
00435     // However, the direction of the message subject within the header is
00436     // determined according to the contents of the subject itself. Since
00437     // the "Re:" and "Fwd:" prefixes would always cause the subject to be
00438     // considered left-to-right, they are ignored when determining its
00439     // direction.
00440 
00441     QString subjectDir;
00442     if ( !message->subject().isEmpty() )
00443       subjectDir = directionOf( message->cleanSubject() );
00444     else
00445       subjectDir = directionOf( i18n("No Subject") );
00446 
00447     // Prepare the date string (when printing always use the localized date)
00448     QString dateString;
00449     if( printing ) {
00450       QDateTime dateTime;
00451       KLocale* locale = KGlobal::locale();
00452       dateTime.setTime_t( message->date() );
00453       dateString = locale->formatDateTime( dateTime );
00454     }
00455     else {
00456       dateString = message->dateStr();
00457     }
00458 
00459     // Spam header display.
00460     // If the spamSpamStatus config value is true then we look for headers
00461     // from a few spam filters and try to create visually meaningful graphics
00462     // out of the spam scores.
00463 
00464     QString spamHTML;
00465 
00466     if ( configReader.readBoolEntry( "showSpamStatus", true ) ) {
00467       SpamScores scores = SpamHeaderAnalyzer::getSpamScores( message );
00468       for ( SpamScoresIterator it = scores.begin(); it != scores.end(); ++it )
00469         spamHTML += (*it).agent() + " " +
00470                     drawSpamMeter( (*it).score(), (*it).spamHeader() );
00471     }
00472 
00473     QString userHTML;
00474     QString presence;
00475 
00476     // IM presence and kabc photo
00477 
00478     ::KIMProxy *imProxy = KMKernel::self()->imProxy();
00479     QString kabcUid;
00480     KABC::AddressBook *addressBook = KABC::StdAddressBook::self( true );
00481     KABC::AddresseeList addresses = addressBook->findByEmail( KPIM::getFirstEmailAddress( message->from() ) );
00482 
00483     QString photoURL;
00484     int photoWidth = 60;
00485     int photoHeight = 60;
00486     if( addresses.count() == 1 )
00487     {
00488       // kabcUid is embedded in im: URIs to indicate which IM contact to message
00489       kabcUid = addresses[0].uid();
00490 
00491       if ( imProxy->initialize() ) {
00492           // im status
00493           presence = imProxy->presenceString( kabcUid );
00494           if ( !presence.isEmpty() )
00495           {
00496             QString presenceIcon = QString::fromLatin1( " <img src=\"%1\"/>" )
00497                 .arg( imgToDataUrl( imProxy->presenceIcon( kabcUid ).convertToImage() ) );
00498             presence += presenceIcon;
00499           }
00500       }
00501       // picture
00502       if ( addresses[0].photo().isIntern() )
00503       {
00504         // get photo data and convert to data: url
00505         //kdDebug( 5006 ) << "INTERNAL photo found" << endl;
00506         QImage photo = addresses[0].photo().data();
00507         if ( !photo.isNull() )
00508         {
00509           photoWidth = photo.width();
00510           photoHeight = photo.height();
00511           // scale below 60, otherwise it can get way too large
00512           if ( photoHeight > 60 ) {
00513             double ratio = ( double )photoHeight / ( double )photoWidth;
00514             photoHeight = 60;
00515             photoWidth = (int)( 60 / ratio );
00516             photo = photo.smoothScale( photoWidth, photoHeight );
00517           }
00518           photoURL = imgToDataUrl( photo );
00519         }
00520       }
00521       else
00522       {
00523         //kdDebug( 5006 ) << "URL found" << endl;
00524         photoURL = addresses[0].photo().url();
00525         if ( photoURL.startsWith("/") )
00526           photoURL.prepend( "file:" );
00527       }
00528     }
00529     else // TODO: find a usable one
00530     {
00531       kdDebug( 5006 ) << "Multiple / No addressees matched email address; Count is " << addresses.count() << endl;
00532       userHTML = "&nbsp;";
00533     }
00534 
00535     if( photoURL.isEmpty() ) {
00536       // no photo, look for a Face header
00537       QString faceheader = message->headerField( "Face" );
00538       if ( !faceheader.isEmpty() ) {
00539         QImage faceimage;
00540 
00541         kdDebug( 5006 ) << "Found Face: header" << endl;
00542 
00543         QCString facestring = faceheader.utf8();
00544         // Spec says header should be less than 998 bytes
00545         // Face: is 5 characters
00546         if ( facestring.length() < 993 ) {
00547           QByteArray facearray;
00548           KCodecs::base64Decode(facestring, facearray);
00549 
00550           QImage faceimage;
00551           if ( faceimage.loadFromData( facearray, "png" ) ) {
00552             // Spec says image must be 48x48 pixels
00553             if ( (48 == faceimage.width()) && (48 == faceimage.height()) ) {
00554               photoURL = imgToDataUrl( faceimage );
00555               photoWidth = 48;
00556               photoHeight = 48;
00557             } else {
00558               kdDebug( 5006 ) << "Face: header image is" << faceimage.width() << "by" << faceimage.height() <<"not 48x48 Pixels" << endl;
00559             }
00560           } else {
00561             kdDebug( 5006 ) << "Failed to load decoded png from Face: header" << endl;
00562           }
00563         } else {
00564           kdDebug( 5006 ) << "Face: header too long at " << facestring.length() << endl;
00565         }
00566       }
00567     }
00568 
00569     if( photoURL.isEmpty() )
00570     {
00571       // no photo, look for a X-Face header
00572       QString xfaceURL;
00573       QString xfhead = message->headerField( "X-Face" );
00574       if ( !xfhead.isEmpty() )
00575       {
00576         KXFace xf;
00577         photoURL = imgToDataUrl( xf.toImage( xfhead ) );
00578         photoWidth = 48;
00579         photoHeight = 48;
00580 
00581       }
00582     }
00583 
00584     if( !photoURL.isEmpty() )
00585     {
00586         //kdDebug( 5006 ) << "Got a photo: " << photoURL << endl;
00587       userHTML = QString("<img src=\"%1\" width=\"%2\" height=\"%3\">")
00588           .arg( photoURL ).arg( photoWidth ).arg( photoHeight );
00589       if ( presence.isEmpty() ) {
00590         userHTML = QString("<div class=\"senderpic\">") + userHTML + "</div>";
00591       } else {
00592         userHTML = QString( "<div class=\"senderpic\">"
00593             "<a href=\"im:%1\">%2<div class=\"senderstatus\">"
00594             "<span name=\"presence-%3\">%4</span></div></a>"
00595             "</div>" ).arg( kabcUid )
00596             .arg( userHTML )
00597              .arg( kabcUid )
00598             .arg( presence );
00599       }
00600     } else {
00601        // we don't have a photo, just show presence, if we have it
00602       if ( !presence.isEmpty() )
00603         userHTML = QString( "<a href=\"im:%1\"><div class=\"senderstatus\">"
00604             "<span name=\"presence-%2\">%3</span></div></a>" )
00605             .arg( kabcUid )
00606             .arg( kabcUid )
00607             .arg( presence );
00608     }
00609 #if 0
00610     // Disabled 'Launch IM' link in headers - Will
00611     if ( imProxy->imAppsAvailable() )
00612       presence = "<a name=\"launchim\" href=\"kmail:startIMApp\">" + i18n("Launch IM") + "</a></span>";
00613     // do nothing - no im apps available, leave presence empty
00614     //presence = i18n( "DCOP/InstantMessenger not installed" );
00615     kdDebug( 5006 ) << "final presence: '" << presence << "'" << endl;
00616 #endif
00617 
00618 
00619     //case HdrFancy:
00620     // the subject line and box below for details
00621     if ( strategy->showHeader( "subject" ) ) {
00622       const int flags = LinkLocator::PreserveSpaces |
00623                         ( GlobalSettings::self()->showEmoticons() ?
00624                           LinkLocator::ReplaceSmileys : 0 );
00625       headerStr += QString("<div dir=\"%1\">%2</div>\n")
00626                         .arg(subjectDir)
00627                         .arg(message->subject().isEmpty()?
00628                              i18n("No Subject") :
00629                              strToHtml( message->subject(), flags ));
00630     }
00631     headerStr += "<table class=\"outer\"><tr><td width=\"100%\"><table>\n";
00632     //headerStr += "<table>\n";
00633     // from line
00634     // the mailto: URLs can contain %3 etc., therefore usage of multiple
00635     // QString::arg is not possible
00636     if ( strategy->showHeader( "from" ) ) {
00637       QString fromStr = message->from();
00638       if ( fromStr.isEmpty() ) // no valid email in from, maybe just a name
00639         fromStr = message->fromStrip(); // let's use that
00640       headerStr += QString("<tr><th>%1</th>\n"
00641                            "<td>")
00642                            .arg(i18n("From: "))
00643                  + KMMessage::emailAddrAsAnchor( fromStr, false )
00644                  + ( !message->headerField( "Resent-From" ).isEmpty() ? "&nbsp;"
00645                                 + i18n("(resent from %1)")
00646                                   .arg( KMMessage::emailAddrAsAnchor(
00647                                     message->headerField( "Resent-From" ),false) )
00648                               : QString("") )
00649                  + ( !vCardName.isEmpty() ? "&nbsp;&nbsp;<a href=\"" + vCardName + "\">"
00650                                 + i18n("[vCard]") + "</a>"
00651                               : QString("") )
00652 #if 0
00653                  + ( ( !presence.isEmpty() )
00654                               ? "&nbsp;&nbsp;(<span name=\"presence-" + kabcUid + "\">" + presence + "</span>)"
00655                               : QString("") )
00656 #endif
00657                  + ( message->headerField("Organization").isEmpty()
00658                               ? QString("")
00659                               : "&nbsp;&nbsp;("
00660                                 + strToHtml(message->headerField("Organization"))
00661                                 + ")")
00662                  + "</td></tr>\n";
00663     }
00664     // to line
00665     if ( strategy->showHeader( "to" ) )
00666       headerStr.append(QString("<tr><th>%1</th>\n"
00667                                "<td>%2</td></tr>\n")
00668                        .arg(i18n("To: "))
00669                        .arg(KMMessage::emailAddrAsAnchor(message->to(),false)));
00670 
00671     // cc line, if any
00672     if ( strategy->showHeader( "cc" ) && !message->cc().isEmpty())
00673       headerStr.append(QString("<tr><th>%1</th>\n"
00674                                "<td>%2</td></tr>\n")
00675                        .arg(i18n("CC: "))
00676                        .arg(KMMessage::emailAddrAsAnchor(message->cc(),false)));
00677 
00678     // Bcc line, if any
00679     if ( strategy->showHeader( "bcc" ) && !message->bcc().isEmpty())
00680       headerStr.append(QString("<tr><th>%1</th>\n"
00681                                "<td>%2</td></tr>\n")
00682                        .arg(i18n("BCC: "))
00683                        .arg(KMMessage::emailAddrAsAnchor(message->bcc(),false)));
00684 
00685     if ( strategy->showHeader( "date" ) )
00686       headerStr.append(QString("<tr><th>%1</th>\n"
00687                                "<td dir=\"%2\">%3</td></tr>\n")
00688                        .arg(i18n("Date: "))
00689                        .arg( directionOf( message->dateStr() ) )
00690                        .arg(strToHtml(dateString)));
00691 
00692     if ( GlobalSettings::self()->showUserAgent() ) {
00693       if ( strategy->showHeader( "user-agent" ) ) {
00694         if ( !message->headerField("User-Agent").isEmpty() ) {
00695           headerStr.append(QString("<tr><th>%1</th>\n"
00696                                    "<td>%2</td></tr>\n")
00697                            .arg(i18n("User-Agent: "))
00698                            .arg( strToHtml( message->headerField("User-Agent") ) ) );
00699         }
00700       }
00701 
00702       if ( strategy->showHeader( "x-mailer" ) ) {
00703         if ( !message->headerField("X-Mailer").isEmpty() ) {
00704           headerStr.append(QString("<tr><th>%1</th>\n"
00705                                    "<td>%2</td></tr>\n")
00706                            .arg(i18n("X-Mailer: "))
00707                            .arg( strToHtml( message->headerField("X-Mailer") ) ) );
00708         }
00709       }
00710     }
00711 
00712     // FIXME: Show status in synthetic header style field.  Decide whether this or current in brackets style is best and remove one.
00713     /*    if( strategy->showHeader( "status" ) )
00714       headerStr.append( QString( "<tr><th>%1</th>\n"
00715                                  "<td dir=\"%2\">%3</td></tr>\n")
00716                                     .arg(i18n("Sender status: "))
00717                                     .arg( directionOf( onlineStatus ) )
00718                                     .arg(onlineStatus));
00719     */
00720     headerStr.append( QString("<tr><td colspan=\"2\"><div id=\"attachmentInjectionPoint\"></div></td></tr>" ) );
00721     headerStr.append(
00722           QString( "</table></td><td align=\"center\">%1</td></tr></table>\n" ).arg(userHTML) );
00723 
00724     if ( !spamHTML.isEmpty() )
00725       headerStr.append( QString( "<div class=\"spamheader\" dir=\"%1\"><b>%2</b>&nbsp;<span style=\"padding-left: 20px;\">%3</span></div>\n")
00726                         .arg( subjectDir, i18n("Spam Status:"), spamHTML ) );
00727 
00728     headerStr += "</div>\n\n";
00729     return headerStr;
00730   }
00731 
00732   QString FancyHeaderStyle::imgToDataUrl( const QImage &image, const char* fmt  )
00733   {
00734     QByteArray ba;
00735     QBuffer buffer( ba );
00736     buffer.open( IO_WriteOnly );
00737     image.save( &buffer, fmt );
00738     return QString::fromLatin1("data:image/%1;base64,%2")
00739            .arg( fmt, KCodecs::base64Encode( ba ) );
00740   }
00741 
00742 // #####################
00743 
00744   class EnterpriseHeaderStyle : public HeaderStyle {
00745     friend class ::KMail::HeaderStyle;
00746   protected:
00747     EnterpriseHeaderStyle() : HeaderStyle() {}
00748     virtual ~EnterpriseHeaderStyle() {}
00749 
00750   public:
00751     const char * name() const { return "enterprise"; }
00752     const HeaderStyle * next() const { return brief(); }
00753     const HeaderStyle * prev() const { return fancy(); }
00754 
00755     QString format( const KMMessage * message, const HeaderStrategy * strategy,
00756                     const QString & vCardName, bool printing, bool topLevel ) const;
00757   };
00758 
00759     QString EnterpriseHeaderStyle::format( const KMMessage * message,
00760                                     const HeaderStrategy * strategy,
00761                                     const QString & vCardName, bool printing, bool topLevel ) const {
00762     if ( !message ) return QString::null;
00763     if ( !strategy )
00764         strategy = HeaderStrategy::brief();
00765 
00766     // The direction of the header is determined according to the direction
00767     // of the application layout.
00768 
00769     QString dir = QApplication::reverseLayout() ? "rtl" : "ltr" ;
00770 
00771     // However, the direction of the message subject within the header is
00772     // determined according to the contents of the subject itself. Since
00773     // the "Re:" and "Fwd:" prefixes would always cause the subject to be
00774     // considered left-to-right, they are ignored when determining its
00775     // direction.
00776 
00777     QString subjectDir;
00778     if (!message->subject().isEmpty())
00779         subjectDir = directionOf( message->cleanSubject() );
00780     else
00781         subjectDir = directionOf( i18n("No Subject") );
00782 
00783     // colors depend on if its encapsulated or not
00784         QColor fontColor(Qt::white);
00785     QString linkColor = "class =\"white\"";
00786     const QColor activeColor = qApp->palette().active().highlight();
00787     QColor activeColorDark = activeColor.dark(130);
00788         // reverse colors for encapsulated
00789         if( !topLevel ){
00790             activeColorDark = activeColor.dark(50);
00791             fontColor = QColor(Qt::black);
00792         linkColor = "class =\"black\"";
00793         }
00794 
00795     QStringList headerParts;
00796     if( strategy->showHeader( "to" ) )
00797         headerParts << KMMessage::emailAddrAsAnchor( message->to(), false, linkColor );
00798 
00799     if ( strategy->showHeader( "cc" ) && !message->cc().isEmpty() )
00800         headerParts << KMMessage::emailAddrAsAnchor( message->cc(), true, linkColor );
00801 
00802     if ( strategy->showHeader( "bcc" ) && !message->bcc().isEmpty() )
00803         headerParts << KMMessage::emailAddrAsAnchor( message->bcc(), true, linkColor );
00804 
00805     // remove all empty (modulo whitespace) entries and joins them via ", \n"
00806     QString headerPart = " " + headerParts.grep( QRegExp( "\\S" ) ).join( ", " );
00807 
00808     // Prepare the date string (when printing always use the localized date)
00809     QString dateString;
00810     if( printing ) {
00811         QDateTime dateTime;
00812         KLocale * locale = KGlobal::locale();
00813         dateTime.setTime_t( message->date() );
00814         dateString = locale->formatDateTime( dateTime );
00815     } else {
00816         dateString = message->dateStr();
00817     }
00818 
00819     QString imgpath(locate("data","kmail/pics/"));
00820     imgpath.append("enterprise_");
00821     const QString borderSettings(" padding-top: 0px; padding-bottom: 0px; border-width: 0px ");
00822     QString headerStr ("");
00823 
00824     // 3D borders
00825     if(topLevel)
00826         headerStr +=
00827         "<div style=\"position: fixed; top: 0px; left: 0px; background-color: #606060; "
00828         "background-image: url("+imgpath+"shadow_left.png); width: 10px; min-height: 100%;\">&nbsp;</div>"
00829         "<div style=\"position: fixed; top: 0px; right: 0px;  background-color: #606060; "
00830         "background-image: url("+imgpath+"shadow_right.png); width: 10px; min-height: 100%;\">&nbsp;</div>";
00831 
00832     headerStr += ""
00833         "<div style=\"margin-left: 10px; top: 0px;\"><span style=\"font-size: 10px; font-weight: bold;\">"+dateString+"</span></div>"
00834         // #0057ae
00835         "<table style=\"background: "+activeColorDark.name()+"; border-collapse:collapse; top: 14px; min-width: 200px; \" cellpadding=0> \n"
00836         "  <tr> \n"
00837         "   <td style=\"min-width: 6px; background-image: url("+imgpath+"top_left.png); \"></td> \n"
00838         "   <td style=\"height: 6px; width: 100%; background: url("+imgpath+"top.png); \"></td> \n"
00839         "   <td style=\"min-width: 6px; background: url("+imgpath+"top_right.png); \"></td> </tr> \n"
00840         "   <tr> \n"
00841         "   <td style=\"min-width: 6px; max-width: 6px; background: url("+imgpath+"left.png); \"></td> \n"
00842         "   <td style=\"\"> \n"
00843         "    <table style=\"color: "+fontColor.name()+" ! important; margin: 1px; border-spacing: 0px;\" cellpadding=0> \n";
00844 
00845     // subject
00846     //strToHtml( message->subject() )
00847     if ( strategy->showHeader( "subject" ) ){
00848         headerStr +=
00849         "     <tr> \n"
00850         "      <td style=\"font-size: 6px; text-align: right; padding-left: 5px; padding-right: 24px; "+borderSettings+"\"></td> \n"
00851         "      <td style=\"font-weight: bolder; font-size: 120%; padding-right: 91px; "+borderSettings+"\">"+message->subject()+"</td> \n"
00852         "     </tr> \n";
00853     }
00854 
00855     // from
00856     if ( strategy->showHeader( "from" ) ){
00857         QString fromStr = message->from();
00858         if ( fromStr.isEmpty() ) // no valid email in from, maybe just a name
00859         fromStr = message->fromStrip(); // let's use that
00860             // TODO vcard
00861         QString fromPart = KMMessage::emailAddrAsAnchor( fromStr, true, linkColor );
00862         if ( !vCardName.isEmpty() )
00863         fromPart += "&nbsp;&nbsp;<a href=\"" + vCardName + "\" "+linkColor+">" + i18n("[vCard]") + "</a>";
00864         //TDDO strategy date
00865         //if ( strategy->showHeader( "date" ) )
00866         headerStr +=
00867         "     <tr> \n"
00868         "      <td style=\"font-size: 6px; padding-left: 5px; padding-right: 24px; text-align: right; "+borderSettings+"\">"+i18n("From: ")+"</td> \n"
00869         "      <td style=\""+borderSettings+"\">"+ fromPart +"</td> "
00870         "     </tr> ";
00871     }
00872 
00873     // to, cc, bcc
00874     headerStr +=
00875         "     <tr> "
00876         "      <td style=\"font-size: 6px; text-align: right; padding-left: 5px; padding-right: 24px; "+borderSettings+"\">"+i18n("To: ")+"</td> "
00877         "      <td style=\""+borderSettings+"\">"
00878         +headerPart+
00879         "      </td> "
00880         "     </tr> ";
00881 
00882     // header-bottom
00883     headerStr +=
00884         "    </table> \n"
00885         "   </td> \n"
00886         "   <td style=\"min-width: 6px; max-height: 15px; background: url("+imgpath+"right.png); \"></td> \n"
00887         "  </tr> \n"
00888         "  <tr> \n"
00889         "   <td style=\"min-width: 6px; background: url("+imgpath+"s_left.png); \"></td> \n"
00890         "   <td style=\"height: 35px; width: 80%; background: url("+imgpath+"sbar.png);\"> \n"
00891         "    <img src=\""+imgpath+"sw.png\" style=\"margin: 0px; height: 30px; overflow:hidden; \"> \n"
00892         "    <img src=\""+imgpath+"sp_right.png\" style=\"float: right; \"> </td> \n"
00893         "   <td style=\"min-width: 6px; background: url("+imgpath+"s_right.png); \"></td> \n"
00894         "  </tr> \n"
00895         " </table> \n";
00896 
00897     // kmail icon
00898     if(topLevel) {
00899         headerStr +=
00900         "<div class=\"noprint\" style=\"position: absolute; top: -14px; right: 30px; width: 91px; height: 91px;\">\n"
00901         "<img style=\"float: right;\" src=\""+imgpath+"icon.png\">\n"
00902         "</div>\n";
00903 
00904         // attachments
00905         headerStr +=
00906         "<div class=\"noprint\" style=\"position: absolute; top: 60px; right: 20px; width: 91px; height: 200px;\">"
00907         "<div id=\"attachmentInjectionPoint\"></div>"
00908         "</div>\n";
00909     }
00910 
00911     if ( printing ) {
00912       //provide a bit more left padding when printing
00913       //kolab/issue3254 (printed mail cut at the left side)
00914       headerStr += "<div style=\"padding: 6px; padding-left: 10px;\">";
00915     } else {
00916       headerStr += "<div style=\"padding: 6px;\">";
00917     }
00918 
00919     // TODO
00920     // spam status
00921     // ### iterate over the rest of strategy->headerToDisplay() (or
00922     // ### all headers if DefaultPolicy == Display) (elsewhere, too)
00923     return headerStr;
00924   }
00925 
00926 // #####################
00927 
00928   //
00929   // HeaderStyle abstract base:
00930   //
00931 
00932   HeaderStyle::HeaderStyle() {
00933 
00934   }
00935 
00936   HeaderStyle::~HeaderStyle() {
00937 
00938   }
00939 
00940   const HeaderStyle * HeaderStyle::create( Type type ) {
00941     switch ( type ) {
00942     case Brief:  return brief();
00943     case Plain:  return plain();
00944     case Fancy:   return fancy();
00945     case Enterprise: return enterprise();
00946     }
00947     kdFatal( 5006 ) << "HeaderStyle::create(): Unknown header style ( type == "
00948                     << (int)type << " ) requested!" << endl;
00949     return 0; // make compiler happy
00950   }
00951 
00952   const HeaderStyle * HeaderStyle::create( const QString & type ) {
00953     QString lowerType = type.lower();
00954     if ( lowerType == "brief" ) return brief();
00955     if ( lowerType == "plain" )  return plain();
00956     if ( lowerType == "enterprise" )  return enterprise();
00957     //if ( lowerType == "fancy" ) return fancy(); // not needed, see below
00958     // don't kdFatal here, b/c the strings are user-provided
00959     // (KConfig), so fail gracefully to the default:
00960     return fancy();
00961   }
00962 
00963   static const HeaderStyle * briefStyle = 0;
00964   static const HeaderStyle * plainStyle = 0;
00965   static const HeaderStyle * fancyStyle = 0;
00966   static const HeaderStyle * enterpriseStyle = 0;
00967 
00968   const HeaderStyle * HeaderStyle::brief() {
00969     if ( !briefStyle )
00970       briefStyle = new BriefHeaderStyle();
00971     return briefStyle;
00972   }
00973 
00974   const HeaderStyle * HeaderStyle::plain() {
00975     if ( !plainStyle )
00976       plainStyle = new PlainHeaderStyle();
00977     return plainStyle;
00978   }
00979 
00980   const HeaderStyle * HeaderStyle::fancy() {
00981     if ( !fancyStyle )
00982       fancyStyle = new FancyHeaderStyle();
00983     return fancyStyle;
00984   }
00985 
00986   const HeaderStyle * HeaderStyle::enterprise() {
00987     if ( !enterpriseStyle )
00988       enterpriseStyle = new EnterpriseHeaderStyle();
00989     return enterpriseStyle;
00990   }
00991 
00992 } // namespace KMail
KDE Home | KDE Accessibility Home | Description of Access Keys