• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdepim API Reference
  • KDE Home
  • Contact Us
 

knode

  • sources
  • kde-4.12
  • kdepim
  • knode
articlewidget.cpp
Go to the documentation of this file.
1 /*
2  KNode, the KDE newsreader
3  Copyright (c) 2005-2006 Volker Krause <vkrause@kde.org>
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  You should have received a copy of the GNU General Public License
10  along with this program; if not, write to the Free Software Foundation,
11  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US
12 */
13 
14 #include "articlewidget.h"
15 
16 #include "utils/locale.h"
17 
18 
19 #include <QBuffer>
20 #include <QClipboard>
21 #include <QDir>
22 #include <QFile>
23 #include <QImage>
24 #include <QMenu>
25 #include <QStringList>
26 #include <QTextCodec>
27 #include <QTimer>
28 #include <QHBoxLayout>
29 
30 #include <kaction.h>
31 #include <kactioncollection.h>
32 #include <kactionmenu.h>
33 #include <kascii.h>
34 #include <kbookmarkmanager.h>
35 #include <kcharsets.h>
36 #include <khtml_part.h>
37 #include <khtmlview.h>
38 #include <kiconloader.h>
39 #include <klocale.h>
40 #include <kcodecs.h>
41 #include <kmessagebox.h>
42 #include <kmimetype.h>
43 #include <krun.h>
44 #include <kselectaction.h>
45 #include <kstandarddirs.h>
46 #include <kstandardaction.h>
47 #include <ktemporaryfile.h>
48 #include <ktoggleaction.h>
49 #include <kurl.h>
50 #include <kxmlguifactory.h>
51 #include <kicon.h>
52 #include <kde_file.h>
53 
54 #include <libkdepim/job/addemailaddressjob.h>
55 #include <libkdepim/job/openemailaddressjob.h>
56 
57 #include <libkpgp/kpgp.h>
58 #include <libkpgp/kpgpblock.h>
59 
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>
65 
66 #include "csshelper.h"
67 #include "knarticle.h"
68 #include "knarticlecollection.h"
69 #include "knarticlefactory.h"
70 #include "knarticlemanager.h"
71 #include "knconfig.h"
72 #include "knconfigmanager.h"
73 #include "kndisplayedheader.h"
74 #include "knfolder.h"
75 #include "knfoldermanager.h"
76 #include "knglobals.h"
77 #include "kngroup.h"
78 #include "knmainwidget.h"
79 #include "knnntpaccount.h"
80 #include "knsourceviewwindow.h"
81 #include "nntpjobs.h"
82 #include "settings.h"
83 
84 using namespace KNode;
85 
86 QList<ArticleWidget*> ArticleWidget::mInstances;
87 
88 ArticleWidget::ArticleWidget( QWidget *parent,
89  KXMLGUIClient *guiClient,
90  KActionCollection *actionCollection, bool isMainViewer ) :
91  QWidget( parent ),
92  mViewer( 0 ),
93  mCSSHelper( 0 ),
94  mHeaderStyle( "fancy" ),
95  mAttachmentStyle( "inline" ),
96  mShowHtml( false ),
97  mRot13( false ),
98  mForceCharset( false ),
99  mOverrideCharset( KMime::Headers::Latin1 ),
100  mTimer( 0 ),
101  mIsMainViewer( isMainViewer ),
102  mGuiClient( guiClient ),
103  mActionCollection( actionCollection )
104 {
105  mInstances.append( this );
106 
107  QHBoxLayout *box = new QHBoxLayout( this );
108  box->setMargin( 0 );
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)) );
123 
124  mTimer = new QTimer( this );
125  mTimer->setSingleShot( true );
126  connect( mTimer, SIGNAL(timeout()), SLOT(slotTimeout()) );
127 
128  initActions();
129  readConfig();
130  clear();
131 }
132 
133 
134 ArticleWidget::~ArticleWidget()
135 {
136  mInstances.removeAll( this );
137  delete mTimer;
138  delete mCSSHelper;
139  if ( mArticle && mArticle->isOrphant() ) {
140  // if the article manager is still loading the current article,
141  // cancel the job.
142  knGlobals.articleManager()->cancelJobs( mArticle );
143  }
144  removeTempFiles();
145 }
146 
147 
148 void ArticleWidget::initActions()
149 {
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 );
196 
197  QActionGroup *ag = new QActionGroup( this );
198  KToggleAction *ra;
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()));
209  ag->addAction( ra );
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()));
214  ag->addAction( ra );
215  mHeaderStyleMenu->addAction( ra );
216 
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()));
223  ag->addAction( ra );
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()));
228  ag->addAction( ra );
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()));
233  ag->addAction( ra );
234  mAttachmentStyleMenu->addAction( ra );
235 
236  mCharsetSelect = mActionCollection->add<KSelectAction>("set_charset");
237  mCharsetSelect->setText( i18n( "Set chars&et" ) );
238  mCharsetSelect->setShortcutConfigurable( false );
239  QStringList cs = Utilities::Locale::encodings();
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));
248 
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()));
275 }
276 
277 
278 
279 void ArticleWidget::enableActions()
280 {
281  if ( !mArticle ) {
282  disableActions();
283  return;
284  }
285 
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 );
300 
301  // only valid for remote articles
302  bool enabled = ( mArticle->type() == KNArticle::ATremote );
303  mReplyAction->setEnabled( enabled );
304  mRemailAction->setEnabled( enabled );
305 
306  enabled = ( mArticle->type() == KNArticle::ATremote
307  || mArticle->collection() == knGlobals.folderManager()->sent() );
308  mCancelAction->setEnabled( enabled );
309  mSupersedeAction->setEnabled( enabled );
310 }
311 
312 
313 void ArticleWidget::disableActions()
314 {
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 );
333 }
334 
335 
336 
337 void ArticleWidget::readConfig()
338 {
339  mFixedFontToggle->setChecked( knGlobals.settings()->useFixedFont() );
340  mFancyToggle->setChecked( knGlobals.settings()->interpretFormatTags() );
341 
342  mShowHtml = knGlobals.settings()->alwaysShowHTML();
343  mViewer->setOnlyLocalReferences( !knGlobals.settings()->allowExternalReferences() );
344 
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 );
353 
354  delete mCSSHelper;
355  mCSSHelper = new CSSHelper( mViewer->view() );
356 
357  if ( !knGlobals.settings()->autoMark() )
358  mTimer->stop();
359 }
360 
361 
362 void ArticleWidget::writeConfig()
363 {
364  // main viewer determines the settings
365  if ( !mIsMainViewer ) {
366  return;
367  }
368 
369  KConfigGroup conf(knGlobals.config(), "READNEWS" );
370  conf.writeEntry( "attachmentStyle", mAttachmentStyle );
371  conf.writeEntry( "headerStyle", mHeaderStyle );
372 
373  knGlobals.settings()->setUseFixedFont( mFixedFontToggle->isChecked() );
374  knGlobals.settings()->setInterpretFormatTags( mFancyToggle->isChecked() );
375 }
376 
377 
378 
379 void ArticleWidget::setArticle( KNArticle::Ptr article )
380 {
381  mShowHtml = knGlobals.settings()->alwaysShowHTML();
382  mRot13 = false;
383  mRot13Toggle->setChecked( false );
384  mTimer->stop();
385 
386  mArticle = article;
387 
388  if ( !mArticle )
389  clear();
390  else {
391  if ( mArticle->hasContent() ) { // article is already loaded => just show it
392  displayArticle();
393  } else {
394  if( !knGlobals.articleManager()->loadArticle( mArticle ) )
395  articleLoadError( mArticle, i18n("Unable to load the article.") );
396  else
397  // try again for local articles
398  if( mArticle->hasContent() && !( mArticle->type() == KNArticle::ATremote ) )
399  displayArticle();
400  }
401  }
402 }
403 
404 
405 void ArticleWidget::clear()
406 {
407  disableActions();
408  mViewer->begin();
409  mViewer->setUserStyleSheet( mCSSHelper->cssDefinitions( mFixedFontToggle->isChecked() ) );
410  mViewer->write( mCSSHelper->htmlHead( mFixedFontToggle->isChecked() ) );
411  mViewer->write( QString("</body></html>") );
412  mViewer->end();
413 }
414 
415 
416 void ArticleWidget::displayArticle()
417 {
418  if ( !mArticle) {
419  clear();
420  return;
421  }
422 
423  // scroll back to top
424  mViewer->view()->ensureVisible( 0, 0 );
425 
426  if ( !mArticle->hasContent() ) {
427  displayErrorMessage( i18n("The article contains no data.") );
428  return;
429  }
430 
431  if ( mForceCharset != mArticle->forceDefaultCharset()
432  || ( mForceCharset && mArticle->defaultCharset() != mOverrideCharset ) ) {
433  mArticle->setDefaultCharset( mOverrideCharset );
434  mArticle->setForceDefaultCharset( mForceCharset );
435  }
436 
437  removeTempFiles();
438 
439  mViewer->begin();
440  mViewer->setUserStyleSheet( mCSSHelper->cssDefinitions( mFixedFontToggle->isChecked() ) );
441  mViewer->write( mCSSHelper->htmlHead( mFixedFontToggle->isChecked() ) );
442 
443  // headers
444  displayHeader();
445 
446  // body
447  QString html;
448  KMime::Content *text = 0;
449  if ( mShowHtml ) {
450  foreach ( KMime::Content *c, mArticle->contents() ) {
451  if ( c->contentType()->isHTMLText() && c->contentType()->category() == KMime::Headers::CCalternativePart ) {
452  text = c;
453  break;
454  }
455  }
456  }
457  if ( !text )
458  text = mArticle->textContent();
459 
460  // check if codec is available
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();
465  }
466 
467  // if the article is pgp signed and the user asked for verifying the
468  // signature, we show a nice header:
469  QList<Kpgp::Block> pgpBlocks;
470  QList<QByteArray> nonPgpBlocks;
471  bool containsPGP = Kpgp::Module::prepareMessageForDecryption( mArticle->body(), pgpBlocks, nonPgpBlocks );
472 
473  mViewer->write ( html );
474  html.clear();
475 
476  if ( containsPGP ) {
477  QList<Kpgp::Block>::Iterator pbit = pgpBlocks.begin();
478  QList<QByteArray>::Iterator npbit = nonPgpBlocks.begin();
479  QTextCodec *codec;
480  if ( text )
481  codec = KGlobal::charsets()->codecForName( text->contentType()->charset() );
482  else
483  codec = KGlobal::locale()->codecForEncoding();
484 
485  for( ; pbit != pgpBlocks.end(); ++pbit, ++npbit ) {
486  // handle non-pgp block
487  QByteArray str = *npbit;
488  if( !str.isEmpty() ) {
489  QStringList lines = codec->toUnicode( str ).split( '\n' );
490  displayBodyBlock( lines );
491  }
492  // handle pgp block
493  Kpgp::Block block = *pbit;
494  if ( block.type() == Kpgp::ClearsignedBlock )
495  block.verify();
496  QStringList lines = codec->toUnicode( block.text() ).split( '\n' );
497  if ( block.isSigned() ) {
498  QString signClass = displaySigHeader( block );
499  displayBodyBlock( lines );
500  displaySigFooter( signClass );
501  } else {
502  displayBodyBlock( lines );
503  }
504  }
505  // deal with the last non-pgp block
506  QByteArray str = *npbit;
507  if( !str.isEmpty() ) {
508  QStringList lines = codec->toUnicode( str ).split( 'n' );
509  displayBodyBlock( lines );
510  }
511  }
512 
513  KMime::Headers::ContentType *ct = mArticle->contentType();
514 
515  // get attachments
516  mAttachments.clear();
517  mAttachementMap.clear();
518  if( !text || ct->isMultipart() )
519  mAttachments = mArticle->attachments( knGlobals.settings()->showAlternativeContents() );
520 
521  // partial message
522  if(ct->isPartial()) {
523  mViewer->write( i18n("<br /><b>This article has the MIME type &quot;message/partial&quot;, which KNode cannot handle yet.<br />Meanwhile you can save the article as a text file and reassemble it by hand.</b>") );
524  }
525 
526  // display body text
527  if ( text && text->hasContent() && !ct->isPartial() ) {
528  // handle HTML messages
529  if ( text->contentType()->isHTMLText() ) {
530  QString htmlTxt = text->decodedText( true, knGlobals.settings()->removeTrailingNewlines() );
531  if ( mShowHtml ) {
532  // strip </html> & </body>
533  int i = qMin( htmlTxt.lastIndexOf( "</html>", -1, Qt::CaseInsensitive ), htmlTxt.lastIndexOf( "</body>", -1, Qt::CaseInsensitive ) );
534  if ( i >= 0 )
535  htmlTxt.truncate( i );
536  html += htmlTxt;
537  } else {
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 );
547  }
548  }
549  else {
550  if ( !containsPGP ) {
551  QStringList lines = text->decodedText( true, knGlobals.settings()->removeTrailingNewlines() ).split( '\n' );
552  displayBodyBlock( lines );
553  }
554  }
555 
556  }
557  mViewer->write( html );
558 
559  // display attachments
560  if( !mAttachments.isEmpty() && !ct->isPartial() ) {
561  int attCnt = 0;
562  foreach ( KMime::Content *var, mAttachments ) {
563  displayAttachment( var, attCnt );
564  attCnt++;
565  }
566  }
567 
568  mViewer->write( QString("</body></html>") );
569  mViewer->end();
570 
571  enableActions();
572  if( mArticle->type() == KNArticle::ATremote && knGlobals.settings()->autoMark() )
573  mTimer->start( knGlobals.settings()->autoMarkSeconds() * 1000 );
574 }
575 
576 
577 void ArticleWidget::displayErrorMessage( const QString &msg )
578 {
579  mViewer->begin();
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>") );
588  mViewer->end();
589 
590  disableActions();
591 }
592 
593 
594 
595 void ArticleWidget::displayHeader()
596 {
597  QString headerHtml;
598 
599  // full header style
600  if ( mHeaderStyle == "all" ) {
601  QByteArray head = mArticle->head();
602  KMime::Headers::Base *header = 0;
603 
604  headerHtml += "<div class=\"header\">"
605  "<table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\"> ";
606  while ( !head.isEmpty() ) {
607  header = KMime::HeaderParsing::extractFirstHeader( head );
608  if ( header ) {
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 ) );
612  delete header;
613  }
614  }
615  headerHtml += "</table></div>";
616 
617  mViewer->write( headerHtml );
618  return;
619  }
620 
621  // standard & fancy header style
622  KMime::Headers::Base *hb;
623  KNDisplayedHeader::List dhs = knGlobals.configManager()->displayedHeaders()->headers();
624  for ( KNDisplayedHeader::List::Iterator it = dhs.begin(); it != dhs.end(); ++it ) {
625  KNDisplayedHeader *dh = (*it);
626  hb = mArticle->headerByType(dh->header().toLatin1());
627  if ( !hb || hb->is("Subject") || hb->is("Organization") )
628  continue;
629 
630  if ( dh->hasName() ) {
631  headerHtml += "<tr>";
632  if ( mHeaderStyle == "fancy" )
633  headerHtml += "<th>";
634  else
635  headerHtml += "<th align=\"right\">";
636  headerHtml += toHtmlString( dh->translatedName(), None );
637  headerHtml += ":</th><td width=\"100%\">";
638  }
639  else
640  headerHtml+="<tr><td colspan=\"2\">";
641 
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 += "&nbsp;&nbsp;(";
649  headerHtml += toHtmlString( orgHdr->asUnicodeString() );
650  headerHtml += ')';
651  }
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 );
659  } else
660  headerHtml += toHtmlString( hb->asUnicodeString(), ParseURL );
661 
662  headerHtml += "</td></tr>";
663  }
664 
665  // standard header style
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>") );
671  return;
672  }
673 
674  // X-Face support
675  QString xfhead;
676  KMime::Headers::Base *temp = mArticle->headerByType("X-Face");
677  if (temp)
678  xfhead = temp->asUnicodeString();
679  QString xface = "";
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" ) );
684  }
685 
686  // fancy header style
687  QString html = "<div class=\"fancy header\">";
688  html += "<div>";
689  html += toHtmlString( mArticle->subject()->asUnicodeString(), ParseURL | FancyFormatting );
690  html += "</div>";
691 
692  html += "<table class=\"outer\"><tr><td width=\"100%\"><table>";
693  html += headerHtml;
694  html += "</table></td>";
695  html += "<td align=\"center\">" + xface + "</td>";
696  html += "</tr></table>";
697 
698  // references
699  KMime::Headers::References *refs = mArticle->references( false );
700  if ( mArticle->type() == KNArticle::ATremote && refs
701  && knGlobals.settings()->showRefBar() ) {
702  html += "<div class=\"spamheader\">";
703  html += QString( "<b>%1</b>" ).arg( i18n("References:") );
704 
705  QList<QByteArray> references = refs->identifiers();
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>";
709  }
710  html += "</div>";
711  }
712  html += "</div>";
713  mViewer->write( html );
714 }
715 
716 
717 void ArticleWidget::displayBodyBlock( const QStringList &lines )
718 {
719  int oldLevel = -2, newLevel = -2;
720  bool isSig = false;
721  QString line, html;
722  QString quoteChars = knGlobals.settings()->quoteCharacters().simplified();
723  if (quoteChars.isEmpty())
724  quoteChars = '>';
725 
726  for ( QStringList::const_iterator it = lines.constBegin(); it != lines.constEnd(); ++it) {
727  line = (*it);
728  if ( !line.isEmpty() ) {
729  // signature found
730  if ( !isSig && line == "-- " ) {
731  isSig = true;
732  // close previous body tag (if any) and open new one
733  if ( newLevel != -2 )
734  html += "</div>";
735  html += mCSSHelper->nonQuotedFontTag();
736  newLevel = -1;
737  if ( knGlobals.settings()->showSignature() ) {
738  html += "<hr size=\"1\"/>";
739  continue;
740  }
741  else break;
742  }
743  // look for quoting characters
744  if ( !isSig ) {
745  oldLevel = newLevel;
746  newLevel = quotingDepth( line, quoteChars );
747  if ( newLevel >= 3 )
748  newLevel = 2; // no more than three levels supported (0-2)
749 
750  // quoting level changed
751  if ( newLevel != oldLevel ) {
752  if ( oldLevel != -2 )
753  html += "</div>"; // close previous level
754  // open new level
755  if ( newLevel == -1 )
756  html += mCSSHelper->nonQuotedFontTag();
757  else
758  html += mCSSHelper->quoteFontTag( newLevel );
759  }
760  // output the actual line
761  html += toHtmlString( line, ParseURL | FancyFormatting | AllowROT13 ) + "<br/>";
762  } else {
763  // signature
764  html += toHtmlString( line, ParseURL | AllowROT13 ) + "<br/>";
765  }
766  } else {
767  // empty line
768  html += "<br/>";
769  }
770  }
771  // close body quoting level tags
772  if ( newLevel != -2 )
773  html += "</div>";
774 
775  mViewer->write( html );
776 }
777 
778 
779 QString ArticleWidget::displaySigHeader( const Kpgp::Block &block )
780 {
781  QString signClass = "signErr";
782  QString signer = block.signatureUserId();
783  QString signerKey = block.signatureKeyId();
784  QString message;
785  if ( signer.isEmpty() ) {
786  message = i18n( "Message was signed with unknown key 0x%1." ,
787  signerKey );
788  message += "<br/>";
789  message += i18n( "The validity of the signature cannot be verified." );
790  signClass = "signWarn";
791  } else {
792  // determine the validity of the key
793  Kpgp::Module *pgp = Kpgp::Module::getKpgp();
794  Kpgp::Validity keyTrust;
795  if( !signerKey.isEmpty() )
796  keyTrust = pgp->keyTrust( signerKey );
797  else
798  // This is needed for the PGP 6 support because PGP 6 doesn't
799  // print the key id of the signing key if the key is known.
800  keyTrust = pgp->keyTrust( signer );
801 
802  // HTMLize the signer's user id and create mailto: link
803  signer = toHtmlString( signer, None );
804  signer = "<a href=\"mailto:" + KPIMUtils::extractEmailAddress( signer ) + "\">" + signer + "</a>";
805 
806  if( !signerKey.isEmpty() )
807  message += i18n( "Message was signed by %1 (Key ID: 0x%2)." ,
808  signer ,
809  signerKey );
810  else
811  message += i18n( "Message was signed by %1.", signer );
812  message += "<br/>";
813 
814  if( block.goodSignature() ) {
815  if ( keyTrust < Kpgp::KPGP_VALIDITY_MARGINAL )
816  signClass = "signOkKeyBad";
817  else
818  signClass = "signOkKeyOk";
819  switch( keyTrust ) {
820  case Kpgp::KPGP_VALIDITY_UNKNOWN:
821  message += i18n( "The signature is valid, but the key's "
822  "validity is unknown." );
823  break;
824  case Kpgp::KPGP_VALIDITY_MARGINAL:
825  message += i18n( "The signature is valid and the key is "
826  "marginally trusted." );
827  break;
828  case Kpgp::KPGP_VALIDITY_FULL:
829  message += i18n( "The signature is valid and the key is "
830  "fully trusted." );
831  break;
832  case Kpgp::KPGP_VALIDITY_ULTIMATE:
833  message += i18n( "The signature is valid and the key is "
834  "ultimately trusted." );
835  break;
836  default:
837  message += i18n( "The signature is valid, but the key is "
838  "untrusted." );
839  }
840  } else {
841  message += i18n("Warning: The signature is bad.");
842  signClass = "signErr";
843  }
844  }
845 
846  QString html = "<table cellspacing=\"1\" cellpadding=\"1\" class=\"" + signClass + "\">";
847  html += "<tr class=\"" + signClass + "H\"><td>";
848  html += message;
849  html += "</td></tr><tr class=\"" + signClass + "B\"><td>";
850  mViewer->write( html );
851  return signClass;
852 }
853 
854 
855 void ArticleWidget::displaySigFooter( const QString &signClass )
856 {
857  QString html = "</td></tr><tr class=\"" + signClass + "H\">";
858  html += "<td>" + i18n( "End of signed message" ) + "</td></tr></table>";
859  mViewer->write( html );
860 }
861 
862 
863 void ArticleWidget::displayAttachment( KMime::Content *att, int partNum )
864 {
865  if ( mAttachmentStyle == "hide" )
866  return;
867 
868  QString html;
869  KMime::Headers::ContentType *ct = att->contentType();
870 
871  // attachment label
872  QString label = ct->name();
873  if ( label.isEmpty() )
874  label = i18n("unnamed" );
875  // if label consists of only whitespace replace them by underscores
876  if ( label.count( ' ' ) == label.length() )
877  label.replace( QRegExp( " ", Qt::CaseSensitive, QRegExp::Wildcard ), "_" );
878  label = toHtmlString( label, None );
879 
880  // attachment comment
881  QString comment = att->contentDescription()->asUnicodeString();
882  comment = toHtmlString( comment, ParseURL | FancyFormatting );
883 
884  QString href;
885  KUrl file = writeAttachmentToTempFile( att, partNum );
886  if ( file.isEmpty() ) {
887  href = "part://" + QString::number( partNum );
888  } else {
889  href = file.url();
890  mAttachementMap[ file.path() ] = partNum;
891  }
892 
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>";
899  } else { //text
900  // frame
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>";
907  // content
908  QString tmp = att->decodedText();
909  /*if( ct->isHTMLText() )
910  // ### to dangerous, we should use the same stuff as for the main text here
911  html += tmp;
912  else*/
913  html += toHtmlString( tmp, ParseURL );
914  // finish frame
915  html += "</td></tr></table>";
916  }
917  } else { // icon
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" );
923  }
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>";
929  }
930  mViewer->write( html );
931 }
932 
933 
934 QString ArticleWidget::toHtmlString( const QString &line, int flags )
935 {
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;
942  QString text = line;
943  if ( flags & ArticleWidget::AllowROT13 ) {
944  if ( mRot13 )
945  text = MessageComposer::Util::rot13( line );
946  }
947  return KPIMUtils::LinkLocator::convertToHtml( text, llflags );
948 }
949 
950 
951 // from KMail headerstyle.cpp
952 QString ArticleWidget::imgToDataUrl( const QImage &image, const char* fmt )
953 {
954  QByteArray ba;
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 ) ) );
960 }
961 
962 
963 
964 int ArticleWidget::quotingDepth( const QString &line, const QString &quoteChars )
965 {
966  int level = -1;
967  for ( int i = 0; i < line.length(); ++i ) {
968  // skip spaces
969  if ( line[i].isSpace() )
970  continue;
971  if ( quoteChars.indexOf( line[i] ) != -1 )
972  ++level;
973  else
974  break;
975  }
976  return level;
977 }
978 
979 
980 bool ArticleWidget::inlinePossible( KMime::Content *c )
981 {
982  KMime::Headers::ContentType *ct = c->contentType();
983  return ( ct->isText() || ct->isImage() );
984 }
985 
986 
987 bool ArticleWidget::canDecodeText( const QByteArray &charset ) const
988 {
989  kDebug( 5003 ) << charset;
990  if ( charset.isEmpty() )
991  return false;
992  bool ok = true;
993  KGlobal::charsets()->codecForName( charset, ok );
994  return ok;
995 }
996 
997 
998 
999 void ArticleWidget::updateContents()
1000 {
1001  // save current scrollbar position
1002  float savedPosition = (float)mViewer->view()->contentsY() / (float)mViewer->view()->contentsHeight();
1003  if ( mArticle && mArticle->hasContent() )
1004  displayArticle();
1005  else
1006  clear();
1007  // restore scrollbar position
1008  mViewer->view()->setContentsPos( 0, qRound( mViewer->view()->contentsHeight() * savedPosition ) );
1009 }
1010 
1011 
1012 
1013 KUrl ArticleWidget::writeAttachmentToTempFile( KMime::Content *att, int partNum )
1014 {
1015  // more or less KMail code
1016  KTemporaryFile *tempFile = new KTemporaryFile();
1017  tempFile->setSuffix( '.' + QString::number( partNum ) );
1018  tempFile->open();
1019  KUrl file = KUrl( tempFile->fileName() );
1020  delete tempFile;
1021 
1022  if( ::access( QFile::encodeName( file.path() ), W_OK ) != 0 )
1023  // Not there or not writable
1024  if( KDE_mkdir( QFile::encodeName( file.path() ), 0 ) != 0 ||
1025  ::chmod( QFile::encodeName( file.path() ), S_IRWXU ) != 0 )
1026  return KUrl(); //failed create
1027 
1028  Q_ASSERT( !file.fileName().isNull() );
1029 
1030  mTempDirs.append( file.path() );
1031  // strip off a leading 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 );
1040 
1041  QByteArray data = att->decodedContent();
1042  // ### KMail does crlf2lf conversion here before writing the file
1043  if( !KPIMUtils::kByteArrayToFile( data, file.toLocalFile(), false, false, false ) )
1044  return KUrl();
1045 
1046  mTempFiles.append( file.toLocalFile() );
1047  // make file read-only so that nobody gets the impression that he might
1048  // edit attached files
1049  ::chmod( QFile::encodeName( file.toLocalFile() ), S_IRUSR );
1050 
1051  return file;
1052 }
1053 
1054 
1055 void ArticleWidget::removeTempFiles( )
1056 {
1057  for ( QStringList::Iterator it = mTempFiles.begin(); it != mTempFiles.end(); ++it )
1058  QFile::remove(*it);
1059  mTempFiles.clear();
1060  for ( QStringList::ConstIterator it = mTempDirs.constBegin(); it != mTempDirs.constEnd(); ++it )
1061  QDir(*it).rmdir(*it);
1062  mTempDirs.clear();
1063 }
1064 
1065 
1066 
1067 void ArticleWidget::processJob( KNJobData * job )
1068 {
1069  if ( job->type() == KNJobData::JTfetchSource || job->type() == KNJobData::JTfetchArticle ) {
1070  if ( !job->canceled() ) {
1071  if ( !job->success() )
1072  KMessageBox::error( this, i18n("An error occurred while downloading the article source:\n%1",
1073  job->errorString() ) );
1074  else {
1075  KNRemoteArticle::Ptr a = boost::static_pointer_cast<KNRemoteArticle>( job->data() );
1076  new KNSourceViewWindow( a->head() + '\n' + a->body() );
1077  }
1078  }
1079  }
1080  delete job;
1081 }
1082 
1083 
1084 
1085 typedef QList<ArticleWidget*>::ConstIterator InstanceIterator;
1086 
1087 void ArticleWidget::configChanged()
1088 {
1089  for( InstanceIterator it = mInstances.constBegin(); it != mInstances.constEnd(); ++it ) {
1090  (*it)->readConfig();
1091  (*it)->updateContents();
1092  }
1093 }
1094 
1095 
1096 bool ArticleWidget::articleVisible( KNArticle::Ptr article )
1097 {
1098  for ( InstanceIterator it = mInstances.constBegin(); it != mInstances.constEnd(); ++it )
1099  if ( (*it)->article() == article )
1100  return true;
1101  return false;
1102 }
1103 
1104 
1105 void ArticleWidget::articleRemoved( KNArticle::Ptr article )
1106 {
1107  for ( InstanceIterator it = mInstances.constBegin(); it != mInstances.constEnd(); ++it )
1108  if ( (*it)->article() == article )
1109  (*it)->setArticle( KNArticle::Ptr() );
1110 }
1111 
1112 
1113 void ArticleWidget::articleChanged( KNArticle::Ptr article )
1114 {
1115  for ( InstanceIterator it = mInstances.constBegin(); it != mInstances.constEnd(); ++it )
1116  if ( (*it)->article() == article )
1117  (*it)->displayArticle();
1118 }
1119 
1120 
1121 void ArticleWidget::articleLoadError( KNArticle::Ptr article, const QString &error )
1122 {
1123  for ( InstanceIterator it = mInstances.constBegin(); it != mInstances.constEnd(); ++it )
1124  if ( (*it)->article() == article )
1125  (*it)->displayErrorMessage( error );
1126 }
1127 
1128 
1129 void ArticleWidget::collectionRemoved( KNArticleCollection::Ptr coll )
1130 {
1131  for ( InstanceIterator it = mInstances.constBegin(); it != mInstances.constEnd(); ++it )
1132  if ( (*it)->article() && (*it)->article()->collection() == coll )
1133  (*it)->setArticle( KNArticle::Ptr() );
1134 }
1135 
1136 
1137 void ArticleWidget::cleanup()
1138 {
1139  for ( InstanceIterator it = mInstances.constBegin(); it != mInstances.constEnd(); ++it )
1140  (*it)->setArticle( KNArticle::Ptr() ); //delete orphant articles => avoid crash in destructor
1141 }
1142 
1143 
1144 
1145 bool ArticleWidget::atBottom() const
1146 {
1147  const KHTMLView *view = mViewer->view();
1148  return view->contentsY() + view->visibleHeight() >= view->contentsHeight();
1149 }
1150 
1151 void ArticleWidget::scrollUp()
1152 {
1153  mViewer->view()->scrollBy( 0, -10 );
1154 }
1155 
1156 void ArticleWidget::scrollDown()
1157 {
1158  mViewer->view()->scrollBy( 0, 10 );
1159 }
1160 
1161 void ArticleWidget::scrollPrior()
1162 {
1163  mViewer->view()->scrollBy( 0, -(int)(mViewer->view()->height() * 0.8) );
1164 }
1165 
1166 void ArticleWidget::scrollNext()
1167 {
1168  mViewer->view()->scrollBy( 0, (int)(mViewer->view()->height() * 0.8) );
1169 }
1170 
1171 
1172 
1173 void ArticleWidget::slotURLClicked( const KUrl &url, bool forceOpen)
1174 {
1175  // internal URLs
1176  if ( url.protocol() == "knode" ) {
1177  if ( url.path() == "showHTML" ) {
1178  mShowHtml = true;
1179  updateContents();
1180  }
1181  return;
1182  }
1183  // handle mailto
1184  if ( url.protocol() == "mailto" ) {
1185  KMime::Types::Mailbox addr;
1186  addr.fromUnicodeString( url.path() );
1187  KNGlobals::self()->articleFactory()->createMail( &addr );
1188  return;
1189  }
1190  // handle news URL's
1191  if ( url.protocol() == "news" ) {
1192  kDebug( 5003 ) << url;
1193  knGlobals.top->openURL( url );
1194  return;
1195  }
1196  // handle attachments
1197  int partNum = 0;
1198  if ( url.protocol() == "file" || url.protocol() == "part" ) {
1199  if ( url.protocol() == "file" ) {
1200  if ( !mAttachementMap.contains( url.path() ) )
1201  return;
1202  partNum = mAttachementMap[url.path()];
1203  }
1204  if ( url.protocol() == "part" )
1205  partNum = url.path().toInt();
1206  KMime::Content *c = mAttachments.at( partNum );
1207  if ( !c )
1208  return;
1209  // TODO: replace with message box as done in KMail
1210  if ( forceOpen || knGlobals.settings()->openAttachmentsOnClick() )
1211  knGlobals.articleManager()->openContent( c );
1212  else
1213  knGlobals.articleManager()->saveContentToFile( c, this );
1214  return;
1215  }
1216  // let KDE take care of the remaining protocols (http, ftp, etc.)
1217  new KRun( url, this );
1218 }
1219 
1220 
1221 void ArticleWidget::slotURLPopup( const QString &url, const QPoint &point )
1222 {
1223  mCurrentURL = KUrl( url );
1224  QString popupName;
1225  if ( url.isEmpty() ) // plain text
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";
1231  // ### news URLS?
1232  else if ( mCurrentURL.protocol() == "knode" )
1233  return; // skip
1234  else
1235  popupName = "url_popup"; // all other URLs
1236  QMenu *popup = static_cast<QMenu*>( mGuiClient->factory()->container( popupName, mGuiClient ) );
1237  if ( popup )
1238  popup->popup( point );
1239 }
1240 
1241 
1242 
1243 void ArticleWidget::slotTimeout()
1244 {
1245  if ( mArticle && mArticle->type() == KNArticle::ATremote && !mArticle->isOrphant() ) {
1246  KNRemoteArticle::List l;
1247  l.append( boost::static_pointer_cast<KNRemoteArticle>( mArticle ) );
1248  knGlobals.articleManager()->setRead( l, true );
1249  }
1250 }
1251 
1252 
1253 
1254 void ArticleWidget::slotSave()
1255 {
1256  if ( mArticle )
1257  knGlobals.articleManager()->saveArticleToFile( mArticle, this );
1258 }
1259 
1260 void ArticleWidget::slotPrint( )
1261 {
1262  if ( mArticle )
1263  mViewer->view()->print();
1264 }
1265 
1266 
1267 void ArticleWidget::slotCopySelection( )
1268 {
1269  QApplication::clipboard()->setText( mViewer->selectedText() );
1270 }
1271 
1272 
1273 void ArticleWidget::slotSelectAll()
1274 {
1275  mViewer->selectAll();
1276 }
1277 
1278 
1279 void ArticleWidget::slotFind()
1280 {
1281  mViewer->findText();
1282 }
1283 
1284 
1285 void ArticleWidget::slotViewSource()
1286 {
1287  // local article can be shown directly
1288  if ( mArticle && mArticle->type() == KNArticle::ATlocal && mArticle->hasContent() ) {
1289  new KNSourceViewWindow( mArticle->encodedContent( false ) );
1290  } else {
1291  // download remote article
1292  if ( mArticle && mArticle->type() == KNArticle::ATremote ) {
1293  KNGroup::Ptr g = boost::static_pointer_cast<KNGroup>( mArticle->collection() );
1294  KNRemoteArticle::Ptr a = KNRemoteArticle::Ptr( new KNRemoteArticle( g ) ); //we need "g" to access the nntp-account
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() );
1298  emitJob( new ArticleFetchJob( this, g->account(), a, false ) );
1299  }
1300  }
1301 }
1302 
1303 
1304 void ArticleWidget::slotReply()
1305 {
1306  if ( mArticle && mArticle->type() == KNArticle::ATremote )
1307  KNGlobals::self()->articleFactory()->createReply( boost::static_pointer_cast<KNRemoteArticle>( mArticle ),
1308  mViewer->selectedText(), true, false );
1309 }
1310 
1311 
1312 void ArticleWidget::slotRemail()
1313 {
1314  if ( mArticle && mArticle->type()==KNArticle::ATremote )
1315  KNGlobals::self()->articleFactory()->createReply( boost::static_pointer_cast<KNRemoteArticle>( mArticle ),
1316  mViewer->selectedText(), false, true );
1317 }
1318 
1319 
1320 void ArticleWidget::slotForward()
1321 {
1322  KNGlobals::self()->articleFactory()->createForward( mArticle );
1323 }
1324 
1325 
1326 void ArticleWidget::slotCancel()
1327 {
1328  KNGlobals::self()->articleFactory()->createCancel( mArticle );
1329 }
1330 
1331 
1332 void ArticleWidget::slotSupersede()
1333 {
1334  KNGlobals::self()->articleFactory()->createSupersede( mArticle );
1335 }
1336 
1337 
1338 void ArticleWidget::slotToggleFixedFont()
1339 {
1340  writeConfig();
1341  updateContents();
1342 }
1343 
1344 
1345 void ArticleWidget::slotToggleFancyFormating( )
1346 {
1347  writeConfig();
1348  updateContents();
1349 }
1350 
1351 
1352 void ArticleWidget::slotFancyHeaders()
1353 {
1354  mHeaderStyle = "fancy";
1355  writeConfig();
1356  updateContents();
1357 }
1358 
1359 void ArticleWidget::slotStandardHeaders()
1360 {
1361  mHeaderStyle = "standard";
1362  writeConfig();
1363  updateContents();
1364 }
1365 
1366 void ArticleWidget::slotAllHeaders()
1367 {
1368  mHeaderStyle = "all";
1369  writeConfig();
1370  updateContents();
1371 }
1372 
1373 
1374 void ArticleWidget::slotIconAttachments()
1375 {
1376  mAttachmentStyle = "icon";
1377  writeConfig();
1378  updateContents();
1379 }
1380 
1381 void ArticleWidget::slotInlineAttachments()
1382 {
1383  mAttachmentStyle = "inline";
1384  writeConfig();
1385  updateContents();
1386 }
1387 
1388 void ArticleWidget::slotHideAttachments()
1389 {
1390  mAttachmentStyle = "hide";
1391  writeConfig();
1392  updateContents();
1393 }
1394 
1395 
1396 void ArticleWidget::slotToggleRot13()
1397 {
1398  mRot13 = !mRot13;
1399  updateContents();
1400 }
1401 
1402 
1403 
1404 void ArticleWidget::slotSetCharset( const QString &charset )
1405 {
1406  if ( charset.isEmpty() )
1407  return;
1408 
1409  if ( charset == i18nc( "@item default character set", "Default") ) {
1410  mForceCharset = false;
1411  mOverrideCharset = KMime::Headers::Latin1;
1412  } else {
1413  mForceCharset = true;
1414  mOverrideCharset = KGlobal::charsets()->encodingForName( charset ).toLatin1();
1415  }
1416 
1417  if ( mArticle && mArticle->hasContent() ) {
1418  mArticle->setDefaultCharset( mOverrideCharset ); // the article will choose the correct default,
1419  mArticle->setForceDefaultCharset( mForceCharset ); // when we disable the overdrive
1420  updateContents();
1421  }
1422 }
1423 
1424 
1425 void ArticleWidget::slotSetCharsetKeyboard( )
1426 {
1427  int charset = KNHelper::selectDialog( this, i18n("Select Charset"),
1428  mCharsetSelect->items(), mCharsetSelect->currentItem() );
1429  if ( charset != -1 ) {
1430  mCharsetSelect->setCurrentItem( charset );
1431  slotSetCharset( mCharsetSelect->items()[charset] );
1432  }
1433 }
1434 
1435 
1436 
1437 void ArticleWidget::slotOpenURL()
1438 {
1439  slotURLClicked( mCurrentURL );
1440 }
1441 
1442 void ArticleWidget::slotCopyURL()
1443 {
1444  QString address;
1445  if ( mCurrentURL.protocol() == "mailto" )
1446  address = mCurrentURL.path();
1447  else
1448  address = mCurrentURL.url();
1449  QApplication::clipboard()->setText( address, QClipboard::Clipboard );
1450  QApplication::clipboard()->setText( address, QClipboard::Selection );
1451 }
1452 
1453 void ArticleWidget::slotAddBookmark()
1454 {
1455  if ( mCurrentURL.isEmpty() )
1456  return;
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();
1462 }
1463 
1464 void ArticleWidget::slotAddToAddressBook()
1465 {
1466  KPIM::AddEmailAddressJob *job = new KPIM::AddEmailAddressJob( mCurrentURL.path(), this, this );
1467  job->start();
1468 }
1469 
1470 void ArticleWidget::slotOpenInAddressBook()
1471 {
1472  KPIM::OpenEmailAddressJob *job = new KPIM::OpenEmailAddressJob( mCurrentURL.path(), this, this );
1473  job->start();
1474 }
1475 
1476 void ArticleWidget::slotOpenAttachment()
1477 {
1478  slotURLClicked( mCurrentURL, true );
1479 }
1480 
1481 void ArticleWidget::slotSaveAttachment()
1482 {
1483  if ( mCurrentURL.protocol() != "file" && mCurrentURL.protocol() != "part" )
1484  return;
1485  int partNum = 0;
1486  if ( mCurrentURL.protocol() == "file" ) {
1487  if ( !mAttachementMap.contains( mCurrentURL.path() ) )
1488  return;
1489  partNum = mAttachementMap[mCurrentURL.path()];
1490  }
1491  if ( mCurrentURL.protocol() == "part" )
1492  partNum = mCurrentURL.path().toInt();
1493  KMime::Content *c = mAttachments.at( partNum );
1494  if ( !c )
1495  return;
1496  knGlobals.articleManager()->saveContentToFile( c, this );
1497 }
1498 
1499 #include "articlewidget.moc"
KNode::ArticleWidget::readConfig
void readConfig()
read config settings
Definition: articlewidget.cpp:337
KNode::ArticleWidget::setArticle
void setArticle(KNArticle::Ptr article)
display the given article
Definition: articlewidget.cpp:379
KNode::ArticleFetchJob
Downloads one specific article from the news server.
Definition: nntpjobs.h:83
KNode::ArticleWidget::collectionRemoved
static void collectionRemoved(KNArticleCollection::Ptr coll)
notify all instances that the given collection has been removed
Definition: articlewidget.cpp:1129
KNode::ArticleWidget::writeConfig
void writeConfig()
write config settings (call only for the main viewer)
Definition: articlewidget.cpp:362
KNArticle::ATremote
Definition: knarticle.h:45
KNDisplayedHeader::header
const QString & header()
Definition: kndisplayedheader.h:44
KNode::Utilities::Locale::encodings
static QStringList encodings()
Returns a list of encodings along with a description.
Definition: locale.cpp:120
KNJobConsumer::emitJob
void emitJob(KNJobData *j)
Send the job to the scheduler and append it to the job queue.
Definition: knjobdata.cpp:42
KPIM::OpenEmailAddressJob
KNArticleFactory::createReply
void createReply(KNRemoteArticle::Ptr a, const QString &selectedText=QString(), bool post=true, bool mail=false)
Definition: knarticlefactory.cpp:110
KNode::ArticleWidget::scrollPrior
void scrollPrior()
scroll up by one page
Definition: articlewidget.cpp:1161
KPIM::OpenEmailAddressJob::start
virtual void start()
knfolder.h
text
virtual QByteArray text(quint32 serialNumber) const =0
KNJobData::JTfetchArticle
Definition: knjobdata.h:112
KPIM::AddEmailAddressJob
date
time_t date() const
KNJobData
Abstract base class for all KNode internal jobs.
Definition: knjobdata.h:101
InstanceIterator
QList< ArticleWidget * >::ConstIterator InstanceIterator
Definition: articlewidget.cpp:1085
KNDisplayedHeader::translatedName
QString translatedName()
Definition: kndisplayedheader.cpp:114
articlewidget.h
QWidget
knarticlefactory.h
KNDisplayedHeader
A header displayed in the article viewer.
Definition: kndisplayedheader.h:23
KNGlobals::self
static KNGlobals * self()
Return the KNGlobals instance.
Definition: knglobals.cpp:72
KNode::ArticleWidget::~ArticleWidget
~ArticleWidget()
Destroy the article widget.
Definition: articlewidget.cpp:134
KNGroup
Representation of a news group.
Definition: kngroup.h:41
KNode::ArticleWidget::scrollNext
void scrollNext()
scroll down by one page
Definition: articlewidget.cpp:1166
openemailaddressjob.h
knarticlemanager.h
kngroup.h
KNArticleFactory::createForward
void createForward(KNArticle::Ptr a)
Definition: knarticlefactory.cpp:277
knnntpaccount.h
nntpjobs.h
KNode::CSSHelper
Helper class to generate a CSS style sheet for article widget.
Definition: csshelper.h:24
KNode::ArticleWidget::scrollDown
void scrollDown()
scroll down by one line
Definition: articlewidget.cpp:1156
KNode::ArticleWidget::processJob
void processJob(KNJobData *j)
process download jobs for view source action
Definition: articlewidget.cpp:1067
KNSourceViewWindow
Article source view window.
Definition: knsourceviewwindow.h:22
KNode::ArticleWidget::atBottom
bool atBottom() const
checks whether the readers is scrolled down to the bottom
Definition: articlewidget.cpp:1145
KNJobData::type
jobType type() const
Definition: knjobdata.h:120
KNArticle::ATlocal
Definition: knarticle.h:46
KNJobData::JTfetchSource
Definition: knjobdata.h:115
KNGlobals::articleFactory
KNArticleFactory * articleFactory()
Returns the article factory.
Definition: knglobals.cpp:131
KNJobData::data
KNJobItem::Ptr data() const
Definition: knjobdata.h:123
KPIM::AddEmailAddressJob::start
virtual void start()
locale.h
knmainwidget.h
KNode::ArticleWidget::scrollUp
void scrollUp()
scroll up by one line
Definition: articlewidget.cpp:1151
KNode::ArticleWidget::configChanged
static void configChanged()
notify all instances about a config change
Definition: articlewidget.cpp:1087
KNJobData::canceled
bool canceled() const
Returns true if the job has been canceled by the user.
Definition: knjobdata.h:132
KNArticleFactory::createMail
void createMail(KMime::Types::Mailbox *address)
Definition: knarticlefactory.cpp:506
KNode::ArticleWidget::articleVisible
static bool articleVisible(KNArticle::Ptr article)
check whether the given article is displayed in any instance
Definition: articlewidget.cpp:1096
kndisplayedheader.h
knarticlecollection.h
KNArticleFactory::createSupersede
void createSupersede(KNArticle::Ptr a)
Definition: knarticlefactory.cpp:431
knglobals.h
QMenu
KNJobData::errorString
QString errorString() const
Returns the error message.
Definition: knjobdata.h:128
KNode::ArticleWidget::ArticleWidget
ArticleWidget(QWidget *parent, KXMLGUIClient *guiClient, KActionCollection *actionCollection, bool isMainViewer=false)
Construct a new article widget.
Definition: articlewidget.cpp:88
KNode::ArticleWidget::articleRemoved
static void articleRemoved(KNArticle::Ptr article)
notify all instances that the given article has been removed
Definition: articlewidget.cpp:1105
KActionMenu
KNode::ArticleWidget::cleanup
static void cleanup()
cleanup all instances
Definition: articlewidget.cpp:1137
KNRemoteArticle::Ptr
boost::shared_ptr< KNRemoteArticle > Ptr
Shared pointer to a KNRemoteArticle. To be used instead of raw KNRemoteArticle*.
Definition: knarticle.h:107
addemailaddressjob.h
knfoldermanager.h
settings.h
KNDisplayedHeader::hasName
bool hasName() const
Definition: kndisplayedheader.h:35
KNArticleFactory::createCancel
void createCancel(KNArticle::Ptr a)
Definition: knarticlefactory.cpp:353
KNArticle::Ptr
boost::shared_ptr< KNArticle > Ptr
Shared pointer to a KNArticle. To be used instead of raw KNArticle*.
Definition: knarticle.h:39
KNJobData::success
bool success() const
Returns true if the job finished successfully.
Definition: knjobdata.h:130
knarticle.h
KNode::ArticleWidget::articleLoadError
static void articleLoadError(KNArticle::Ptr article, const QString &error)
notify all instances about an error during loading the given article
Definition: articlewidget.cpp:1121
KNHelper::selectDialog
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...
Definition: utilities.cpp:163
KNArticleCollection::Ptr
boost::shared_ptr< KNArticleCollection > Ptr
Shared pointer to a KNArticle.
Definition: knarticlecollection.h:82
knconfigmanager.h
KNRemoteArticle
KNRemoteArticle represents an article, whos body has to be retrieved from a remote host or from the l...
Definition: knarticle.h:103
knconfig.h
knsourceviewwindow.h
KNode::ArticleWidget::articleChanged
static void articleChanged(KNArticle::Ptr article)
notify all instances that the given article has changed
Definition: articlewidget.cpp:1113
knGlobals
#define knGlobals
Keep compatibility with the old way.
Definition: knglobals.h:28
KNode::ArticleWidget::article
KNArticle::Ptr article() const
returns the currently shown article
Definition: articlewidget.h:69
csshelper.h
QList
KNGroup::Ptr
boost::shared_ptr< KNGroup > Ptr
Shared pointer to a KNGroup.
Definition: kngroup.h:47
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 22:58:36 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

knode

Skip menu "knode"
  • Main Page
  • Namespace List
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdepim API Reference

Skip menu "kdepim API Reference"
  • akonadi_next
  • akregator
  • blogilo
  • calendarsupport
  • console
  •   kabcclient
  •   konsolekalendar
  • kaddressbook
  • kalarm
  •   lib
  • kdgantt2
  • kjots
  • kleopatra
  • kmail
  • knode
  • knotes
  • kontact
  • korgac
  • korganizer
  • ktimetracker
  • libkdepim
  • libkleo
  • libkpgp
  • mailcommon
  • messagelist
  • messageviewer

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal