messageactions.cpp 29.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*
    Copyright (c) 2007 Volker Krause <vkrause@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.
*/

#include "messageactions.h"

Laurent Montel's avatar
Laurent Montel committed
21
#include "settings/kmailsettings.h"
22
#include "kmreaderwin.h"
Stephen Kelly's avatar
Stephen Kelly committed
23
#include "kmkernel.h"
Laurent Montel's avatar
Laurent Montel committed
24
#include "mailcommon/mailkernel.h"
25
#include "kmmainwidget.h"
Stephen Kelly's avatar
Stephen Kelly committed
26
#include "util.h"
27
#include "kmcommands.h"
28
#include <TemplateParser/CustomTemplatesMenu>
29

Laurent Montel's avatar
Laurent Montel committed
30
#include <PimCommonAkonadi/AnnotationDialog>
Laurent Montel's avatar
Laurent Montel committed
31
#include <MessageCore/MessageCoreSettings>
Laurent Montel's avatar
Laurent Montel committed
32
#include "MessageCore/MailingList"
Laurent Montel's avatar
Laurent Montel committed
33
#include <MessageCore/StringUtil>
34
#include "messageviewer/messageviewersettings.h"
Laurent Montel's avatar
Laurent Montel committed
35
#include "messageviewer/headerstyleplugin.h"
36

Laurent Montel's avatar
Laurent Montel committed
37
#include <AkonadiCore/itemfetchjob.h>
38
#include <Akonadi/KMime/MessageParts>
Laurent Montel's avatar
Laurent Montel committed
39
#include <AkonadiCore/ChangeRecorder>
Laurent Montel's avatar
Laurent Montel committed
40
#include <QAction>
Laurent Montel's avatar
Laurent Montel committed
41
#include <AkonadiSearch/Debug/akonadisearchdebugdialog.h>
42
#include <KIO/KUriFilterSearchProviderActions>
43

44
#include "messagecomposer/followupreminderselectdatedialog.h"
45 46
#include "job/createfollowupreminderonexistingmessagejob.h"

47
#include <AkonadiCore/ItemFetchJob>
48 49
#include <KActionMenu>
#include <KActionCollection>
Laurent Montel's avatar
Laurent Montel committed
50
#include "kmail_debug.h"
Laurent Montel's avatar
Laurent Montel committed
51
#include <KLocalizedString>
52
#include <KXMLGUIClient>
53
#include <KRun>
Laurent Montel's avatar
Laurent Montel committed
54
#include <QMenu>
55
#include <KUriFilter>
Laurent Montel's avatar
Laurent Montel committed
56
#include <KStringHandler>
Laurent Montel's avatar
Laurent Montel committed
57
#include <QIcon>
58
#include <QFileDialog>
59

60
#include <QVariant>
Laurent Montel's avatar
Laurent Montel committed
61
#include <QWidget>
Laurent Montel's avatar
Laurent Montel committed
62
#include <AkonadiCore/collection.h>
Laurent Montel's avatar
Laurent Montel committed
63
#include <AkonadiCore/entityannotationsattribute.h>
Laurent Montel's avatar
Laurent Montel committed
64
#include <mailcommon/mailutil.h>
Laurent Montel's avatar
Laurent Montel committed
65
#include <MessageViewer/MessageViewerUtil>
66 67 68

using namespace KMail;

Laurent Montel's avatar
Laurent Montel committed
69
MessageActions::MessageActions(KActionCollection *ac, QWidget *parent)
Laurent Montel's avatar
Laurent Montel committed
70 71
    : QObject(parent)
    , mParent(parent)
