mirror of
https://bitbucket.org/smil3y/kde-playground.git
synced 2025-02-24 10:52:52 +00:00
3401 lines
124 KiB
C++
3401 lines
124 KiB
C++
/* -*- mode: C++; c-file-style: "gnu" -*-
|
|
Copyright (c) 1997 Markus Wuebben <markus.wuebben@kde.org>
|
|
Copyright (C) 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net
|
|
Copyright (c) 2009 Andras Mantia <andras@kdab.net>
|
|
Copyright (c) 2010 Torgny Nyblom <nyblom@kde.org>
|
|
Copyright (c) 2011, 2012, 2013, 2014 Laurent Montel <montel@kde.org>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License along
|
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
//#define MESSAGEVIEWER_READER_HTML_DEBUG 1
|
|
|
|
|
|
#include "viewer_p.h"
|
|
#include "viewer.h"
|
|
#include "viewer/objecttreeemptysource.h"
|
|
#include "viewer/objecttreeviewersource.h"
|
|
#include "messagedisplayformatattribute.h"
|
|
#include "grantleetheme/grantleethememanager.h"
|
|
#include "grantleetheme/globalsettings_base.h"
|
|
#include "scamdetection/scamdetectionwarningwidget.h"
|
|
#include "scamdetection/scamattribute.h"
|
|
#include "adblock/adblockmanager.h"
|
|
#include "widgets/todoedit.h"
|
|
#include "widgets/eventedit.h"
|
|
#include "viewer/mimeparttreeview.h"
|
|
#include "widgets/openattachmentfolderwidget.h"
|
|
#include "pimcommon/widgets/slidecontainer.h"
|
|
|
|
#ifdef MESSAGEVIEWER_READER_HTML_DEBUG
|
|
#include "htmlwriter/filehtmlwriter.h"
|
|
#include "htmlwriter/teehtmlwriter.h"
|
|
#endif
|
|
#include <unistd.h> // link()
|
|
#include <errno.h>
|
|
//KDE includes
|
|
#include <KAction>
|
|
#include <KActionCollection>
|
|
#include <KActionMenu>
|
|
#include <kascii.h>
|
|
#include <KCharsets>
|
|
#include <KFileDialog>
|
|
#include <KGuiItem>
|
|
#include <QWebView>
|
|
#include <QWebPage>
|
|
#include <QWebFrame>
|
|
#include <KMenu>
|
|
#include <KMessageBox>
|
|
#include <KMimeType>
|
|
#include <KMimeTypeChooser>
|
|
#include <KMimeTypeTrader>
|
|
#include <KRun>
|
|
#include <KSelectAction>
|
|
#include <KSharedConfigPtr>
|
|
#include <KStandardDirs>
|
|
#include <KStandardGuiItem>
|
|
#include <KTempDir>
|
|
#include <KTemporaryFile>
|
|
#include <KToggleAction>
|
|
#include <KPrintPreview>
|
|
#include <kdeprintdialog.h>
|
|
|
|
#include <kfileitemactions.h>
|
|
#include <KFileItemListProperties>
|
|
|
|
#include <KIO/NetAccess>
|
|
#include <KABC/Addressee>
|
|
#include <KABC/VCardConverter>
|
|
#include <KPIMUtils/Email>
|
|
#include <Akonadi/ItemModifyJob>
|
|
#include <Akonadi/ItemCreateJob>
|
|
|
|
#include <kpimutils/kfileio.h>
|
|
|
|
#include <kleo/cryptobackendfactory.h>
|
|
#include <kleo/cryptobackend.h>
|
|
|
|
#include <mailtransport/errorattribute.h>
|
|
|
|
//Qt includes
|
|
#include <QClipboard>
|
|
#include <QDesktopWidget>
|
|
#include <QFileInfo>
|
|
#include <QItemSelectionModel>
|
|
#include <QSignalMapper>
|
|
#include <QSplitter>
|
|
#include <QTreeView>
|
|
#include <QVBoxLayout>
|
|
#include <QPrinter>
|
|
#include <QPrintDialog>
|
|
#include <QHeaderView>
|
|
|
|
|
|
//libkdepim
|
|
#include "libkdepim/misc/broadcaststatus.h"
|
|
#include <messagecore/attachment/attachmentpropertiesdialog.h>
|
|
|
|
#include <akonadi/collection.h>
|
|
#include <akonadi/itemfetchjob.h>
|
|
#include <akonadi/itemfetchscope.h>
|
|
#include <akonadi/kmime/messagestatus.h>
|
|
#include <akonadi/kmime/specialmailcollections.h>
|
|
#include <akonadi/attributefactory.h>
|
|
#include <Akonadi/KMime/MessageParts>
|
|
#include <kleo/specialjob.h>
|
|
|
|
#include "chiasmuskeyselector.h"
|
|
#include "utils/autoqpointer.h"
|
|
|
|
|
|
//own includes
|
|
#include "widgets/attachmentdialog.h"
|
|
#include "viewer/attachmentstrategy.h"
|
|
#include "csshelper.h"
|
|
#include "viewer/editorwatcher.h"
|
|
#include "settings/globalsettings.h"
|
|
#include "header/headerstyle.h"
|
|
#include "header/headerstrategy.h"
|
|
#include "widgets/htmlstatusbar.h"
|
|
#include "htmlwriter/webkitparthtmlwriter.h"
|
|
#include "viewer/mailsourceviewer.h"
|
|
#include "viewer/mimetreemodel.h"
|
|
#include "viewer/nodehelper.h"
|
|
#include "viewer/objecttreeparser.h"
|
|
#include "viewer/urlhandlermanager.h"
|
|
#include "utils/util.h"
|
|
#include "widgets/vcardviewer.h"
|
|
#include "viewer/mailwebview.h"
|
|
#include "findbar/findbarmailwebview.h"
|
|
#include "pimcommon/translator/translatorwidget.h"
|
|
#include "job/createtodojob.h"
|
|
#include "job/createeventjob.h"
|
|
|
|
#include "interfaces/bodypart.h"
|
|
#include "interfaces/htmlwriter.h"
|
|
|
|
#include <messagecore/utils/stringutil.h>
|
|
|
|
#include <kio/jobuidelegate.h>
|
|
|
|
#include <gpgme++/error.h>
|
|
#include <messagecore/helpers/nodehelper.h>
|
|
#include "messagecore/settings/globalsettings.h"
|
|
#include <akonadi/agentinstance.h>
|
|
#include <akonadi/agentmanager.h>
|
|
#include <Akonadi/CollectionFetchJob>
|
|
#include <akonadi/collectionfetchscope.h>
|
|
|
|
#include <boost/bind.hpp>
|
|
|
|
using namespace boost;
|
|
using namespace MailTransport;
|
|
using namespace MessageViewer;
|
|
using namespace MessageCore;
|
|
|
|
const int ViewerPrivate::delay = 150;
|
|
const qreal ViewerPrivate::zoomBy = 20;
|
|
|
|
static QAtomicInt _k_attributeInitialized;
|
|
|
|
ViewerPrivate::ViewerPrivate(Viewer *aParent, QWidget *mainWindow,
|
|
KActionCollection *actionCollection )
|
|
: QObject(aParent),
|
|
mNodeHelper( new NodeHelper ),
|
|
mViewer( 0 ),
|
|
mFindBar( 0 ),
|
|
mTranslatorWidget(0),
|
|
mAttachmentStrategy( 0 ),
|
|
mHeaderStrategy( 0 ),
|
|
mHeaderStyle( 0 ),
|
|
mUpdateReaderWinTimer( 0 ),
|
|
mResizeTimer( 0 ),
|
|
mOldGlobalOverrideEncoding( QLatin1String("---") ), // init with dummy value
|
|
mMsgDisplay( true ),
|
|
mCSSHelper( 0 ),
|
|
mMainWindow( mainWindow ),
|
|
mActionCollection( actionCollection ),
|
|
mCopyAction( 0 ),
|
|
mCopyURLAction( 0 ),
|
|
mUrlOpenAction( 0 ),
|
|
mSelectAllAction( 0 ),
|
|
mScrollUpAction( 0 ),
|
|
mScrollDownAction( 0 ),
|
|
mScrollUpMoreAction( 0 ),
|
|
mScrollDownMoreAction( 0 ),
|
|
mHeaderOnlyAttachmentsAction( 0 ),
|
|
mSelectEncodingAction( 0 ),
|
|
mToggleFixFontAction( 0 ),
|
|
mToggleDisplayModeAction( 0 ),
|
|
mZoomTextOnlyAction( 0 ),
|
|
mZoomInAction( 0 ),
|
|
mZoomOutAction( 0 ),
|
|
mZoomResetAction( 0 ),
|
|
mToggleMimePartTreeAction( 0 ),
|
|
mSpeakTextAction(0),
|
|
mCanStartDrag( false ),
|
|
mHtmlWriter( 0 ),
|
|
mSavedRelativePosition( 0 ),
|
|
mDecrytMessageOverwrite( false ),
|
|
mShowSignatureDetails( false ),
|
|
mShowAttachmentQuicklist( true ),
|
|
mShowRawToltecMail( false ),
|
|
mRecursionCountForDisplayMessage( 0 ),
|
|
mCurrentContent( 0 ),
|
|
mMessagePartNode( 0 ),
|
|
mJob( 0 ),
|
|
q( aParent ),
|
|
mShowFullToAddressList( true ),
|
|
mShowFullCcAddressList( true ),
|
|
mPreviouslyViewedItem( -1 ),
|
|
mScamDetectionWarning( 0 ),
|
|
mOpenAttachmentFolderWidget( 0 ),
|
|
mZoomFactor( 100 ),
|
|
mSliderContainer( 0 )
|
|
{
|
|
mMimePartTree = 0;
|
|
if ( !mainWindow )
|
|
mMainWindow = aParent;
|
|
if ( _k_attributeInitialized.testAndSetAcquire( 0, 1 ) ) {
|
|
Akonadi::AttributeFactory::registerAttribute<MessageViewer::MessageDisplayFormatAttribute>();
|
|
Akonadi::AttributeFactory::registerAttribute<MessageViewer::ScamAttribute>();
|
|
}
|
|
|
|
|
|
mThemeManager = new GrantleeTheme::GrantleeThemeManager(GrantleeTheme::GrantleeThemeManager::Mail, QString::fromLatin1( "header.desktop" ), mActionCollection, QLatin1String("messageviewer/themes/"));
|
|
mThemeManager->setDownloadNewStuffConfigFile(QLatin1String("messageviewer_header_themes.knsrc"));
|
|
connect(mThemeManager, SIGNAL(grantleeThemeSelected()), this, SLOT(slotGrantleeHeaders()));
|
|
connect(mThemeManager, SIGNAL(updateThemes()), this, SLOT(slotGrantleeThemesUpdated()));
|
|
|
|
mDisplayFormatMessageOverwrite = MessageViewer::Viewer::UseGlobalSetting;
|
|
mHtmlLoadExtOverride = false;
|
|
|
|
mHtmlLoadExternalGlobalSetting = false;
|
|
mHtmlMailGlobalSetting = false;
|
|
|
|
mZoomTextOnly = false;
|
|
|
|
mUpdateReaderWinTimer.setObjectName( QLatin1String("mUpdateReaderWinTimer") );
|
|
mResizeTimer.setObjectName( QLatin1String("mResizeTimer") );
|
|
|
|
mExternalWindow = false;
|
|
mPrinting = false;
|
|
|
|
createWidgets();
|
|
createActions();
|
|
initHtmlWidget();
|
|
readConfig();
|
|
|
|
mLevelQuote = GlobalSettings::self()->collapseQuoteLevelSpin() - 1;
|
|
|
|
mResizeTimer.setSingleShot( true );
|
|
connect( &mResizeTimer, SIGNAL(timeout()),
|
|
this, SLOT(slotDelayedResize()) );
|
|
|
|
mUpdateReaderWinTimer.setSingleShot( true );
|
|
connect( &mUpdateReaderWinTimer, SIGNAL(timeout()),
|
|
this, SLOT(updateReaderWin()) );
|
|
|
|
connect( mColorBar, SIGNAL(clicked()),
|
|
this, SLOT(slotToggleHtmlMode()) );
|
|
|
|
// FIXME: Don't use the full payload here when attachment loading on demand is used, just
|
|
// like in KMMainWidget::slotMessageActivated().
|
|
Akonadi::ItemFetchScope fs;
|
|
fs.fetchFullPayload();
|
|
fs.fetchAttribute<MailTransport::ErrorAttribute>();
|
|
fs.fetchAttribute<MessageViewer::MessageDisplayFormatAttribute>();
|
|
fs.fetchAttribute<MessageViewer::ScamAttribute>();
|
|
mMonitor.setItemFetchScope( fs );
|
|
connect( &mMonitor, SIGNAL(itemChanged(Akonadi::Item,QSet<QByteArray>)),
|
|
this, SLOT(slotItemChanged(Akonadi::Item,QSet<QByteArray>)) );
|
|
connect( &mMonitor, SIGNAL(itemRemoved(Akonadi::Item)),
|
|
this, SLOT(slotClear()) );
|
|
connect( &mMonitor, SIGNAL(itemMoved(Akonadi::Item,Akonadi::Collection,Akonadi::Collection)),
|
|
this, SLOT(slotItemMoved(Akonadi::Item,Akonadi::Collection,Akonadi::Collection)) );
|
|
}
|
|
|
|
ViewerPrivate::~ViewerPrivate()
|
|
{
|
|
GlobalSettings::self()->writeConfig();
|
|
delete mHtmlWriter; mHtmlWriter = 0;
|
|
delete mViewer; mViewer = 0;
|
|
delete mCSSHelper;
|
|
mNodeHelper->forceCleanTempFiles();
|
|
delete mNodeHelper;
|
|
delete mThemeManager;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
KMime::Content * ViewerPrivate::nodeFromUrl( const KUrl & url )
|
|
{
|
|
KMime::Content *node = 0;
|
|
if ( url.isEmpty() ) {
|
|
return mMessage.get();
|
|
}
|
|
if ( !url.isLocalFile() ) {
|
|
QString path = url.path(KUrl::RemoveTrailingSlash);
|
|
if ( path.contains(QLatin1Char(':')) ) {
|
|
//if the content was not found, it might be in an extra node. Get the index of the extra node (the first part of the url),
|
|
//and use the remaining part as a ContentIndex to find the node inside the extra node
|
|
int i = path.left( path.indexOf(QLatin1Char(':')) ).toInt();
|
|
path = path.mid( path.indexOf(QLatin1Char(':')) + 1 );
|
|
KMime::ContentIndex idx(path);
|
|
QList<KMime::Content*> extras = mNodeHelper->extraContents( mMessage.get() );
|
|
if ( i >= 0 && i < extras.size() ) {
|
|
KMime::Content* c = extras[i];
|
|
node = c->content( idx );
|
|
}
|
|
} else {
|
|
if( mMessage )
|
|
node= mMessage->content( KMime::ContentIndex( path ) );
|
|
}
|
|
} else {
|
|
const QString path = url.toLocalFile();
|
|
const uint right = path.lastIndexOf( QLatin1Char('/') );
|
|
const uint left = path.lastIndexOf( QLatin1Char('.'), right );
|
|
|
|
KMime::ContentIndex index(path.mid( left + 1, right - left - 1 ));
|
|
node = mMessage->content( index );
|
|
}
|
|
return node;
|
|
}
|
|
|
|
void ViewerPrivate::openAttachment( KMime::Content* node, const QString & name )
|
|
{
|
|
if( !node ) {
|
|
return;
|
|
}
|
|
|
|
bool deletedAttachment = false;
|
|
if(node->contentType(false)) {
|
|
deletedAttachment = (node->contentType()->mimeType() == "text/x-moz-deleted");
|
|
}
|
|
if(deletedAttachment)
|
|
return;
|
|
|
|
const bool isEncapsulatedMessage = node->parent() && node->parent()->bodyIsMessage();
|
|
if ( isEncapsulatedMessage ) {
|
|
|
|
// the viewer/urlhandlermanager expects that the message (mMessage) it is passed is the root when doing index calculation
|
|
// in urls. Simply passing the result of bodyAsMessage() does not cut it as the resulting pointer is a child in its tree.
|
|
KMime::Message::Ptr m = KMime::Message::Ptr( new KMime::Message );
|
|
m->setContent( node->parent()->bodyAsMessage()->encodedContent() );
|
|
m->parse();
|
|
atmViewMsg( m );
|
|
return;
|
|
}
|
|
// determine the MIME type of the attachment
|
|
KMimeType::Ptr mimetype;
|
|
// prefer the value of the Content-Type header
|
|
mimetype = KMimeType::mimeType( QString::fromLatin1( node->contentType()->mimeType().toLower() ),
|
|
KMimeType::ResolveAliases );
|
|
if ( !mimetype.isNull() && mimetype->is( KABC::Addressee::mimeType() ) ) {
|
|
showVCard( node );
|
|
return;
|
|
}
|
|
|
|
// special case treatment on mac and windows
|
|
QString atmName = name;
|
|
if ( name.isEmpty() )
|
|
atmName = mNodeHelper->tempFileUrlFromNode( node ).toLocalFile();
|
|
if ( Util::handleUrlWithQDesktopServices( atmName ) )
|
|
return;
|
|
|
|
if ( mimetype.isNull() || mimetype->name() == QLatin1String("application/octet-stream") ) {
|
|
mimetype = Util::mimetype(name);
|
|
}
|
|
KService::Ptr offer =
|
|
KMimeTypeTrader::self()->preferredService( mimetype->name(), QLatin1String("Application") );
|
|
|
|
const QString filenameText = NodeHelper::fileName( node );
|
|
|
|
AttachmentDialog dialog ( mMainWindow, filenameText, offer ? offer->name() : QString(),
|
|
QString::fromLatin1( "askSave_" ) + mimetype->name() );
|
|
const int choice = dialog.exec();
|
|
|
|
if ( choice == AttachmentDialog::Save ) {
|
|
KUrl currentUrl;
|
|
if (Util::saveContents( mMainWindow, KMime::Content::List() << node, currentUrl )) {
|
|
showOpenAttachmentFolderWidget(currentUrl);
|
|
}
|
|
}
|
|
else if ( choice == AttachmentDialog::Open ) { // Open
|
|
if( offer )
|
|
attachmentOpenWith( node, offer );
|
|
else
|
|
attachmentOpen( node );
|
|
} else if ( choice == AttachmentDialog::OpenWith ) {
|
|
attachmentOpenWith( node );
|
|
} else { // Cancel
|
|
kDebug() << "Canceled opening attachment";
|
|
}
|
|
|
|
}
|
|
|
|
bool ViewerPrivate::deleteAttachment(KMime::Content * node, bool showWarning)
|
|
{
|
|
if ( !node )
|
|
return true;
|
|
KMime::Content *parent = node->parent();
|
|
if ( !parent )
|
|
return true;
|
|
|
|
QList<KMime::Content*> extraNodes = mNodeHelper->extraContents( mMessage.get() );
|
|
if ( extraNodes.contains( node->topLevel() ) ) {
|
|
KMessageBox::error( mMainWindow,
|
|
i18n("Deleting an attachment from an encrypted or old-style mailman message is not supported."),
|
|
i18n("Delete Attachment") );
|
|
return true; //cancelled
|
|
}
|
|
|
|
if ( showWarning && KMessageBox::warningContinueCancel( mMainWindow,
|
|
i18n("Deleting an attachment might invalidate any digital signature on this message."),
|
|
i18n("Delete Attachment"), KStandardGuiItem::del(), KStandardGuiItem::cancel(),
|
|
QLatin1String("DeleteAttachmentSignatureWarning") )
|
|
!= KMessageBox::Continue ) {
|
|
return false; //cancelled
|
|
}
|
|
//don't confuse the model
|
|
mMimePartTree->clearModel();
|
|
QString filename;
|
|
QString name;
|
|
QByteArray mimetype;
|
|
if(node->contentDisposition(false)) {
|
|
filename = node->contentDisposition()->filename();
|
|
}
|
|
|
|
if(node->contentType(false)) {
|
|
name = node->contentType()->name();
|
|
mimetype = node->contentType()->mimeType();
|
|
}
|
|
|
|
parent->removeContent( node, true );
|
|
|
|
// text/plain part:
|
|
KMime::Content* deletePart = new KMime::Content(parent);
|
|
deletePart->contentType()->setMimeType( "text/x-moz-deleted" );
|
|
deletePart->contentType()->setName(QString::fromLatin1("Deleted: %1").arg(name),"utf8");
|
|
deletePart->contentDisposition()->setDisposition(KMime::Headers::CDattachment);
|
|
deletePart->contentDisposition()->setFilename(QString::fromLatin1("Deleted: %1").arg(name));
|
|
|
|
deletePart->contentType()->setCharset( "utf-8" );
|
|
deletePart->contentTransferEncoding()->from7BitString( "7bit" );
|
|
QByteArray bodyMessage = QByteArray("\nYou deleted an attachment from this message. The original MIME headers for the attachment were:");
|
|
bodyMessage +=("\nContent-Type: ") + mimetype;
|
|
bodyMessage +=("\nname=\"") + name.toUtf8() + "\"";
|
|
bodyMessage +=("\nfilename=\"") + filename.toUtf8() + "\"";
|
|
deletePart->setBody(bodyMessage);
|
|
parent->addContent( deletePart );
|
|
|
|
|
|
parent->assemble();
|
|
|
|
KMime::Message* modifiedMessage = mNodeHelper->messageWithExtraContent( mMessage.get() );
|
|
mMimePartTree->mimePartModel()->setRoot( modifiedMessage );
|
|
mMessageItem.setPayloadFromData( modifiedMessage->encodedContent() );
|
|
Akonadi::ItemModifyJob *job = new Akonadi::ItemModifyJob( mMessageItem );
|
|
job->disableRevisionCheck();
|
|
connect( job, SIGNAL(result(KJob*)), SLOT(itemModifiedResult(KJob*)) );
|
|
return true;
|
|
}
|
|
|
|
|
|
void ViewerPrivate::itemModifiedResult( KJob* job )
|
|
{
|
|
if ( job->error() ) {
|
|
kDebug() << "Item update failed:" << job->errorString();
|
|
} else {
|
|
setMessageItem( mMessageItem, MessageViewer::Viewer::Force );
|
|
}
|
|
}
|
|
|
|
|
|
bool ViewerPrivate::editAttachment( KMime::Content * node, bool showWarning )
|
|
{
|
|
//FIXME(Andras) just that I don't forget...handle the case when the user starts editing and switches to another message meantime
|
|
if ( showWarning && KMessageBox::warningContinueCancel( mMainWindow,
|
|
i18n("Modifying an attachment might invalidate any digital signature on this message."),
|
|
i18n("Edit Attachment"), KGuiItem( i18n("Edit"), QLatin1String("document-properties") ), KStandardGuiItem::cancel(),
|
|
QLatin1String("EditAttachmentSignatureWarning") )
|
|
!= KMessageBox::Continue ) {
|
|
return false;
|
|
}
|
|
|
|
KTemporaryFile file;
|
|
file.setAutoRemove( false );
|
|
if ( !file.open() ) {
|
|
kWarning() << "Edit Attachment: Unable to open temp file.";
|
|
return true;
|
|
}
|
|
file.write( node->decodedContent() );
|
|
file.flush();
|
|
|
|
EditorWatcher *watcher =
|
|
new EditorWatcher( KUrl( file.fileName() ), QLatin1String(node->contentType()->mimeType()),
|
|
false, this, mMainWindow );
|
|
mEditorWatchers[ watcher ] = node;
|
|
|
|
connect( watcher, SIGNAL(editDone(EditorWatcher*)), SLOT(slotAttachmentEditDone(EditorWatcher*)) );
|
|
if ( !watcher->start() ) {
|
|
mEditorWatchers.remove( watcher );
|
|
QFile::remove( file.fileName() );
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void ViewerPrivate::createOpenWithMenu( KMenu *topMenu, const QString &contentTypeStr, bool fromCurrentContent )
|
|
{
|
|
const KService::List offers = KFileItemActions::associatedApplications(QStringList()<<contentTypeStr, QString() );
|
|
if (!offers.isEmpty()) {
|
|
QMenu* menu = topMenu;
|
|
QActionGroup *actionGroup = new QActionGroup( menu );
|
|
|
|
if(fromCurrentContent)
|
|
connect( actionGroup, SIGNAL(triggered(QAction*)), this, SLOT(slotOpenWithActionCurrentContent(QAction*)) );
|
|
else
|
|
connect( actionGroup, SIGNAL(triggered(QAction*)), this, SLOT(slotOpenWithAction(QAction*)) );
|
|
|
|
if (offers.count() > 1) { // submenu 'open with'
|
|
menu = new QMenu(i18nc("@title:menu", "&Open With"), topMenu);
|
|
menu->menuAction()->setObjectName(QLatin1String("openWith_submenu")); // for the unittest
|
|
topMenu->addMenu(menu);
|
|
}
|
|
//kDebug() << offers.count() << "offers" << topMenu << menu;
|
|
|
|
KService::List::ConstIterator it = offers.constBegin();
|
|
KService::List::ConstIterator end = offers.constEnd();
|
|
for(; it != end; ++it) {
|
|
KAction* act = MessageViewer::Util::createAppAction(*it,
|
|
// no submenu -> prefix single offer
|
|
menu == topMenu, actionGroup, menu);
|
|
menu->addAction(act);
|
|
}
|
|
|
|
QString openWithActionName;
|
|
if (menu != topMenu) { // submenu
|
|
menu->addSeparator();
|
|
openWithActionName = i18nc("@action:inmenu Open With", "&Other...");
|
|
} else {
|
|
openWithActionName = i18nc("@title:menu", "&Open With...");
|
|
}
|
|
KAction *openWithAct = new KAction(menu);
|
|
openWithAct->setText(openWithActionName);
|
|
if(fromCurrentContent)
|
|
connect(openWithAct, SIGNAL(triggered()), this, SLOT(slotOpenWithDialogCurrentContent()));
|
|
else
|
|
connect(openWithAct, SIGNAL(triggered()), this, SLOT(slotOpenWithDialog()));
|
|
|
|
menu->addAction(openWithAct);
|
|
} else { // no app offers -> Open With...
|
|
KAction *act = new KAction(topMenu);
|
|
act->setText(i18nc("@title:menu", "&Open With..."));
|
|
if(fromCurrentContent)
|
|
connect(act, SIGNAL(triggered()), this, SLOT(slotOpenWithDialogCurrentContent()));
|
|
else
|
|
connect(act, SIGNAL(triggered()), this, SLOT(slotOpenWithDialog()));
|
|
topMenu->addAction(act);
|
|
}
|
|
}
|
|
|
|
void ViewerPrivate::slotOpenWithDialogCurrentContent()
|
|
{
|
|
if(!mCurrentContent)
|
|
return;
|
|
attachmentOpenWith( mCurrentContent );
|
|
}
|
|
|
|
void ViewerPrivate::slotOpenWithDialog()
|
|
{
|
|
KMime::Content::List contents = selectedContents();
|
|
if(contents.count() == 1) {
|
|
attachmentOpenWith( contents.first() );
|
|
}
|
|
}
|
|
|
|
void ViewerPrivate::slotOpenWithActionCurrentContent(QAction* act)
|
|
{
|
|
if(!mCurrentContent)
|
|
return;
|
|
KService::Ptr app = act->data().value<KService::Ptr>();
|
|
attachmentOpenWith( mCurrentContent,app );
|
|
}
|
|
|
|
void ViewerPrivate::slotOpenWithAction(QAction *act)
|
|
{
|
|
KService::Ptr app = act->data().value<KService::Ptr>();
|
|
KMime::Content::List contents = selectedContents();
|
|
if(contents.count() == 1) {
|
|
attachmentOpenWith( contents.first(),app );
|
|
}
|
|
}
|
|
|
|
|
|
void ViewerPrivate::showAttachmentPopup( KMime::Content* node, const QString & name, const QPoint & globalPos )
|
|
{
|
|
prepareHandleAttachment( node, name );
|
|
KMenu *menu = new KMenu();
|
|
QAction *action;
|
|
bool deletedAttachment = false;
|
|
if(node->contentType(false)) {
|
|
deletedAttachment = (node->contentType()->mimeType() == "text/x-moz-deleted");
|
|
}
|
|
QSignalMapper *attachmentMapper = new QSignalMapper( menu );
|
|
connect( attachmentMapper, SIGNAL(mapped(int)),
|
|
this, SLOT(slotHandleAttachment(int)) );
|
|
|
|
action = menu->addAction(SmallIcon(QLatin1String("document-open")),i18nc("to open", "Open"));
|
|
action->setEnabled(!deletedAttachment);
|
|
connect( action, SIGNAL(triggered(bool)), attachmentMapper, SLOT(map()) );
|
|
attachmentMapper->setMapping( action, Viewer::Open );
|
|
|
|
if(!deletedAttachment)
|
|
createOpenWithMenu( menu, QLatin1String(node->contentType()->mimeType()),true );
|
|
|
|
action = menu->addAction(i18nc("to view something", "View") );
|
|
action->setEnabled(!deletedAttachment);
|
|
connect( action, SIGNAL(triggered(bool)), attachmentMapper, SLOT(map()) );
|
|
attachmentMapper->setMapping( action, Viewer::View );
|
|
|
|
const bool attachmentInHeader = mViewer->isAttachmentInjectionPoint( globalPos );
|
|
const bool hasScrollbar = mViewer->hasVerticalScrollBar();
|
|
if ( attachmentInHeader && hasScrollbar ) {
|
|
action = menu->addAction( i18n( "Scroll To" ) );
|
|
connect( action, SIGNAL(triggered(bool)), attachmentMapper, SLOT(map()) );
|
|
attachmentMapper->setMapping( action, Viewer::ScrollTo );
|
|
}
|
|
|
|
action = menu->addAction(SmallIcon(QLatin1String("document-save-as")),i18n("Save As...") );
|
|
action->setEnabled(!deletedAttachment);
|
|
connect( action, SIGNAL(triggered(bool)), attachmentMapper, SLOT(map()) );
|
|
attachmentMapper->setMapping( action, Viewer::Save );
|
|
|
|
action = menu->addAction(SmallIcon(QLatin1String("edit-copy")), i18n("Copy") );
|
|
action->setEnabled(!deletedAttachment);
|
|
connect( action, SIGNAL(triggered(bool)), attachmentMapper, SLOT(map()) );
|
|
attachmentMapper->setMapping( action, Viewer::Copy );
|
|
|
|
const bool isEncapsulatedMessage = node->parent() && node->parent()->bodyIsMessage();
|
|
const bool canChange = mMessageItem.isValid() && mMessageItem.parentCollection().isValid() &&
|
|
( mMessageItem.parentCollection().rights() != Akonadi::Collection::ReadOnly ) &&
|
|
!isEncapsulatedMessage;
|
|
|
|
|
|
if ( GlobalSettings::self()->allowAttachmentEditing() ) {
|
|
action = menu->addAction(SmallIcon(QLatin1String("document-properties")), i18n("Edit Attachment") );
|
|
connect( action, SIGNAL(triggered()), attachmentMapper, SLOT(map()) );
|
|
attachmentMapper->setMapping( action, Viewer::Edit );
|
|
action->setEnabled( canChange );
|
|
}
|
|
if ( GlobalSettings::self()->allowAttachmentDeletion() ) {
|
|
action = menu->addAction(SmallIcon(QLatin1String("edit-delete")), i18n("Delete Attachment") );
|
|
connect( action, SIGNAL(triggered()), attachmentMapper, SLOT(map()) );
|
|
attachmentMapper->setMapping( action, Viewer::Delete );
|
|
action->setEnabled( canChange && !deletedAttachment );
|
|
}
|
|
if ( name.endsWith( QLatin1String(".xia"), Qt::CaseInsensitive )
|
|
&& Kleo::CryptoBackendFactory::instance()->protocol( "Chiasmus" )) {
|
|
action = menu->addAction( i18n( "Decrypt With Chiasmus..." ) );
|
|
connect( action, SIGNAL(triggered(bool)), attachmentMapper, SLOT(map()) );
|
|
attachmentMapper->setMapping( action, Viewer::ChiasmusEncrypt );
|
|
}
|
|
action = menu->addAction(i18n("Properties") );
|
|
connect( action, SIGNAL(triggered(bool)), attachmentMapper, SLOT(map()) );
|
|
attachmentMapper->setMapping( action, Viewer::Properties );
|
|
menu->exec( globalPos );
|
|
delete menu;
|
|
}
|
|
|
|
void ViewerPrivate::prepareHandleAttachment( KMime::Content *node, const QString& fileName )
|
|
{
|
|
mCurrentContent = node;
|
|
mCurrentFileName = fileName;
|
|
}
|
|
|
|
QString ViewerPrivate::createAtmFileLink( const QString& atmFileName ) const
|
|
{
|
|
QFileInfo atmFileInfo( atmFileName );
|
|
|
|
// tempfile name is /TMP/attachmentsRANDOM/atmFileInfo.fileName()"
|
|
KTempDir *linkDir = new KTempDir( KStandardDirs::locateLocal( "tmp", QLatin1String("attachments") ) );
|
|
QString linkPath = linkDir->name() + atmFileInfo.fileName();
|
|
QFile *linkFile = new QFile( linkPath );
|
|
linkFile->open( QIODevice::ReadWrite );
|
|
const QString linkName = linkFile->fileName();
|
|
delete linkFile;
|
|
delete linkDir;
|
|
|
|
if ( ::link(QFile::encodeName( atmFileName ), QFile::encodeName( linkName )) == 0 ) {
|
|
return linkName; // success
|
|
}
|
|
return QString();
|
|
}
|
|
|
|
KService::Ptr ViewerPrivate::getServiceOffer( KMime::Content *content)
|
|
{
|
|
const QString fileName = mNodeHelper->writeNodeToTempFile( content );
|
|
|
|
const QString contentTypeStr = QLatin1String(content->contentType()->mimeType());
|
|
|
|
// determine the MIME type of the attachment
|
|
KMimeType::Ptr mimetype;
|
|
// prefer the value of the Content-Type header
|
|
mimetype = KMimeType::mimeType( contentTypeStr, KMimeType::ResolveAliases );
|
|
|
|
if ( !mimetype.isNull() && mimetype->is( KABC::Addressee::mimeType() ) ) {
|
|
attachmentView( content );
|
|
return KService::Ptr( 0 );
|
|
}
|
|
|
|
if ( mimetype.isNull() || mimetype->name() == QLatin1String("application/octet-stream") ) {
|
|
/*TODO(Andris) port when on-demand loading is done && msgPart.isComplete() */
|
|
mimetype = MessageViewer::Util::mimetype(fileName);
|
|
}
|
|
return KMimeTypeTrader::self()->preferredService( mimetype->name(), QLatin1String("Application") );
|
|
}
|
|
|
|
KMime::Content::List ViewerPrivate::selectedContents()
|
|
{
|
|
KMime::Content::List contents;
|
|
#ifndef QT_NO_TREEVIEW
|
|
contents = mMimePartTree->selectedContents();
|
|
#endif
|
|
return contents;
|
|
}
|
|
|
|
|
|
void ViewerPrivate::attachmentOpenWith( KMime::Content *node, KService::Ptr offer )
|
|
{
|
|
QString name = mNodeHelper->writeNodeToTempFile( node );
|
|
QString linkName = createAtmFileLink( name );
|
|
KUrl::List lst;
|
|
KUrl url;
|
|
bool autoDelete = true;
|
|
|
|
if ( linkName.isEmpty() ) {
|
|
autoDelete = false;
|
|
linkName = name;
|
|
}
|
|
|
|
KPIMUtils::checkAndCorrectPermissionsIfPossible( linkName, false, true, true );
|
|
|
|
url.setPath( linkName );
|
|
lst.append( url );
|
|
if(offer) {
|
|
if ( (!KRun::run( *offer, lst, 0, autoDelete )) && autoDelete ) {
|
|
QFile::remove(url.toLocalFile());
|
|
}
|
|
} else {
|
|
if ( (! KRun::displayOpenWithDialog(lst, mMainWindow, autoDelete)) && autoDelete ) {
|
|
QFile::remove( url.toLocalFile() );
|
|
}
|
|
}
|
|
}
|
|
|
|
void ViewerPrivate::attachmentOpen( KMime::Content *node )
|
|
{
|
|
KService::Ptr offer = getServiceOffer( node );
|
|
if ( !offer ) {
|
|
kDebug() << "got no offer";
|
|
return;
|
|
}
|
|
attachmentOpenWith( node, offer );
|
|
}
|
|
|
|
CSSHelper* ViewerPrivate::cssHelper() const
|
|
{
|
|
return mCSSHelper;
|
|
}
|
|
|
|
bool ViewerPrivate::decryptMessage() const
|
|
{
|
|
if ( !GlobalSettings::self()->alwaysDecrypt() )
|
|
return mDecrytMessageOverwrite;
|
|
else
|
|
return true;
|
|
}
|
|
|
|
int ViewerPrivate::pointsToPixel(int pointSize) const
|
|
{
|
|
return (pointSize * mViewer->logicalDpiY() + 36) / 72;
|
|
}
|
|
|
|
void ViewerPrivate::displaySplashPage( const QString &info )
|
|
{
|
|
mMsgDisplay = false;
|
|
adjustLayout();
|
|
|
|
#ifdef KDEPIM_MOBILE_UI
|
|
const QString location = KStandardDirs::locate( "data", QLatin1String("messageviewer/about/main_mobile.html") );
|
|
QString content = QLatin1String( KPIMUtils::kFileToByteArray( location ) );
|
|
content = content.arg( QLatin1String( "" ) ); // infopage stylesheet
|
|
content = content.arg( QLatin1String( "" ) ); // rtl infopage stylesheet
|
|
#else
|
|
const QString location = KStandardDirs::locate( "data", QLatin1String("kmail2/about/main.html") ); //FIXME(Andras) copy to $KDEDIR/share/apps/messageviewer
|
|
QString content = QLatin1String(KPIMUtils::kFileToByteArray( location ));
|
|
content = content.arg( KStandardDirs::locate( "data", QLatin1String("kdeui/about/kde_infopage.css") ) );
|
|
if ( QApplication::isRightToLeft() )
|
|
content = content.arg( QLatin1String("@import \"") + KStandardDirs::locate( "data",
|
|
QLatin1String("kdeui/about/kde_infopage_rtl.css") ) + QLatin1String("\";"));
|
|
else
|
|
content = content.arg( QString() );
|
|
#endif
|
|
|
|
const QString fontSize = QString::number( pointsToPixel( mCSSHelper->bodyFont().pointSize() ) );
|
|
const QString catchPhrase; //not enough space for a catch phrase at default window size i18n("Part of the Kontact Suite");
|
|
const QString quickDescription = i18n( "The KDE email client." );
|
|
|
|
mViewer->setHtml( content.arg( fontSize ).arg( mAppName ).arg( catchPhrase ).arg( quickDescription ).arg( info ), KUrl::fromPath( location ) );
|
|
mViewer->show();
|
|
}
|
|
|
|
void ViewerPrivate::enableMessageDisplay()
|
|
{
|
|
mMsgDisplay = true;
|
|
adjustLayout();
|
|
}
|
|
|
|
void ViewerPrivate::displayMessage()
|
|
{
|
|
showHideMimeTree();
|
|
|
|
mNodeHelper->setOverrideCodec( mMessage.get(), overrideCodec() );
|
|
|
|
if ( mMessageItem.hasAttribute<MessageViewer::MessageDisplayFormatAttribute>() ) {
|
|
const MessageViewer::MessageDisplayFormatAttribute* const attr = mMessageItem.attribute<MessageViewer::MessageDisplayFormatAttribute>();
|
|
setHtmlLoadExtOverride(attr->remoteContent());
|
|
setDisplayFormatMessageOverwrite(attr->messageFormat());
|
|
}
|
|
|
|
htmlWriter()->begin( QString() );
|
|
htmlWriter()->queue( mCSSHelper->htmlHead( mUseFixedFont ) );
|
|
|
|
if ( !mMainWindow )
|
|
q->setWindowTitle( mMessage->subject()->asUnicodeString() );
|
|
|
|
// Don't update here, parseMsg() can overwrite the HTML mode, which would lead to flicker.
|
|
// It is updated right after parseMsg() instead.
|
|
mColorBar->setMode( Util::Normal, HtmlStatusBar::NoUpdate );
|
|
|
|
if ( mMessageItem.hasAttribute<ErrorAttribute>() ) {
|
|
//TODO: Insert link to clear error so that message might be resent
|
|
const ErrorAttribute* const attr = mMessageItem.attribute<ErrorAttribute>();
|
|
Q_ASSERT( attr );
|
|
const QColor foreground = KColorScheme( QPalette::Active, KColorScheme::View ).foreground( KColorScheme::NegativeText ).color();
|
|
const QColor background = KColorScheme( QPalette::Active, KColorScheme::View ).background( KColorScheme::NegativeBackground ).color();
|
|
|
|
htmlWriter()->queue( QString::fromLatin1("<div style=\"background:%1;color:%2;border:1px solid %3\">%4</div>").arg( background.name(), foreground.name(), foreground.name(), Qt::escape( attr->message() ) ) );
|
|
htmlWriter()->queue( QLatin1String("<p></p>") );
|
|
}
|
|
|
|
parseContent( mMessage.get() );
|
|
mMimePartTree->setRoot( mNodeHelper->messageWithExtraContent( mMessage.get() ) );
|
|
mColorBar->update();
|
|
|
|
htmlWriter()->queue(QLatin1String("</body></html>"));
|
|
connect( mPartHtmlWriter, SIGNAL(finished()), this, SLOT(injectAttachments()), Qt::UniqueConnection );
|
|
connect( mPartHtmlWriter, SIGNAL(finished()), this, SLOT(toggleFullAddressList()), Qt::UniqueConnection );
|
|
connect( mPartHtmlWriter, SIGNAL(finished()), this, SLOT(slotMessageRendered()), Qt::UniqueConnection );
|
|
htmlWriter()->flush();
|
|
}
|
|
|
|
void ViewerPrivate::collectionFetchedForStoringDecryptedMessage( KJob* job )
|
|
{
|
|
if ( job->error() )
|
|
return;
|
|
|
|
Akonadi::Collection col;
|
|
Q_FOREACH( const Akonadi::Collection &c, static_cast<Akonadi::CollectionFetchJob*>( job )->collections() ) {
|
|
if ( c == mMessageItem.parentCollection() ) {
|
|
col = c;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( !col.isValid() )
|
|
return;
|
|
Akonadi::AgentInstance::List instances = Akonadi::AgentManager::self()->instances();
|
|
const QString itemResource = col.resource();
|
|
Akonadi::AgentInstance resourceInstance;
|
|
foreach ( const Akonadi::AgentInstance &instance, instances ) {
|
|
if ( instance.identifier() == itemResource ) {
|
|
resourceInstance = instance;
|
|
break;
|
|
}
|
|
}
|
|
bool isInOutbox = true;
|
|
Akonadi::Collection outboxCollection = Akonadi::SpecialMailCollections::self()->collection(
|
|
Akonadi::SpecialMailCollections::Outbox, resourceInstance );
|
|
if ( resourceInstance.isValid() && outboxCollection != col ) {
|
|
isInOutbox = false;
|
|
}
|
|
|
|
if ( !isInOutbox ) {
|
|
KMime::Message::Ptr unencryptedMessage = mNodeHelper->unencryptedMessage( mMessage );
|
|
if ( unencryptedMessage ) {
|
|
mMessageItem.setPayload<KMime::Message::Ptr>( unencryptedMessage );
|
|
Akonadi::ItemModifyJob *job = new Akonadi::ItemModifyJob( mMessageItem );
|
|
connect( job, SIGNAL(result(KJob*)), SLOT(itemModifiedResult(KJob*)) );
|
|
}
|
|
}
|
|
}
|
|
|
|
void ViewerPrivate::postProcessMessage( ObjectTreeParser *otp, KMMsgEncryptionState encryptionState )
|
|
{
|
|
if ( GlobalSettings::self()->storeDisplayedMessagesUnencrypted() )
|
|
{
|
|
|
|
// Hack to make sure the S/MIME CryptPlugs follows the strict requirement
|
|
// of german government:
|
|
// --> All received encrypted messages *must* be stored in unencrypted form
|
|
// after they have been decrypted once the user has read them.
|
|
// ( "Aufhebung der Verschluesselung nach dem Lesen" )
|
|
//
|
|
// note: Since there is no configuration option for this, we do that for
|
|
// all kinds of encryption now - *not* just for S/MIME.
|
|
// This could be changed in the objectTreeToDecryptedMsg() function
|
|
// by deciding when (or when not, resp.) to set the 'dataNode' to
|
|
// something different than 'curNode'.
|
|
|
|
const bool messageAtLeastPartiallyEncrypted = ( KMMsgFullyEncrypted == encryptionState ) ||
|
|
( KMMsgPartiallyEncrypted == encryptionState );
|
|
// only proceed if we were called the normal way - not by
|
|
// double click on the message (==not running in a separate window)
|
|
if( decryptMessage() && // only proceed if the message has actually been decrypted
|
|
!otp->hasPendingAsyncJobs() && // only proceed if no pending async jobs are running:
|
|
messageAtLeastPartiallyEncrypted ) {
|
|
//check if the message is in the outbox folder
|
|
//FIXME: using root() is too much, but using mMessageItem.parentCollection() returns no collections in job->collections()
|
|
//FIXME: this is done async, which means it is possible that the user selects another message while
|
|
// this job is running. In that case, collectionFetchedForStoringDecryptedMessage() will work
|
|
// on the wrong item!
|
|
Akonadi::CollectionFetchJob* job = new Akonadi::CollectionFetchJob( Akonadi::Collection::root(),
|
|
Akonadi::CollectionFetchJob::Recursive );
|
|
connect( job, SIGNAL(result(KJob*)),
|
|
this, SLOT(collectionFetchedForStoringDecryptedMessage(KJob*)) );
|
|
}
|
|
}
|
|
}
|
|
|
|
void ViewerPrivate::parseContent( KMime::Content *content )
|
|
{
|
|
assert( content != 0 );
|
|
|
|
// Check if any part of this message is a v-card
|
|
// v-cards can be either text/x-vcard or text/directory, so we need to check
|
|
// both.
|
|
KMime::Content* vCardContent = findContentByType( content, "text/x-vcard" );
|
|
if ( !vCardContent )
|
|
vCardContent = findContentByType( content, "text/directory" );
|
|
|
|
bool hasVCard = false;
|
|
|
|
if( vCardContent ) {
|
|
// ### FIXME: We should only do this if the vCard belongs to the sender,
|
|
// ### i.e. if the sender's email address is contained in the vCard.
|
|
const QByteArray vCard = vCardContent->decodedContent();
|
|
KABC::VCardConverter t;
|
|
if ( !t.parseVCards( vCard ).isEmpty() ) {
|
|
hasVCard = true;
|
|
mNodeHelper->writeNodeToTempFile( vCardContent );
|
|
}
|
|
}
|
|
|
|
if ( !NodeHelper::isToltecMessage( content ) || mShowRawToltecMail ) {
|
|
KMime::Message *message = dynamic_cast<KMime::Message*>( content );
|
|
if ( message ) {
|
|
htmlWriter()->queue( writeMsgHeader( message, hasVCard ? vCardContent : 0, true ) );
|
|
}
|
|
}
|
|
|
|
// Pass control to the OTP now, which does the real work
|
|
mNodeHelper->removeTempFiles();
|
|
mNodeHelper->setNodeUnprocessed( mMessage.get(), true );
|
|
MailViewerSource otpSource( this );
|
|
ObjectTreeParser otp( &otpSource, mNodeHelper, 0, mMessage.get() != content /* show only single node */ );
|
|
otp.setAllowAsync( !mPrinting );
|
|
otp.setShowRawToltecMail( mShowRawToltecMail );
|
|
otp.parseObjectTree( content );
|
|
|
|
// TODO: Setting the signature state to nodehelper is not enough, it should actually
|
|
// be added to the store, so that the message list correctly displays the signature state
|
|
// of messages that were parsed at least once
|
|
// store encrypted/signed status information in the KMMessage
|
|
// - this can only be done *after* calling parseObjectTree()
|
|
KMMsgEncryptionState encryptionState = mNodeHelper->overallEncryptionState( content );
|
|
KMMsgSignatureState signatureState = mNodeHelper->overallSignatureState( content );
|
|
mNodeHelper->setEncryptionState( content, encryptionState );
|
|
// Don't reset the signature state to "not signed" (e.g. if one canceled the
|
|
// decryption of a signed messages which has already been decrypted before).
|
|
if ( signatureState != KMMsgNotSigned ||
|
|
mNodeHelper->signatureState( content ) == KMMsgSignatureStateUnknown ) {
|
|
mNodeHelper->setSignatureState( content, signatureState );
|
|
}
|
|
|
|
postProcessMessage( &otp, encryptionState );
|
|
|
|
showHideMimeTree();
|
|
}
|
|
|
|
|
|
QString ViewerPrivate::writeMsgHeader( KMime::Message *aMsg, KMime::Content* vCardNode,
|
|
bool topLevel )
|
|
{
|
|
if ( !headerStyle() )
|
|
kFatal() << "trying to writeMsgHeader() without a header style set!";
|
|
if ( !headerStrategy() )
|
|
kFatal() << "trying to writeMsgHeader() without a header strategy set!";
|
|
QString href;
|
|
if ( vCardNode )
|
|
href = mNodeHelper->asHREF( vCardNode, QLatin1String("body") );
|
|
|
|
headerStyle()->setHeaderStrategy( headerStrategy() );
|
|
headerStyle()->setVCardName( href );
|
|
headerStyle()->setPrinting( mPrinting );
|
|
headerStyle()->setTopLevel( topLevel );
|
|
headerStyle()->setAllowAsync( true );
|
|
headerStyle()->setSourceObject( this );
|
|
headerStyle()->setNodeHelper( mNodeHelper );
|
|
headerStyle()->setMessagePath( mMessagePath );
|
|
|
|
if ( mMessageItem.isValid() ) {
|
|
Akonadi::MessageStatus status;
|
|
status.setStatusFromFlags( mMessageItem.flags() );
|
|
|
|
headerStyle()->setMessageStatus( status );
|
|
}
|
|
|
|
return headerStyle()->format( aMsg );
|
|
}
|
|
|
|
void ViewerPrivate::showVCard( KMime::Content* msgPart ) {
|
|
const QByteArray vCard = msgPart->decodedContent();
|
|
|
|
VCardViewer *vcv = new VCardViewer( mMainWindow, vCard );
|
|
vcv->setAttribute( Qt::WA_DeleteOnClose );
|
|
vcv->show();
|
|
}
|
|
|
|
|
|
void ViewerPrivate::initHtmlWidget()
|
|
{
|
|
mViewer->setFocusPolicy( Qt::WheelFocus );
|
|
#if 0
|
|
// (marc) I guess this is not needed? All events go through the
|
|
// Viewer, since the Page is just a QObject, and we're only
|
|
// interested in mouse events...
|
|
mViewer->page()->view()->installEventFilter( this );
|
|
#endif
|
|
mViewer->installEventFilter( this );
|
|
|
|
if ( !htmlWriter() ) {
|
|
mPartHtmlWriter = new WebKitPartHtmlWriter( mViewer, 0 );
|
|
#ifdef MESSAGEVIEWER_READER_HTML_DEBUG
|
|
mHtmlWriter = new TeeHtmlWriter( new FileHtmlWriter( QString() ),
|
|
mPartHtmlWriter );
|
|
#else
|
|
mHtmlWriter = mPartHtmlWriter;
|
|
#endif
|
|
}
|
|
|
|
connect( mViewer, SIGNAL(linkHovered(QString,QString,QString)),
|
|
this, SLOT(slotUrlOn(QString,QString,QString)) );
|
|
connect( mViewer, SIGNAL(linkClicked(QUrl)),
|
|
this, SLOT(slotUrlOpen(QUrl)), Qt::QueuedConnection );
|
|
connect( mViewer, SIGNAL(popupMenu(QUrl,QUrl,QPoint)),
|
|
SLOT(slotUrlPopup(QUrl,QUrl,QPoint)) );
|
|
connect( mViewer, SIGNAL(messageMayBeAScam()), this, SLOT(slotMessageMayBeAScam()));
|
|
connect( mScamDetectionWarning, SIGNAL(showDetails()), mViewer, SLOT(slotShowDetails()));
|
|
connect( mScamDetectionWarning, SIGNAL(moveMessageToTrash()), this, SIGNAL(moveMessageToTrash()));
|
|
connect( mScamDetectionWarning, SIGNAL(messageIsNotAScam()), this, SLOT(slotMessageIsNotAScam()));
|
|
connect( mScamDetectionWarning, SIGNAL(addToWhiteList()), this, SLOT(slotAddToWhiteList()));
|
|
}
|
|
|
|
bool ViewerPrivate::eventFilter( QObject *, QEvent *e )
|
|
{
|
|
if ( e->type() == QEvent::MouseButtonPress ) {
|
|
QMouseEvent* me = static_cast<QMouseEvent*>(e);
|
|
if ( me->button() == Qt::LeftButton && ( me->modifiers() & Qt::ShiftModifier ) ) {
|
|
// special processing for shift+click
|
|
URLHandlerManager::instance()->handleShiftClick( mHoveredUrl, this );
|
|
return true;
|
|
}
|
|
if ( me->button() == Qt::LeftButton ) {
|
|
mCanStartDrag = URLHandlerManager::instance()->willHandleDrag( mHoveredUrl, this );
|
|
mLastClickPosition = me->pos();
|
|
}
|
|
}
|
|
else if ( e->type() == QEvent::MouseButtonRelease ) {
|
|
mCanStartDrag = false;
|
|
}
|
|
else if ( e->type() == QEvent::MouseMove ) {
|
|
|
|
QMouseEvent* me = static_cast<QMouseEvent*>( e );
|
|
|
|
// First, update the hovered URL
|
|
mHoveredUrl = mViewer->linkOrImageUrlAt( me->globalPos() );
|
|
|
|
// If we are potentially handling a drag, deal with that.
|
|
if ( mCanStartDrag && me->buttons() & Qt::LeftButton ) {
|
|
|
|
if ( ( mLastClickPosition - me->pos() ).manhattanLength() > KGlobalSettings::dndEventDelay() ) {
|
|
if ( URLHandlerManager::instance()->handleDrag( mHoveredUrl, this ) ) {
|
|
|
|
// If the URL handler manager started a drag, don't handle this in the future
|
|
mCanStartDrag = false;
|
|
}
|
|
}
|
|
|
|
// Don't tell WebKit about this mouse move event, or it might start its own drag!
|
|
return true;
|
|
}
|
|
}
|
|
//Don't tell to Webkit to get zoom > 300 and < 100
|
|
else if ( e->type() == QEvent::Wheel ) {
|
|
QWheelEvent* me = static_cast<QWheelEvent*>( e );
|
|
if ( QApplication::keyboardModifiers() & Qt::ControlModifier ) {
|
|
const int numDegrees = me->delta() / 8;
|
|
const int numSteps = numDegrees / 15;
|
|
const qreal factor = mZoomFactor + numSteps * 10;
|
|
if ( factor >= 10 && factor <= 300 ) {
|
|
mZoomFactor = factor;
|
|
setZoomFactor( factor/100.0 );
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// standard event processing
|
|
return false;
|
|
}
|
|
|
|
void ViewerPrivate::readConfig()
|
|
{
|
|
delete mCSSHelper;
|
|
mCSSHelper = new CSSHelper( mViewer );
|
|
|
|
mUseFixedFont = GlobalSettings::self()->useFixedFont();
|
|
if ( mToggleFixFontAction )
|
|
mToggleFixFontAction->setChecked( mUseFixedFont );
|
|
|
|
mHtmlMailGlobalSetting = GlobalSettings::self()->htmlMail();
|
|
mHtmlLoadExternalGlobalSetting = GlobalSettings::self()->htmlLoadExternal();
|
|
|
|
mZoomTextOnly = GlobalSettings::self()->zoomTextOnly();
|
|
setZoomTextOnly( mZoomTextOnly );
|
|
|
|
if (headerStrategy() )
|
|
headerStrategy()->readConfig();
|
|
KToggleAction *raction = actionForHeaderStyle( headerStyle(), headerStrategy() );
|
|
if ( raction )
|
|
raction->setChecked( true );
|
|
|
|
setAttachmentStrategy( AttachmentStrategy::create( GlobalSettings::self()->attachmentStrategy() ) );
|
|
raction = actionForAttachmentStrategy( attachmentStrategy() );
|
|
if ( raction )
|
|
raction->setChecked( true );
|
|
|
|
adjustLayout();
|
|
|
|
readGlobalOverrideCodec();
|
|
|
|
// Note that this call triggers an update, see this call has to be at the
|
|
// bottom when all settings are already est.
|
|
setHeaderStyleAndStrategy( HeaderStyle::create( GlobalSettings::self()->headerStyle() ),
|
|
HeaderStrategy::create( GlobalSettings::self()->headerSetDisplayed() ) );
|
|
initGrantleeThemeName();
|
|
|
|
#ifndef KDEPIM_NO_WEBKIT
|
|
mViewer->settings()->setFontSize( QWebSettings::MinimumFontSize, GlobalSettings::self()->minimumFontSize() );
|
|
mViewer->settings()->setFontSize( QWebSettings::MinimumLogicalFontSize, GlobalSettings::self()->minimumFontSize() );
|
|
mViewer->settings()->setAttribute( QWebSettings::PrintElementBackgrounds, GlobalSettings::self()->printBackgroundColorImages() );
|
|
#endif
|
|
|
|
if ( mMessage )
|
|
update();
|
|
mColorBar->update();
|
|
}
|
|
|
|
void ViewerPrivate::slotGeneralFontChanged()
|
|
{
|
|
delete mCSSHelper;
|
|
mCSSHelper = new CSSHelper( mViewer );
|
|
if ( mMessage )
|
|
update();
|
|
}
|
|
|
|
|
|
void ViewerPrivate::writeConfig( bool sync )
|
|
{
|
|
GlobalSettings::self()->setUseFixedFont( mUseFixedFont );
|
|
if ( headerStyle() ) {
|
|
GlobalSettings::self()->setHeaderStyle( QLatin1String(headerStyle()->name()) );
|
|
GrantleeTheme::GrantleeSettings::self()->setGrantleeMailThemeName( headerStyle()->theme().dirName() );
|
|
}
|
|
if ( headerStrategy() )
|
|
GlobalSettings::self()->setHeaderSetDisplayed( QLatin1String(headerStrategy()->name()) );
|
|
if ( attachmentStrategy() )
|
|
GlobalSettings::self()->setAttachmentStrategy( QLatin1String(attachmentStrategy()->name()) );
|
|
GlobalSettings::self()->setZoomTextOnly( mZoomTextOnly );
|
|
|
|
saveSplitterSizes();
|
|
if ( sync )
|
|
emit requestConfigSync();
|
|
}
|
|
|
|
|
|
void ViewerPrivate::setHeaderStyleAndStrategy( HeaderStyle * style,
|
|
HeaderStrategy * strategy , bool writeInConfigFile ) {
|
|
|
|
if ( mHeaderStyle == style && mHeaderStrategy == strategy )
|
|
return;
|
|
|
|
mHeaderStyle = style ? style : HeaderStyle::fancy();
|
|
mHeaderStrategy = strategy ? strategy : HeaderStrategy::rich();
|
|
if ( mHeaderOnlyAttachmentsAction ) {
|
|
mHeaderOnlyAttachmentsAction->setEnabled( mHeaderStyle->hasAttachmentQuickList() );
|
|
if ( !mHeaderStyle->hasAttachmentQuickList() &&
|
|
mAttachmentStrategy->requiresAttachmentListInHeader() ) {
|
|
// Style changed to something without an attachment quick list, need to change attachment
|
|
// strategy
|
|
setAttachmentStrategy( AttachmentStrategy::smart() );
|
|
actionForAttachmentStrategy( mAttachmentStrategy )->setChecked( true );
|
|
}
|
|
}
|
|
update( Viewer::Force );
|
|
|
|
if( !mExternalWindow && writeInConfigFile)
|
|
writeConfig();
|
|
|
|
}
|
|
|
|
|
|
void ViewerPrivate::setAttachmentStrategy( const AttachmentStrategy * strategy ) {
|
|
if ( mAttachmentStrategy == strategy )
|
|
return;
|
|
mAttachmentStrategy = strategy ? strategy : AttachmentStrategy::smart();
|
|
update( Viewer::Force );
|
|
}
|
|
|
|
|
|
void ViewerPrivate::setOverrideEncoding( const QString & encoding )
|
|
{
|
|
if ( encoding == mOverrideEncoding )
|
|
return;
|
|
|
|
mOverrideEncoding = encoding;
|
|
if ( mSelectEncodingAction ) {
|
|
if ( encoding.isEmpty() ) {
|
|
mSelectEncodingAction->setCurrentItem( 0 );
|
|
}
|
|
else {
|
|
const QStringList encodings = mSelectEncodingAction->items();
|
|
int i = 0;
|
|
for ( QStringList::const_iterator it = encodings.constBegin(), end = encodings.constEnd(); it != end; ++it, ++i ) {
|
|
if ( NodeHelper::encodingForName( *it ) == encoding ) {
|
|
mSelectEncodingAction->setCurrentItem( i );
|
|
break;
|
|
}
|
|
}
|
|
if ( i == encodings.size() ) {
|
|
// the value of encoding is unknown => use Auto
|
|
kWarning() << "Unknown override character encoding" << encoding
|
|
<< ". Using Auto instead.";
|
|
mSelectEncodingAction->setCurrentItem( 0 );
|
|
mOverrideEncoding.clear();
|
|
}
|
|
}
|
|
}
|
|
update( Viewer::Force );
|
|
}
|
|
|
|
void ViewerPrivate::printMessage( const Akonadi::Item &message )
|
|
{
|
|
disconnect( mPartHtmlWriter, SIGNAL(finished()), this, SLOT(slotPrintMsg()) );
|
|
connect( mPartHtmlWriter, SIGNAL(finished()), this, SLOT(slotPrintMsg()) );
|
|
setMessageItem( message, Viewer::Force );
|
|
}
|
|
|
|
void ViewerPrivate::printPreviewMessage( const Akonadi::Item &message )
|
|
{
|
|
disconnect( mPartHtmlWriter, SIGNAL(finished()), this, SLOT(slotPrintPreview()) );
|
|
connect( mPartHtmlWriter, SIGNAL(finished()), this, SLOT(slotPrintPreview()) );
|
|
setMessageItem( message, Viewer::Force );
|
|
}
|
|
|
|
|
|
void ViewerPrivate::resetStateForNewMessage()
|
|
{
|
|
mClickedUrl.clear();
|
|
mImageUrl.clear();
|
|
enableMessageDisplay(); // just to make sure it's on
|
|
mMessage.reset();
|
|
mNodeHelper->clear();
|
|
mMessagePartNode = 0;
|
|
mMimePartTree->clearModel();
|
|
mSavedRelativePosition = 0;
|
|
setShowSignatureDetails( false );
|
|
mShowRawToltecMail = !GlobalSettings::self()->showToltecReplacementText();
|
|
mFindBar->closeBar();
|
|
mTranslatorWidget->slotCloseWidget();
|
|
mCreateTodo->slotCloseWidget();
|
|
mCreateEvent->slotCloseWidget();
|
|
mScamDetectionWarning->setVisible(false);
|
|
mOpenAttachmentFolderWidget->setVisible(false);
|
|
|
|
if ( mPrinting ) {
|
|
if (MessageViewer::GlobalSettings::self()->respectExpandCollapseSettings()) {
|
|
if (MessageViewer::GlobalSettings::self()->showExpandQuotesMark()) {
|
|
mLevelQuote = MessageViewer::GlobalSettings::self()->collapseQuoteLevelSpin() -1;
|
|
} else {
|
|
mLevelQuote = -1;
|
|
}
|
|
} else {
|
|
mLevelQuote = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ViewerPrivate::setMessageInternal( const KMime::Message::Ptr message,
|
|
Viewer::UpdateMode updateMode )
|
|
{
|
|
mMessage = message;
|
|
if ( message ) {
|
|
mNodeHelper->setOverrideCodec( mMessage.get(), overrideCodec() );
|
|
}
|
|
|
|
mMimePartTree->setRoot( mNodeHelper->messageWithExtraContent( message.get() ) );
|
|
update( updateMode );
|
|
}
|
|
|
|
void ViewerPrivate::setMessageItem( const Akonadi::Item &item, Viewer::UpdateMode updateMode )
|
|
{
|
|
resetStateForNewMessage();
|
|
foreach( const Akonadi::Entity::Id monitoredId, mMonitor.itemsMonitoredEx() ) {
|
|
mMonitor.setItemMonitored( Akonadi::Item( monitoredId ), false );
|
|
}
|
|
Q_ASSERT( mMonitor.itemsMonitoredEx().isEmpty() );
|
|
|
|
mMessageItem = item;
|
|
if ( mMessageItem.isValid() )
|
|
mMonitor.setItemMonitored( mMessageItem, true );
|
|
|
|
if ( !mMessageItem.hasPayload<KMime::Message::Ptr>() ) {
|
|
if ( mMessageItem.isValid() )
|
|
kWarning() << "Payload is not a MessagePtr!";
|
|
return;
|
|
}
|
|
|
|
setMessageInternal( mMessageItem.payload<KMime::Message::Ptr>(), updateMode );
|
|
}
|
|
|
|
void ViewerPrivate::setMessage( const KMime::Message::Ptr& aMsg, Viewer::UpdateMode updateMode )
|
|
{
|
|
resetStateForNewMessage();
|
|
|
|
Akonadi::Item item;
|
|
item.setMimeType( KMime::Message::mimeType() );
|
|
item.setPayload( aMsg );
|
|
mMessageItem = item;
|
|
|
|
setMessageInternal( aMsg, updateMode );
|
|
}
|
|
|
|
void ViewerPrivate::setMessagePart( KMime::Content * node )
|
|
{
|
|
// Cancel scheduled updates of the reader window, as that would stop the
|
|
// timer of the HTML writer, which would make viewing attachment not work
|
|
// anymore as not all HTML is written to the HTML part.
|
|
// We're updating the reader window here ourselves anyway.
|
|
mUpdateReaderWinTimer.stop();
|
|
|
|
if ( node ) {
|
|
mMessagePartNode = node;
|
|
if ( node->bodyIsMessage() ) {
|
|
mMainWindow->setWindowTitle( node->bodyAsMessage()->subject()->asUnicodeString() );
|
|
} else {
|
|
QString windowTitle = NodeHelper::fileName( node );
|
|
if ( windowTitle.isEmpty() ) {
|
|
windowTitle = node->contentDescription()->asUnicodeString();
|
|
}
|
|
if ( !windowTitle.isEmpty() ) {
|
|
mMainWindow->setWindowTitle( i18n( "View Attachment: %1", windowTitle ) );
|
|
}
|
|
}
|
|
|
|
htmlWriter()->begin( QString() );
|
|
htmlWriter()->queue( mCSSHelper->htmlHead( mUseFixedFont ) );
|
|
|
|
parseContent( node );
|
|
|
|
htmlWriter()->queue(QLatin1String("</body></html>"));
|
|
htmlWriter()->flush();
|
|
}
|
|
}
|
|
|
|
void ViewerPrivate::showHideMimeTree( )
|
|
{
|
|
#ifndef QT_NO_TREEVIEW
|
|
bool showMimeTree = false;
|
|
if ( GlobalSettings::self()->mimeTreeMode() == GlobalSettings::EnumMimeTreeMode::Always )
|
|
{
|
|
mMimePartTree->show();
|
|
showMimeTree = true;
|
|
}
|
|
else {
|
|
// don't rely on QSplitter maintaining sizes for hidden widgets:
|
|
saveSplitterSizes();
|
|
mMimePartTree->hide();
|
|
showMimeTree = false;
|
|
}
|
|
if ( mToggleMimePartTreeAction && ( mToggleMimePartTreeAction->isChecked() != showMimeTree ) )
|
|
mToggleMimePartTreeAction->setChecked( showMimeTree );
|
|
#endif
|
|
}
|
|
|
|
|
|
void ViewerPrivate::atmViewMsg( KMime::Message::Ptr message )
|
|
{
|
|
Q_ASSERT( message );
|
|
emit showMessage( message, overrideEncoding() );
|
|
}
|
|
|
|
void ViewerPrivate::adjustLayout()
|
|
{
|
|
#ifndef QT_NO_TREEVIEW
|
|
const int mimeH = GlobalSettings::self()->mimePaneHeight();
|
|
const int messageH = GlobalSettings::self()->messagePaneHeight();
|
|
QList<int> splitterSizes;
|
|
if ( GlobalSettings::self()->mimeTreeLocation() == GlobalSettings::EnumMimeTreeLocation::bottom )
|
|
splitterSizes << messageH << mimeH;
|
|
else
|
|
splitterSizes << mimeH << messageH;
|
|
|
|
if ( GlobalSettings::self()->mimeTreeLocation() == GlobalSettings::EnumMimeTreeLocation::bottom )
|
|
mSplitter->addWidget( mMimePartTree );
|
|
else
|
|
mSplitter->insertWidget( 0, mMimePartTree );
|
|
mSplitter->setSizes( splitterSizes );
|
|
|
|
if ( GlobalSettings::self()->mimeTreeMode() == GlobalSettings::EnumMimeTreeMode::Always &&
|
|
mMsgDisplay )
|
|
mMimePartTree->show();
|
|
else
|
|
mMimePartTree->hide();
|
|
#endif
|
|
|
|
if ( GlobalSettings::self()->showColorBar() && mMsgDisplay )
|
|
mColorBar->show();
|
|
else
|
|
mColorBar->hide();
|
|
}
|
|
|
|
|
|
void ViewerPrivate::saveSplitterSizes() const
|
|
{
|
|
#ifndef QT_NO_TREEVIEW
|
|
if ( !mSplitter || !mMimePartTree )
|
|
return;
|
|
if ( mMimePartTree->isHidden() )
|
|
return; // don't rely on QSplitter maintaining sizes for hidden widgets.
|
|
|
|
const bool mimeTreeAtBottom = GlobalSettings::self()->mimeTreeLocation() == GlobalSettings::EnumMimeTreeLocation::bottom;
|
|
GlobalSettings::self()->setMimePaneHeight( mSplitter->sizes()[ mimeTreeAtBottom ? 1 : 0 ] );
|
|
GlobalSettings::self()->setMessagePaneHeight( mSplitter->sizes()[ mimeTreeAtBottom ? 0 : 1 ] );
|
|
#endif
|
|
}
|
|
|
|
void ViewerPrivate::createWidgets() {
|
|
//TODO: Make a MDN bar similar to Mozillas password bar and show MDNs here as soon as a
|
|
// MDN enabled message is shown.
|
|
QVBoxLayout * vlay = new QVBoxLayout( q );
|
|
vlay->setMargin( 0 );
|
|
mSplitter = new QSplitter( Qt::Vertical, q );
|
|
connect(mSplitter, SIGNAL(splitterMoved(int,int)), this, SLOT(saveSplitterSizes()));
|
|
mSplitter->setObjectName( QLatin1String("mSplitter") );
|
|
mSplitter->setChildrenCollapsible( false );
|
|
vlay->addWidget( mSplitter );
|
|
#ifndef QT_NO_TREEVIEW
|
|
mMimePartTree = new MimePartTreeView( mSplitter );
|
|
connect(mMimePartTree, SIGNAL(activated(QModelIndex)), this, SLOT(slotMimePartSelected(QModelIndex)) );
|
|
connect(mMimePartTree, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotMimeTreeContextMenuRequested(QPoint)) );
|
|
#endif
|
|
|
|
mBox = new KHBox( mSplitter );
|
|
|
|
#ifndef KDEPIM_MOBILE_UI
|
|
mColorBar = new HtmlStatusBar( mBox );
|
|
KVBox *readerBox = new KVBox( mBox );
|
|
#else // for mobile ui we position the HTML status bar on the right side
|
|
KVBox *readerBox = new KVBox( mBox );
|
|
mColorBar = new HtmlStatusBar( mBox );
|
|
#endif
|
|
|
|
mColorBar->setObjectName( QLatin1String("mColorBar") );
|
|
mColorBar->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Expanding );
|
|
|
|
mScamDetectionWarning = new ScamDetectionWarningWidget(readerBox);
|
|
|
|
mOpenAttachmentFolderWidget = new OpenAttachmentFolderWidget(readerBox);
|
|
|
|
mViewer = new MailWebView( mActionCollection, readerBox );
|
|
mViewer->setObjectName( QLatin1String("mViewer") );
|
|
|
|
mCreateTodo = new MessageViewer::TodoEdit(readerBox);
|
|
connect(mCreateTodo, SIGNAL(createTodo(KCalCore::Todo::Ptr,Akonadi::Collection)), this, SLOT(slotCreateTodo(KCalCore::Todo::Ptr,Akonadi::Collection)));
|
|
mCreateTodo->setObjectName(QLatin1String("createtodowidget"));
|
|
mCreateTodo->hide();
|
|
|
|
mCreateEvent = new MessageViewer::EventEdit(readerBox);
|
|
connect(mCreateEvent, SIGNAL(createEvent(KCalCore::Event::Ptr,Akonadi::Collection)), this, SLOT(slotCreateEvent(KCalCore::Event::Ptr,Akonadi::Collection)));
|
|
mCreateEvent->setObjectName(QLatin1String("createeventwidget"));
|
|
mCreateEvent->hide();
|
|
|
|
mSliderContainer = new PimCommon::SlideContainer(readerBox);
|
|
mFindBar = new FindBarMailWebView( mViewer, q );
|
|
connect(mFindBar, SIGNAL(hideFindBar()), mSliderContainer, SLOT(slideOut()));
|
|
mSliderContainer->setContent(mFindBar);
|
|
|
|
mTranslatorWidget = new PimCommon::TranslatorWidget(readerBox);
|
|
#ifndef QT_NO_TREEVIEW
|
|
mSplitter->setStretchFactor( mSplitter->indexOf(mMimePartTree), 0 );
|
|
#endif
|
|
mSplitter->setOpaqueResize( KGlobalSettings::opaqueResize() );
|
|
}
|
|
|
|
void ViewerPrivate::createActions()
|
|
{
|
|
KActionCollection *ac = mActionCollection;
|
|
if ( !ac ) {
|
|
return;
|
|
}
|
|
|
|
KToggleAction *raction = 0;
|
|
|
|
// header style
|
|
KActionMenu *headerMenu = new KActionMenu(i18nc("View->", "&Headers"), this);
|
|
ac->addAction(QLatin1String("view_headers"), headerMenu );
|
|
headerMenu->setHelpText( i18n("Choose display style of message headers") );
|
|
|
|
QActionGroup *group = new QActionGroup( this );
|
|
raction = new KToggleAction( i18nc("View->headers->", "&Enterprise Headers"), this);
|
|
ac->addAction( QLatin1String("view_headers_enterprise"), raction );
|
|
connect( raction, SIGNAL(triggered(bool)), SLOT(slotEnterpriseHeaders()) );
|
|
raction->setHelpText( i18n("Show the list of headers in Enterprise style") );
|
|
group->addAction( raction );
|
|
headerMenu->addAction( raction );
|
|
|
|
raction = new KToggleAction(i18nc("View->headers->", "&Fancy Headers"), this);
|
|
ac->addAction(QLatin1String("view_headers_fancy"), raction );
|
|
connect(raction, SIGNAL(triggered(bool)), SLOT(slotFancyHeaders()));
|
|
raction->setHelpText( i18n("Show the list of headers in a fancy format") );
|
|
group->addAction( raction );
|
|
headerMenu->addAction( raction );
|
|
|
|
raction = new KToggleAction(i18nc("View->headers->", "&Brief Headers"), this);
|
|
ac->addAction(QLatin1String("view_headers_brief"), raction );
|
|
connect(raction, SIGNAL(triggered(bool)), SLOT(slotBriefHeaders()));
|
|
raction->setHelpText( i18n("Show brief list of message headers") );
|
|
group->addAction( raction );
|
|
headerMenu->addAction( raction );
|
|
|
|
raction = new KToggleAction(i18nc("View->headers->", "&Standard Headers"), this);
|
|
ac->addAction(QLatin1String("view_headers_standard"), raction );
|
|
connect(raction, SIGNAL(triggered(bool)), SLOT(slotStandardHeaders()));
|
|
raction->setHelpText( i18n("Show standard list of message headers") );
|
|
group->addAction( raction );
|
|
headerMenu->addAction( raction );
|
|
|
|
raction = new KToggleAction(i18nc("View->headers->", "&Long Headers"), this);
|
|
ac->addAction(QLatin1String("view_headers_long"), raction );
|
|
connect(raction, SIGNAL(triggered(bool)), SLOT(slotLongHeaders()));
|
|
raction->setHelpText( i18n("Show long list of message headers") );
|
|
group->addAction( raction );
|
|
headerMenu->addAction( raction );
|
|
|
|
raction = new KToggleAction(i18nc("View->headers->", "&All Headers"), this);
|
|
ac->addAction(QLatin1String("view_headers_all"), raction );
|
|
connect(raction, SIGNAL(triggered(bool)), SLOT(slotAllHeaders()));
|
|
raction->setHelpText( i18n("Show all message headers") );
|
|
group->addAction( raction );
|
|
headerMenu->addAction( raction );
|
|
|
|
raction = new KToggleAction(i18nc("View->headers->", "&Custom Headers"), this);
|
|
ac->addAction(QLatin1String("view_custom_headers"), raction );
|
|
connect(raction, SIGNAL(triggered(bool)), SLOT(slotCustomHeaders()));
|
|
raction->setHelpText( i18n("Show custom headers") );
|
|
group->addAction( raction );
|
|
headerMenu->addAction( raction );
|
|
//Same action group
|
|
mThemeManager->setActionGroup(group);
|
|
mThemeManager->setThemeMenu(headerMenu);
|
|
|
|
// attachment style
|
|
KActionMenu *attachmentMenu = new KActionMenu(i18nc("View->", "&Attachments"), this);
|
|
ac->addAction(QLatin1String("view_attachments"), attachmentMenu );
|
|
attachmentMenu->setHelpText( i18n("Choose display style of attachments") );
|
|
|
|
group = new QActionGroup( this );
|
|
raction = new KToggleAction(i18nc("View->attachments->", "&As Icons"), this);
|
|
ac->addAction(QLatin1String("view_attachments_as_icons"), raction );
|
|
connect(raction, SIGNAL(triggered(bool)), SLOT(slotIconicAttachments()));
|
|
raction->setHelpText( i18n("Show all attachments as icons. Click to see them.") );
|
|
group->addAction( raction );
|
|
attachmentMenu->addAction( raction );
|
|
|
|
raction = new KToggleAction(i18nc("View->attachments->", "&Smart"), this);
|
|
ac->addAction(QLatin1String("view_attachments_smart"), raction );
|
|
connect(raction, SIGNAL(triggered(bool)), SLOT(slotSmartAttachments()));
|
|
raction->setHelpText( i18n("Show attachments as suggested by sender.") );
|
|
group->addAction( raction );
|
|
attachmentMenu->addAction( raction );
|
|
|
|
raction = new KToggleAction(i18nc("View->attachments->", "&Inline"), this);
|
|
ac->addAction(QLatin1String("view_attachments_inline"), raction );
|
|
connect(raction, SIGNAL(triggered(bool)), SLOT(slotInlineAttachments()));
|
|
raction->setHelpText( i18n("Show all attachments inline (if possible)") );
|
|
group->addAction( raction );
|
|
attachmentMenu->addAction( raction );
|
|
|
|
raction = new KToggleAction(i18nc("View->attachments->", "&Hide"), this);
|
|
ac->addAction(QLatin1String("view_attachments_hide"), raction );
|
|
connect(raction, SIGNAL(triggered(bool)), SLOT(slotHideAttachments()));
|
|
raction->setHelpText( i18n("Do not show attachments in the message viewer") );
|
|
group->addAction( raction );
|
|
attachmentMenu->addAction( raction );
|
|
|
|
mHeaderOnlyAttachmentsAction = new KToggleAction( i18nc( "View->attachments->", "In Header Only" ), this );
|
|
ac->addAction( QLatin1String("view_attachments_headeronly"), mHeaderOnlyAttachmentsAction );
|
|
connect( mHeaderOnlyAttachmentsAction, SIGNAL(triggered(bool)),
|
|
SLOT(slotHeaderOnlyAttachments()) );
|
|
mHeaderOnlyAttachmentsAction->setHelpText( i18n( "Show Attachments only in the header of the mail" ) );
|
|
group->addAction( mHeaderOnlyAttachmentsAction );
|
|
attachmentMenu->addAction( mHeaderOnlyAttachmentsAction );
|
|
|
|
// Set Encoding submenu
|
|
mSelectEncodingAction = new KSelectAction(KIcon(QLatin1String("character-set")), i18n("&Set Encoding"), this);
|
|
mSelectEncodingAction->setToolBarMode( KSelectAction::MenuMode );
|
|
ac->addAction(QLatin1String("encoding"), mSelectEncodingAction );
|
|
connect(mSelectEncodingAction,SIGNAL(triggered(int)),
|
|
SLOT(slotSetEncoding()));
|
|
QStringList encodings = NodeHelper::supportedEncodings( false );
|
|
encodings.prepend( i18n( "Auto" ) );
|
|
mSelectEncodingAction->setItems( encodings );
|
|
mSelectEncodingAction->setCurrentItem( 0 );
|
|
|
|
//
|
|
// Message Menu
|
|
//
|
|
|
|
// copy selected text to clipboard
|
|
mCopyAction = ac->addAction( KStandardAction::Copy, QLatin1String("kmail_copy"), this,
|
|
SLOT(slotCopySelectedText()) );
|
|
|
|
connect( mViewer, SIGNAL(selectionChanged()),
|
|
this, SLOT(viewerSelectionChanged()) );
|
|
viewerSelectionChanged();
|
|
|
|
// copy all text to clipboard
|
|
mSelectAllAction = new KAction(i18n("Select All Text"), this);
|
|
ac->addAction(QLatin1String("mark_all_text"), mSelectAllAction );
|
|
connect(mSelectAllAction, SIGNAL(triggered(bool)), SLOT(selectAll()));
|
|
mSelectAllAction->setShortcut( QKeySequence( Qt::CTRL + Qt::SHIFT + Qt::Key_A ) );
|
|
|
|
// copy Email address to clipboard
|
|
mCopyURLAction = new KAction( KIcon( QLatin1String("edit-copy" )),
|
|
i18n( "Copy Link Address" ), this );
|
|
ac->addAction( QLatin1String("copy_url"), mCopyURLAction );
|
|
connect( mCopyURLAction, SIGNAL(triggered(bool)), SLOT(slotUrlCopy()) );
|
|
|
|
// open URL
|
|
mUrlOpenAction = new KAction( KIcon(QLatin1String( "document-open" )), i18n( "Open URL" ), this );
|
|
ac->addAction( QLatin1String("open_url"), mUrlOpenAction );
|
|
connect( mUrlOpenAction, SIGNAL(triggered(bool)), this, SLOT(slotUrlOpen()) );
|
|
|
|
// use fixed font
|
|
mToggleFixFontAction = new KToggleAction( i18n( "Use Fi&xed Font" ), this );
|
|
ac->addAction(QLatin1String( "toggle_fixedfont"), mToggleFixFontAction );
|
|
connect( mToggleFixFontAction, SIGNAL(triggered(bool)), SLOT(slotToggleFixedFont()) );
|
|
mToggleFixFontAction->setShortcut( QKeySequence( Qt::Key_X ) );
|
|
|
|
|
|
// Zoom actions
|
|
mZoomTextOnlyAction = new KToggleAction( i18n( "Zoom Text Only" ), this );
|
|
ac->addAction( QLatin1String("toggle_zoomtextonly"), mZoomTextOnlyAction );
|
|
connect( mZoomTextOnlyAction, SIGNAL(triggered(bool)), SLOT(slotZoomTextOnly()) );
|
|
mZoomInAction = new KAction( KIcon(QLatin1String("zoom-in")), i18n("&Zoom In"), this);
|
|
ac->addAction(QLatin1String("zoom_in"), mZoomInAction);
|
|
connect(mZoomInAction, SIGNAL(triggered(bool)), SLOT(slotZoomIn()));
|
|
mZoomInAction->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_Plus));
|
|
|
|
mZoomOutAction = new KAction( KIcon(QLatin1String("zoom-out")), i18n("Zoom &Out"), this);
|
|
ac->addAction(QLatin1String("zoom_out"), mZoomOutAction);
|
|
connect(mZoomOutAction, SIGNAL(triggered(bool)), SLOT(slotZoomOut()));
|
|
mZoomOutAction->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_Minus));
|
|
|
|
mZoomResetAction = new KAction( i18n("Reset"), this);
|
|
ac->addAction(QLatin1String("zoom_reset"), mZoomResetAction);
|
|
connect(mZoomResetAction, SIGNAL(triggered(bool)), SLOT(slotZoomReset()));
|
|
mZoomResetAction->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_0));
|
|
|
|
|
|
// Show message structure viewer
|
|
mToggleMimePartTreeAction = new KToggleAction( i18n( "Show Message Structure" ), this );
|
|
ac->addAction( QLatin1String("toggle_mimeparttree"), mToggleMimePartTreeAction );
|
|
connect( mToggleMimePartTreeAction, SIGNAL(toggled(bool)),
|
|
SLOT(slotToggleMimePartTree()));
|
|
|
|
mViewSourceAction = new KAction(i18n("&View Source"), this);
|
|
ac->addAction(QLatin1String("view_source"), mViewSourceAction );
|
|
connect(mViewSourceAction, SIGNAL(triggered(bool)), SLOT(slotShowMessageSource()));
|
|
mViewSourceAction->setShortcut(QKeySequence(Qt::Key_V));
|
|
|
|
mSaveMessageAction = new KAction(KIcon(QLatin1String("document-save-as")), i18n("&Save message..."), this);
|
|
ac->addAction(QLatin1String("save_message"), mSaveMessageAction);
|
|
connect(mSaveMessageAction, SIGNAL(triggered(bool)), SLOT(slotSaveMessage()));
|
|
//Laurent: conflict with kmail shortcut
|
|
//mSaveMessageAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_S));
|
|
|
|
mSaveMessageDisplayFormat = new KAction( i18n("&Save Display Format"), this);
|
|
ac->addAction(QLatin1String("save_message_display_format"), mSaveMessageDisplayFormat );
|
|
connect(mSaveMessageDisplayFormat, SIGNAL(triggered(bool)), SLOT(slotSaveMessageDisplayFormat()));
|
|
|
|
mResetMessageDisplayFormat = new KAction( i18n("&Reset Display Format"), this);
|
|
ac->addAction(QLatin1String("reset_message_display_format"), mResetMessageDisplayFormat );
|
|
connect(mResetMessageDisplayFormat, SIGNAL(triggered(bool)), SLOT(slotResetMessageDisplayFormat()));
|
|
|
|
//
|
|
// Scroll actions
|
|
//
|
|
mScrollUpAction = new KAction( i18n("Scroll Message Up"), this );
|
|
mScrollUpAction->setShortcut( QKeySequence( Qt::Key_Up ) );
|
|
ac->addAction( QLatin1String("scroll_up"), mScrollUpAction );
|
|
connect( mScrollUpAction, SIGNAL(triggered(bool)),
|
|
q, SLOT(slotScrollUp()) );
|
|
|
|
mScrollDownAction = new KAction( i18n("Scroll Message Down"), this );
|
|
mScrollDownAction->setShortcut( QKeySequence( Qt::Key_Down ) );
|
|
ac->addAction( QLatin1String("scroll_down"), mScrollDownAction );
|
|
connect( mScrollDownAction, SIGNAL(triggered(bool)),
|
|
q, SLOT(slotScrollDown()) );
|
|
|
|
mScrollUpMoreAction = new KAction( i18n("Scroll Message Up (More)"), this );
|
|
mScrollUpMoreAction->setShortcut( QKeySequence( Qt::Key_PageUp ) );
|
|
ac->addAction( QLatin1String("scroll_up_more"), mScrollUpMoreAction );
|
|
connect( mScrollUpMoreAction, SIGNAL(triggered(bool)),
|
|
q, SLOT(slotScrollPrior()) );
|
|
|
|
mScrollDownMoreAction = new KAction( i18n("Scroll Message Down (More)"), this );
|
|
mScrollDownMoreAction->setShortcut( QKeySequence( Qt::Key_PageDown ) );
|
|
ac->addAction( QLatin1String("scroll_down_more"), mScrollDownMoreAction );
|
|
connect( mScrollDownMoreAction, SIGNAL(triggered(bool)),
|
|
q, SLOT(slotScrollNext()) );
|
|
|
|
//
|
|
// Actions not in menu
|
|
//
|
|
|
|
// Toggle HTML display mode.
|
|
mToggleDisplayModeAction = new KToggleAction( i18n( "Toggle HTML Display Mode" ), this );
|
|
ac->addAction( QLatin1String("toggle_html_display_mode"), mToggleDisplayModeAction );
|
|
mToggleDisplayModeAction->setShortcut(QKeySequence(Qt::SHIFT + Qt::Key_H));
|
|
connect( mToggleDisplayModeAction, SIGNAL(triggered(bool)),
|
|
SLOT(slotToggleHtmlMode()) );
|
|
mToggleDisplayModeAction->setHelpText( i18n( "Toggle display mode between HTML and plain text" ) );
|
|
|
|
// Load external reference
|
|
KAction *loadExternalReferenceAction = new KAction( i18n( "Load external references" ), this );
|
|
ac->addAction( QLatin1String("load_external_reference"), loadExternalReferenceAction );
|
|
loadExternalReferenceAction->setShortcut(QKeySequence(Qt::SHIFT + Qt::CTRL + Qt::Key_R));
|
|
connect( loadExternalReferenceAction, SIGNAL(triggered(bool)),
|
|
SLOT(slotLoadExternalReference()) );
|
|
loadExternalReferenceAction->setHelpText( i18n( "Load external references from the Internet for this message." ) );
|
|
|
|
|
|
mSpeakTextAction = new KAction(i18n("Speak Text"),this);
|
|
mSpeakTextAction->setIcon(KIcon(QLatin1String("preferences-desktop-text-to-speech")));
|
|
ac->addAction( QLatin1String("speak_text"), mSpeakTextAction );
|
|
connect( mSpeakTextAction, SIGNAL(triggered(bool)),
|
|
this, SLOT(slotSpeakText()) );
|
|
|
|
mCopyImageLocation = new KAction(i18n("Copy Image Location"),this);
|
|
mCopyImageLocation->setIcon(KIcon(QLatin1String("view-media-visualization")));
|
|
ac->addAction(QLatin1String("copy_image_location"), mCopyImageLocation);
|
|
mCopyImageLocation->setShortcutConfigurable( false );
|
|
connect( mCopyImageLocation, SIGNAL(triggered(bool)),
|
|
SLOT(slotCopyImageLocation()) );
|
|
|
|
mTranslateAction = new KAction(i18n("Translate..."),this);
|
|
mTranslateAction->setShortcut(QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_T));
|
|
mTranslateAction->setIcon(KIcon(QLatin1String("preferences-desktop-locale")));
|
|
ac->addAction(QLatin1String("translate_text"), mTranslateAction);
|
|
connect( mTranslateAction, SIGNAL(triggered(bool)),
|
|
SLOT(slotTranslate()) );
|
|
|
|
mFindInMessageAction = new KAction(KIcon(QLatin1String("edit-find")), i18n("&Find in Message..."), this);
|
|
ac->addAction(QLatin1String("find_in_messages"), mFindInMessageAction );
|
|
connect(mFindInMessageAction, SIGNAL(triggered(bool)), SLOT(slotFind()));
|
|
mFindInMessageAction->setShortcut(KStandardShortcut::find());
|
|
|
|
#ifndef KDEPIM_NO_WEBKIT
|
|
#if QTWEBKIT_VERSION >= QTWEBKIT_VERSION_CHECK(2, 3, 0)
|
|
mCaretBrowsing = new KToggleAction(i18n("Toggle Caret Browsing"), this);
|
|
mCaretBrowsing->setShortcut(Qt::Key_F7);
|
|
ac->addAction( QLatin1String("toggle_caret_browsing"), mCaretBrowsing );
|
|
connect( mCaretBrowsing, SIGNAL(triggered(bool)), SLOT(slotToggleCaretBrowsing(bool)) );
|
|
mCaretBrowsing->setChecked(false);
|
|
#endif
|
|
#endif
|
|
mBlockImage = new KAction(i18n("Block image"), this);
|
|
ac->addAction(QLatin1String("adblock_image"), mBlockImage);
|
|
mBlockImage->setShortcutConfigurable( false );
|
|
connect( mBlockImage, SIGNAL(triggered(bool)), SLOT(slotBlockImage()) );
|
|
|
|
mBlockableItems = new KAction(i18n("Open Blockable Items..."), this);
|
|
ac->addAction(QLatin1String("adblock_blockable_items"), mBlockableItems);
|
|
connect( mBlockableItems, SIGNAL(triggered(bool)), SLOT(slotOpenBlockableItems()) );
|
|
|
|
|
|
mExpandUrlAction = new KAction(i18n("Expand Short URL"), this);
|
|
ac->addAction(QLatin1String("expand_short_url"), mExpandUrlAction);
|
|
mExpandUrlAction->setShortcutConfigurable( false );
|
|
connect( mExpandUrlAction, SIGNAL(triggered(bool)), SLOT(slotExpandShortUrl()) );
|
|
|
|
mCreateTodoAction = new KAction(KIcon( QLatin1String("task-new") ),i18n("Create Todo"), this);
|
|
mCreateTodoAction->setIconText( i18n( "Create To-do" ) );
|
|
mCreateTodoAction->setHelpText( i18n( "Allows you to create a calendar to-do or reminder from this message" ) );
|
|
mCreateTodoAction->setWhatsThis( i18n( "This option starts the KOrganizer to-do editor with initial values taken from the currently selected message. Then you can edit the to-do to your liking before saving it to your calendar." ) );
|
|
ac->addAction(QLatin1String("create_todo"), mCreateTodoAction);
|
|
mCreateTodoAction->setShortcut(Qt::CTRL + Qt::Key_T);
|
|
connect( mCreateTodoAction, SIGNAL(triggered(bool)), SLOT(slotShowCreateTodoWidget()) );
|
|
|
|
mCreateEventAction = new KAction(KIcon( QLatin1String("appointment-new") ),i18n("Create Event"), this);
|
|
mCreateEventAction->setIconText( i18n( "Create Event" ) );
|
|
mCreateEventAction->setHelpText( i18n( "Allows you to create a calendar Event" ) );
|
|
ac->addAction(QLatin1String("create_event"), mCreateEventAction);
|
|
mCreateEventAction->setShortcut(Qt::CTRL + Qt::Key_E);
|
|
connect( mCreateEventAction, SIGNAL(triggered(bool)), SLOT(slotShowCreateEventWidget()) );
|
|
}
|
|
|
|
|
|
void ViewerPrivate::showContextMenu( KMime::Content* content, const QPoint &pos )
|
|
{
|
|
#ifndef QT_NO_TREEVIEW
|
|
if ( !content )
|
|
return;
|
|
|
|
bool deletedAttachment = false;
|
|
if(content->contentType(false)) {
|
|
deletedAttachment = (content->contentType()->mimeType() == "text/x-moz-deleted");
|
|
}
|
|
if(deletedAttachment)
|
|
return;
|
|
|
|
const bool isAttachment = !content->contentType()->isMultipart() && !content->isTopLevel();
|
|
const bool isRoot = ( content == mMessage.get() );
|
|
const KMime::Content::List contents = Util::extractAttachments( mMessage.get() );
|
|
|
|
KMenu popup;
|
|
|
|
if ( !isRoot ) {
|
|
popup.addAction( SmallIcon( QLatin1String("document-save-as") ), i18n( "Save &As..." ),
|
|
this, SLOT(slotAttachmentSaveAs()) );
|
|
|
|
if ( isAttachment ) {
|
|
popup.addAction( SmallIcon( QLatin1String("document-open") ), i18nc( "to open", "Open" ),
|
|
this, SLOT(slotAttachmentOpen()) );
|
|
|
|
if(selectedContents().count() == 1)
|
|
createOpenWithMenu(&popup, QLatin1String(content->contentType()->mimeType()),false);
|
|
else
|
|
popup.addAction( i18n( "Open With..." ), this, SLOT(slotAttachmentOpenWith()) );
|
|
popup.addAction( i18nc( "to view something", "View" ), this, SLOT(slotAttachmentView()) );
|
|
}
|
|
}
|
|
|
|
if( !contents.isEmpty() ) {
|
|
popup.addAction( i18n( "Save All Attachments..." ), this,
|
|
SLOT(slotAttachmentSaveAll()) );
|
|
}
|
|
|
|
// edit + delete only for attachments
|
|
if ( !isRoot ) {
|
|
if ( isAttachment ) {
|
|
popup.addAction( SmallIcon( QLatin1String("edit-copy") ), i18n( "Copy" ),
|
|
this, SLOT(slotAttachmentCopy()) );
|
|
#if 0 //FIXME Laurent Comment for the moment it crash see Bug 287177
|
|
if ( GlobalSettings::self()->allowAttachmentDeletion() )
|
|
popup.addAction( SmallIcon( "edit-delete" ), i18n( "Delete Attachment" ),
|
|
this, SLOT(slotAttachmentDelete()) );
|
|
#endif
|
|
if ( GlobalSettings::self()->allowAttachmentEditing() )
|
|
popup.addAction( SmallIcon( QLatin1String("document-properties") ), i18n( "Edit Attachment" ),
|
|
this, SLOT(slotAttachmentEdit()) );
|
|
}
|
|
|
|
if ( !content->isTopLevel() )
|
|
popup.addAction( i18n( "Properties" ), this, SLOT(slotAttachmentProperties()) );
|
|
}
|
|
popup.exec( mMimePartTree->viewport()->mapToGlobal( pos ) );
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
KToggleAction *ViewerPrivate::actionForHeaderStyle( const HeaderStyle * style, const HeaderStrategy * strategy )
|
|
{
|
|
if ( !mActionCollection )
|
|
return 0;
|
|
QString actionName;
|
|
if ( style == HeaderStyle::enterprise() )
|
|
actionName = QLatin1String("view_headers_enterprise");
|
|
else if ( style == HeaderStyle::fancy() )
|
|
actionName = QLatin1String("view_headers_fancy");
|
|
else if ( style == HeaderStyle::brief() )
|
|
actionName = QLatin1String("view_headers_brief");
|
|
else if ( style == HeaderStyle::plain() ) {
|
|
if ( strategy == HeaderStrategy::standard() )
|
|
actionName = QLatin1String("view_headers_standard");
|
|
else if ( strategy == HeaderStrategy::rich() )
|
|
actionName = QLatin1String("view_headers_long");
|
|
else if ( strategy == HeaderStrategy::all() )
|
|
actionName = QLatin1String("view_headers_all");
|
|
} else if (style == HeaderStyle::custom() ) {
|
|
actionName = QLatin1String("view_custom_headers");
|
|
} else if (style == HeaderStyle::grantlee()) {
|
|
return mThemeManager->actionForTheme();
|
|
}
|
|
|
|
if ( actionName.isEmpty() )
|
|
return 0;
|
|
else
|
|
return static_cast<KToggleAction*>(mActionCollection->action(actionName));
|
|
}
|
|
|
|
KToggleAction *ViewerPrivate::actionForAttachmentStrategy( const AttachmentStrategy * as )
|
|
{
|
|
if ( !mActionCollection )
|
|
return 0;
|
|
QString actionName ;
|
|
if ( as == AttachmentStrategy::iconic() )
|
|
actionName = QLatin1String("view_attachments_as_icons");
|
|
else if ( as == AttachmentStrategy::smart() )
|
|
actionName = QLatin1String("view_attachments_smart");
|
|
else if ( as == AttachmentStrategy::inlined() )
|
|
actionName = QLatin1String("view_attachments_inline");
|
|
else if ( as == AttachmentStrategy::hidden() )
|
|
actionName = QLatin1String("view_attachments_hide");
|
|
else if ( as == AttachmentStrategy::headerOnly() )
|
|
actionName = QLatin1String("view_attachments_headeronly");
|
|
|
|
if (actionName.isEmpty())
|
|
return 0;
|
|
else
|
|
return static_cast<KToggleAction*>(mActionCollection->action(actionName));
|
|
}
|
|
|
|
|
|
void ViewerPrivate::readGlobalOverrideCodec()
|
|
{
|
|
// if the global character encoding wasn't changed then there's nothing to do
|
|
if ( MessageCore::GlobalSettings::self()->overrideCharacterEncoding() == mOldGlobalOverrideEncoding )
|
|
return;
|
|
|
|
setOverrideEncoding( MessageCore::GlobalSettings::self()->overrideCharacterEncoding() );
|
|
mOldGlobalOverrideEncoding = MessageCore::GlobalSettings::self()->overrideCharacterEncoding();
|
|
}
|
|
|
|
|
|
const QTextCodec * ViewerPrivate::overrideCodec() const
|
|
{
|
|
if ( mOverrideEncoding.isEmpty() || mOverrideEncoding == QLatin1String("Auto") ) // Auto
|
|
return 0;
|
|
else
|
|
return ViewerPrivate::codecForName( mOverrideEncoding.toLatin1() );
|
|
}
|
|
|
|
|
|
static QColor nextColor( const QColor & c )
|
|
{
|
|
int h, s, v;
|
|
c.getHsv( &h, &s, &v );
|
|
return QColor::fromHsv( (h + 50) % 360, qMax(s, 64), v );
|
|
}
|
|
|
|
QString ViewerPrivate::renderAttachments( KMime::Content * node, const QColor &bgColor ) const
|
|
{
|
|
|
|
if ( !node )
|
|
return QString();
|
|
|
|
QString html;
|
|
KMime::Content * child = MessageCore::NodeHelper::firstChild( node );
|
|
|
|
if ( child) {
|
|
QString subHtml = renderAttachments( child, nextColor( bgColor ) );
|
|
if ( !subHtml.isEmpty() ) {
|
|
|
|
QString visibility;
|
|
if( !mShowAttachmentQuicklist ) {
|
|
visibility.append( QLatin1String("display:none;") );
|
|
}
|
|
|
|
QString margin;
|
|
if ( node != mMessage.get() || headerStyle() != HeaderStyle::enterprise() )
|
|
margin = QLatin1String("padding:2px; margin:2px; ");
|
|
QString align = QLatin1String("left");
|
|
if ( headerStyle() == HeaderStyle::enterprise() )
|
|
align = QLatin1String("right");
|
|
const bool result = ( node->contentType()->mediaType().toLower() == "message" || node->contentType()->mediaType().toLower() == "multipart" || node == mMessage.get() );
|
|
if ( result )
|
|
html += QString::fromLatin1("<div style=\"background:%1; %2"
|
|
"vertical-align:middle; float:%3; %4\">").arg( bgColor.name() ).arg( margin )
|
|
.arg( align ).arg( visibility );
|
|
html += subHtml;
|
|
if ( result )
|
|
html += QLatin1String("</div>");
|
|
}
|
|
} else {
|
|
NodeHelper::AttachmentDisplayInfo info = NodeHelper::attachmentDisplayInfo( node );
|
|
if ( info.displayInHeader ) {
|
|
html += QLatin1String("<div style=\"float:left;\">");
|
|
html += QString::fromLatin1( "<span style=\"white-space:nowrap; border-width: 0px; border-left-width: 5px; border-color: %1; 2px; border-left-style: solid;\">" ).arg( bgColor.name() );
|
|
mNodeHelper->writeNodeToTempFile( node );
|
|
const QString href = mNodeHelper->asHREF( node, QLatin1String("header") );
|
|
html += QString::fromLatin1( "<a href=\"" ) + href +
|
|
QString::fromLatin1( "\">" );
|
|
QString imageMaxSize;
|
|
if(!info.icon.isEmpty()) {
|
|
QImage tmpImg(info.icon);
|
|
if(tmpImg.width() > 48 || tmpImg.height() > 48) {
|
|
imageMaxSize = QLatin1String("width=\"48\" height=\"48\"");
|
|
}
|
|
}
|
|
html += QString::fromLatin1("<img %1 style=\"vertical-align:middle;\" src=\"").arg(imageMaxSize) + info.icon + QLatin1String("\"/> ");
|
|
if ( headerStyle() == HeaderStyle::enterprise() ) {
|
|
QFont bodyFont = mCSSHelper->bodyFont( mUseFixedFont );
|
|
QFontMetrics fm( bodyFont );
|
|
html += fm.elidedText( info.label, Qt::ElideRight, 180 );
|
|
} else if ( headerStyle() == HeaderStyle::fancy() ) {
|
|
QFont bodyFont = mCSSHelper->bodyFont( mUseFixedFont );
|
|
QFontMetrics fm( bodyFont );
|
|
html += fm.elidedText( info.label, Qt::ElideRight, 1000 );
|
|
} else {
|
|
html += info.label;
|
|
}
|
|
html += QLatin1String("</a></span></div> ");
|
|
}
|
|
}
|
|
|
|
KMime::Content *next = MessageCore::NodeHelper::nextSibling( node );
|
|
if ( next )
|
|
html += renderAttachments( next, nextColor ( bgColor ) );
|
|
|
|
return html;
|
|
}
|
|
|
|
KMime::Content* ViewerPrivate::findContentByType(KMime::Content *content, const QByteArray &type)
|
|
{
|
|
KMime::Content::List list = content->contents();
|
|
Q_FOREACH(KMime::Content *c, list) {
|
|
if (c->contentType()->mimeType() == type)
|
|
return c;
|
|
}
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
const QTextCodec* ViewerPrivate::codecForName(const QByteArray& _str)
|
|
{
|
|
if (_str.isEmpty())
|
|
return 0;
|
|
QByteArray codec = _str;
|
|
kAsciiToLower(codec.data());
|
|
return KGlobal::charsets()->codecForName(QLatin1String(codec));
|
|
}
|
|
|
|
void ViewerPrivate::update( MessageViewer::Viewer::UpdateMode updateMode )
|
|
{
|
|
// Avoid flicker, somewhat of a cludge
|
|
if ( updateMode == Viewer::Force ) {
|
|
// stop the timer to avoid calling updateReaderWin twice
|
|
mUpdateReaderWinTimer.stop();
|
|
saveRelativePosition();
|
|
updateReaderWin();
|
|
} else if ( mUpdateReaderWinTimer.isActive() ) {
|
|
mUpdateReaderWinTimer.setInterval( delay );
|
|
} else {
|
|
mUpdateReaderWinTimer.start( 0 );
|
|
}
|
|
}
|
|
|
|
void ViewerPrivate::slotUrlOpen( const QUrl& url )
|
|
{
|
|
KUrl aUrl(url);
|
|
if( !url.isEmpty() )
|
|
mClickedUrl = aUrl;
|
|
|
|
// First, let's see if the URL handler manager can handle the URL. If not, try KRun for some
|
|
// known URLs, otherwise fallback to emitting a signal.
|
|
// That signal is caught by KMail, and in case of mailto URLs, a composer is shown.
|
|
|
|
if ( URLHandlerManager::instance()->handleClick( mClickedUrl, this ) )
|
|
return;
|
|
|
|
emit urlClicked( mMessageItem, mClickedUrl );
|
|
}
|
|
|
|
|
|
void ViewerPrivate::slotUrlOn(const QString& link, const QString& title, const QString& textContent )
|
|
{
|
|
Q_UNUSED(title)
|
|
Q_UNUSED(textContent)
|
|
|
|
// The "link" we get here is not URL-encoded, and therefore there is no way the KUrl or QUrl could
|
|
// parse it correctly. To workaround that, we use QWebFrame::hitTestContent() on the mouse position
|
|
// to get the URL before WebKit managed to mangle it.
|
|
KUrl url( mViewer->linkOrImageUrlAt( QCursor::pos() ) );
|
|
const QString protocol = url.protocol();
|
|
if ( protocol == QLatin1String( "kmail" ) ||
|
|
protocol == QLatin1String( "x-kmail" ) ||
|
|
protocol == QLatin1String( "attachment" ) ||
|
|
( protocol.isEmpty() && url.path().isEmpty() ) ) {
|
|
mViewer->setAcceptDrops( false );
|
|
} else {
|
|
mViewer->setAcceptDrops( true );
|
|
}
|
|
|
|
if ( link.trimmed().isEmpty() ) {
|
|
KPIM::BroadcastStatus::instance()->reset();
|
|
emit showStatusBarMessage( QString() );
|
|
return;
|
|
}
|
|
|
|
QString msg = URLHandlerManager::instance()->statusBarMessage( url, this );
|
|
if ( msg.isEmpty() ) {
|
|
msg = link;
|
|
}
|
|
|
|
KPIM::BroadcastStatus::instance()->setTransientStatusMsg( msg );
|
|
emit showStatusBarMessage( msg );
|
|
}
|
|
|
|
void ViewerPrivate::slotUrlPopup(const QUrl &aUrl, const QUrl &imageUrl, const QPoint& aPos)
|
|
{
|
|
const KUrl url( aUrl );
|
|
const KUrl iUrl( imageUrl );
|
|
mClickedUrl = url;
|
|
mImageUrl = iUrl;
|
|
|
|
if ( URLHandlerManager::instance()->handleContextMenuRequest( url, aPos, this ) )
|
|
return;
|
|
|
|
if ( !mActionCollection )
|
|
return;
|
|
|
|
if ( url.protocol() == QLatin1String( "mailto" ) ) {
|
|
mCopyURLAction->setText( i18n( "Copy Email Address" ) );
|
|
} else {
|
|
mCopyURLAction->setText( i18n( "Copy Link Address" ) );
|
|
}
|
|
|
|
emit popupMenu( mMessageItem, aUrl, imageUrl, aPos );
|
|
}
|
|
|
|
void ViewerPrivate::slotLoadExternalReference()
|
|
{
|
|
if(mColorBar->isNormal() || htmlLoadExtOverride())
|
|
return;
|
|
setHtmlLoadExtOverride( true );
|
|
update( Viewer::Force );
|
|
}
|
|
|
|
void ViewerPrivate::slotToggleHtmlMode()
|
|
{
|
|
if(mColorBar->isNormal())
|
|
return;
|
|
mScamDetectionWarning->setVisible(false);
|
|
const bool useHtml = !htmlMail();
|
|
setDisplayFormatMessageOverwrite( useHtml ? MessageViewer::Viewer::Html : MessageViewer::Viewer::Text );
|
|
update( Viewer::Force );
|
|
}
|
|
|
|
void ViewerPrivate::slotFind()
|
|
{
|
|
#ifndef KDEPIM_NO_WEBKIT
|
|
if ( mViewer->hasSelection() )
|
|
mFindBar->setText( mViewer->selectedText() );
|
|
#endif
|
|
mSliderContainer->slideIn();
|
|
mFindBar->focusAndSetCursor();
|
|
}
|
|
|
|
void ViewerPrivate::slotTranslate()
|
|
{
|
|
const QString text = mViewer->selectedText();
|
|
mTranslatorWidget->show();
|
|
if(!text.isEmpty())
|
|
mTranslatorWidget->setTextToTranslate(text);
|
|
}
|
|
|
|
void ViewerPrivate::slotToggleFixedFont()
|
|
{
|
|
mUseFixedFont = !mUseFixedFont;
|
|
update( Viewer::Force );
|
|
}
|
|
|
|
void ViewerPrivate::slotToggleMimePartTree()
|
|
{
|
|
if ( mToggleMimePartTreeAction->isChecked() )
|
|
GlobalSettings::self()->setMimeTreeMode( GlobalSettings::EnumMimeTreeMode::Always );
|
|
else
|
|
GlobalSettings::self()->setMimeTreeMode( GlobalSettings::EnumMimeTreeMode::Never );
|
|
showHideMimeTree();
|
|
}
|
|
|
|
|
|
void ViewerPrivate::slotShowMessageSource()
|
|
{
|
|
if( !mMessage )
|
|
return;
|
|
mNodeHelper->messageWithExtraContent( mMessage.get() );
|
|
const QString rawMessage = QString::fromLatin1( mMessage->encodedContent() );
|
|
const QString htmlSource = mViewer->htmlSource();
|
|
|
|
MailSourceViewer *viewer = new MailSourceViewer(); // deletes itself upon close
|
|
viewer->setWindowTitle( i18n("Message as Plain Text") );
|
|
viewer->setRawSource( rawMessage );
|
|
viewer->setDisplayedSource( htmlSource );
|
|
if( mUseFixedFont ) {
|
|
viewer->setFixedFont();
|
|
}
|
|
|
|
// Well, there is no widget to be seen here, so we have to use QCursor::pos()
|
|
// Update: (GS) I'm not going to make this code behave according to Xinerama
|
|
// configuration because this is quite the hack.
|
|
if ( QApplication::desktop()->isVirtualDesktop() ) {
|
|
#ifndef QT_NO_CURSOR
|
|
int scnum = QApplication::desktop()->screenNumber( QCursor::pos() );
|
|
#else
|
|
int scnum = 0;
|
|
#endif
|
|
viewer->resize( QApplication::desktop()->screenGeometry( scnum ).width()/2,
|
|
2 * QApplication::desktop()->screenGeometry( scnum ).height()/3);
|
|
} else {
|
|
viewer->resize( QApplication::desktop()->geometry().width()/2,
|
|
2 * QApplication::desktop()->geometry().height()/3);
|
|
}
|
|
viewer->show();
|
|
}
|
|
|
|
void ViewerPrivate::updateReaderWin()
|
|
{
|
|
if ( !mMsgDisplay ) {
|
|
return;
|
|
}
|
|
|
|
if ( mRecursionCountForDisplayMessage + 1 > 1 ) {
|
|
// This recursion here can happen because the ObjectTreeParser in parseMsg() can exec() an
|
|
// eventloop.
|
|
// This happens in two cases:
|
|
// 1) The ContactSearchJob started by FancyHeaderStyle::format
|
|
// 2) Various modal passphrase dialogs for decryption of a message (bug 96498)
|
|
//
|
|
// While the exec() eventloop is running, it is possible that a timer calls updateReaderWin(),
|
|
// and not aborting here would confuse the state terribly.
|
|
kWarning() << "Danger, recursion while displaying a message!";
|
|
return;
|
|
}
|
|
mRecursionCountForDisplayMessage++;
|
|
|
|
mViewer->setAllowExternalContent( htmlLoadExternal() );
|
|
|
|
htmlWriter()->reset();
|
|
//TODO: if the item doesn't have the payload fetched, try to fetch it? Maybe not here, but in setMessageItem.
|
|
if ( mMessage ) {
|
|
if ( GlobalSettings::self()->showColorBar() ) {
|
|
mColorBar->show();
|
|
} else {
|
|
mColorBar->hide();
|
|
}
|
|
displayMessage();
|
|
} else if( mMessagePartNode ) {
|
|
setMessagePart( mMessagePartNode );
|
|
} else {
|
|
mColorBar->hide();
|
|
#ifndef QT_NO_TREEVIEW
|
|
mMimePartTree->hide();
|
|
#endif
|
|
htmlWriter()->begin( QString() );
|
|
htmlWriter()->write( mCSSHelper->htmlHead( mUseFixedFont ) + QLatin1String("</body></html>") );
|
|
htmlWriter()->end();
|
|
}
|
|
|
|
if ( mSavedRelativePosition ) {
|
|
mViewer->scrollToRelativePosition( mSavedRelativePosition );
|
|
mSavedRelativePosition = 0;
|
|
}
|
|
mRecursionCountForDisplayMessage--;
|
|
}
|
|
|
|
|
|
void ViewerPrivate::slotMimePartSelected( const QModelIndex &index )
|
|
{
|
|
KMime::Content *content = static_cast<KMime::Content*>( index.internalPointer() );
|
|
if ( !mMimePartTree->mimePartModel()->parent( index ).isValid() && index.row() == 0 ) {
|
|
update( Viewer::Force );
|
|
} else {
|
|
setMessagePart( content );
|
|
}
|
|
}
|
|
|
|
void ViewerPrivate::slotBriefHeaders()
|
|
{
|
|
setHeaderStyleAndStrategy( HeaderStyle::brief(),
|
|
HeaderStrategy::brief(),true );
|
|
}
|
|
|
|
|
|
void ViewerPrivate::slotFancyHeaders()
|
|
{
|
|
|
|
setHeaderStyleAndStrategy( HeaderStyle::fancy(),
|
|
HeaderStrategy::rich(), true );
|
|
}
|
|
|
|
|
|
void ViewerPrivate::slotEnterpriseHeaders()
|
|
{
|
|
setHeaderStyleAndStrategy( HeaderStyle::enterprise(),
|
|
HeaderStrategy::rich(),true );
|
|
}
|
|
|
|
|
|
void ViewerPrivate::slotStandardHeaders()
|
|
{
|
|
setHeaderStyleAndStrategy( HeaderStyle::plain(),
|
|
HeaderStrategy::standard(), true);
|
|
}
|
|
|
|
|
|
void ViewerPrivate::slotLongHeaders()
|
|
{
|
|
setHeaderStyleAndStrategy( HeaderStyle::plain(),
|
|
HeaderStrategy::rich(),true );
|
|
}
|
|
|
|
void ViewerPrivate::slotAllHeaders()
|
|
{
|
|
setHeaderStyleAndStrategy( HeaderStyle::plain(),
|
|
HeaderStrategy::all(), true );
|
|
}
|
|
|
|
void ViewerPrivate::slotCustomHeaders()
|
|
{
|
|
setHeaderStyleAndStrategy( HeaderStyle::custom(),
|
|
HeaderStrategy::custom(), true );
|
|
}
|
|
|
|
void ViewerPrivate::slotGrantleeHeaders()
|
|
{
|
|
setHeaderStyleAndStrategy( HeaderStyle::grantlee(),
|
|
HeaderStrategy::grantlee(), true );
|
|
initGrantleeThemeName();
|
|
update( Viewer::Force );
|
|
}
|
|
|
|
void ViewerPrivate::initGrantleeThemeName()
|
|
{
|
|
const QString themeName = GrantleeTheme::GrantleeSettings::self()->grantleeMailThemeName();
|
|
headerStyle()->setTheme(mThemeManager->theme(themeName));
|
|
}
|
|
|
|
void ViewerPrivate::slotIconicAttachments()
|
|
{
|
|
setAttachmentStrategy( AttachmentStrategy::iconic() );
|
|
}
|
|
|
|
|
|
void ViewerPrivate::slotSmartAttachments()
|
|
{
|
|
setAttachmentStrategy( AttachmentStrategy::smart() );
|
|
}
|
|
|
|
|
|
void ViewerPrivate::slotInlineAttachments()
|
|
{
|
|
setAttachmentStrategy( AttachmentStrategy::inlined() );
|
|
}
|
|
|
|
|
|
void ViewerPrivate::slotHideAttachments()
|
|
{
|
|
setAttachmentStrategy( AttachmentStrategy::hidden() );
|
|
}
|
|
|
|
void ViewerPrivate::slotHeaderOnlyAttachments()
|
|
{
|
|
setAttachmentStrategy( AttachmentStrategy::headerOnly() );
|
|
}
|
|
|
|
void ViewerPrivate::attachmentView( KMime::Content *atmNode )
|
|
{
|
|
if ( atmNode ) {
|
|
|
|
const bool isEncapsulatedMessage = atmNode->parent() && atmNode->parent()->bodyIsMessage();
|
|
if ( isEncapsulatedMessage ) {
|
|
atmViewMsg( atmNode->parent()->bodyAsMessage() );
|
|
} else if ((kasciistricmp(atmNode->contentType()->mediaType(), "text")==0) &&
|
|
( (kasciistricmp(atmNode->contentType()->subType(), "x-vcard")==0) ||
|
|
(kasciistricmp(atmNode->contentType()->subType(), "directory")==0) )) {
|
|
setMessagePart( atmNode );
|
|
} else {
|
|
emit showReader( atmNode, htmlMail(), overrideEncoding() );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void ViewerPrivate::slotDelayedResize()
|
|
{
|
|
mSplitter->setGeometry( 0, 0, q->width(), q->height() );
|
|
}
|
|
|
|
void ViewerPrivate::slotPrintPreview()
|
|
{
|
|
disconnect( mPartHtmlWriter, SIGNAL(finished()), this, SLOT(slotPrintPreview()) );
|
|
if ( !mMessage )
|
|
return;
|
|
QPrinter printer;
|
|
KPrintPreview previewdlg( &printer/*, mViewer*/ );
|
|
mViewer->print( &printer );
|
|
previewdlg.exec();
|
|
}
|
|
|
|
void ViewerPrivate::slotPrintMsg()
|
|
{
|
|
disconnect( mPartHtmlWriter, SIGNAL(finished()), this, SLOT(slotPrintMsg()) );
|
|
|
|
if ( !mMessage )
|
|
return;
|
|
QPrinter printer;
|
|
|
|
AutoQPointer<QPrintDialog> dlg(KdePrint::createPrintDialog(&printer));
|
|
|
|
if ( dlg && dlg->exec() == QDialog::Accepted ) {
|
|
mViewer->print( &printer );
|
|
}
|
|
}
|
|
|
|
|
|
void ViewerPrivate::slotSetEncoding()
|
|
{
|
|
if ( mSelectEncodingAction->currentItem() == 0 ) // Auto
|
|
mOverrideEncoding.clear();
|
|
else
|
|
mOverrideEncoding = NodeHelper::encodingForName( mSelectEncodingAction->currentText() );
|
|
update( Viewer::Force );
|
|
}
|
|
|
|
QString ViewerPrivate::attachmentInjectionHtml() const
|
|
{
|
|
QString imgpath( KStandardDirs::locate("data",QLatin1String("libmessageviewer/pics/")) );
|
|
QString urlHandle;
|
|
QString imgSrc;
|
|
if( !mShowAttachmentQuicklist ) {
|
|
urlHandle.append( QLatin1String("kmail:showAttachmentQuicklist") );
|
|
imgSrc.append( QLatin1String("quicklistClosed.png") );
|
|
} else {
|
|
urlHandle.append( QLatin1String("kmail:hideAttachmentQuicklist") );
|
|
imgSrc.append( QLatin1String("quicklistOpened.png") );
|
|
}
|
|
|
|
QColor background = KColorScheme( QPalette::Active, KColorScheme::View ).background().color();
|
|
QString html = renderAttachments( mMessage.get(), background );
|
|
Q_FOREACH( KMime::Content* node, mNodeHelper->extraContents( mMessage.get() ) ) {
|
|
html += renderAttachments( node, background );
|
|
}
|
|
if ( html.isEmpty() )
|
|
return QString();
|
|
|
|
QString link;
|
|
if ( headerStyle() == HeaderStyle::fancy() ) {
|
|
link += QLatin1String("<div style=\"text-align: left;\"><a href=\"")+urlHandle+QLatin1String("\"><img src=\"file:///")+imgpath+imgSrc+QLatin1String("\"/></a></div>");
|
|
html.prepend( link );
|
|
html.prepend( QString::fromLatin1("<div style=\"float:left;\">%1 </div>" ).arg(i18n("Attachments:")) );
|
|
} else {
|
|
link += QLatin1String("<div style=\"text-align: right;\"><a href=\"")+urlHandle+QLatin1String("\"><img src=\"file:///")+imgpath+imgSrc+QLatin1String("\"/></a></div>");
|
|
html.prepend( link );
|
|
}
|
|
return html;
|
|
}
|
|
|
|
void ViewerPrivate::injectAttachments()
|
|
{
|
|
disconnect( mPartHtmlWriter, SIGNAL(finished()), this, SLOT(injectAttachments()) );
|
|
// inject attachments in header view
|
|
// we have to do that after the otp has run so we also see encrypted parts
|
|
|
|
mViewer->injectAttachments( bind( &ViewerPrivate::attachmentInjectionHtml, this ) );
|
|
}
|
|
|
|
void ViewerPrivate::slotSettingsChanged()
|
|
{
|
|
update( Viewer::Force );
|
|
}
|
|
|
|
void ViewerPrivate::slotMimeTreeContextMenuRequested( const QPoint& pos )
|
|
{
|
|
#ifndef QT_NO_TREEVIEW
|
|
QModelIndex index = mMimePartTree->indexAt( pos );
|
|
if ( index.isValid() ) {
|
|
KMime::Content *content = static_cast<KMime::Content*>( index.internalPointer() );
|
|
showContextMenu( content, pos );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void ViewerPrivate::slotAttachmentOpenWith()
|
|
{
|
|
#ifndef QT_NO_TREEVIEW
|
|
QItemSelectionModel *selectionModel = mMimePartTree->selectionModel();
|
|
QModelIndexList selectedRows = selectionModel->selectedRows();
|
|
|
|
Q_FOREACH( const QModelIndex &index, selectedRows )
|
|
{
|
|
KMime::Content *content = static_cast<KMime::Content*>( index.internalPointer() );
|
|
attachmentOpenWith( content );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void ViewerPrivate::slotAttachmentOpen()
|
|
{
|
|
#ifndef QT_NO_TREEVIEW
|
|
QItemSelectionModel *selectionModel = mMimePartTree->selectionModel();
|
|
QModelIndexList selectedRows = selectionModel->selectedRows();
|
|
|
|
Q_FOREACH( const QModelIndex &index, selectedRows )
|
|
{
|
|
KMime::Content *content = static_cast<KMime::Content*>( index.internalPointer() );
|
|
attachmentOpen( content );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void ViewerPrivate::showOpenAttachmentFolderWidget(const KUrl &url)
|
|
{
|
|
mOpenAttachmentFolderWidget->setFolder(url);
|
|
mOpenAttachmentFolderWidget->slotShowWarning();
|
|
}
|
|
|
|
void ViewerPrivate::slotAttachmentSaveAs()
|
|
{
|
|
const KMime::Content::List contents = selectedContents();
|
|
KUrl currentUrl;
|
|
if (Util::saveAttachments( contents, mMainWindow, currentUrl))
|
|
showOpenAttachmentFolderWidget(currentUrl);
|
|
}
|
|
|
|
void ViewerPrivate::slotAttachmentSaveAll()
|
|
{
|
|
const KMime::Content::List contents = Util::extractAttachments( mMessage.get() );
|
|
KUrl currentUrl;
|
|
if (Util::saveAttachments( contents, mMainWindow, currentUrl ))
|
|
showOpenAttachmentFolderWidget(currentUrl);
|
|
}
|
|
|
|
void ViewerPrivate::slotAttachmentView()
|
|
{
|
|
KMime::Content::List contents = selectedContents();
|
|
|
|
Q_FOREACH(KMime::Content *content, contents)
|
|
{
|
|
attachmentView( content );
|
|
}
|
|
|
|
}
|
|
|
|
void ViewerPrivate::slotAttachmentProperties()
|
|
{
|
|
KMime::Content::List contents = selectedContents();
|
|
|
|
if ( contents.isEmpty() )
|
|
return;
|
|
|
|
Q_FOREACH( KMime::Content *content, contents ) {
|
|
attachmentProperties( content );
|
|
}
|
|
}
|
|
|
|
void ViewerPrivate::attachmentProperties( KMime::Content *content )
|
|
{
|
|
MessageCore::AttachmentPropertiesDialog *dialog = new MessageCore::AttachmentPropertiesDialog( content, mMainWindow );
|
|
dialog->setAttribute( Qt::WA_DeleteOnClose );
|
|
dialog->show();
|
|
}
|
|
|
|
void ViewerPrivate::slotAttachmentCopy()
|
|
{
|
|
#ifndef QT_NO_CLIPBOARD
|
|
KMime::Content::List contents = selectedContents();
|
|
attachmentCopy( contents );
|
|
#endif
|
|
}
|
|
|
|
void ViewerPrivate::attachmentCopy( const KMime::Content::List & contents )
|
|
{
|
|
#ifndef QT_NO_CLIPBOARD
|
|
if ( contents.isEmpty() )
|
|
return;
|
|
|
|
QList<QUrl> urls;
|
|
Q_FOREACH( KMime::Content *content, contents) {
|
|
KUrl kUrl = mNodeHelper->writeNodeToTempFile( content );
|
|
QUrl url = QUrl::fromPercentEncoding( kUrl.toEncoded() );
|
|
if ( !url.isValid() )
|
|
continue;
|
|
urls.append( url );
|
|
}
|
|
|
|
if ( urls.isEmpty() )
|
|
return;
|
|
|
|
QMimeData *mimeData = new QMimeData;
|
|
mimeData->setUrls( urls );
|
|
QApplication::clipboard()->setMimeData( mimeData, QClipboard::Clipboard );
|
|
#endif
|
|
}
|
|
|
|
|
|
void ViewerPrivate::slotAttachmentDelete()
|
|
{
|
|
KMime::Content::List contents = selectedContents();
|
|
if ( contents.isEmpty() )
|
|
return;
|
|
|
|
bool showWarning = true;
|
|
Q_FOREACH( KMime::Content *content, contents ) {
|
|
if ( !deleteAttachment( content, showWarning ) )
|
|
return;
|
|
showWarning = false;
|
|
}
|
|
update();
|
|
}
|
|
|
|
|
|
void ViewerPrivate::slotAttachmentEdit()
|
|
{
|
|
KMime::Content::List contents = selectedContents();
|
|
if ( contents.isEmpty() )
|
|
return;
|
|
|
|
bool showWarning = true;
|
|
Q_FOREACH( KMime::Content *content, contents ) {
|
|
if ( !editAttachment( content, showWarning ) )
|
|
return;
|
|
showWarning = false;
|
|
}
|
|
}
|
|
|
|
|
|
void ViewerPrivate::slotAttachmentEditDone( EditorWatcher* editorWatcher )
|
|
{
|
|
QString name = editorWatcher->url().fileName();
|
|
if ( editorWatcher->fileChanged() ) {
|
|
QFile file( name );
|
|
if ( file.open( QIODevice::ReadOnly ) ) {
|
|
QByteArray data = file.readAll();
|
|
KMime::Content *node = mEditorWatchers[editorWatcher];
|
|
node->setBody( data );
|
|
file.close();
|
|
|
|
mMessageItem.setPayloadFromData( mMessage->encodedContent() );
|
|
Akonadi::ItemModifyJob *job = new Akonadi::ItemModifyJob( mMessageItem );
|
|
connect( job, SIGNAL(result(KJob*)), SLOT(itemModifiedResult(KJob*)) );
|
|
}
|
|
}
|
|
mEditorWatchers.remove( editorWatcher );
|
|
QFile::remove( name );
|
|
}
|
|
|
|
void ViewerPrivate::slotLevelQuote( int l )
|
|
{
|
|
if (mLevelQuote != l) {
|
|
mLevelQuote = l;
|
|
update( Viewer::Force );
|
|
}
|
|
}
|
|
|
|
|
|
void ViewerPrivate::slotHandleAttachment( int choice )
|
|
{
|
|
if(!mCurrentContent)
|
|
return;
|
|
if ( choice == Viewer::Delete ) {
|
|
deleteAttachment( mCurrentContent );
|
|
} else if ( choice == Viewer::Edit ) {
|
|
editAttachment( mCurrentContent );
|
|
} else if ( choice == Viewer::Properties ) {
|
|
attachmentProperties( mCurrentContent );
|
|
} else if ( choice == Viewer::Save ) {
|
|
KUrl currentUrl;
|
|
if (Util::saveContents( mMainWindow, KMime::Content::List() << mCurrentContent, currentUrl )) {
|
|
showOpenAttachmentFolderWidget(currentUrl);
|
|
}
|
|
} else if ( choice == Viewer::OpenWith ) {
|
|
attachmentOpenWith( mCurrentContent );
|
|
} else if ( choice == Viewer::Open ) {
|
|
attachmentOpen( mCurrentContent );
|
|
} else if ( choice == Viewer::View ) {
|
|
attachmentView( mCurrentContent );
|
|
} else if ( choice == Viewer::ChiasmusEncrypt ) {
|
|
attachmentEncryptWithChiasmus( mCurrentContent );
|
|
} else if ( choice == Viewer::Copy ) {
|
|
attachmentCopy( KMime::Content::List()<< mCurrentContent );
|
|
} else if ( choice == Viewer::ScrollTo ) {
|
|
scrollToAttachment( mCurrentContent );
|
|
}
|
|
else {
|
|
kDebug() << " not implemented :" << choice;
|
|
}
|
|
}
|
|
|
|
void ViewerPrivate::slotSpeakText()
|
|
{
|
|
const QString text = mViewer->selectedText();
|
|
MessageViewer::Util::speakSelectedText( text, mMainWindow);
|
|
}
|
|
|
|
void ViewerPrivate::slotCopyImageLocation()
|
|
{
|
|
#ifndef QT_NO_CLIPBOARD
|
|
QApplication::clipboard()->setText( mImageUrl.url() );
|
|
#endif
|
|
}
|
|
|
|
void ViewerPrivate::slotCopySelectedText()
|
|
{
|
|
#ifndef QT_NO_CLIPBOARD
|
|
QString selection = mViewer->selectedText();
|
|
selection.replace( QChar::Nbsp, QLatin1Char(' ') );
|
|
QApplication::clipboard()->setText( selection );
|
|
#endif
|
|
}
|
|
|
|
void ViewerPrivate::viewerSelectionChanged()
|
|
{
|
|
mActionCollection->action( QLatin1String("kmail_copy") )->setEnabled( !mViewer->selectedText().isEmpty() );
|
|
}
|
|
|
|
void ViewerPrivate::selectAll()
|
|
{
|
|
mViewer->selectAll();
|
|
}
|
|
|
|
void ViewerPrivate::clearSelection()
|
|
{
|
|
mViewer->clearSelection();
|
|
}
|
|
|
|
|
|
void ViewerPrivate::slotUrlCopy()
|
|
{
|
|
#ifndef QT_NO_CLIPBOARD
|
|
QClipboard* clip = QApplication::clipboard();
|
|
if ( mClickedUrl.protocol() == QLatin1String( "mailto" ) ) {
|
|
// put the url into the mouse selection and the clipboard
|
|
const QString address = KPIMUtils::decodeMailtoUrl( mClickedUrl );
|
|
clip->setText( address, QClipboard::Clipboard );
|
|
clip->setText( address, QClipboard::Selection );
|
|
KPIM::BroadcastStatus::instance()->setStatusMsg( i18n( "Address copied to clipboard." ));
|
|
} else {
|
|
// put the url into the mouse selection and the clipboard
|
|
clip->setText( mClickedUrl.url(), QClipboard::Clipboard );
|
|
clip->setText( mClickedUrl.url(), QClipboard::Selection );
|
|
KPIM::BroadcastStatus::instance()->setStatusMsg( i18n( "URL copied to clipboard." ));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void ViewerPrivate::slotSaveMessage()
|
|
{
|
|
if ( !mMessageItem.hasPayload<KMime::Message::Ptr>() ) {
|
|
if ( mMessageItem.isValid() ) {
|
|
kWarning() << "Payload is not a MessagePtr!";
|
|
}
|
|
return;
|
|
}
|
|
|
|
Util::saveMessageInMbox( QList<Akonadi::Item>() << mMessageItem, mMainWindow );
|
|
}
|
|
|
|
void ViewerPrivate::saveRelativePosition()
|
|
{
|
|
mSavedRelativePosition = mViewer->relativePosition();
|
|
}
|
|
|
|
//TODO(Andras) inline them
|
|
bool ViewerPrivate::htmlMail() const
|
|
{
|
|
if (mDisplayFormatMessageOverwrite == Viewer::UseGlobalSetting) {
|
|
return mHtmlMailGlobalSetting;
|
|
} else {
|
|
return (mDisplayFormatMessageOverwrite == Viewer::Html);
|
|
}
|
|
}
|
|
|
|
bool ViewerPrivate::htmlLoadExternal() const
|
|
{
|
|
return ((mHtmlLoadExternalGlobalSetting && !mHtmlLoadExtOverride) ||
|
|
(!mHtmlLoadExternalGlobalSetting && mHtmlLoadExtOverride));
|
|
}
|
|
|
|
void ViewerPrivate::setDisplayFormatMessageOverwrite(Viewer::DisplayFormatMessage format)
|
|
{
|
|
mDisplayFormatMessageOverwrite = format;
|
|
// keep toggle display mode action state in sync.
|
|
if ( mToggleDisplayModeAction )
|
|
mToggleDisplayModeAction->setChecked( htmlMail() );
|
|
}
|
|
|
|
Viewer::DisplayFormatMessage ViewerPrivate::displayFormatMessageOverwrite() const
|
|
{
|
|
return mDisplayFormatMessageOverwrite;
|
|
}
|
|
|
|
void ViewerPrivate::setHtmlLoadExtOverride( bool override )
|
|
{
|
|
mHtmlLoadExtOverride = override;
|
|
}
|
|
|
|
bool ViewerPrivate::htmlLoadExtOverride() const
|
|
{
|
|
return mHtmlLoadExtOverride;
|
|
}
|
|
|
|
void ViewerPrivate::setDecryptMessageOverwrite( bool overwrite )
|
|
{
|
|
mDecrytMessageOverwrite = overwrite;
|
|
}
|
|
|
|
bool ViewerPrivate::showSignatureDetails() const
|
|
{
|
|
return mShowSignatureDetails;
|
|
}
|
|
|
|
void ViewerPrivate::setShowSignatureDetails( bool showDetails )
|
|
{
|
|
mShowSignatureDetails = showDetails;
|
|
}
|
|
|
|
bool ViewerPrivate::showAttachmentQuicklist() const
|
|
{
|
|
return mShowAttachmentQuicklist;
|
|
}
|
|
|
|
void ViewerPrivate::setShowAttachmentQuicklist( bool showAttachmentQuicklist )
|
|
{
|
|
mShowAttachmentQuicklist = showAttachmentQuicklist;
|
|
}
|
|
|
|
void ViewerPrivate::setExternalWindow( bool b )
|
|
{
|
|
mExternalWindow = b;
|
|
}
|
|
|
|
|
|
void ViewerPrivate::scrollToAttachment( KMime::Content *node )
|
|
{
|
|
const QString indexStr = node->index().toString();
|
|
// The anchors for this are created in ObjectTreeParser::parseObjectTree()
|
|
mViewer->scrollToAnchor( QLatin1String("att") + indexStr );
|
|
|
|
// Remove any old color markings which might be there
|
|
const KMime::Content *root = node->topLevel();
|
|
const int totalChildCount = Util::allContents( root ).size();
|
|
for ( int i = 0 ; i < totalChildCount + 1 ; ++i ) {
|
|
mViewer->removeAttachmentMarking( QString::fromLatin1( "attachmentDiv%1" ).arg( i + 1 ) );
|
|
}
|
|
|
|
// Don't mark hidden nodes, that would just produce a strange yellow line
|
|
if ( mNodeHelper->isNodeDisplayedHidden( node ) ) {
|
|
return;
|
|
}
|
|
|
|
// Now, color the div of the attachment in yellow, so that the user sees what happened.
|
|
// We created a special marked div for this in writeAttachmentMarkHeader() in ObjectTreeParser,
|
|
// find and modify that now.
|
|
mViewer->markAttachment( QLatin1String("attachmentDiv") + indexStr, QString::fromLatin1( "border:2px solid %1" ).arg( cssHelper()->pgpWarnColor().name() ) );
|
|
}
|
|
|
|
void ViewerPrivate::setUseFixedFont( bool useFixedFont )
|
|
{
|
|
mUseFixedFont = useFixedFont;
|
|
if ( mToggleFixFontAction ) {
|
|
mToggleFixFontAction->setChecked( mUseFixedFont );
|
|
}
|
|
}
|
|
|
|
void ViewerPrivate::attachmentEncryptWithChiasmus( KMime::Content *content )
|
|
{
|
|
Q_UNUSED( content );
|
|
|
|
// FIXME: better detection of mimetype??
|
|
if ( !mCurrentFileName.endsWith( QLatin1String(".xia"), Qt::CaseInsensitive ) )
|
|
return;
|
|
|
|
const Kleo::CryptoBackend::Protocol * chiasmus =
|
|
Kleo::CryptoBackendFactory::instance()->protocol( "Chiasmus" );
|
|
Q_ASSERT( chiasmus );
|
|
if ( !chiasmus )
|
|
return;
|
|
|
|
const std::auto_ptr<Kleo::SpecialJob> listjob( chiasmus->specialJob( "x-obtain-keys", QMap<QString,QVariant>() ) );
|
|
if ( !listjob.get() ) {
|
|
const QString msg = i18n( "Chiasmus backend does not offer the "
|
|
"\"x-obtain-keys\" function. Please report this bug." );
|
|
KMessageBox::error( mMainWindow, msg, i18n( "Chiasmus Backend Error" ) );
|
|
return;
|
|
}
|
|
|
|
if ( listjob->exec() ) {
|
|
listjob->showErrorDialog( mMainWindow, i18n( "Chiasmus Backend Error" ) );
|
|
return;
|
|
}
|
|
|
|
const QVariant result = listjob->property( "result" );
|
|
if ( result.type() != QVariant::StringList ) {
|
|
const QString msg = i18n( "Unexpected return value from Chiasmus backend: "
|
|
"The \"x-obtain-keys\" function did not return a "
|
|
"string list. Please report this bug." );
|
|
KMessageBox::error( mMainWindow, msg, i18n( "Chiasmus Backend Error" ) );
|
|
return;
|
|
}
|
|
|
|
const QStringList keys = result.toStringList();
|
|
if ( keys.empty() ) {
|
|
const QString msg = i18n( "No keys have been found. Please check that a "
|
|
"valid key path has been set in the Chiasmus "
|
|
"configuration." );
|
|
KMessageBox::error( mMainWindow, msg, i18n( "Chiasmus Backend Error" ) );
|
|
return;
|
|
}
|
|
AutoQPointer<ChiasmusKeySelector> selectorDlg( new ChiasmusKeySelector( mMainWindow,
|
|
i18n( "Chiasmus Decryption Key Selection" ),
|
|
keys, GlobalSettings::chiasmusDecryptionKey(),
|
|
GlobalSettings::chiasmusDecryptionOptions() ) );
|
|
if ( selectorDlg->exec() != QDialog::Accepted || !selectorDlg ) {
|
|
return;
|
|
}
|
|
|
|
GlobalSettings::setChiasmusDecryptionOptions( selectorDlg->options() );
|
|
GlobalSettings::setChiasmusDecryptionKey( selectorDlg->key() );
|
|
assert( !GlobalSettings::chiasmusDecryptionKey().isEmpty() );
|
|
Kleo::SpecialJob * job = chiasmus->specialJob( "x-decrypt", QMap<QString,QVariant>() );
|
|
if ( !job ) {
|
|
const QString msg = i18n( "Chiasmus backend does not offer the "
|
|
"\"x-decrypt\" function. Please report this bug." );
|
|
KMessageBox::error( mMainWindow, msg, i18n( "Chiasmus Backend Error" ) );
|
|
return;
|
|
}
|
|
|
|
//PORT IT
|
|
const QByteArray input;// = node->msgPart().bodyDecodedBinary();
|
|
|
|
if ( !job->setProperty( "key", GlobalSettings::chiasmusDecryptionKey() ) ||
|
|
!job->setProperty( "options", GlobalSettings::chiasmusDecryptionOptions() ) ||
|
|
!job->setProperty( "input", input ) ) {
|
|
const QString msg = i18n( "The \"x-decrypt\" function does not accept "
|
|
"the expected parameters. Please report this bug." );
|
|
KMessageBox::error( mMainWindow, msg, i18n( "Chiasmus Backend Error" ) );
|
|
return;
|
|
}
|
|
|
|
if ( job->start() ) {
|
|
job->showErrorDialog( mMainWindow, i18n( "Chiasmus Decryption Error" ) );
|
|
return;
|
|
}
|
|
|
|
mJob = job;
|
|
connect( job, SIGNAL(result(GpgME::Error,QVariant)),
|
|
this, SLOT(slotAtmDecryptWithChiasmusResult(GpgME::Error,QVariant)) );
|
|
}
|
|
|
|
|
|
static const QString chomp( const QString & base, const QString & suffix, bool cs )
|
|
{
|
|
return base.endsWith( suffix, cs ? (Qt::CaseSensitive) : (Qt::CaseInsensitive) ) ? base.left( base.length() - suffix.length() ) : base ;
|
|
}
|
|
|
|
|
|
void ViewerPrivate::slotAtmDecryptWithChiasmusResult( const GpgME::Error & err, const QVariant & result )
|
|
{
|
|
if ( !mJob )
|
|
return;
|
|
Q_ASSERT( mJob == sender() );
|
|
if ( mJob != sender() )
|
|
return;
|
|
Kleo::Job * job = mJob;
|
|
mJob = 0;
|
|
if ( err.isCanceled() )
|
|
return;
|
|
if ( err ) {
|
|
job->showErrorDialog( mMainWindow, i18n( "Chiasmus Decryption Error" ) );
|
|
return;
|
|
}
|
|
|
|
if ( result.type() != QVariant::ByteArray ) {
|
|
const QString msg = i18n( "Unexpected return value from Chiasmus backend: "
|
|
"The \"x-decrypt\" function did not return a "
|
|
"byte array. Please report this bug." );
|
|
KMessageBox::error( mMainWindow, msg, i18n( "Chiasmus Backend Error" ) );
|
|
return;
|
|
}
|
|
|
|
const KUrl url = KFileDialog::getSaveUrl( chomp( mCurrentFileName, QLatin1String(".xia"), false ), QString(), mMainWindow );
|
|
if ( url.isEmpty() )
|
|
return;
|
|
|
|
bool overwrite = Util::checkOverwrite( url, mMainWindow );
|
|
if ( !overwrite )
|
|
return;
|
|
|
|
KIO::Job * uploadJob = KIO::storedPut( result.toByteArray(), url, -1, KIO::Overwrite );
|
|
uploadJob->ui()->setWindow( mMainWindow );
|
|
connect( uploadJob, SIGNAL(result(KJob*)),
|
|
this, SLOT(slotAtmDecryptWithChiasmusUploadResult(KJob*)) );
|
|
}
|
|
|
|
void ViewerPrivate::slotAtmDecryptWithChiasmusUploadResult( KJob * job )
|
|
{
|
|
if ( job->error() )
|
|
static_cast<KIO::Job*>(job)->ui()->showErrorMessage();
|
|
}
|
|
|
|
bool ViewerPrivate::showFullToAddressList() const
|
|
{
|
|
return mShowFullToAddressList;
|
|
}
|
|
|
|
void ViewerPrivate::setShowFullToAddressList( bool showFullToAddressList )
|
|
{
|
|
mShowFullToAddressList = showFullToAddressList;
|
|
}
|
|
|
|
bool ViewerPrivate::showFullCcAddressList() const
|
|
{
|
|
return mShowFullCcAddressList;
|
|
}
|
|
|
|
void ViewerPrivate::setShowFullCcAddressList( bool showFullCcAddressList )
|
|
{
|
|
mShowFullCcAddressList = showFullCcAddressList;
|
|
}
|
|
|
|
void ViewerPrivate::toggleFullAddressList()
|
|
{
|
|
toggleFullAddressList( QLatin1String("To") );
|
|
toggleFullAddressList( QLatin1String("Cc") );
|
|
}
|
|
|
|
QString ViewerPrivate::recipientsQuickListLinkHtml( bool doShow, const QString & field ) const
|
|
{
|
|
QString imgpath( KStandardDirs::locate( "data",QLatin1String("libmessageviewer/pics/") ) );
|
|
QString urlHandle;
|
|
QString imgSrc;
|
|
QString altText;
|
|
if ( doShow ) {
|
|
urlHandle.append( QLatin1String("kmail:hideFull") + field + QLatin1String("AddressList"));
|
|
imgSrc.append( QLatin1String("quicklistOpened.png") );
|
|
altText = i18n("Hide full address list");
|
|
} else {
|
|
urlHandle.append( QLatin1String("kmail:showFull") + field + QLatin1String("AddressList") );
|
|
imgSrc.append( QLatin1String("quicklistClosed.png") );
|
|
altText = i18n("Show full address list");
|
|
}
|
|
|
|
return QLatin1String("<span style=\"text-align: right;\"><a href=\"") + urlHandle + QLatin1String("\"><img src=\"file:///") + imgpath + imgSrc + QLatin1String("\""
|
|
"alt=\"") + altText + QLatin1String("\" /></a></span>");
|
|
}
|
|
|
|
void ViewerPrivate::toggleFullAddressList( const QString &field )
|
|
{
|
|
const bool doShow = ( field == QLatin1String( "To" ) && showFullToAddressList() ) || ( field == QLatin1String( "Cc" ) && showFullCcAddressList() );
|
|
// First inject the correct icon
|
|
if ( mViewer->replaceInnerHtml( QLatin1String("iconFull") + field + QLatin1String("AddressList"),
|
|
bind( &ViewerPrivate::recipientsQuickListLinkHtml, this, doShow, field ) ) )
|
|
{
|
|
// Then show/hide the full address list
|
|
mViewer->setElementByIdVisible( QLatin1String("dotsFull") + field + QLatin1String("AddressList"), !doShow );
|
|
mViewer->setElementByIdVisible( QLatin1String("hiddenFull") + field + QLatin1String("AddressList"), doShow );
|
|
}
|
|
}
|
|
|
|
void ViewerPrivate::itemFetchResult( KJob* job )
|
|
{
|
|
if ( job->error() ) {
|
|
displaySplashPage( i18n( "Message loading failed: %1.", job->errorText() ) );
|
|
} else {
|
|
Akonadi::ItemFetchJob* fetch = qobject_cast<Akonadi::ItemFetchJob*>( job );
|
|
Q_ASSERT( fetch );
|
|
if ( fetch->items().isEmpty() ) {
|
|
displaySplashPage( i18n( "Message not found." ) );
|
|
} else {
|
|
setMessageItem( fetch->items().first() );
|
|
}
|
|
}
|
|
}
|
|
|
|
void ViewerPrivate::slotItemChanged( const Akonadi::Item &item, const QSet<QByteArray> & parts )
|
|
{
|
|
if ( item.id() != messageItem().id() ) {
|
|
kDebug() << "Update for an already forgotten item. Weird.";
|
|
return;
|
|
}
|
|
if( parts.contains( "PLD:RFC822" ) )
|
|
setMessageItem( item, Viewer::Force );
|
|
}
|
|
|
|
void ViewerPrivate::slotItemMoved( const Akonadi::Item &item, const Akonadi::Collection&,
|
|
const Akonadi::Collection& )
|
|
{
|
|
// clear the view after the current item has been moved somewhere else (e.g. to trash)
|
|
if ( item.id() == messageItem().id() )
|
|
slotClear();
|
|
}
|
|
|
|
void ViewerPrivate::slotClear()
|
|
{
|
|
q->clear( Viewer::Force );
|
|
emit itemRemoved();
|
|
}
|
|
|
|
void ViewerPrivate::slotMessageRendered()
|
|
{
|
|
if ( !mMessageItem.isValid() ) {
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* This slot might be called multiple times for the same message if
|
|
* some asynchronous mementos are involved in rendering. Therefor we
|
|
* have to make sure we execute the MessageLoadedHandlers only once.
|
|
*/
|
|
if ( mMessageItem.id() == mPreviouslyViewedItem )
|
|
return;
|
|
|
|
mPreviouslyViewedItem = mMessageItem.id();
|
|
|
|
foreach ( AbstractMessageLoadedHandler *handler, mMessageLoadedHandlers )
|
|
handler->setItem( mMessageItem );
|
|
}
|
|
|
|
void ViewerPrivate::setZoomFactor( qreal zoomFactor )
|
|
{
|
|
#ifndef KDEPIM_NO_WEBKIT
|
|
mViewer->setZoomFactor ( zoomFactor );
|
|
#endif
|
|
}
|
|
|
|
|
|
void ViewerPrivate::slotZoomIn()
|
|
{
|
|
#ifndef KDEPIM_NO_WEBKIT
|
|
if( mZoomFactor >= 300 )
|
|
return;
|
|
mZoomFactor += zoomBy;
|
|
if( mZoomFactor > 300 )
|
|
mZoomFactor = 300;
|
|
mViewer->setZoomFactor( mZoomFactor/100.0 );
|
|
#endif
|
|
}
|
|
|
|
void ViewerPrivate::slotZoomOut()
|
|
{
|
|
#ifndef KDEPIM_NO_WEBKIT
|
|
if ( mZoomFactor <= 10 )
|
|
return;
|
|
mZoomFactor -= zoomBy;
|
|
if( mZoomFactor < 10 )
|
|
mZoomFactor = 10;
|
|
mViewer->setZoomFactor( mZoomFactor/100.0 );
|
|
#endif
|
|
}
|
|
|
|
void ViewerPrivate::setZoomTextOnly( bool textOnly )
|
|
{
|
|
mZoomTextOnly = textOnly;
|
|
if ( mZoomTextOnlyAction )
|
|
{
|
|
mZoomTextOnlyAction->setChecked( mZoomTextOnly );
|
|
}
|
|
#ifndef KDEPIM_NO_WEBKIT
|
|
mViewer->settings()->setAttribute(QWebSettings::ZoomTextOnly, mZoomTextOnly);
|
|
#endif
|
|
}
|
|
|
|
void ViewerPrivate::slotZoomTextOnly()
|
|
{
|
|
setZoomTextOnly( !mZoomTextOnly );
|
|
}
|
|
|
|
void ViewerPrivate::slotZoomReset()
|
|
{
|
|
#ifndef KDEPIM_NO_WEBKIT
|
|
mZoomFactor = 100;
|
|
mViewer->setZoomFactor( 1.0 );
|
|
#endif
|
|
}
|
|
|
|
void ViewerPrivate::goOnline()
|
|
{
|
|
emit makeResourceOnline(Viewer::AllResources);
|
|
}
|
|
|
|
void ViewerPrivate::goResourceOnline()
|
|
{
|
|
emit makeResourceOnline(Viewer::SelectedResource);
|
|
}
|
|
|
|
void ViewerPrivate::slotToggleCaretBrowsing(bool toggle)
|
|
{
|
|
#ifndef KDEPIM_NO_WEBKIT
|
|
#if QTWEBKIT_VERSION >= QTWEBKIT_VERSION_CHECK(2, 3, 0)
|
|
if( toggle ) {
|
|
KMessageBox::information( mMainWindow,
|
|
i18n("Caret Browsing will be activated. Switch off with F7 shortcut."),
|
|
i18n("Activate Caret Browsing") );
|
|
}
|
|
mViewer->settings()->setAttribute(QWebSettings::CaretBrowsingEnabled, toggle);
|
|
#endif
|
|
#endif
|
|
Q_UNUSED( toggle );
|
|
}
|
|
|
|
void ViewerPrivate::slotSaveMessageDisplayFormat()
|
|
{
|
|
if (mMessageItem.isValid()) {
|
|
MessageViewer::MessageDisplayFormatAttribute *attr = mMessageItem.attribute<MessageViewer::MessageDisplayFormatAttribute>( Akonadi::Entity::AddIfMissing );
|
|
attr->setRemoteContent(htmlLoadExtOverride());
|
|
attr->setMessageFormat(displayFormatMessageOverwrite());
|
|
Akonadi::ItemModifyJob *modify = new Akonadi::ItemModifyJob( mMessageItem );
|
|
modify->setIgnorePayload( true );
|
|
modify->disableRevisionCheck();
|
|
connect( modify, SIGNAL(result(KJob*)), this, SLOT(slotModifyItemDone(KJob*)) );
|
|
}
|
|
}
|
|
|
|
void ViewerPrivate::slotResetMessageDisplayFormat()
|
|
{
|
|
if (mMessageItem.isValid()) {
|
|
if (mMessageItem.hasAttribute<MessageViewer::MessageDisplayFormatAttribute>()) {
|
|
mMessageItem.removeAttribute<MessageViewer::MessageDisplayFormatAttribute>();
|
|
Akonadi::ItemModifyJob *modify = new Akonadi::ItemModifyJob( mMessageItem );
|
|
modify->setIgnorePayload( true );
|
|
modify->disableRevisionCheck();
|
|
connect( modify, SIGNAL(result(KJob*)), this, SLOT(slotModifyItemDone(KJob*)) );
|
|
}
|
|
}
|
|
}
|
|
|
|
void ViewerPrivate::slotMessageMayBeAScam()
|
|
{
|
|
if (mMessageItem.isValid()) {
|
|
if (mMessageItem.hasAttribute<MessageViewer::ScamAttribute>()) {
|
|
const MessageViewer::ScamAttribute* const attr = mMessageItem.attribute<MessageViewer::ScamAttribute>();
|
|
if (attr && !attr->isAScam())
|
|
return;
|
|
}
|
|
if ( mMessageItem.hasPayload<KMime::Message::Ptr>() ) {
|
|
KMime::Message::Ptr message = mMessageItem.payload<KMime::Message::Ptr>();
|
|
const QString email = QLatin1String(KPIMUtils::firstEmailAddress( message->from()->as7BitString(false) ));
|
|
const QStringList lst = MessageViewer::GlobalSettings::self()->scamDetectionWhiteList();
|
|
if (lst.contains(email))
|
|
return;
|
|
}
|
|
}
|
|
mScamDetectionWarning->slotShowWarning();
|
|
}
|
|
|
|
void ViewerPrivate::slotMessageIsNotAScam()
|
|
{
|
|
if (mMessageItem.isValid()) {
|
|
MessageViewer::ScamAttribute *attr = mMessageItem.attribute<MessageViewer::ScamAttribute>( Akonadi::Entity::AddIfMissing );
|
|
attr->setIsAScam(false);
|
|
Akonadi::ItemModifyJob *modify = new Akonadi::ItemModifyJob( mMessageItem );
|
|
modify->setIgnorePayload( true );
|
|
modify->disableRevisionCheck();
|
|
connect( modify, SIGNAL(result(KJob*)), this, SLOT(slotModifyItemDone(KJob*)) );
|
|
}
|
|
}
|
|
|
|
|
|
void ViewerPrivate::slotModifyItemDone(KJob* job)
|
|
{
|
|
if ( job && job->error() ) {
|
|
kWarning() << " Error trying to change attribute:" << job->errorText();
|
|
}
|
|
}
|
|
|
|
void ViewerPrivate::slotGrantleeThemesUpdated()
|
|
{
|
|
update( Viewer::Force );
|
|
}
|
|
|
|
void ViewerPrivate::saveMainFrameScreenshotInFile(const QString &filename)
|
|
{
|
|
#ifndef KDEPIM_NO_WEBKIT
|
|
mViewer->saveMainFrameScreenshotInFile(filename);
|
|
#endif
|
|
}
|
|
|
|
void ViewerPrivate::slotAddToWhiteList()
|
|
{
|
|
if (mMessageItem.isValid()) {
|
|
if ( mMessageItem.hasPayload<KMime::Message::Ptr>() ) {
|
|
KMime::Message::Ptr message = mMessageItem.payload<KMime::Message::Ptr>();
|
|
const QString email = QLatin1String(KPIMUtils::firstEmailAddress( message->from()->as7BitString(false)) );
|
|
QStringList lst = MessageViewer::GlobalSettings::self()->scamDetectionWhiteList();
|
|
if (lst.contains(email))
|
|
return;
|
|
lst << email;
|
|
MessageViewer::GlobalSettings::self()->setScamDetectionWhiteList( lst );
|
|
MessageViewer::GlobalSettings::self()->writeConfig();
|
|
}
|
|
}
|
|
}
|
|
|
|
void ViewerPrivate::slotBlockImage()
|
|
{
|
|
if (mImageUrl.isEmpty())
|
|
return;
|
|
#ifndef KDEPIM_NO_WEBKIT
|
|
MessageViewer::AdBlockManager::self()->addCustomRule(mImageUrl.url(), true);
|
|
#endif
|
|
}
|
|
|
|
void ViewerPrivate::slotOpenBlockableItems()
|
|
{
|
|
#ifndef KDEPIM_NO_WEBKIT
|
|
mViewer->openBlockableItemsDialog();
|
|
#endif
|
|
}
|
|
|
|
bool ViewerPrivate::isAShortUrl(const KUrl &url) const
|
|
{
|
|
return mViewer->isAShortUrl(url);
|
|
}
|
|
|
|
void ViewerPrivate::slotExpandShortUrl()
|
|
{
|
|
if (mClickedUrl.isValid()) {
|
|
mViewer->expandUrl(mClickedUrl);
|
|
}
|
|
}
|
|
|
|
void ViewerPrivate::slotShowCreateTodoWidget()
|
|
{
|
|
if (mMessage) {
|
|
mCreateTodo->setMessage(mMessage);
|
|
mCreateTodo->showToDoWidget();
|
|
} else {
|
|
qDebug()<<" There is not valid message";
|
|
}
|
|
}
|
|
|
|
void ViewerPrivate::slotCreateTodo(const KCalCore::Todo::Ptr &todoPtr, const Akonadi::Collection &collection)
|
|
{
|
|
CreateTodoJob *createJob = new CreateTodoJob(todoPtr, collection, mMessageItem, this);
|
|
createJob->start();
|
|
}
|
|
|
|
void ViewerPrivate::slotShowCreateEventWidget()
|
|
{
|
|
if (mMessage) {
|
|
mCreateEvent->setMessage(mMessage);
|
|
mCreateEvent->showEventEdit();
|
|
} else {
|
|
qDebug()<<" There is not valid message";
|
|
}
|
|
}
|
|
|
|
void ViewerPrivate::slotCreateEvent(const KCalCore::Event::Ptr &eventPtr, const Akonadi::Collection &collection)
|
|
{
|
|
CreateEventJob *createJob = new CreateEventJob(eventPtr, collection, mMessageItem, this);
|
|
createJob->start();
|
|
}
|