25 #include <QStringList>
28 #include <QHBoxLayout>
31 #include <kactioncollection.h>
32 #include <kactionmenu.h>
34 #include <kbookmarkmanager.h>
35 #include <kcharsets.h>
36 #include <khtml_part.h>
37 #include <khtmlview.h>
38 #include <kiconloader.h>
41 #include <kmessagebox.h>
42 #include <kmimetype.h>
44 #include <kselectaction.h>
45 #include <kstandarddirs.h>
46 #include <kstandardaction.h>
47 #include <ktemporaryfile.h>
48 #include <ktoggleaction.h>
50 #include <kxmlguifactory.h>
57 #include <libkpgp/kpgp.h>
58 #include <libkpgp/kpgpblock.h>
60 #include <messageviewer/header/kxface.h>
61 #include <messagecomposer/utils/util.h>
62 #include <kpimutils/kfileio.h>
63 #include <kpimutils/linklocator.h>
64 #include <kpimutils/email.h>
84 using namespace KNode;
89 KXMLGUIClient *guiClient,
90 KActionCollection *actionCollection,
bool isMainViewer ) :
94 mHeaderStyle(
"fancy" ),
95 mAttachmentStyle(
"inline" ),
98 mForceCharset( false ),
99 mOverrideCharset( KMime::Headers::Latin1 ),
101 mIsMainViewer( isMainViewer ),
102 mGuiClient( guiClient ),
103 mActionCollection( actionCollection )
105 mInstances.append(
this );
107 QHBoxLayout *box =
new QHBoxLayout(
this );
109 box->setSpacing( 0 );
110 mViewer =
new KHTMLPart(
this );
111 box->addWidget( mViewer->widget() );
112 mViewer->widget()->setFocusPolicy( Qt::WheelFocus );
113 mViewer->setPluginsEnabled(
false );
114 mViewer->setJScriptEnabled(
false );
115 mViewer->setJavaEnabled(
false );
116 mViewer->setMetaRefreshEnabled(
false );
117 mViewer->setOnlyLocalReferences(
true );
118 mViewer->view()->setFocusPolicy( Qt::WheelFocus );
119 connect( mViewer->browserExtension(), SIGNAL(openUrlRequestDelayed(KUrl,KParts::OpenUrlArguments,KParts::BrowserArguments)),
120 SLOT(slotURLClicked(KUrl)) );
121 connect( mViewer, SIGNAL(popupMenu(QString,QPoint)),
122 SLOT(slotURLPopup(QString,QPoint)) );
124 mTimer =
new QTimer(
this );
125 mTimer->setSingleShot(
true );
126 connect( mTimer, SIGNAL(timeout()), SLOT(slotTimeout()) );
136 mInstances.removeAll(
this );
139 if ( mArticle && mArticle->isOrphant() ) {
142 knGlobals.articleManager()->cancelJobs( mArticle );
148 void ArticleWidget::initActions()
150 mSaveAction = KStandardAction::save(
this, SLOT(slotSave()), mActionCollection );
151 mSaveAction->setText( KStandardGuiItem::saveAs().
text() );
152 mPrintAction = KStandardAction::print(
this, SLOT(slotPrint()), mActionCollection );
153 mCopySelectionAction = KStandardAction::copy(
this, SLOT(slotCopySelection()), mActionCollection );
154 mSelectAllAction = KStandardAction::selectAll(
this, SLOT(slotSelectAll()), mActionCollection );
155 mFindAction = KStandardAction::find(
this, SLOT(slotFind()), mActionCollection);
156 mActionCollection->addAction(
"find_in_article", mFindAction);
157 mFindAction->setText( i18n(
"F&ind in Article...") );
158 mViewSourceAction = mActionCollection->addAction(
"article_viewSource");
159 mViewSourceAction->setText(i18n(
"&View Source"));
160 connect(mViewSourceAction, SIGNAL(triggered(
bool)), SLOT(slotViewSource()));
161 mViewSourceAction->setShortcut(QKeySequence(Qt::Key_V));
162 mReplyAction = mActionCollection->addAction(
"article_postReply");
163 mReplyAction->setIcon(KIcon(
"mail-reply-all"));
164 mReplyAction->setText(i18n(
"&Followup to Newsgroup..."));
165 connect(mReplyAction, SIGNAL(triggered(
bool)), SLOT(slotReply()));
166 mReplyAction->setShortcut(QKeySequence(Qt::Key_R));
167 mRemailAction = mActionCollection->addAction(
"article_mailReply" );
168 mRemailAction->setIcon(KIcon(
"mail-reply-sender"));
169 mRemailAction->setText(i18n(
"Reply by E&mail..."));
170 connect(mRemailAction, SIGNAL(triggered(
bool)), SLOT(slotRemail()));
171 mRemailAction->setShortcut(QKeySequence(Qt::Key_A));
172 mForwardAction = mActionCollection->addAction(
"article_forward");
173 mForwardAction->setIcon(KIcon(
"mail-forward"));
174 mForwardAction->setText(i18n(
"Forw&ard by Email..."));
175 connect(mForwardAction, SIGNAL(triggered(
bool)), SLOT(slotForward()));
176 mForwardAction->setShortcut(QKeySequence(Qt::Key_F));
177 mCancelAction = mActionCollection->addAction(
"article_cancel");
178 mCancelAction->setText(i18nc(
"article",
"&Cancel Article"));
179 connect(mCancelAction, SIGNAL(triggered(
bool)), SLOT(slotCancel()));
180 mSupersedeAction = mActionCollection->addAction(
"article_supersede");
181 mSupersedeAction->setText(i18n(
"S&upersede Article"));
182 connect(mSupersedeAction, SIGNAL(triggered(
bool)), SLOT(slotSupersede()));
183 mFixedFontToggle = mActionCollection->add<KToggleAction>(
"view_useFixedFont");
184 mFixedFontToggle->setText(i18n(
"U&se Fixed Font"));
185 connect(mFixedFontToggle, SIGNAL(triggered(
bool)), SLOT(slotToggleFixedFont()));
186 mFixedFontToggle->setShortcut(QKeySequence(Qt::Key_X));
187 mFancyToggle = mActionCollection->add<KToggleAction>(
"view_fancyFormating");
188 mFancyToggle->setText(i18n(
"Fancy Formatting"));
189 connect(mFancyToggle, SIGNAL(triggered(
bool)), SLOT(slotToggleFancyFormating()));
190 mFancyToggle->setShortcut(QKeySequence(Qt::Key_Y));
191 mRot13Toggle = mActionCollection->add<KToggleAction>(
"view_rot13");
192 mRot13Toggle->setIcon(KIcon(
"document-decrypt"));
193 mRot13Toggle->setText(i18n(
"&Unscramble (Rot 13)"));
194 connect(mRot13Toggle, SIGNAL(triggered(
bool)), SLOT(slotToggleRot13()));
195 mRot13Toggle->setChecked(
false );
197 QActionGroup *ag =
new QActionGroup(
this );
199 mHeaderStyleMenu = mActionCollection->add<
KActionMenu>(
"view_headers");
200 mHeaderStyleMenu->setText(i18n(
"&Headers"));
201 ra = mActionCollection->add<KToggleAction>(
"view_headers_fancy");
202 ra->setText(i18n(
"&Fancy Headers"));
203 connect(ra, SIGNAL(triggered(
bool)), SLOT(slotFancyHeaders()));
204 ag->addAction ( ra );
205 mHeaderStyleMenu->addAction( ra );
206 ra = mActionCollection->add<KToggleAction>(
"view_headers_standard");
207 ra->setText(i18n(
"&Standard Headers"));
208 connect(ra, SIGNAL(triggered(
bool)), SLOT(slotStandardHeaders()));
210 mHeaderStyleMenu->addAction( ra );
211 ra = mActionCollection->add<KToggleAction>(
"view_headers_all");
212 ra->setText(i18n(
"&All Headers"));
213 connect(ra, SIGNAL(triggered(
bool)), SLOT(slotAllHeaders()));
215 mHeaderStyleMenu->addAction( ra );
217 ag =
new QActionGroup(
this );
218 mAttachmentStyleMenu = mActionCollection->add<
KActionMenu>(
"view_attachments");
219 mAttachmentStyleMenu->setText(i18n(
"&Attachments"));
220 ra = mActionCollection->add<KToggleAction>(
"view_attachments_icon");
221 ra->setText(i18n(
"&As Icon"));
222 connect(ra, SIGNAL(triggered(
bool)), SLOT(slotIconAttachments()));
224 mAttachmentStyleMenu->addAction( ra );
225 ra = mActionCollection->add<KToggleAction>(
"view_attachments_inline");
226 ra->setText(i18n(
"&Inline"));
227 connect(ra, SIGNAL(triggered(
bool)), SLOT(slotInlineAttachments()));
229 mAttachmentStyleMenu->addAction( ra );
230 ra = mActionCollection->add<KToggleAction>(
"view_attachments_hide");
231 ra->setText(i18n(
"&Hide"));
232 connect(ra, SIGNAL(triggered(
bool)), SLOT(slotHideAttachments()));
234 mAttachmentStyleMenu->addAction( ra );
236 mCharsetSelect = mActionCollection->add<KSelectAction>(
"set_charset");
237 mCharsetSelect->setText( i18n(
"Set chars&et" ) );
238 mCharsetSelect->setShortcutConfigurable(
false );
240 cs.prepend( i18nc(
"@item default character set",
"Default") );
241 mCharsetSelect->setItems( cs );
242 mCharsetSelect->setCurrentItem( 0 );
243 connect( mCharsetSelect, SIGNAL(triggered(QString)),SLOT(slotSetCharset(QString)) );
244 mCharsetSelectKeyb = mActionCollection->addAction(
"set_charset_keyboard");
245 mCharsetSelectKeyb->setText( i18n(
"Set charset" ) );
246 connect(mCharsetSelectKeyb, SIGNAL(triggered(
bool)), SLOT(slotSetCharsetKeyboard()));
247 mCharsetSelectKeyb->setShortcut(QKeySequence(Qt::Key_C));
249 QAction *action = mActionCollection->addAction(
"open_url");
250 action->setIcon(KIcon(
"document-open"));
251 action->setText(i18n(
"&Open URL"));
252 connect(action, SIGNAL(triggered(
bool)), SLOT(slotOpenURL()));
253 action = mActionCollection->addAction(
"copy_url");
254 action->setIcon(KIcon(
"edit-copy"));
255 action->setText(i18n(
"&Copy Link Address"));
256 connect(action, SIGNAL(triggered(
bool)), SLOT(slotCopyURL()));
257 action = mActionCollection->addAction(
"add_bookmark");
258 action->setIcon(KIcon(
"bookmark-new"));
259 action->setText(i18n(
"&Bookmark This Link"));
260 connect(action, SIGNAL(triggered(
bool)), SLOT(slotAddBookmark()));
261 action = mActionCollection->addAction(
"add_addr_book");
262 action->setText(i18n(
"&Add to Address Book"));
263 connect(action, SIGNAL(triggered(
bool)), SLOT(slotAddToAddressBook()));
264 action = mActionCollection->addAction(
"openin_addr_book");
265 action->setText(i18n(
"&Open in Address Book"));
266 connect(action, SIGNAL(triggered(
bool)), SLOT(slotOpenInAddressBook()));
267 action = mActionCollection->addAction(
"open_attachment");
268 action->setIcon(KIcon(
"document-open"));
269 action->setText(i18n(
"&Open Attachment"));
270 connect(action, SIGNAL(triggered(
bool)), SLOT(slotOpenAttachment()));
271 action = mActionCollection->addAction(
"save_attachment");
272 action->setIcon(KIcon(
"document-save-as"));
273 action->setText(i18n(
"&Save Attachment As..."));
274 connect(action, SIGNAL(triggered(
bool)), SLOT(slotSaveAttachment()));
279 void ArticleWidget::enableActions()
286 mSaveAction->setEnabled(
true );
287 mPrintAction->setEnabled(
true );
288 mCopySelectionAction->setEnabled(
true );
289 mSelectAllAction->setEnabled(
true );
290 mFindAction->setEnabled(
true );
291 mForwardAction->setEnabled(
true );
292 mHeaderStyleMenu->setEnabled(
true );
293 mAttachmentStyleMenu->setEnabled(
true );
294 mRot13Toggle->setEnabled(
true );
295 mViewSourceAction->setEnabled(
true );
296 mCharsetSelect->setEnabled(
true );
297 mCharsetSelectKeyb->setEnabled(
true );
298 mFixedFontToggle->setEnabled(
true );
299 mFancyToggle->setEnabled(
true );
303 mReplyAction->setEnabled( enabled );
304 mRemailAction->setEnabled( enabled );
307 || mArticle->collection() ==
knGlobals.folderManager()->sent() );
308 mCancelAction->setEnabled( enabled );
309 mSupersedeAction->setEnabled( enabled );
313 void ArticleWidget::disableActions()
315 mSaveAction->setEnabled(
false );
316 mPrintAction->setEnabled(
false );
317 mCopySelectionAction->setEnabled(
false );
318 mSelectAllAction->setEnabled(
false );
319 mFindAction->setEnabled(
false );
320 mReplyAction->setEnabled(
false );
321 mRemailAction->setEnabled(
false );
322 mForwardAction->setEnabled(
false );
323 mCancelAction->setEnabled(
false );
324 mSupersedeAction->setEnabled(
false );
325 mHeaderStyleMenu->setEnabled(
false );
326 mAttachmentStyleMenu->setEnabled(
false );
327 mRot13Toggle->setEnabled(
false );
328 mViewSourceAction->setEnabled(
false );
329 mCharsetSelect->setEnabled(
false );
330 mCharsetSelectKeyb->setEnabled(
false );
331 mFixedFontToggle->setEnabled(
false );
332 mFancyToggle->setEnabled(
false );
339 mFixedFontToggle->setChecked(
knGlobals.settings()->useFixedFont() );
340 mFancyToggle->setChecked(
knGlobals.settings()->interpretFormatTags() );
342 mShowHtml =
knGlobals.settings()->alwaysShowHTML();
343 mViewer->setOnlyLocalReferences( !
knGlobals.settings()->allowExternalReferences() );
345 KConfigGroup conf(
knGlobals.config(),
"READNEWS" );
346 mAttachmentStyle = conf.readEntry(
"attachmentStyle",
"inline" );
347 mHeaderStyle = conf.readEntry(
"headerStyle",
"fancy" );
348 KToggleAction *ra = 0;
349 ra =
static_cast<KToggleAction*
>( mActionCollection->action( QString(
"view_attachments_%1").arg(mAttachmentStyle) ) );
350 ra->setChecked(
true );
351 ra =
static_cast<KToggleAction*
>( mActionCollection->action( QString(
"view_headers_%1").arg(mHeaderStyle) ) );
352 ra->setChecked(
true );
355 mCSSHelper =
new CSSHelper( mViewer->view() );
365 if ( !mIsMainViewer ) {
369 KConfigGroup conf(
knGlobals.config(),
"READNEWS" );
370 conf.writeEntry(
"attachmentStyle", mAttachmentStyle );
371 conf.writeEntry(
"headerStyle", mHeaderStyle );
373 knGlobals.settings()->setUseFixedFont( mFixedFontToggle->isChecked() );
374 knGlobals.settings()->setInterpretFormatTags( mFancyToggle->isChecked() );
381 mShowHtml =
knGlobals.settings()->alwaysShowHTML();
383 mRot13Toggle->setChecked(
false );
391 if ( mArticle->hasContent() ) {
394 if( !
knGlobals.articleManager()->loadArticle( mArticle ) )
405 void ArticleWidget::clear()
409 mViewer->setUserStyleSheet( mCSSHelper->cssDefinitions( mFixedFontToggle->isChecked() ) );
410 mViewer->write( mCSSHelper->htmlHead( mFixedFontToggle->isChecked() ) );
411 mViewer->write( QString(
"</body></html>") );
416 void ArticleWidget::displayArticle()
424 mViewer->view()->ensureVisible( 0, 0 );
426 if ( !mArticle->hasContent() ) {
427 displayErrorMessage( i18n(
"The article contains no data.") );
431 if ( mForceCharset != mArticle->forceDefaultCharset()
432 || ( mForceCharset && mArticle->defaultCharset() != mOverrideCharset ) ) {
433 mArticle->setDefaultCharset( mOverrideCharset );
434 mArticle->setForceDefaultCharset( mForceCharset );
440 mViewer->setUserStyleSheet( mCSSHelper->cssDefinitions( mFixedFontToggle->isChecked() ) );
441 mViewer->write( mCSSHelper->htmlHead( mFixedFontToggle->isChecked() ) );
448 KMime::Content *
text = 0;
450 foreach ( KMime::Content *c, mArticle->contents() ) {
451 if ( c->contentType()->isHTMLText() && c->contentType()->category() == KMime::Headers::CCalternativePart ) {
458 text = mArticle->textContent();
461 if ( text && !canDecodeText( text->contentType()->charset() ) ) {
462 html += QString(
"<table width=\"100%\" border=\"0\"><tr><td bgcolor=\"#FF0000\">%1</td></tr></table>")
463 .arg( i18n(
"Unknown charset. Default charset is used instead.") );
464 kDebug(5003) <<
"unknown charset =" << text->contentType()->charset();
471 bool containsPGP = Kpgp::Module::prepareMessageForDecryption( mArticle->body(), pgpBlocks, nonPgpBlocks );
473 mViewer->write ( html );
481 codec = KGlobal::charsets()->codecForName( text->contentType()->charset() );
483 codec = KGlobal::locale()->codecForEncoding();
485 for( ; pbit != pgpBlocks.end(); ++pbit, ++npbit ) {
487 QByteArray str = *npbit;
488 if( !str.isEmpty() ) {
489 QStringList lines = codec->toUnicode( str ).split(
'\n' );
490 displayBodyBlock( lines );
493 Kpgp::Block block = *pbit;
494 if ( block.type() == Kpgp::ClearsignedBlock )
496 QStringList lines = codec->toUnicode( block.text() ).split(
'\n' );
497 if ( block.isSigned() ) {
498 QString signClass = displaySigHeader( block );
499 displayBodyBlock( lines );
500 displaySigFooter( signClass );
502 displayBodyBlock( lines );
506 QByteArray str = *npbit;
507 if( !str.isEmpty() ) {
508 QStringList lines = codec->toUnicode( str ).split(
'n' );
509 displayBodyBlock( lines );
513 KMime::Headers::ContentType *ct = mArticle->contentType();
516 mAttachments.clear();
517 mAttachementMap.clear();
518 if( !text || ct->isMultipart() )
519 mAttachments = mArticle->attachments(
knGlobals.settings()->showAlternativeContents() );
522 if(ct->isPartial()) {
523 mViewer->write( i18n(
"<br /><b>This article has the MIME type "message/partial", which KNode cannot handle yet.<br />Meanwhile you can save the article as a text file and reassemble it by hand.</b>") );
527 if ( text && text->hasContent() && !ct->isPartial() ) {
529 if ( text->contentType()->isHTMLText() ) {
530 QString htmlTxt = text->decodedText(
true,
knGlobals.settings()->removeTrailingNewlines() );
533 int i = qMin( htmlTxt.lastIndexOf(
"</html>", -1, Qt::CaseInsensitive ), htmlTxt.lastIndexOf(
"</body>", -1, Qt::CaseInsensitive ) );
535 htmlTxt.truncate( i );
538 html +=
"<div class=\"htmlWarn\">\n";
539 html += i18n(
"<b>Note:</b> This is an HTML message. For "
540 "security reasons, only the raw HTML code "
541 "is shown. If you trust the sender of this "
542 "message then you can activate formatted "
543 "HTML display for this message "
544 "<a href=\"knode:showHTML\">by clicking here</a>.");
545 html +=
"</div><br><br>";
546 html += toHtmlString( htmlTxt );
550 if ( !containsPGP ) {
551 QStringList lines = text->decodedText(
true,
knGlobals.settings()->removeTrailingNewlines() ).split(
'\n' );
552 displayBodyBlock( lines );
557 mViewer->write( html );
560 if( !mAttachments.isEmpty() && !ct->isPartial() ) {
562 foreach ( KMime::Content *var, mAttachments ) {
563 displayAttachment( var, attCnt );
568 mViewer->write( QString(
"</body></html>") );
573 mTimer->start(
knGlobals.settings()->autoMarkSeconds() * 1000 );
577 void ArticleWidget::displayErrorMessage(
const QString &msg )
580 mViewer->setUserStyleSheet( mCSSHelper->cssDefinitions( mFixedFontToggle->isChecked() ) );
581 mViewer->write( mCSSHelper->htmlHead( mFixedFontToggle->isChecked() ) );
582 QString errMsg = msg;
583 mViewer->write( QString(
"<b><font size=\"+1\" color=\"red\">") );
584 mViewer->write( i18n(
"An error occurred.") );
585 mViewer->write( QString(
"</font></b><hr/><br/>") );
586 mViewer->write( errMsg.replace(
'\n',
"<br/>" ) );
587 mViewer->write( QString(
"</body></html>") );
595 void ArticleWidget::displayHeader()
600 if ( mHeaderStyle ==
"all" ) {
601 QByteArray head = mArticle->head();
602 KMime::Headers::Base *header = 0;
604 headerHtml +=
"<div class=\"header\">"
605 "<table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\"> ";
606 while ( !head.isEmpty() ) {
607 header = KMime::HeaderParsing::extractFirstHeader( head );
609 headerHtml+=QString(
"<tr><td align=\"right\" valign=\"top\"><b>%1</b></td><td width=\"100%\">%2</td></tr>" )
610 .arg( toHtmlString( header->type(), None ) +
": " )
611 .arg( toHtmlString( header->asUnicodeString().remove( QRegExp(
"^[^:]+:\\s*" ) ) , ParseURL ) );
615 headerHtml +=
"</table></div>";
617 mViewer->write( headerHtml );
622 KMime::Headers::Base *hb;
624 for ( KNDisplayedHeader::List::Iterator it = dhs.begin(); it != dhs.end(); ++it ) {
626 hb = mArticle->headerByType(dh->
header().toLatin1());
627 if ( !hb || hb->is(
"Subject") || hb->is(
"Organization") )
631 headerHtml +=
"<tr>";
632 if ( mHeaderStyle ==
"fancy" )
633 headerHtml +=
"<th>";
635 headerHtml +=
"<th align=\"right\">";
637 headerHtml +=
":</th><td width=\"100%\">";
640 headerHtml+=
"<tr><td colspan=\"2\">";
642 if ( hb->is(
"From") ) {
643 headerHtml += QString(
"<a href=\"mailto:%1\">%2</a>")
644 .arg( KPIMUtils::extractEmailAddress( hb->asUnicodeString() ) )
645 .arg( toHtmlString( hb->asUnicodeString(), None ) );
646 KMime::Headers::Base *orgHdr = mArticle->headerByType(
"Organization" );
647 if ( orgHdr && !orgHdr->isEmpty() ) {
648 headerHtml +=
" (";
649 headerHtml += toHtmlString( orgHdr->asUnicodeString() );
652 }
else if ( hb->is(
"Date") ) {
653 KMime::Headers::Date *
date=
static_cast<KMime::Headers::Date*
>(hb);
654 headerHtml += toHtmlString( KGlobal::locale()->formatDateTime(date->dateTime().toLocalZone().dateTime(), KLocale::LongDate,
true), None );
655 }
else if ( hb->is(
"Newsgroups") ) {
656 QString groups = hb->asUnicodeString();
657 groups.replace(
',',
", " );
658 headerHtml += toHtmlString( groups, ParseURL );
660 headerHtml += toHtmlString( hb->asUnicodeString(), ParseURL );
662 headerHtml +=
"</td></tr>";
666 if ( mHeaderStyle ==
"standard" ) {
667 mViewer->write( QString(
"<b style=\"font-size:130%\">" + toHtmlString( mArticle->subject()->asUnicodeString() ) +
"</b>") );
668 mViewer->write( QString(
"<div class=\"header\">") );
669 mViewer->write( QString(
"<table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\">") + headerHtml );
670 mViewer->write( QString(
"</table></div>") );
676 KMime::Headers::Base *temp = mArticle->headerByType(
"X-Face");
678 xfhead = temp->asUnicodeString();
680 if ( !xfhead.isEmpty() ) {
681 MessageViewer::KXFace xf;
682 xface = QString::fromLatin1(
"<div class=\"senderpic\"><img src=\"%1\" width=\"48\" height=\"48\"/></div>" )
683 .arg( imgToDataUrl( xf.toImage( xfhead ),
"PNG" ) );
687 QString html =
"<div class=\"fancy header\">";
689 html += toHtmlString( mArticle->subject()->asUnicodeString(), ParseURL | FancyFormatting );
692 html +=
"<table class=\"outer\"><tr><td width=\"100%\"><table>";
694 html +=
"</table></td>";
695 html +=
"<td align=\"center\">" + xface +
"</td>";
696 html +=
"</tr></table>";
699 KMime::Headers::References *refs = mArticle->references(
false );
701 &&
knGlobals.settings()->showRefBar() ) {
702 html +=
"<div class=\"spamheader\">";
703 html += QString(
"<b>%1</b>" ).arg( i18n(
"References:") );
706 for (
int i = 0; i < references.count(); ++i ) {
707 html +=
" <a href=\"news:" + references.at(references.count() - i - 1)
708 +
"\">" + QString::number( i + 1 ) +
"</a>";
713 mViewer->write( html );
717 void ArticleWidget::displayBodyBlock(
const QStringList &lines )
719 int oldLevel = -2, newLevel = -2;
722 QString quoteChars =
knGlobals.settings()->quoteCharacters().simplified();
723 if (quoteChars.isEmpty())
726 for ( QStringList::const_iterator it = lines.constBegin(); it != lines.constEnd(); ++it) {
728 if ( !line.isEmpty() ) {
730 if ( !isSig && line ==
"-- " ) {
733 if ( newLevel != -2 )
735 html += mCSSHelper->nonQuotedFontTag();
737 if (
knGlobals.settings()->showSignature() ) {
738 html +=
"<hr size=\"1\"/>";
746 newLevel = quotingDepth( line, quoteChars );
751 if ( newLevel != oldLevel ) {
752 if ( oldLevel != -2 )
755 if ( newLevel == -1 )
756 html += mCSSHelper->nonQuotedFontTag();
758 html += mCSSHelper->quoteFontTag( newLevel );
761 html += toHtmlString( line, ParseURL | FancyFormatting | AllowROT13 ) +
"<br/>";
764 html += toHtmlString( line, ParseURL | AllowROT13 ) +
"<br/>";
772 if ( newLevel != -2 )
775 mViewer->write( html );
779 QString ArticleWidget::displaySigHeader(
const Kpgp::Block &block )
781 QString signClass =
"signErr";
782 QString signer = block.signatureUserId();
783 QString signerKey = block.signatureKeyId();
785 if ( signer.isEmpty() ) {
786 message = i18n(
"Message was signed with unknown key 0x%1." ,
789 message += i18n(
"The validity of the signature cannot be verified." );
790 signClass =
"signWarn";
793 Kpgp::Module *pgp = Kpgp::Module::getKpgp();
794 Kpgp::Validity keyTrust;
795 if( !signerKey.isEmpty() )
796 keyTrust = pgp->keyTrust( signerKey );
800 keyTrust = pgp->keyTrust( signer );
803 signer = toHtmlString( signer, None );
804 signer =
"<a href=\"mailto:" + KPIMUtils::extractEmailAddress( signer ) +
"\">" + signer +
"</a>";
806 if( !signerKey.isEmpty() )
807 message += i18n(
"Message was signed by %1 (Key ID: 0x%2)." ,
811 message += i18n(
"Message was signed by %1.", signer );
814 if( block.goodSignature() ) {
815 if ( keyTrust < Kpgp::KPGP_VALIDITY_MARGINAL )
816 signClass =
"signOkKeyBad";
818 signClass =
"signOkKeyOk";
820 case Kpgp::KPGP_VALIDITY_UNKNOWN:
821 message += i18n(
"The signature is valid, but the key's "
822 "validity is unknown." );
824 case Kpgp::KPGP_VALIDITY_MARGINAL:
825 message += i18n(
"The signature is valid and the key is "
826 "marginally trusted." );
828 case Kpgp::KPGP_VALIDITY_FULL:
829 message += i18n(
"The signature is valid and the key is "
832 case Kpgp::KPGP_VALIDITY_ULTIMATE:
833 message += i18n(
"The signature is valid and the key is "
834 "ultimately trusted." );
837 message += i18n(
"The signature is valid, but the key is "
841 message += i18n(
"Warning: The signature is bad.");
842 signClass =
"signErr";
846 QString html =
"<table cellspacing=\"1\" cellpadding=\"1\" class=\"" + signClass +
"\">";
847 html +=
"<tr class=\"" + signClass +
"H\"><td>";
849 html +=
"</td></tr><tr class=\"" + signClass +
"B\"><td>";
850 mViewer->write( html );
855 void ArticleWidget::displaySigFooter(
const QString &signClass )
857 QString html =
"</td></tr><tr class=\"" + signClass +
"H\">";
858 html +=
"<td>" + i18n(
"End of signed message" ) +
"</td></tr></table>";
859 mViewer->write( html );
863 void ArticleWidget::displayAttachment( KMime::Content *att,
int partNum )
865 if ( mAttachmentStyle ==
"hide" )
869 KMime::Headers::ContentType *ct = att->contentType();
872 QString label = ct->name();
873 if ( label.isEmpty() )
874 label = i18n(
"unnamed" );
876 if ( label.count(
' ' ) == label.length() )
877 label.replace( QRegExp(
" ", Qt::CaseSensitive, QRegExp::Wildcard ),
"_" );
878 label = toHtmlString( label, None );
881 QString comment = att->contentDescription()->asUnicodeString();
882 comment = toHtmlString( comment, ParseURL | FancyFormatting );
885 KUrl file = writeAttachmentToTempFile( att, partNum );
886 if ( file.isEmpty() ) {
887 href =
"part://" + QString::number( partNum );
890 mAttachementMap[ file.path() ] = partNum;
893 if ( mAttachmentStyle ==
"inline" && inlinePossible( att ) ) {
894 if ( ct->isImage() ) {
895 html +=
"<div><a href=\"" + href +
"\">"
896 "<img src=\"" + href +
"\" border=\"0\"></a>"
897 "</div><div><a href=\"" + href +
"\">" + label +
"</a>"
898 "</div><div>" + comment +
"</div><br>";
901 html +=
"<table cellspacing=\"1\" class=\"textAtm\">"
902 "<tr class=\"textAtmH\"><td>"
903 "<a href=\"" + href +
"\">" + label +
"</a>";
904 if ( !comment.isEmpty() )
905 html +=
"<br>" + comment;
906 html +=
"</td></tr><tr class=\"textAtmB\"><td>";
908 QString tmp = att->decodedText();
913 html += toHtmlString( tmp, ParseURL );
915 html +=
"</td></tr></table>";
918 QByteArray mimetype = ct->mimeType();
919 kAsciiToLower( mimetype.data() );
920 KMimeType::Ptr mimetypePtr = KMimeType::mimeType( mimetype );
921 if(mimetypePtr.isNull()) {
922 mimetypePtr = KMimeType::mimeType(
"text/plain" );
924 QString iconName = mimetypePtr->iconName( QString() );
925 QString iconFile = KIconLoader::global()->iconPath( iconName, KIconLoader::Desktop );
926 html +=
"<div><a href=\"" + href +
"\"><img src=\"" +
927 iconFile +
"\" border=\"0\">" + label +
928 "</a></div><div>" + comment +
"</div><br>";
930 mViewer->write( html );
934 QString ArticleWidget::toHtmlString(
const QString &line,
int flags )
936 int llflags = KPIMUtils::LinkLocator::PreserveSpaces;
937 if ( !(flags & ArticleWidget::ParseURL) )
938 llflags |= KPIMUtils::LinkLocator::IgnoreUrls;
939 if ( mFancyToggle->isChecked() && (flags & ArticleWidget::FancyFormatting) )
940 llflags |= KPIMUtils::LinkLocator::ReplaceSmileys |
941 KPIMUtils::LinkLocator::HighlightText;
943 if ( flags & ArticleWidget::AllowROT13 ) {
945 text = MessageComposer::Util::rot13( line );
947 return KPIMUtils::LinkLocator::convertToHtml( text, llflags );
952 QString ArticleWidget::imgToDataUrl(
const QImage &image,
const char* fmt )
955 QBuffer buffer( &ba );
956 buffer.open( QIODevice::WriteOnly );
957 image.save( &buffer, fmt );
958 return QString::fromLatin1(
"data:image/%1;base64,%2")
959 .arg( fmt ).arg( QString( KCodecs::base64Encode( ba ) ) );
964 int ArticleWidget::quotingDepth(
const QString &line,
const QString "eChars )
967 for (
int i = 0; i < line.length(); ++i ) {
969 if ( line[i].isSpace() )
971 if ( quoteChars.indexOf( line[i] ) != -1 )
980 bool ArticleWidget::inlinePossible( KMime::Content *c )
982 KMime::Headers::ContentType *ct = c->contentType();
983 return ( ct->isText() || ct->isImage() );
987 bool ArticleWidget::canDecodeText(
const QByteArray &charset )
const
989 kDebug( 5003 ) << charset;
990 if ( charset.isEmpty() )
993 KGlobal::charsets()->codecForName( charset, ok );
999 void ArticleWidget::updateContents()
1002 float savedPosition = (float)mViewer->view()->contentsY() / (float)mViewer->view()->contentsHeight();
1003 if ( mArticle && mArticle->hasContent() )
1008 mViewer->view()->setContentsPos( 0, qRound( mViewer->view()->contentsHeight() * savedPosition ) );
1013 KUrl ArticleWidget::writeAttachmentToTempFile( KMime::Content *att,
int partNum )
1016 KTemporaryFile *tempFile =
new KTemporaryFile();
1017 tempFile->setSuffix(
'.' + QString::number( partNum ) );
1019 KUrl file = KUrl( tempFile->fileName() );
1022 if( ::access( QFile::encodeName( file.path() ), W_OK ) != 0 )
1024 if( KDE_mkdir( QFile::encodeName( file.path() ), 0 ) != 0 ||
1025 ::chmod( QFile::encodeName( file.path() ), S_IRWXU ) != 0 )
1028 Q_ASSERT( !file.fileName().isNull() );
1030 mTempDirs.append( file.path() );
1032 KMime::Headers::ContentType* ct = att->contentType();
1033 QString attName = ct->name();
1034 int slashPos = attName.lastIndexOf(
'/' );
1035 if( -1 != slashPos )
1036 attName = attName.mid( slashPos + 1 );
1037 if( attName.isEmpty() )
1038 attName =
"unnamed";
1039 file.addPath( attName );
1041 QByteArray data = att->decodedContent();
1043 if( !KPIMUtils::kByteArrayToFile( data, file.toLocalFile(),
false,
false, false ) )
1046 mTempFiles.append( file.toLocalFile() );
1049 ::chmod( QFile::encodeName( file.toLocalFile() ), S_IRUSR );
1055 void ArticleWidget::removeTempFiles( )
1057 for ( QStringList::Iterator it = mTempFiles.begin(); it != mTempFiles.end(); ++it )
1060 for ( QStringList::ConstIterator it = mTempDirs.constBegin(); it != mTempDirs.constEnd(); ++it )
1061 QDir(*it).rmdir(*it);
1072 KMessageBox::error(
this, i18n(
"An error occurred while downloading the article source:\n%1",
1089 for(
InstanceIterator it = mInstances.constBegin(); it != mInstances.constEnd(); ++it ) {
1090 (*it)->readConfig();
1091 (*it)->updateContents();
1098 for (
InstanceIterator it = mInstances.constBegin(); it != mInstances.constEnd(); ++it )
1099 if ( (*it)->article() ==
article )
1107 for (
InstanceIterator it = mInstances.constBegin(); it != mInstances.constEnd(); ++it )
1108 if ( (*it)->article() ==
article )
1115 for (
InstanceIterator it = mInstances.constBegin(); it != mInstances.constEnd(); ++it )
1116 if ( (*it)->article() ==
article )
1117 (*it)->displayArticle();
1123 for (
InstanceIterator it = mInstances.constBegin(); it != mInstances.constEnd(); ++it )
1124 if ( (*it)->article() ==
article )
1125 (*it)->displayErrorMessage( error );
1131 for (
InstanceIterator it = mInstances.constBegin(); it != mInstances.constEnd(); ++it )
1132 if ( (*it)->article() && (*it)->article()->collection() == coll )
1139 for (
InstanceIterator it = mInstances.constBegin(); it != mInstances.constEnd(); ++it )
1147 const KHTMLView *view = mViewer->view();
1148 return view->contentsY() + view->visibleHeight() >= view->contentsHeight();
1153 mViewer->view()->scrollBy( 0, -10 );
1158 mViewer->view()->scrollBy( 0, 10 );
1163 mViewer->view()->scrollBy( 0, -(
int)(mViewer->view()->height() * 0.8) );
1168 mViewer->view()->scrollBy( 0, (
int)(mViewer->view()->height() * 0.8) );
1173 void ArticleWidget::slotURLClicked(
const KUrl &url,
bool forceOpen)
1176 if ( url.protocol() ==
"knode" ) {
1177 if ( url.path() ==
"showHTML" ) {
1184 if ( url.protocol() ==
"mailto" ) {
1185 KMime::Types::Mailbox addr;
1186 addr.fromUnicodeString( url.path() );
1191 if ( url.protocol() ==
"news" ) {
1192 kDebug( 5003 ) << url;
1198 if ( url.protocol() ==
"file" || url.protocol() ==
"part" ) {
1199 if ( url.protocol() ==
"file" ) {
1200 if ( !mAttachementMap.contains( url.path() ) )
1202 partNum = mAttachementMap[url.path()];
1204 if ( url.protocol() ==
"part" )
1205 partNum = url.path().toInt();
1206 KMime::Content *c = mAttachments.at( partNum );
1210 if ( forceOpen ||
knGlobals.settings()->openAttachmentsOnClick() )
1211 knGlobals.articleManager()->openContent( c );
1213 knGlobals.articleManager()->saveContentToFile( c,
this );
1217 new KRun( url,
this );
1221 void ArticleWidget::slotURLPopup(
const QString &url,
const QPoint &point )
1223 mCurrentURL = KUrl( url );
1225 if ( url.isEmpty() )
1226 popupName =
"body_popup";
1227 else if ( mCurrentURL.protocol() ==
"mailto" )
1228 popupName =
"mailto_popup";
1229 else if ( mCurrentURL.protocol() ==
"file" || mCurrentURL.protocol() ==
"part" )
1230 popupName =
"attachment_popup";
1232 else if ( mCurrentURL.protocol() ==
"knode" )
1235 popupName =
"url_popup";
1236 QMenu *popup =
static_cast<QMenu*
>( mGuiClient->factory()->container( popupName, mGuiClient ) );
1238 popup->popup( point );
1243 void ArticleWidget::slotTimeout()
1247 l.append( boost::static_pointer_cast<KNRemoteArticle>( mArticle ) );
1248 knGlobals.articleManager()->setRead( l,
true );
1254 void ArticleWidget::slotSave()
1257 knGlobals.articleManager()->saveArticleToFile( mArticle,
this );
1260 void ArticleWidget::slotPrint( )
1263 mViewer->view()->print();
1267 void ArticleWidget::slotCopySelection( )
1269 QApplication::clipboard()->setText( mViewer->selectedText() );
1273 void ArticleWidget::slotSelectAll()
1275 mViewer->selectAll();
1279 void ArticleWidget::slotFind()
1281 mViewer->findText();
1285 void ArticleWidget::slotViewSource()
1288 if ( mArticle && mArticle->type() ==
KNArticle::ATlocal && mArticle->hasContent() ) {
1295 a->messageID(
true )->from7BitString( mArticle->messageID()->as7BitString(
false ) );
1296 a->lines(
true )->from7BitString( mArticle->lines(
true )->as7BitString(
false ) );
1297 a->setArticleNumber( boost::static_pointer_cast<KNRemoteArticle>( mArticle )->articleNumber() );
1304 void ArticleWidget::slotReply()
1308 mViewer->selectedText(),
true, false );
1312 void ArticleWidget::slotRemail()
1316 mViewer->selectedText(),
false, true );
1320 void ArticleWidget::slotForward()
1326 void ArticleWidget::slotCancel()
1332 void ArticleWidget::slotSupersede()
1338 void ArticleWidget::slotToggleFixedFont()
1345 void ArticleWidget::slotToggleFancyFormating( )
1352 void ArticleWidget::slotFancyHeaders()
1354 mHeaderStyle =
"fancy";
1359 void ArticleWidget::slotStandardHeaders()
1361 mHeaderStyle =
"standard";
1366 void ArticleWidget::slotAllHeaders()
1368 mHeaderStyle =
"all";
1374 void ArticleWidget::slotIconAttachments()
1376 mAttachmentStyle =
"icon";
1381 void ArticleWidget::slotInlineAttachments()
1383 mAttachmentStyle =
"inline";
1388 void ArticleWidget::slotHideAttachments()
1390 mAttachmentStyle =
"hide";
1396 void ArticleWidget::slotToggleRot13()
1404 void ArticleWidget::slotSetCharset(
const QString &charset )
1406 if ( charset.isEmpty() )
1409 if ( charset == i18nc(
"@item default character set",
"Default") ) {
1410 mForceCharset =
false;
1411 mOverrideCharset = KMime::Headers::Latin1;
1413 mForceCharset =
true;
1414 mOverrideCharset = KGlobal::charsets()->encodingForName( charset ).toLatin1();
1417 if ( mArticle && mArticle->hasContent() ) {
1418 mArticle->setDefaultCharset( mOverrideCharset );
1419 mArticle->setForceDefaultCharset( mForceCharset );
1425 void ArticleWidget::slotSetCharsetKeyboard( )
1428 mCharsetSelect->items(), mCharsetSelect->currentItem() );
1429 if ( charset != -1 ) {
1430 mCharsetSelect->setCurrentItem( charset );
1431 slotSetCharset( mCharsetSelect->items()[charset] );
1437 void ArticleWidget::slotOpenURL()
1439 slotURLClicked( mCurrentURL );
1442 void ArticleWidget::slotCopyURL()
1445 if ( mCurrentURL.protocol() ==
"mailto" )
1446 address = mCurrentURL.path();
1448 address = mCurrentURL.url();
1449 QApplication::clipboard()->setText( address, QClipboard::Clipboard );
1450 QApplication::clipboard()->setText( address, QClipboard::Selection );
1453 void ArticleWidget::slotAddBookmark()
1455 if ( mCurrentURL.isEmpty() )
1457 QString filename = KStandardDirs::locateLocal(
"data", QString::fromLatin1(
"konqueror/bookmarks.xml") );
1458 KBookmarkManager *bookManager = KBookmarkManager::managerForFile( filename,
"konqueror" );
1459 KBookmarkGroup group = bookManager->root();
1460 group.addBookmark( mCurrentURL.url(), mCurrentURL );
1461 bookManager->save();
1464 void ArticleWidget::slotAddToAddressBook()
1470 void ArticleWidget::slotOpenInAddressBook()
1476 void ArticleWidget::slotOpenAttachment()
1478 slotURLClicked( mCurrentURL,
true );
1481 void ArticleWidget::slotSaveAttachment()
1483 if ( mCurrentURL.protocol() !=
"file" && mCurrentURL.protocol() !=
"part" )
1486 if ( mCurrentURL.protocol() ==
"file" ) {
1487 if ( !mAttachementMap.contains( mCurrentURL.path() ) )
1489 partNum = mAttachementMap[mCurrentURL.path()];
1491 if ( mCurrentURL.protocol() ==
"part" )
1492 partNum = mCurrentURL.path().toInt();
1493 KMime::Content *c = mAttachments.at( partNum );
1496 knGlobals.articleManager()->saveContentToFile( c,
this );
1499 #include "articlewidget.moc"
void readConfig()
read config settings
void setArticle(KNArticle::Ptr article)
display the given article
Downloads one specific article from the news server.
static void collectionRemoved(KNArticleCollection::Ptr coll)
notify all instances that the given collection has been removed
void writeConfig()
write config settings (call only for the main viewer)
static QStringList encodings()
Returns a list of encodings along with a description.
void emitJob(KNJobData *j)
Send the job to the scheduler and append it to the job queue.
void createReply(KNRemoteArticle::Ptr a, const QString &selectedText=QString(), bool post=true, bool mail=false)
void scrollPrior()
scroll up by one page
virtual QByteArray text(quint32 serialNumber) const =0
Abstract base class for all KNode internal jobs.
QList< ArticleWidget * >::ConstIterator InstanceIterator
static KNGlobals * self()
Return the KNGlobals instance.
~ArticleWidget()
Destroy the article widget.
Representation of a news group.
void scrollNext()
scroll down by one page
void createForward(KNArticle::Ptr a)
Helper class to generate a CSS style sheet for article widget.
void scrollDown()
scroll down by one line
void processJob(KNJobData *j)
process download jobs for view source action
Article source view window.
bool atBottom() const
checks whether the readers is scrolled down to the bottom
KNArticleFactory * articleFactory()
Returns the article factory.
KNJobItem::Ptr data() const
void scrollUp()
scroll up by one line
static void configChanged()
notify all instances about a config change
bool canceled() const
Returns true if the job has been canceled by the user.
void createMail(KMime::Types::Mailbox *address)
static bool articleVisible(KNArticle::Ptr article)
check whether the given article is displayed in any instance
void createSupersede(KNArticle::Ptr a)
QString errorString() const
Returns the error message.
ArticleWidget(QWidget *parent, KXMLGUIClient *guiClient, KActionCollection *actionCollection, bool isMainViewer=false)
Construct a new article widget.
static void articleRemoved(KNArticle::Ptr article)
notify all instances that the given article has been removed
static void cleanup()
cleanup all instances
boost::shared_ptr< KNRemoteArticle > Ptr
Shared pointer to a KNRemoteArticle. To be used instead of raw KNRemoteArticle*.
void createCancel(KNArticle::Ptr a)
boost::shared_ptr< KNArticle > Ptr
Shared pointer to a KNArticle. To be used instead of raw KNArticle*.
bool success() const
Returns true if the job finished successfully.
static void articleLoadError(KNArticle::Ptr article, const QString &error)
notify all instances about an error during loading the given article
static int selectDialog(QWidget *parent, const QString &caption, const QStringList &options, int initialValue)
list selection dialog, used instead of a popup menu when a select action is called via the keyboard...
boost::shared_ptr< KNArticleCollection > Ptr
Shared pointer to a KNArticle.
KNRemoteArticle represents an article, whos body has to be retrieved from a remote host or from the l...
static void articleChanged(KNArticle::Ptr article)
notify all instances that the given article has changed
#define knGlobals
Keep compatibility with the old way.
KNArticle::Ptr article() const
returns the currently shown article
boost::shared_ptr< KNGroup > Ptr
Shared pointer to a KNGroup.