72
{
73
    mWebShortcutMenuManager = new KIO::KUriFilterSearchProviderActions(this);
74 75
    mReplyActionMenu = new KActionMenu(QIcon::fromTheme(QStringLiteral("mail-reply-sender")), i18nc("Message->", "&Reply"), this);
    ac->addAction(QStringLiteral("message_reply_menu"), mReplyActionMenu);
Laurent Montel's avatar
Laurent Montel committed
76
    connect(mReplyActionMenu, &KActionMenu::triggered, this, &MessageActions::slotReplyToMsg);
Laurent Montel's avatar
Laurent Montel committed
77

78 79
    mReplyAction = new QAction(QIcon::fromTheme(QStringLiteral("mail-reply-sender")), i18n("&Reply..."), this);
    ac->addAction(QStringLiteral("reply"), mReplyAction);
Laurent Montel's avatar
Laurent Montel committed
80
    ac->setDefaultShortcut(mReplyAction, Qt::Key_R);
Laurent Montel's avatar
Laurent Montel committed
81
    connect(mReplyAction, &QAction::triggered, this, &MessageActions::slotReplyToMsg);
Laurent Montel's avatar
Laurent Montel committed
82
    mReplyActionMenu->addAction(mReplyAction);
Laurent Montel's avatar
Laurent Montel committed
83

84 85
    mReplyAuthorAction = new QAction(QIcon::fromTheme(QStringLiteral("mail-reply-sender")), i18n("Reply to A&uthor..."), this);
    ac->addAction(QStringLiteral("reply_author"), mReplyAuthorAction);
Laurent Montel's avatar
Laurent Montel committed
86
    ac->setDefaultShortcut(mReplyAuthorAction, Qt::SHIFT + Qt::Key_A);
Laurent Montel's avatar
Laurent Montel committed
87
    connect(mReplyAuthorAction, &QAction::triggered, this, &MessageActions::slotReplyAuthorToMsg);
Laurent Montel's avatar
Laurent Montel committed
88
    mReplyActionMenu->addAction(mReplyAuthorAction);
Laurent Montel's avatar
Laurent Montel committed
89

90 91
    mReplyAllAction = new QAction(QIcon::fromTheme(QStringLiteral("mail-reply-all")), i18n("Reply to &All..."), this);
    ac->addAction(QStringLiteral("reply_all"), mReplyAllAction);
Laurent Montel's avatar
Laurent Montel committed
92
    ac->setDefaultShortcut(mReplyAllAction, Qt::Key_A);
Laurent Montel's avatar
Laurent Montel committed
93
    connect(mReplyAllAction, &QAction::triggered, this, &MessageActions::slotReplyAllToMsg);
Laurent Montel's avatar
Laurent Montel committed
94
    mReplyActionMenu->addAction(mReplyAllAction);
Laurent Montel's avatar
Laurent Montel committed
95

96 97
    mReplyListAction = new QAction(QIcon::fromTheme(QStringLiteral("mail-reply-list")), i18n("Reply to Mailing-&List..."), this);
    ac->addAction(QStringLiteral("reply_list"), mReplyListAction);
Laurent Montel's avatar
Laurent Montel committed
98 99

    ac->setDefaultShortcut(mReplyListAction, Qt::Key_L);
Laurent Montel's avatar
Laurent Montel committed
100
    connect(mReplyListAction, &QAction::triggered, this, &MessageActions::slotReplyListToMsg);
Laurent Montel's avatar
Laurent Montel committed
101
    mReplyActionMenu->addAction(mReplyListAction);
Laurent Montel's avatar
Laurent Montel committed
102

Laurent Montel's avatar
Laurent Montel committed
103
    mNoQuoteReplyAction = new QAction(i18n("Reply Without &Quote..."), this);
104
    ac->addAction(QStringLiteral("noquotereply"), mNoQuoteReplyAction);
Laurent Montel's avatar
Laurent Montel committed
105
    ac->setDefaultShortcut(mNoQuoteReplyAction, Qt::SHIFT + Qt::Key_R);
Laurent Montel's avatar
Laurent Montel committed
106
    connect(mNoQuoteReplyAction, &QAction::triggered, this, &MessageActions::slotNoQuoteReplyToMsg);
Laurent Montel's avatar
Laurent Montel committed
107

Laurent Montel's avatar
Laurent Montel committed
108
    mListFilterAction = new QAction(i18n("Filter on Mailing-&List..."), this);
109
    ac->addAction(QStringLiteral("mlist_filter"), mListFilterAction);
Laurent Montel's avatar
Laurent Montel committed
110
    connect(mListFilterAction, &QAction::triggered, this, &MessageActions::slotMailingListFilter);
Laurent Montel's avatar
Laurent Montel committed
111

Laurent Montel's avatar
Laurent Montel committed
112
    mStatusMenu = new KActionMenu(i18n("Mar&k Message"), this);
113
    ac->addAction(QStringLiteral("set_status"), mStatusMenu);
Laurent Montel's avatar
Laurent Montel committed
114

Laurent Montel's avatar
Laurent Montel committed
115 116 117 118
    KMMainWidget *mainwin = kmkernel->getKMMainWidget();
    if (mainwin) {
        QAction *action = mainwin->akonadiStandardAction(Akonadi::StandardMailActionManager::MarkMailAsRead);
        mStatusMenu->addAction(action);
Laurent Montel's avatar
Laurent Montel committed
119

Laurent Montel's avatar
Laurent Montel committed
120 121
        action = mainwin->akonadiStandardAction(Akonadi::StandardMailActionManager::MarkMailAsUnread);
        mStatusMenu->addAction(action);
Laurent Montel's avatar
Laurent Montel committed
122 123

        mStatusMenu->addSeparator();
Laurent Montel's avatar
Laurent Montel committed
124 125
        action = mainwin->akonadiStandardAction(Akonadi::StandardMailActionManager::MarkMailAsImportant);
        mStatusMenu->addAction(action);
Laurent Montel's avatar
Laurent Montel committed
126

Laurent Montel's avatar
Laurent Montel committed
127 128
        action = mainwin->akonadiStandardAction(Akonadi::StandardMailActionManager::MarkMailAsActionItem);
        mStatusMenu->addAction(action);
Laurent Montel's avatar
Laurent Montel committed
129 130
    }

131 132
    mAnnotateAction = new QAction(QIcon::fromTheme(QStringLiteral("view-pim-notes")), i18n("Add Note..."), this);
    ac->addAction(QStringLiteral("annotate"), mAnnotateAction);
Laurent Montel's avatar
Laurent Montel committed
133
    connect(mAnnotateAction, &QAction::triggered, this, &MessageActions::annotateMessage);
Laurent Montel's avatar
Laurent Montel committed
134

135
    mEditAsNewAction = new QAction(QIcon::fromTheme(QStringLiteral("document-edit")), i18n("&Edit As New"), this);
Laurent Montel's avatar
Laurent Montel committed
136 137 138 139
    ac->addAction(QStringLiteral("editasnew"), mEditAsNewAction);
    connect(mEditAsNewAction, &QAction::triggered, this, &MessageActions::editCurrentMessage);
    ac->setDefaultShortcut(mEditAsNewAction, Qt::Key_T);

Laurent Montel's avatar
Laurent Montel committed
140 141
    mPrintAction = KStandardAction::print(this, &MessageActions::slotPrintMessage, ac);
    mPrintPreviewAction = KStandardAction::printPreview(this, &MessageActions::slotPrintPreviewMsg, ac);
Laurent Montel's avatar
Laurent Montel committed
142

Laurent Montel's avatar
Laurent Montel committed
143
    mForwardActionMenu = new KActionMenu(QIcon::fromTheme(QStringLiteral("mail-forward")), i18nc("Message->", "&Forward"), this);
144
    ac->addAction(QStringLiteral("message_forward"), mForwardActionMenu);
Laurent Montel's avatar
Laurent Montel committed
145

146
    mForwardAttachedAction = new QAction(QIcon::fromTheme(QStringLiteral("mail-forward")),
Laurent Montel's avatar
Laurent Montel committed
147
                                         i18nc("@action:inmenu Message->Forward->",
Laurent Montel's avatar
Laurent Montel committed
148
                                               "As &Attachment..."),
Laurent Montel's avatar
Laurent Montel committed
149
                                         this);
Laurent Montel's avatar
Laurent Montel committed
150
    connect(mForwardAttachedAction, SIGNAL(triggered(bool)), parent, SLOT(slotForwardAttachedMessage()));
Laurent Montel's avatar
Laurent Montel committed
151

152
    ac->addAction(QStringLiteral("message_forward_as_attachment"), mForwardAttachedAction);
Laurent Montel's avatar
Laurent Montel committed
153

154
    mForwardInlineAction = new QAction(QIcon::fromTheme(QStringLiteral("mail-forward")),
Laurent Montel's avatar
Laurent Montel committed
155
                                       i18nc("@action:inmenu Message->Forward->",
Laurent Montel's avatar
Laurent Montel committed
156
                                             "&Inline..."),
Laurent Montel's avatar
Laurent Montel committed
157 158
                                       this);
    connect(mForwardInlineAction, SIGNAL(triggered(bool)), parent, SLOT(slotForwardInlineMsg()));
Laurent Montel's avatar
Laurent Montel committed
159

160
    ac->addAction(QStringLiteral("message_forward_inline"), mForwardInlineAction);
Laurent Montel's avatar
Laurent Montel committed
161

Laurent Montel's avatar
Laurent Montel committed
162
    setupForwardActions(ac);
Laurent Montel's avatar
Laurent Montel committed
163

Laurent Montel's avatar
Laurent Montel committed
164
    mRedirectAction = new QAction(i18nc("Message->Forward->", "&Redirect..."), this);
165
    ac->addAction(QStringLiteral("message_forward_redirect"), mRedirectAction);
Laurent Montel's avatar
Laurent Montel committed
166
    connect(mRedirectAction, SIGNAL(triggered(bool)), parent, SLOT(slotRedirectMessage()));
Laurent Montel's avatar
Laurent Montel committed
167

Laurent Montel's avatar
Laurent Montel committed
168 169
    ac->setDefaultShortcut(mRedirectAction, QKeySequence(Qt::Key_E));
    mForwardActionMenu->addAction(mRedirectAction);
Laurent Montel's avatar
Laurent Montel committed
170

Laurent Montel's avatar
Laurent Montel committed
171
    mMailingListActionMenu = new KActionMenu(QIcon::fromTheme(QStringLiteral("mail-message-new-list")), i18nc("Message->", "Mailing-&List"), this);
Laurent Montel's avatar
Laurent Montel committed
172
    connect(mMailingListActionMenu->menu(), &QMenu::triggered, this, &MessageActions::slotRunUrl);
173
    ac->addAction(QStringLiteral("mailing_list"), mMailingListActionMenu);
Laurent Montel's avatar
Laurent Montel committed
174 175
    mMailingListActionMenu->setEnabled(false);

176 177
    connect(kmkernel->folderCollectionMonitor(), &Akonadi::Monitor::itemChanged, this, &MessageActions::slotItemModified);
    connect(kmkernel->folderCollectionMonitor(), &Akonadi::Monitor::itemRemoved, this, &MessageActions::slotItemRemoved);
Laurent Montel's avatar
Laurent Montel committed
178

Laurent Montel's avatar
Laurent Montel committed
179
    mCustomTemplatesMenu = new TemplateParser::CustomTemplatesMenu(parent, ac);
Laurent Montel's avatar
Laurent Montel committed
180

Laurent Montel's avatar
Laurent Montel committed
181 182 183
    connect(mCustomTemplatesMenu, SIGNAL(replyTemplateSelected(QString)), parent, SLOT(slotCustomReplyToMsg(QString)));
    connect(mCustomTemplatesMenu, SIGNAL(replyAllTemplateSelected(QString)), parent, SLOT(slotCustomReplyAllToMsg(QString)));
    connect(mCustomTemplatesMenu, SIGNAL(forwardTemplateSelected(QString)), parent, SLOT(slotCustomForwardMsg(QString)));
Laurent Montel's avatar
Laurent Montel committed
184
    connect(KMKernel::self(), &KMKernel::customTemplatesChanged, mCustomTemplatesMenu, &TemplateParser::CustomTemplatesMenu::update);
Laurent Montel's avatar
Laurent Montel committed
185

Laurent Montel's avatar
Laurent Montel committed
186
    forwardMenu()->addSeparator();
Laurent Montel's avatar
Laurent Montel committed
187
    forwardMenu()->addAction(mCustomTemplatesMenu->forwardActionMenu());
Laurent Montel's avatar
Laurent Montel committed
188
    replyMenu()->addSeparator();
Laurent Montel's avatar
Laurent Montel committed
189 190
    replyMenu()->addAction(mCustomTemplatesMenu->replyActionMenu());
    replyMenu()->addAction(mCustomTemplatesMenu->replyAllActionMenu());
Laurent Montel's avatar
Laurent Montel committed
191

Laurent Montel's avatar
Laurent Montel committed
192 193 194
    //Don't translate it. Shown only when we set env variable AKONADI_SEARCH_DEBUG
    mDebugAkonadiSearchAction = new QAction(QStringLiteral("Debug Akonadi Search..."), this);
    connect(mDebugAkonadiSearchAction, &QAction::triggered, this, &MessageActions::slotDebugAkonadiSearch);
195

Laurent Montel's avatar
Laurent Montel committed
196
    mAddFollowupReminderAction = new QAction(i18n("Add Followup Reminder..."), this);
197
    ac->addAction(QStringLiteral("message_followup_reminder"), mAddFollowupReminderAction);
198
    connect(mAddFollowupReminderAction, &QAction::triggered, this, &MessageActions::slotAddFollowupReminder);
199

200 201 202 203
    mSendAgainAction = new QAction(i18n("Send A&gain..."), this);
    ac->addAction(QStringLiteral("send_again"), mSendAgainAction);
    connect(mSendAgainAction, &QAction::triggered, this, &MessageActions::slotResendMessage);

Laurent Montel's avatar
Laurent Montel committed
204 205 206 207 208
    mNewMessageFromTemplateAction = new QAction(QIcon::fromTheme(QStringLiteral("document-new")), i18n("New Message From &Template"), this);
    ac->addAction(QStringLiteral("use_template"), mNewMessageFromTemplateAction);
    connect(mNewMessageFromTemplateAction, &QAction::triggered, this, &MessageActions::slotUseTemplate);
    ac->setDefaultShortcut(mNewMessageFromTemplateAction, QKeySequence(Qt::SHIFT + Qt::Key_N));

209 210 211 212
    mExportToPdfAction = new QAction(QIcon::fromTheme(QStringLiteral("application-pdf")), i18n("Export to PDF..."), this);
    ac->addAction(QStringLiteral("file_export_pdf"), mExportToPdfAction);
    connect(mExportToPdfAction, &QAction::triggered, this, &MessageActions::slotExportToPdf);

Laurent Montel's avatar
Laurent Montel committed
213
    updateActions();
214 215
}

