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
42namespace 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.