You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

290 lines
9.2 KiB

/*
SPDX-FileCopyrightText: 2010 Thomas McGuire <mcguire@kde.org>
SPDX-FileCopyrightText: 2011-2020 Laurent Montel <montel@kde.org>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
#include "tagactionmanager.h"
#include "tagmonitormanager.h"
#include "messageactions.h"
#include <MailCommon/AddTagDialog>
#include <QAction>
#include <KActionCollection>
#include <KToggleAction>
#include <KXMLGUIClient>
#include <KActionMenu>
#include <QMenu>
#include <KLocalizedString>
#include <KJob>
#include <QIcon>
#include <AkonadiCore/Monitor>
#include "kmail_debug.h"
#include <QPointer>
#include <AkonadiCore/TagFetchJob>
#include <AkonadiCore/TagFetchScope>
#include <AkonadiCore/TagAttribute>
using namespace KMail;
static int s_numberMaxTag = 10;
TagActionManager::TagActionManager(QObject *parent, KActionCollection *actionCollection, MessageActions *messageActions, KXMLGUIClient *guiClient)
: QObject(parent)
, mActionCollection(actionCollection)
, mMessageActions(messageActions)
, mGUIClient(guiClient)
{
mMessageActions->messageStatusMenu()->menu()->addSeparator();
TagMonitorManager *tagMonitorManager = TagMonitorManager::self();
connect(tagMonitorManager, &TagMonitorManager::tagAdded, this, &TagActionManager::onTagAdded);
connect(tagMonitorManager, &TagMonitorManager::tagRemoved, this, &TagActionManager::onTagRemoved);
connect(tagMonitorManager, &TagMonitorManager::tagChanged, this, &TagActionManager::onTagChanged);
connect(tagMonitorManager, &TagMonitorManager::fetchTagDone, this, &TagActionManager::createActions);
}
TagActionManager::~TagActionManager()
{
}
void TagActionManager::clearActions()
{
//Remove the tag actions from the toolbar
if (!mToolbarActions.isEmpty()) {
if (mGUIClient->factory()) {
mGUIClient->unplugActionList(QStringLiteral("toolbar_messagetag_actions"));
}
mToolbarActions.clear();
}
//Remove the tag actions from the status menu and the action collection,
//then delete them.
for (KToggleAction *action : qAsConst(mTagActions)) {
mMessageActions->messageStatusMenu()->removeAction(action);
// This removes and deletes the action at the same time
mActionCollection->removeAction(action);
}
if (mSeparatorMoreAction) {
mMessageActions->messageStatusMenu()->removeAction(mSeparatorMoreAction);
}
if (mSeparatorNewTagAction) {
mMessageActions->messageStatusMenu()->removeAction(mSeparatorNewTagAction);
}
if (mNewTagAction) {
mMessageActions->messageStatusMenu()->removeAction(mNewTagAction);
}
if (mMoreAction) {
mMessageActions->messageStatusMenu()->removeAction(mMoreAction);
}
mTagActions.clear();
}
void TagActionManager::createTagAction(const MailCommon::Tag::Ptr &tag, bool addToMenu)
{
QString cleanName(i18n("Message Tag: %1", tag->tagName));
cleanName.replace(QLatin1Char('&'), QStringLiteral("&&"));
KToggleAction *const tagAction = new KToggleAction(QIcon::fromTheme(tag->iconName),
cleanName, this);
tagAction->setIconText(tag->name());
tagAction->setChecked(tag->id() == mNewTagId);
mActionCollection->addAction(tag->name(), tagAction);
mActionCollection->setDefaultShortcut(tagAction, QKeySequence(tag->shortcut));
const QString tagName = QString::number(tag->tag().id());
connect(tagAction, &KToggleAction::triggered, this, [this, tagName] {
onSignalMapped(tagName);
});
// The shortcut configuration is done in the config dialog.
// The shortcut set in the shortcut dialog would not be saved back to
// the tag descriptions correctly.
mActionCollection->setShortcutsConfigurable(tagAction, false);
mTagActions.insert(tag->id(), tagAction);
if (addToMenu) {
mMessageActions->messageStatusMenu()->menu()->addAction(tagAction);
}
if (tag->inToolbar) {
mToolbarActions.append(tagAction);
}
}
void TagActionManager::createActions()
{
createTagActions(TagMonitorManager::self()->tags());
}
void TagActionManager::onSignalMapped(const QString &tag)
{
Q_EMIT tagActionTriggered(Akonadi::Tag(tag.toLongLong()));
}
void TagActionManager::createTagActions(const QVector<MailCommon::Tag::Ptr> &tags)
{
clearActions();
// Create a action for each tag and plug it into various places
int i = 0;
bool needToAddMoreAction = false;
const int numberOfTag(tags.size());
//It is assumed the tags are sorted
for (const MailCommon::Tag::Ptr &tag : tags) {
if (i < s_numberMaxTag) {
createTagAction(tag, true);
} else {
if (tag->inToolbar || !tag->shortcut.isEmpty()) {
createTagAction(tag, false);
}
if (i == s_numberMaxTag && i < numberOfTag) {
needToAddMoreAction = true;
}
}
++i;
}
if (!mSeparatorNewTagAction) {
mSeparatorNewTagAction = new QAction(this);
mSeparatorNewTagAction->setSeparator(true);
}
mMessageActions->messageStatusMenu()->menu()->addAction(mSeparatorNewTagAction);
if (!mNewTagAction) {
mNewTagAction = new QAction(QIcon::fromTheme(QStringLiteral("tag-new")), i18n("Add new tag..."), this);
connect(mNewTagAction, &QAction::triggered, this, &TagActionManager::newTagActionClicked);
}
mMessageActions->messageStatusMenu()->menu()->addAction(mNewTagAction);
if (needToAddMoreAction) {
if (!mSeparatorMoreAction) {
mSeparatorMoreAction = new QAction(this);
mSeparatorMoreAction->setSeparator(true);
}
mMessageActions->messageStatusMenu()->menu()->addAction(mSeparatorMoreAction);
if (!mMoreAction) {
mMoreAction = new QAction(i18n("More..."), this);
connect(mMoreAction, &QAction::triggered,
this, &TagActionManager::tagMoreActionClicked);
}
mMessageActions->messageStatusMenu()->menu()->addAction(mMoreAction);
}
if (!mToolbarActions.isEmpty() && mGUIClient->factory()) {
mGUIClient->plugActionList(QStringLiteral("toolbar_messagetag_actions"), mToolbarActions);
}
if (mMessageInfo.hasMessageInfo()) {
updateActionStates(mMessageInfo.numberOfSelectedMessages, mMessageInfo.selectedItem);
}
}
void TagActionManager::updateActionStates(int numberOfSelectedMessages, const Akonadi::Item &selectedItem)
{
if (mTagFetchInProgress) {
mMessageInfo.numberOfSelectedMessages = numberOfSelectedMessages;
mMessageInfo.selectedItem = selectedItem;
return;
}
mNewTagId = -1;
QMap<qint64, KToggleAction *>::const_iterator it = mTagActions.constBegin();
QMap<qint64, KToggleAction *>::const_iterator end = mTagActions.constEnd();
if (numberOfSelectedMessages >= 1) {
Q_ASSERT(selectedItem.isValid());
for (; it != end; ++it) {
//FIXME Not very performant tag label retrieval
QString label(i18n("Tag not Found"));
const auto tags = TagMonitorManager::self()->tags();
for (const MailCommon::Tag::Ptr &tag : tags) {
if (tag->id() == it.key()) {
label = tag->name();
break;
}
}
it.value()->setEnabled(true);
if (numberOfSelectedMessages == 1) {
const bool hasTag = selectedItem.hasTag(Akonadi::Tag(it.key()));
it.value()->setChecked(hasTag);
it.value()->setText(i18n("Message Tag: %1", label));
} else {
it.value()->setChecked(false);
it.value()->setText(i18n("Toggle Message Tag: %1", label));
}
}
} else {
for (; it != end; ++it) {
it.value()->setEnabled(false);
}
}
}
void TagActionManager::onTagAdded()
{
fillTagList();
}
void TagActionManager::onTagRemoved()
{
fillTagList();
}
void TagActionManager::onTagChanged()
{
fillTagList();
}
void TagActionManager::fillTagList()
{
const QList<qint64> checked = checkedTags();
clearActions();
createTagActions(TagMonitorManager::self()->tags());
checkTags(checked);
}
void TagActionManager::newTagActionClicked()
{
QPointer<MailCommon::AddTagDialog> dialog = new MailCommon::AddTagDialog(QList<KActionCollection *>() << mActionCollection, nullptr);
dialog->setTags(TagMonitorManager::self()->tags());
if (dialog->exec() == QDialog::Accepted) {
mNewTagId = dialog->tag().id();
// Assign tag to all selected items right away
Q_EMIT tagActionTriggered(dialog->tag());
}
delete dialog;
}
void TagActionManager::checkTags(const QList<qint64> &tags)
{
for (const qint64 id : tags) {
if (mTagActions.contains(id)) {
mTagActions[id]->setChecked(true);
}
}
}
QList<qint64> TagActionManager::checkedTags() const
{
QMap<qint64, KToggleAction *>::const_iterator it = mTagActions.constBegin();
QMap<qint64, KToggleAction *>::const_iterator end = mTagActions.constEnd();
QList<qint64> checked;
for (; it != end; ++it) {
if (it.value()->isChecked()) {
checked << it.key();
}
}
return checked;
}