Laurent Montel's avatar
Laurent Montel committed
216 217
MessageActions::~MessageActions()
{
Laurent Montel's avatar
Laurent Montel committed
218
    delete mCustomTemplatesMenu;
Laurent Montel's avatar
Laurent Montel committed
219 220
}

Laurent Montel's avatar
Laurent Montel committed
221
TemplateParser::CustomTemplatesMenu *MessageActions::customTemplatesMenu() const
222
{
Laurent Montel's avatar
Laurent Montel committed
223
    return mCustomTemplatesMenu;
224 225
}

Laurent Montel's avatar
Laurent Montel committed
226 227 228 229 230 231 232 233 234
void MessageActions::slotUseTemplate()
{
    if (!mCurrentItem.isValid()) {
        return;
    }
    KMCommand *command = new KMUseTemplateCommand(mParent, mCurrentItem);
    command->start();
}

Laurent Montel's avatar
Laurent Montel committed
235 236 237 238 239
QAction *MessageActions::editAsNewAction() const
{
    return mEditAsNewAction;
}

Laurent Montel's avatar
Laurent Montel committed
240
void MessageActions::setCurrentMessage(const Akonadi::Item &msg, const Akonadi::Item::List &items)
241
{
Laurent Montel's avatar
Laurent Montel committed
242
    mCurrentItem = msg;
Laurent Montel's avatar
Laurent Montel committed
243

Laurent Montel's avatar
Laurent Montel committed
244
    if (!items.isEmpty()) {
Laurent Montel's avatar
Laurent Montel committed
245
        if (msg.isValid()) {
Laurent Montel's avatar
Laurent Montel committed
246
            mVisibleItems = items;
Laurent Montel's avatar
Laurent Montel committed
247 248
        } else {
            mVisibleItems.clear();
Laurent Montel's avatar
Laurent Montel committed
249
        }
250
    }
Laurent Montel's avatar
Laurent Montel committed
251

Laurent Montel's avatar
Laurent Montel committed
252
    if (!msg.isValid()) {
253
        mVisibleItems.clear();
Laurent Montel's avatar
Laurent Montel committed
254
        clearMailingListActions();
255
    }
256

Laurent Montel's avatar
Laurent Montel committed
257
    updateActions();
258 259
}

