libtdepim

csshelper.cpp
1 /*
2  csshelper.cpp
3 
4  This file is part of KMail, the KDE mail client.
5  Copyright (c) 2003 Marc Mutz <mutz@kde.org>
6 
7  KMail is free software; you can redistribute it and/or modify it
8  under the terms of the GNU General Public License, version 2, as
9  published by the Free Software Foundation.
10 
11  KMail is distributed in the hope that it will be useful, but
12  WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with this program; if not, write to the Free Software
18  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 
20  In addition, as a special exception, the copyright holders give
21  permission to link the code of this program with any edition of
22  the TQt library by Trolltech AS, Norway (or with modified versions
23  of TQt that use the same license as TQt), and distribute linked
24  combinations including the two. You must obey the GNU General
25  Public License in all respects for all of the code used other than
26  TQt. If you modify this file, you may extend this exception to
27  your version of the file, but you are not obligated to do so. If
28  you do not wish to do so, delete this exception statement from
29  your version.
30 */
31 
32 #include "csshelper.h"
33 
34 #include <tdeconfig.h>
35 #include <tdeglobalsettings.h>
36 #include <kdebug.h>
37 #include <tdeglobal.h>
38 
39 #include <tqstring.h>
40 #include <tqapplication.h>
41 
42 namespace KPIM {
43 
44  namespace {
45  // some TQColor manipulators that hide the ugly TQColor API w.r.t. HSV:
46  inline TQColor darker( const TQColor & c ) {
47  int h, s, v;
48  c.hsv( &h, &s, &v );
49  return TQColor( h, s, v*4/5, TQColor::Hsv );
50  }
51 
52  inline TQColor desaturate( const TQColor & c ) {
53  int h, s, v;
54  c.hsv( &h, &s, &v );
55  return TQColor( h, s/8, v, TQColor::Hsv );
56  }
57 
58  inline TQColor fixValue( const TQColor & c, int newV ) {
59  int h, s, v;
60  c.hsv( &h, &s, &v );
61  return TQColor( h, s, newV, TQColor::Hsv );
62  }
63 
64  inline int getValueOf( const TQColor & c ) {
65  int h, s, v;
66  c.hsv( &h, &s, &v );
67  return v;
68  }
69  }
70 
71  CSSHelper::CSSHelper( const TQPaintDeviceMetrics &pdm ) :
72  mShrinkQuotes( false ),
73  mMetrics( pdm )
74  {
75  // initialize with defaults - should match the corresponding application defaults
76  mForegroundColor = TQApplication::palette().active().text();
77  mLinkColor = TDEGlobalSettings::linkColor();
78  mVisitedLinkColor = TDEGlobalSettings::visitedLinkColor();
79  mBackgroundColor = TQApplication::palette().active().base();
80  cHtmlWarning = TQColor( 0xFF, 0x40, 0x40 ); // warning frame color: light red
81 
82  cPgpEncrH = TQColor( 0x00, 0x80, 0xFF ); // light blue
83  cPgpOk1H = TQColor( 0x40, 0xFF, 0x40 ); // light green
84  cPgpOk0H = TQColor( 0xFF, 0xFF, 0x40 ); // light yellow
85  cPgpWarnH = TQColor( 0xFF, 0xFF, 0x40 ); // light yellow
86  cPgpErrH = TQt::red;
87 
88  for ( int i = 0 ; i < 3 ; ++i )
89  mQuoteColor[i] = TQColor( 0x00, 0x80 - i * 0x10, 0x00 ); // shades of green
90  mRecycleQuoteColors = false;
91 
92  TQFont defaultFont = TDEGlobalSettings::generalFont();
93  TQFont defaultFixedFont = TDEGlobalSettings::fixedFont();
94  mBodyFont = mPrintFont = defaultFont;
95  mFixedFont = mFixedPrintFont = defaultFixedFont;
96  defaultFont.setItalic( true );
97  for ( int i = 0 ; i < 3 ; ++i )
98  mQuoteFont[i] = defaultFont;
99 
100  mBackingPixmapOn = false;
101 
102  recalculatePGPColors();
103  }
104 
105  void CSSHelper::recalculatePGPColors() {
106  // determine the frame and body color for PGP messages from the header color
107  // if the header color equals the background color then the other colors are
108  // also set to the background color (-> old style PGP message viewing)
109  // else
110  // the brightness of the frame is set to 4/5 of the brightness of the header
111  // and in case of a light background color
112  // the saturation of the body is set to 1/8 of the saturation of the header
113  // while in case of a dark background color
114  // the value of the body is set to the value of the background color
115 
116  // Check whether the user uses a light color scheme
117  const int vBG = getValueOf( mBackgroundColor );
118  const bool lightBG = vBG >= 128;
119  if ( cPgpOk1H == mBackgroundColor ) {
120  cPgpOk1F = mBackgroundColor;
121  cPgpOk1B = mBackgroundColor;
122  } else {
123  cPgpOk1F= darker( cPgpOk1H );
124  cPgpOk1B = lightBG ? desaturate( cPgpOk1H ) : fixValue( cPgpOk1H, vBG );
125  }
126  if ( cPgpOk0H == mBackgroundColor ) {
127  cPgpOk0F = mBackgroundColor;
128  cPgpOk0B = mBackgroundColor;
129  } else {
130  cPgpOk0F = darker( cPgpOk0H );
131  cPgpOk0B = lightBG ? desaturate( cPgpOk0H ) : fixValue( cPgpOk0H, vBG );
132  }
133  if ( cPgpWarnH == mBackgroundColor ) {
134  cPgpWarnF = mBackgroundColor;
135  cPgpWarnB = mBackgroundColor;
136  } else {
137  cPgpWarnF = darker( cPgpWarnH );
138  cPgpWarnB = lightBG ? desaturate( cPgpWarnH ) : fixValue( cPgpWarnH, vBG );
139  }
140  if ( cPgpErrH == mBackgroundColor ) {
141  cPgpErrF = mBackgroundColor;
142  cPgpErrB = mBackgroundColor;
143  } else {
144  cPgpErrF = darker( cPgpErrH );
145  cPgpErrB = lightBG ? desaturate( cPgpErrH ) : fixValue( cPgpErrH, vBG );
146  }
147  if ( cPgpEncrH == mBackgroundColor ) {
148  cPgpEncrF = mBackgroundColor;
149  cPgpEncrB = mBackgroundColor;
150  } else {
151  cPgpEncrF = darker( cPgpEncrH );
152  cPgpEncrB = lightBG ? desaturate( cPgpEncrH ) : fixValue( cPgpEncrH, vBG );
153  }
154  }
155 
156  TQString CSSHelper::cssDefinitions( bool fixed ) const {
157  return
158  commonCssDefinitions()
159  +
160  "@media screen {\n\n"
161  +
162  screenCssDefinitions( this, fixed )
163  +
164  "}\n"
165  "@media print {\n\n"
166  +
167  printCssDefinitions( fixed )
168  +
169  "}\n";
170  }
171 
172  TQString CSSHelper::htmlHead( bool /*fixed*/ ) const {
173  return
174  "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n"
175  "<html><head><title></title></head>\n"
176  "<body>\n";
177  }
178 
179  TQString CSSHelper::quoteFontTag( int level ) const {
180  if ( level < 0 )
181  level = 0;
182  static const int numQuoteLevels = sizeof mQuoteFont / sizeof *mQuoteFont;
183  const int effectiveLevel = mRecycleQuoteColors
184  ? level % numQuoteLevels + 1
185  : kMin( level + 1, numQuoteLevels ) ;
186  if ( level >= numQuoteLevels )
187  return TQString( "<div class=\"deepquotelevel%1\">" ).arg( effectiveLevel );
188  else
189  return TQString( "<div class=\"quotelevel%1\">" ).arg( effectiveLevel );
190  }
191 
192  TQString CSSHelper::nonQuotedFontTag() const {
193  return "<div class=\"noquote\">";
194  }
195 
196  TQFont CSSHelper::bodyFont( bool fixed, bool print ) const {
197  return fixed ? ( print ? mFixedPrintFont : mFixedFont )
198  : ( print ? mPrintFont : mBodyFont );
199  }
200 
201  int CSSHelper::fontSize( bool fixed, bool print ) const {
202  return bodyFont( fixed, print ).pointSize();
203  }
204 
205 
206  namespace {
207  int pointsToPixel( const TQPaintDeviceMetrics & metrics, int pointSize ) {
208  return ( pointSize * metrics.logicalDpiY() + 36 ) / 72 ;
209  }
210  }
211 
212  static const char * const quoteFontSizes[] = { "85", "80", "75" };
213 
214  TQString CSSHelper::printCssDefinitions( bool fixed ) const {
215  const TQString headerFont = TQString( " font-family: \"%1\" ! important;\n"
216  " font-size: %2pt ! important;\n" )
217  .arg( mPrintFont.family() )
218  .arg( mPrintFont.pointSize() );
219  const TQColorGroup & cg = TQApplication::palette().active();
220 
221  const TQFont printFont = bodyFont( fixed, true /* print */ );
222  TQString quoteCSS;
223  if ( printFont.italic() )
224  quoteCSS += " font-style: italic ! important;\n";
225  if ( printFont.bold() )
226  quoteCSS += " font-weight: bold ! important;\n";
227  if ( !quoteCSS.isEmpty() )
228  quoteCSS = "div.noquote {\n" + quoteCSS + "}\n\n";
229 
230  return
231  TQString( "body {\n"
232  " font-family: \"%1\" ! important;\n"
233  " font-size: %2pt ! important;\n"
234  " color: #000000 ! important;\n"
235  " background-color: #ffffff ! important\n"
236  "}\n\n" )
237  .arg( printFont.family(),
238  TQString::number( printFont.pointSize() ) )
239  +
240  TQString( "tr.textAtmH,\n"
241  "tr.signInProgressH,\n"
242  "tr.rfc822H,\n"
243  "tr.encrH,\n"
244  "tr.signOkKeyOkH,\n"
245  "tr.signOkKeyBadH,\n"
246  "tr.signWarnH,\n"
247  "tr.signErrH,\n"
248  "div.header {\n"
249  "%1"
250  "}\n\n"
251 
252  "div.fancy.header > div {\n"
253  " background-color: %2 ! important;\n"
254  " color: %3 ! important;\n"
255  " padding: 4px ! important;\n"
256  " border: solid %3 1px ! important;\n"
257  "}\n\n"
258 
259  "div.fancy.header > div a[href] { color: %3 ! important; }\n\n"
260 
261  "div.fancy.header > table.outer{\n"
262  " background-color: %2 ! important;\n"
263  " color: %3 ! important;\n"
264  " border-bottom: solid %3 1px ! important;\n"
265  " border-left: solid %3 1px ! important;\n"
266  " border-right: solid %3 1px ! important;\n"
267  "}\n\n"
268 
269  "div.spamheader {\n"
270  " display:none ! important;\n"
271  "}\n\n"
272 
273  "div.htmlWarn {\n"
274  " border: 2px solid #ffffff ! important;\n"
275  "}\n\n"
276 
277  "div.senderpic{\n"
278  " font-size:0.8em ! important;\n"
279  " border:1px solid black ! important;\n"
280  " background-color:%2 ! important;\n"
281  "}\n\n"
282 
283  "div.senderstatus{\n"
284  " text-align:center ! important;\n"
285  "}\n\n"
286 
287  "div.noprint {\n"
288  " display:none ! important;\n"
289  "}\n\n"
290  )
291  .arg( headerFont,
292  cg.background().name(),
293  cg.foreground().name() )
294  + quoteCSS;
295  }
296 
297  TQString CSSHelper::screenCssDefinitions( const CSSHelper * helper, bool fixed ) const {
298  const TQString fgColor = mForegroundColor.name();
299  const TQString bgColor = mBackgroundColor.name();
300  const TQString linkColor = mLinkColor.name();
301  const TQString headerFont = TQString(" font-family: \"%1\" ! important;\n"
302  " font-size: %2px ! important;\n")
303  .arg( mBodyFont.family() )
304  .arg( pointsToPixel( helper->mMetrics, mBodyFont.pointSize() ) );
305  const TQString background = ( mBackingPixmapOn
306  ? TQString( " background-image:url(file://%1) ! important;\n" )
307  .arg( mBackingPixmapStr )
308  : TQString( " background-color: %1 ! important;\n" )
309  .arg( bgColor ) );
310  const TQString bodyFontSize = TQString::number( pointsToPixel( helper->mMetrics, fontSize( fixed ) ) ) + "px" ;
311  const TQColorGroup & cg = TQApplication::palette().active();
312 
313  TQString quoteCSS;
314  if ( bodyFont( fixed ).italic() )
315  quoteCSS += " font-style: italic ! important;\n";
316  if ( bodyFont( fixed ).bold() )
317  quoteCSS += " font-weight: bold ! important;\n";
318  if ( !quoteCSS.isEmpty() )
319  quoteCSS = "div.noquote {\n" + quoteCSS + "}\n\n";
320 
321  // CSS definitions for quote levels 1-3
322  for ( int i = 0 ; i < 3 ; ++i ) {
323  quoteCSS += TQString( "div.quotelevel%1 {\n"
324  " color: %2 ! important;\n" )
325  .arg( TQString::number(i+1), mQuoteColor[i].name() );
326  if ( mQuoteFont[i].italic() )
327  quoteCSS += " font-style: italic ! important;\n";
328  if ( mQuoteFont[i].bold() )
329  quoteCSS += " font-weight: bold ! important;\n";
330  if ( mShrinkQuotes )
331  quoteCSS += " font-size: " + TQString::fromLatin1( quoteFontSizes[i] )
332  + "% ! important;\n";
333  quoteCSS += "}\n\n";
334  }
335 
336  // CSS definitions for quote levels 4+
337  for ( int i = 0 ; i < 3 ; ++i ) {
338  quoteCSS += TQString( "div.deepquotelevel%1 {\n"
339  " color: %2 ! important;\n" )
340  .arg( TQString::number(i+1), mQuoteColor[i].name() );
341  if ( mQuoteFont[i].italic() )
342  quoteCSS += " font-style: italic ! important;\n";
343  if ( mQuoteFont[i].bold() )
344  quoteCSS += " font-weight: bold ! important;\n";
345  if ( mShrinkQuotes )
346  quoteCSS += " font-size: 70% ! important;\n";
347  quoteCSS += "}\n\n";
348  }
349 
350  return
351  TQString( "body {\n"
352  " font-family: \"%1\" ! important;\n"
353  " font-size: %2 ! important;\n"
354  " color: %3 ! important;\n"
355  "%4"
356  "}\n\n" )
357  .arg( bodyFont( fixed ).family(),
358  bodyFontSize,
359  fgColor,
360  background )
361  +
362  TQString( "a {\n"
363  " color: %1 ! important;\n"
364  " text-decoration: none ! important;\n"
365  "}\n\n"
366 
367  "a.white {\n"
368  " color: white ! important;\n"
369  "}\n\n"
370 
371  "a.black {\n"
372  " color: black ! important;\n"
373  "}\n\n"
374 
375  "table.textAtm { background-color: %2 ! important; }\n\n"
376 
377  "tr.textAtmH {\n"
378  " background-color: %3 ! important;\n"
379  "%4"
380  "}\n\n"
381 
382  "tr.textAtmB {\n"
383  " background-color: %3 ! important;\n"
384  "}\n\n"
385 
386  "table.signInProgress,\n"
387  "table.rfc822 {\n"
388  " background-color: %3 ! important;\n"
389  "}\n\n"
390 
391  "tr.signInProgressH,\n"
392  "tr.rfc822H {\n"
393  "%4"
394  "}\n\n" )
395  .arg( linkColor, fgColor, bgColor, headerFont )
396  +
397  TQString( "table.encr {\n"
398  " background-color: %1 ! important;\n"
399  "}\n\n"
400 
401  "tr.encrH {\n"
402  " background-color: %2 ! important;\n"
403  "%3"
404  "}\n\n"
405 
406  "tr.encrB { background-color: %4 ! important; }\n\n" )
407  .arg( cPgpEncrF.name(),
408  cPgpEncrH.name(),
409  headerFont,
410  cPgpEncrB.name() )
411  +
412  TQString( "table.signOkKeyOk {\n"
413  " background-color: %1 ! important;\n"
414  "}\n\n"
415 
416  "tr.signOkKeyOkH {\n"
417  " background-color: %2 ! important;\n"
418  "%3"
419  "}\n\n"
420 
421  "tr.signOkKeyOkB { background-color: %4 ! important; }\n\n" )
422  .arg( cPgpOk1F.name(),
423  cPgpOk1H.name(),
424  headerFont,
425  cPgpOk1B.name() )
426  +
427  TQString( "table.signOkKeyBad {\n"
428  " background-color: %1 ! important;\n"
429  "}\n\n"
430 
431  "tr.signOkKeyBadH {\n"
432  " background-color: %2 ! important;\n"
433  "%3"
434  "}\n\n"
435 
436  "tr.signOkKeyBadB { background-color: %4 ! important; }\n\n" )
437  .arg( cPgpOk0F.name(),
438  cPgpOk0H.name(),
439  headerFont,
440  cPgpOk0B.name() )
441  +
442  TQString( "table.signWarn {\n"
443  " background-color: %1 ! important;\n"
444  "}\n\n"
445 
446  "tr.signWarnH {\n"
447  " background-color: %2 ! important;\n"
448  "%3"
449  "}\n\n"
450 
451  "tr.signWarnB { background-color: %4 ! important; }\n\n" )
452  .arg( cPgpWarnF.name(),
453  cPgpWarnH.name(),
454  headerFont,
455  cPgpWarnB.name() )
456  +
457  TQString( "table.signErr {\n"
458  " background-color: %1 ! important;\n"
459  "}\n\n"
460 
461  "tr.signErrH {\n"
462  " background-color: %2 ! important;\n"
463  "%3"
464  "}\n\n"
465 
466  "tr.signErrB { background-color: %4 ! important; }\n\n" )
467  .arg( cPgpErrF.name(),
468  cPgpErrH.name(),
469  headerFont,
470  cPgpErrB.name() )
471  +
472  TQString( "div.htmlWarn {\n"
473  " border: 2px solid %1 ! important;\n"
474  "}\n\n" )
475  .arg( cHtmlWarning.name() )
476  +
477  TQString( "div.header {\n"
478  "%1"
479  "}\n\n"
480 
481  "div.fancy.header > div {\n"
482  " background-color: %2 ! important;\n"
483  " color: %3 ! important;\n"
484  " border: solid %4 1px ! important;\n"
485  "}\n\n"
486 
487  "div.fancy.header > div a[href] { color: %3 ! important; }\n\n"
488 
489  "div.fancy.header > div a[href]:hover { text-decoration: underline ! important; }\n\n"
490 
491  "div.fancy.header > div.spamheader {\n"
492  " background-color: #cdcdcd ! important;\n"
493  " border-top: 0px ! important;\n"
494  " padding: 3px ! important;\n"
495  " color: black ! important;\n"
496  " font-weight: bold ! important;\n"
497  " font-size: smaller ! important;\n"
498  "}\n\n"
499 
500  "div.fancy.header > table.outer {\n"
501  " background-color: %5 ! important;\n"
502  " color: %4 ! important;\n"
503  " border-bottom: solid %4 1px ! important;\n"
504  " border-left: solid %4 1px ! important;\n"
505  " border-right: solid %4 1px ! important;\n"
506  "}\n\n"
507 
508  "div.senderpic{\n"
509  " padding: 0px ! important;\n"
510  " font-size:0.8em ! important;\n"
511  " border:1px solid %6 ! important;\n"
512  // FIXME: InfoBackground crashes TDEHTML
513  //" background-color:InfoBackground ! important;\n"
514  " background-color:%5 ! important;\n"
515  "}\n\n"
516 
517  "div.senderstatus{\n"
518  " text-align:center ! important;\n"
519  "}\n\n"
520  )
521 
522  .arg( headerFont )
523  .arg( cg.highlight().name(),
524  cg.highlightedText().name(),
525  cg.foreground().name(),
526  cg.background().name() )
527  .arg( cg.mid().name() )
528  + quoteCSS;
529  }
530 
531  TQString CSSHelper::commonCssDefinitions() const {
532  return
533  "div.header {\n"
534  " margin-bottom: 10pt ! important;\n"
535  "}\n\n"
536 
537  "table.textAtm {\n"
538  " margin-top: 10pt ! important;\n"
539  " margin-bottom: 10pt ! important;\n"
540  "}\n\n"
541 
542  "tr.textAtmH,\n"
543  "tr.textAtmB,\n"
544  "tr.rfc822B {\n"
545  " font-weight: normal ! important;\n"
546  "}\n\n"
547 
548  "tr.signInProgressH,\n"
549  "tr.rfc822H,\n"
550  "tr.encrH,\n"
551  "tr.signOkKeyOkH,\n"
552  "tr.signOkKeyBadH,\n"
553  "tr.signWarnH,\n"
554  "tr.signErrH {\n"
555  " font-weight: bold ! important;\n"
556  "}\n\n"
557 
558  "tr.textAtmH td,\n"
559  "tr.textAtmB td {\n"
560  " padding: 3px ! important;\n"
561  "}\n\n"
562 
563  "table.rfc822 {\n"
564  " width: 100% ! important;\n"
565  " border: solid 1px black ! important;\n"
566  " margin-top: 10pt ! important;\n"
567  " margin-bottom: 10pt ! important;\n"
568  "}\n\n"
569 
570  "table.textAtm,\n"
571  "table.encr,\n"
572  "table.signWarn,\n"
573  "table.signErr,\n"
574  "table.signOkKeyBad,\n"
575  "table.signOkKeyOk,\n"
576  "table.signInProgress,\n"
577  "div.fancy.header table {\n"
578  " width: 100% ! important;\n"
579  " border-width: 0px ! important;\n"
580  "}\n\n"
581 
582  "div.htmlWarn {\n"
583  " margin: 0px 5% ! important;\n"
584  " padding: 10px ! important;\n"
585  " text-align: left ! important;\n"
586  "}\n\n"
587 
588  "div.fancy.header > div {\n"
589  " font-weight: bold ! important;\n"
590  " padding: 4px ! important;\n"
591  "}\n\n"
592 
593  "div.fancy.header table {\n"
594  " padding: 2px ! important;\n" // ### tdehtml bug: this is ignored
595  " text-align: left ! important\n"
596  "}\n\n"
597 
598  "div.fancy.header table th {\n"
599  " padding: 0px ! important;\n"
600  " white-space: nowrap ! important;\n"
601  " border-spacing: 0px ! important;\n"
602  " text-align: left ! important;\n"
603  " vertical-align: top ! important;\n"
604  "}\n\n"
605 
606  "div.fancy.header table td {\n"
607  " padding: 0px ! important;\n"
608  " border-spacing: 0px ! important;\n"
609  " text-align: left ! important;\n"
610  " vertical-align: top ! important;\n"
611  " width: 100% ! important;\n"
612  "}\n\n"
613 
614  "span.pimsmileytext {\n"
615  " position: absolute;\n"
616  " top: 0px;\n"
617  " left: 0px;\n"
618  " visibility: hidden;\n"
619  "}\n\n"
620 
621  "img.pimsmileyimg {\n"
622  "}\n\n"
623 
624  "div.quotelevelmark {\n"
625  " position: absolute;\n"
626  " margin-left:-10px;\n"
627  "}\n\n"
628  ;
629  }
630 
631 
632  void CSSHelper::setBodyFont( const TQFont& font )
633  {
634  mBodyFont = font;
635  }
636 
637  void CSSHelper::setPrintFont( const TQFont& font )
638  {
639  mPrintFont = font;
640  }
641 
642  TQColor CSSHelper::pgpWarnColor() const
643  {
644  return cPgpWarnH;
645  }
646 
647 } // namespace KPIM
TDEPIM classes for drag and drop of mails.