260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319
KActionMenu *MessageActions::replyMenu() const
{
    return mReplyActionMenu;
}

QAction *MessageActions::replyListAction() const
{
    return mReplyListAction;
}

QAction *MessageActions::forwardInlineAction() const
{
    return mForwardInlineAction;
}

QAction *MessageActions::forwardAttachedAction() const
{
    return mForwardAttachedAction;
}

QAction *MessageActions::redirectAction() const
{
    return mRedirectAction;
}

KActionMenu *MessageActions::messageStatusMenu() const
{
    return mStatusMenu;
}

KActionMenu *MessageActions::forwardMenu() const
{
    return mForwardActionMenu;
}

QAction *MessageActions::annotateAction() const
{
    return mAnnotateAction;
}

QAction *MessageActions::printAction() const
{
    return mPrintAction;
}

QAction *MessageActions::printPreviewAction() const
{
    return mPrintPreviewAction;
}

QAction *MessageActions::listFilterAction() const
{
    return mListFilterAction;
}

KActionMenu *MessageActions::mailingListActionMenu() const
{
    return mMailingListActionMenu;
}

Laurent Montel's avatar
Laurent Montel committed
320
void MessageActions::slotItemRemoved(const Akonadi::Item &item)
321
{
Laurent Montel's avatar
Laurent Montel committed
322
    if (item == mCurrentItem) {
Laurent Montel's avatar
Laurent Montel committed
323 324 325
        mCurrentItem = Akonadi::Item();
        updateActions();
    }
326 327
}

Laurent Montel's avatar
Laurent Montel committed
328
void MessageActions::slotItemModified(const Akonadi::Item &item, const QSet< QByteArray > &partIdentifiers)
329
{
Laurent Montel's avatar
Laurent Montel committed
330 331
    Q_UNUSED(partIdentifiers);
    if (item == mCurrentItem) {
Laurent Montel's avatar
Laurent Montel committed
332 333
        mCurrentItem = item;
        const int numberOfVisibleItems = mVisibleItems.count();
Laurent Montel's avatar
Laurent Montel committed
334
        for (int i = 0; i < numberOfVisibleItems; ++i) {
Laurent Montel's avatar
Laurent Montel committed
335
            Akonadi::Item it = mVisibleItems.at(i);
Laurent Montel's avatar
Laurent Montel committed
336
            if (item == it) {
Laurent Montel's avatar
Laurent Montel committed
337 338 339 340
                mVisibleItems[i] = item;
            }
        }
        updateActions();
341
    }
342 343
}

344 345
void MessageActions::updateActions()
{
Laurent Montel's avatar
Laurent Montel committed
346 347 348
    const bool hasPayload = mCurrentItem.hasPayload<KMime::Message::Ptr>();
    bool itemValid = mCurrentItem.isValid();
    Akonadi::Collection parent;
Laurent Montel's avatar
Laurent Montel committed
349
    if (itemValid) { //=> valid
Laurent Montel's avatar
Laurent Montel committed
350
        parent = mCurrentItem.parentCollection();
Laurent Montel's avatar
Laurent Montel committed
351 352 353
    }
    if (parent.isValid()) {
        if (CommonKernel->folderIsTemplates(parent)) {
Laurent Montel's avatar
Laurent Montel committed
354
            itemValid = false;
Laurent Montel's avatar
Laurent Montel committed
355
        }
Laurent Montel's avatar
Laurent Montel committed
356 357
    }

Laurent Montel's avatar
Laurent Montel committed
358
    const bool multiVisible = !mVisibleItems.isEmpty() || mCurrentItem.isValid();
Laurent Montel's avatar
Laurent Montel committed
359 360 361 362 363 364 365 366
    const bool uniqItem = (itemValid || hasPayload) && (mVisibleItems.count() <= 1);
    mReplyActionMenu->setEnabled(hasPayload);
    mReplyAction->setEnabled(hasPayload);
    mNoQuoteReplyAction->setEnabled(hasPayload);
    mReplyAuthorAction->setEnabled(hasPayload);
    mReplyAllAction->setEnabled(hasPayload);
    mReplyListAction->setEnabled(hasPayload);
    mNoQuoteReplyAction->setEnabled(hasPayload);
367
    mSendAgainAction->setEnabled(itemValid);
Laurent Montel's avatar
Laurent Montel committed
368 369

    mAnnotateAction->setEnabled(uniqItem);
370
    mAddFollowupReminderAction->setEnabled(uniqItem);
Laurent Montel's avatar
Laurent Montel committed
371 372 373 374 375 376 377 378
    if (!mCurrentItem.hasAttribute<Akonadi::EntityAnnotationsAttribute>()) {
        mAnnotateAction->setText(i18n("Add Note..."));
    } else {
        mAnnotateAction->setText(i18n("Edit Note..."));
    }

    mStatusMenu->setEnabled(multiVisible);

379 380 381
    mPrintAction->setEnabled(mMessageView != nullptr);
    mPrintPreviewAction->setEnabled(mMessageView != nullptr);

Laurent Montel's avatar
Laurent Montel committed
382 383 384
    if (mCurrentItem.hasPayload<KMime::Message::Ptr>()) {
        if (mCurrentItem.loadedPayloadParts().contains("RFC822")) {
            updateMailingListActions(mCurrentItem);
Laurent Montel's avatar
Laurent Montel committed
385
        } else {
Laurent Montel's avatar
Laurent Montel committed
386
            Akonadi::ItemFetchJob *job = new Akonadi::ItemFetchJob(mCurrentItem);
Laurent Montel's avatar
Laurent Montel committed
387
            job->fetchScope().fetchAllAttributes();
Laurent Montel's avatar
Laurent Montel committed
388 389
            job->fetchScope().fetchFullPayload(true);
            job->fetchScope().fetchPayloadPart(Akonadi::MessagePart::Header);
390
            job->fetchScope().fetchAttribute<Akonadi::EntityAnnotationsAttribute>();
Laurent Montel's avatar
Laurent Montel committed
391
            connect(job, &Akonadi::ItemFetchJob::result, this, &MessageActions::slotUpdateActionsFetchDone);
Laurent Montel's avatar
Laurent Montel committed
392
        }
393
    }
Laurent Montel's avatar
Laurent Montel committed
394
    mEditAsNewAction->setEnabled(uniqItem);
395
}
396

Laurent Montel's avatar
Laurent Montel committed
397
void MessageActions::slotUpdateActionsFetchDone(KJob *job)
398
{
Laurent Montel's avatar
Laurent Montel committed
399
    if (job->error()) {
Laurent Montel's avatar
Laurent Montel committed
400
        return;
Laurent Montel's avatar
Laurent Montel committed
401
    }
402

Laurent Montel's avatar
Laurent Montel committed
403 404
    Akonadi::ItemFetchJob *fetchJob = static_cast<Akonadi::ItemFetchJob *>(job);
    if (fetchJob->items().isEmpty()) {
Laurent Montel's avatar
Laurent Montel committed
405
        return;
Laurent Montel's avatar
Laurent Montel committed
406
    }
407
    const Akonadi::Item messageItem = fetchJob->items().constFirst();
Laurent Montel's avatar
Laurent Montel committed
408
    if (messageItem == mCurrentItem) {
Laurent Montel's avatar
Laurent Montel committed
409
        mCurrentItem = messageItem;
Laurent Montel's avatar
Laurent Montel committed
410
        updateMailingListActions(messageItem);
Laurent Montel's avatar
Laurent Montel committed
411
    }
412 413
}

414 415
void MessageActions::clearMailingListActions()
{
Laurent Montel's avatar
Laurent Montel committed
416 417 418
    mMailingListActionMenu->setEnabled(false);
    mListFilterAction->setEnabled(false);
    mListFilterAction->setText(i18n("Filter on Mailing-List..."));
419 420
}

Laurent Montel's avatar
Laurent Montel committed
421
void MessageActions::updateMailingListActions(const Akonadi::Item &messageItem)
422
{
Laurent Montel's avatar
Laurent Montel committed
423 424 425
    if (!messageItem.hasPayload<KMime::Message::Ptr>()) {
        return;
    }
Laurent Montel's avatar
Laurent Montel committed
426
    KMime::Message::Ptr message = messageItem.payload<KMime::Message::Ptr>();
Laurent Montel's avatar
Laurent Montel committed
427
    const MessageCore::MailingList mailList = MessageCore::MailingList::detect(message);
Laurent Montel's avatar
Laurent Montel committed
428

Laurent Montel's avatar
Laurent Montel committed
429
    if (mailList.features() == MessageCore::MailingList::None) {
Laurent Montel's avatar
Laurent Montel committed
430 431 432 433 434
        clearMailingListActions();
    } else {
        // A mailing list menu with only a title is pretty boring
        // so make sure theres at least some content
        QString listId;
Laurent Montel's avatar
Laurent Montel committed
435
        if (mailList.features() & MessageCore::MailingList::Id) {
Laurent Montel's avatar
Laurent Montel committed
436 437 438
            // From a list-id in the form, "Birds of France <bof.yahoo.com>",
            // take "Birds of France" if it exists otherwise "bof.yahoo.com".
            listId = mailList.id();
Laurent Montel's avatar
Laurent Montel committed
439 440 441 442 443 444 445
            const int start = listId.indexOf(QLatin1Char('<'));
            if (start > 0) {
                listId.truncate(start - 1);
            } else if (start == 0) {
                const int end = listId.lastIndexOf(QLatin1Char('>'));
                if (end < 1) {   // shouldn't happen but account for it anyway
                    listId.remove(0, 1);
Laurent Montel's avatar
Laurent Montel committed
446
                } else {
Laurent Montel's avatar
Laurent Montel committed
447
                    listId = listId.mid(1, end - 1);
Laurent Montel's avatar
Laurent Montel committed
448 449 450 451 452 453
                }
            }
        }
        mMailingListActionMenu->menu()->clear();
        qDeleteAll(mMailListActionList);
        mMailListActionList.clear();
454
        mMailingListActionMenu->menu()->setTitle(KStringHandler::rsqueeze(i18n("Mailing List Name: %1", (listId.isEmpty() ? i18n("<unknown>") : listId)), 40));
Laurent Montel's avatar
Laurent Montel committed
455
        if (mailList.features() & MessageCore::MailingList::ArchivedAt) {
Laurent Montel's avatar
Laurent Montel committed
456
            // IDEA: this may be something you want to copy - "Copy in submenu"?
Laurent Montel's avatar
Laurent Montel committed
457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477
            addMailingListActions(i18n("Open Message in List Archive"), mailList.archivedAtUrls());
        }
        if (mailList.features() & MessageCore::MailingList::Post) {
            addMailingListActions(i18n("Post New Message"), mailList.postUrls());
        }
        if (mailList.features() & MessageCore::MailingList::Archive) {
            addMailingListActions(i18n("Go to Archive"), mailList.archiveUrls());
        }
        if (mailList.features() & MessageCore::MailingList::Help) {
            addMailingListActions(i18n("Request Help"), mailList.helpUrls());
        }
        if (mailList.features() & MessageCore::MailingList::Owner) {
            addMailingListActions(i18nc("Contact the owner of the mailing list", "Contact Owner"), mailList.ownerUrls());
        }
        if (mailList.features() & MessageCore::MailingList::Subscribe) {
            addMailingListActions(i18n("Subscribe to List"), mailList.subscribeUrls());
        }
        if (mailList.features() & MessageCore::MailingList::Unsubscribe) {
            addMailingListActions(i18n("Unsubscribe from List"), mailList.unsubscribeUrls());
        }
        mMailingListActionMenu->setEnabled(true);
Laurent Montel's avatar
Laurent Montel committed
478 479 480

        QByteArray name;
        QString value;
Laurent Montel's avatar
Laurent Montel committed
481 482 483 484
        const QString lname = MailingList::name(message, name, value);
        if (!lname.isEmpty()) {
            mListFilterAction->setEnabled(true);
            mListFilterAction->setText(i18n("Filter on Mailing-List %1...", lname));
485
        }
486
    }
487 488
}

Laurent Montel's avatar
Laurent Montel committed
489
void MessageActions::replyCommand(MessageComposer::ReplyStrategy strategy)
Thomas McGuire's avatar
Thomas McGuire committed
490
{
Laurent Montel's avatar
Laurent Montel committed
491 492 493
    if (!mCurrentItem.hasPayload<KMime::Message::Ptr>()) {
        return;
    }
494

Laurent Montel's avatar
Laurent Montel committed
495
    const QString text = mMessageView ? mMessageView->copyText() : QString();
Laurent Montel's avatar
Laurent Montel committed
496
    KMCommand *command = new KMReplyCommand(mParent, mCurrentItem, strategy, text);
Laurent Montel's avatar
Laurent Montel committed
497
    connect(command, &KMCommand::completed, this, &MessageActions::replyActionFinished);
Laurent Montel's avatar
Laurent Montel committed
498
    command->start();
Thomas McGuire's avatar
Thomas McGuire committed
499 500
}

Laurent Montel's avatar
Laurent Montel committed
501
void MessageActions::setMessageView(KMReaderWin *msgView)
502
{
Laurent Montel's avatar
Laurent Montel committed
503
    mMessageView = msgView;
504 505
}

Laurent Montel's avatar
Laurent Montel committed
506
void MessageActions::setupForwardActions(KActionCollection *ac)
507
{
Laurent Montel's avatar
Laurent Montel committed
508
    disconnect(mForwardActionMenu, SIGNAL(triggered(bool)), nullptr, nullptr);
Laurent Montel's avatar
Laurent Montel committed
509 510 511
    mForwardActionMenu->removeAction(mForwardInlineAction);
    mForwardActionMenu->removeAction(mForwardAttachedAction);

Laurent Montel's avatar
Laurent Montel committed
512
    if (KMailSettings::self()->forwardingInlineByDefault()) {
Laurent Montel's avatar
Laurent Montel committed
513 514 515 516 517 518
        mForwardActionMenu->insertAction(mRedirectAction, mForwardInlineAction);
        mForwardActionMenu->insertAction(mRedirectAction, mForwardAttachedAction);
        ac->setDefaultShortcut(mForwardInlineAction, QKeySequence(Qt::Key_F));
        ac->setDefaultShortcut(mForwardAttachedAction, QKeySequence(Qt::SHIFT + Qt::Key_F));
        QObject::connect(mForwardActionMenu, SIGNAL(triggered(bool)),
                         mParent, SLOT(slotForwardInlineMsg()));
Laurent Montel's avatar
Laurent Montel committed
519
    } else {
Laurent Montel's avatar
Laurent Montel committed
520 521 522 523 524
        mForwardActionMenu->insertAction(mRedirectAction, mForwardAttachedAction);
        mForwardActionMenu->insertAction(mRedirectAction, mForwardInlineAction);
        ac->setDefaultShortcut(mForwardInlineAction, QKeySequence(Qt::Key_F));
        ac->setDefaultShortcut(mForwardAttachedAction, QKeySequence(Qt::SHIFT + Qt::Key_F));
        QObject::connect(mForwardActionMenu, SIGNAL(triggered(bool)),
Laurent Montel's avatar
Laurent Montel committed
525
                         mParent, SLOT(slotForwardAttachedMessage()));
Laurent Montel's avatar
Laurent Montel committed
526
    }
527 528
}

Laurent Montel's avatar
Laurent Montel committed
529
void MessageActions::setupForwardingActionsList(KXMLGUIClient *guiClient)
530
{
Laurent Montel's avatar
Laurent Montel committed
531
    QList<QAction *> forwardActionList;
532
    guiClient->unplugActionList(QStringLiteral("forward_action_list"));
Laurent Montel's avatar
Laurent Montel committed
533
    if (KMailSettings::self()->forwardingInlineByDefault()) {
Laurent Montel's avatar
Laurent Montel committed
534 535
        forwardActionList.append(mForwardInlineAction);
        forwardActionList.append(mForwardAttachedAction);
Laurent Montel's avatar
Laurent Montel committed
536
    } else {
Laurent Montel's avatar
Laurent Montel committed
537 538
        forwardActionList.append(mForwardAttachedAction);
        forwardActionList.append(mForwardInlineAction);
Laurent Montel's avatar
Laurent Montel committed
539
    }
Laurent Montel's avatar
Laurent Montel committed
540
    forwardActionList.append(mRedirectAction);
541
    guiClient->plugActionList(QStringLiteral("forward_action_list"), forwardActionList);
542 543
}

544 545
void MessageActions::slotReplyToMsg()
{
Laurent Montel's avatar
Laurent Montel committed
546
    replyCommand(MessageComposer::ReplySmart);
547 548 549 550
}

void MessageActions::slotReplyAuthorToMsg()
{
Laurent Montel's avatar
Laurent Montel committed
551
    replyCommand(MessageComposer::ReplyAuthor);
552 553 554 555
}

void MessageActions::slotReplyListToMsg()
{
Laurent Montel's avatar
Laurent Montel committed
556
    replyCommand(MessageComposer::ReplyList);
557 558 559 560
}

void MessageActions::slotReplyAllToMsg()
{
Laurent Montel's avatar
Laurent Montel committed
561
    replyCommand(MessageComposer::ReplyAll);
562 563 564 565
}

void MessageActions::slotNoQuoteReplyToMsg()
{
Laurent Montel's avatar
Laurent Montel committed
566
    if (!mCurrentItem.hasPayload<KMime::Message::Ptr>()) {
Laurent Montel's avatar
Laurent Montel committed
567
        return;
Laurent Montel's avatar
Laurent Montel committed
568 569
    }
    KMCommand *command = new KMReplyCommand(mParent, mCurrentItem, MessageComposer::ReplySmart, QString(), true);
Laurent Montel's avatar
Laurent Montel committed
570
    command->start();
571 572
}

Laurent Montel's avatar
Laurent Montel committed
573
void MessageActions::slotRunUrl(QAction *urlAction)
574
{
Laurent Montel's avatar
Laurent Montel committed
575
    const QVariant q = urlAction->data();
Laurent Montel's avatar
Laurent Montel committed
576 577
    if (q.type() == QVariant::Url) {
        new KRun(q.toUrl(), mParent);
Laurent Montel's avatar
Laurent Montel committed
578
    }
579 580
}

581 582
void MessageActions::slotMailingListFilter()
{
Laurent Montel's avatar
Laurent Montel committed
583
    if (!mCurrentItem.hasPayload<KMime::Message::Ptr>()) {
Laurent Montel's avatar
Laurent Montel committed
584
        return;
Laurent Montel's avatar
Laurent Montel committed
585
    }
Laurent Montel's avatar
Laurent Montel committed
586

Laurent Montel's avatar
Laurent Montel committed
587
    KMCommand *command = new KMMailingListFilterCommand(mParent, mCurrentItem);
Laurent Montel's avatar
Laurent Montel committed
588
    command->start();
589 590
}

Laurent Montel's avatar
Laurent Montel committed
591
void MessageActions::printMessage(bool preview)
592
{
Laurent Montel's avatar
Laurent Montel committed
593
    if (mMessageView) {
Laurent Montel's avatar
Laurent Montel committed
594
        bool result = false;
Laurent Montel's avatar
Laurent Montel committed
595
        if (MessageViewer::MessageViewerSettings::self()->printSelectedText()) {
Laurent Montel's avatar
Laurent Montel committed
596 597
            result = mMessageView->printSelectedText(preview);
        }
Laurent Montel's avatar
Laurent Montel committed
598 599 600 601 602
        if (!result) {
            const bool useFixedFont = MessageViewer::MessageViewerSettings::self()->useFixedFont();
            const QString overrideEncoding = MessageCore::MessageCoreSettings::self()->overrideCharacterEncoding();

            const Akonadi::Item message = mCurrentItem;
603 604 605 606
            KMPrintCommandInfo commandInfo;
            commandInfo.mMsg = message;
            commandInfo.mHeaderStylePlugin = mMessageView->viewer()->headerStylePlugin();
            commandInfo.mFormat = mMessageView->viewer()->displayFormatMessageOverwrite();
Laurent Montel's avatar
Laurent Montel committed
607
            commandInfo.mHtmlLoadExtOverride = mMessageView->viewer()->htmlLoadExternal();
608 609 610
            commandInfo.mPrintPreview = preview;
            commandInfo.mUseFixedFont = useFixedFont;
            commandInfo.mOverrideFont = overrideEncoding;
611 612
            commandInfo.mShowSignatureDetails = mMessageView->viewer()->showSignatureDetails() || MessageViewer::MessageViewerSettings::self()->alwaysShowEncryptionSignatureDetails();
            commandInfo.mShowEncryptionDetails = mMessageView->viewer()->showEncryptionDetails() || MessageViewer::MessageViewerSettings::self()->alwaysShowEncryptionSignatureDetails();
613

Laurent Montel's avatar
Laurent Montel committed
614
            KMPrintCommand *command
615
                = new KMPrintCommand(mParent, commandInfo);
Laurent Montel's avatar
Laurent Montel committed
616 617
            command->start();
        }
618 619
    } else {
        qCWarning(KMAIL_LOG) << "MessageActions::printMessage impossible to do it if we don't have a viewer";
620
    }
621
}
622

Laurent Montel's avatar
Laurent Montel committed
623
void MessageActions::slotPrintPreviewMsg()
624
{
Laurent Montel's avatar
Laurent Montel committed
625
    printMessage(true);
Laurent Montel's avatar
Laurent Montel committed
626
}
627

Laurent Montel's avatar
Laurent Montel committed
628
void MessageActions::slotPrintMessage()
Laurent Montel's avatar
Laurent Montel committed
629
{
Laurent Montel's avatar
Laurent Montel committed
630
    printMessage(false);
631 632
}

633 634 635 636 637 638 639 640
/**
 * This adds a list of actions to mMailingListActionMenu mapping the identifier item to
 * the url.
 *
 * e.g.: item = "Contact Owner"
 * "Contact Owner (email)" -> KRun( "mailto:bob@arthouseflowers.example.com" )
 * "Contact Owner (web)" -> KRun( "http://arthouseflowers.example.com/contact-owner.php" )
 */
Laurent Montel's avatar
KUrl--  
Laurent Montel committed
641
void MessageActions::addMailingListActions(const QString &item, const QList<QUrl> &list)
642
{
Laurent Montel's avatar
Laurent Montel committed
643
    for (const QUrl &url : list) {
Laurent Montel's avatar
Laurent Montel committed
644
        addMailingListAction(item, url);
Laurent Montel's avatar
Laurent Montel committed
645
    }
646 647 648 649 650 651
}

/**
 * This adds a action to mMailingListActionMenu mapping the identifier item to
 * the url. See addMailingListActions above.
 */
Laurent Montel's avatar
KUrl--  
Laurent Montel committed
652
void MessageActions::addMailingListAction(const QString &item, const QUrl &url)
653
{
Laurent Montel's avatar
KUrl--  
Laurent Montel committed
654 655
    QString protocol = url.scheme().toLower();
    QString prettyUrl = url.toDisplayString();
656
    if (protocol == QLatin1String("mailto")) {
Laurent Montel's avatar
Laurent Montel committed
657 658
        protocol = i18n("email");
        prettyUrl.remove(0, 7);   // length( "mailto:" )
Laurent Montel's avatar
Laurent Montel committed
659
    } else if (protocol.startsWith(QLatin1String("http"))) {
Laurent Montel's avatar
Laurent Montel committed
660
        protocol = i18n("web");
Laurent Montel's avatar
Laurent Montel committed
661 662
    }
    // item is a mailing list url description passed from the updateActions method above.
Laurent Montel's avatar
Laurent Montel committed
663 664
    QAction *act
        = new QAction(i18nc("%1 is a 'Contact Owner' or similar action. %2 is a protocol normally web or email though could be irc/ftp or other url variant", "%1 (%2)", item, protocol), this);
Laurent Montel's avatar
Laurent Montel committed
665
    mMailListActionList.append(act);
Laurent Montel's avatar
KUrl--  
Laurent Montel committed
666
    const QVariant v(url);
Laurent Montel's avatar
Laurent Montel committed
667
    act->setData(v);
Laurent Montel's avatar
Laurent Montel committed
668
    KMail::Util::addQActionHelpText(act, prettyUrl);
Laurent Montel's avatar
Laurent Montel committed
669
    mMailingListActionMenu->addAction(act);
670 671
}

672 673
void MessageActions::editCurrentMessage()
{
Laurent Montel's avatar
Laurent Montel committed
674
    KMCommand *command = nullptr;
Laurent Montel's avatar
Laurent Montel committed
675
    if (mCurrentItem.isValid()) {
Laurent Montel's avatar
Laurent Montel committed
676
        Akonadi::Collection col = mCurrentItem.parentCollection();
Laurent Montel's avatar
Laurent Montel committed
677
        qCDebug(KMAIL_LOG) << " mCurrentItem.parentCollection()" << mCurrentItem.parentCollection();
Laurent Montel's avatar
Laurent Montel committed
678 679
        // edit, unlike send again, removes the message from the folder
        // we only want that for templates and drafts folders
Laurent Montel's avatar
Laurent Montel committed
680
        if (col.isValid()
Laurent Montel's avatar
Laurent Montel committed
681 682 683
            && (CommonKernel->folderIsDraftOrOutbox(col)
                || CommonKernel->folderIsTemplates(col))
            ) {
Laurent Montel's avatar
Laurent Montel committed
684 685 686 687
            command = new KMEditItemCommand(mParent, mCurrentItem, true);
        } else {
            command = new KMEditItemCommand(mParent, mCurrentItem, false);
        }
Laurent Montel's avatar
Laurent Montel committed
688
        command->start();
Laurent Montel's avatar
Laurent Montel committed
689 690
    } else if (mCurrentItem.hasPayload<KMime::Message::Ptr>()) {
        command = new KMEditMessageCommand(mParent, mCurrentItem.payload<KMime::Message::Ptr>());
Laurent Montel's avatar
Laurent Montel committed
691 692
        command->start();
    }
693 694
}

695 696
void MessageActions::annotateMessage()
{
Laurent Montel's avatar
Laurent Montel committed
697
    if (!mCurrentItem.isValid()) {
Laurent Montel's avatar
Laurent Montel committed
698
        return;
Laurent Montel's avatar
Laurent Montel committed
699
    }
700

Laurent Montel's avatar
Laurent Montel committed
701
    QPointer<PimCommon::AnnotationEditDialog> dialog = new PimCommon::AnnotationEditDialog(mCurrentItem, mParent);
Laurent Montel's avatar
Laurent Montel committed
702
    dialog->setAttribute(Qt::WA_DeleteOnClose);
703
    dialog->exec();
704 705
}

Laurent Montel's avatar
Laurent Montel committed
706
void MessageActions::addWebShortcutsMenu(QMenu *menu, const QString &text)
707
{
Laurent Montel's avatar
Laurent Montel committed
708
    mWebShortcutMenuManager->setSelectedText(text);
Laurent Montel's avatar
Laurent Montel committed
709
    mWebShortcutMenuManager->addWebShortcutsToMenu(menu);
710
}
711

Laurent Montel's avatar
Laurent Montel committed
712
QAction *MessageActions::debugAkonadiSearchAction() const
713
{
Laurent Montel's avatar
Laurent Montel committed
714
    return mDebugAkonadiSearchAction;
715 716 717 718 719 720 721
}

QAction *MessageActions::addFollowupReminderAction() const
{
    return mAddFollowupReminderAction;
}

Laurent Montel's avatar
Laurent Montel committed
722
void MessageActions::slotDebugAkonadiSearch()
723
{
Laurent Montel's avatar
Laurent Montel committed
724
    if (!mCurrentItem.isValid()) {
725
        return;
Laurent Montel's avatar
Laurent Montel committed
726
    }
Laurent Montel's avatar
Laurent Montel committed
727
    QPointer<Akonadi::Search::AkonadiSearchDebugDialog> dlg = new Akonadi::Search::AkonadiSearchDebugDialog;
728
    dlg->setAkonadiId(mCurrentItem.id());
Laurent Montel's avatar
Laurent Montel committed
729
    dlg->setAttribute(Qt::WA_DeleteOnClose);
Laurent Montel's avatar
Laurent Montel committed