diff --git a/incidenceeditor-ng/incidencecategories.cpp b/incidenceeditor-ng/incidencecategories.cpp index 29b6b0edb5..fd650e6a8e 100644 --- a/incidenceeditor-ng/incidencecategories.cpp +++ b/incidenceeditor-ng/incidencecategories.cpp @@ -1,110 +1,135 @@ /* Copyright (c) 2010 Bertjan Broeksema Copyright (c) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "incidencecategories.h" #include "editorconfig.h" #include "ui_dialogdesktop.h" #include #include #include #include +#include using namespace IncidenceEditorNG; IncidenceCategories::IncidenceCategories( Ui::EventOrTodoDesktop *ui ) : mUi( ui ) { setObjectName( "IncidenceCategories" ); connect( mUi->mTagWidget, SIGNAL(selectionChanged(Akonadi::Tag::List)), SLOT(onSelectionChanged(Akonadi::Tag::List)) ); } void IncidenceCategories::onSelectionChanged(const Akonadi::Tag::List &list) { mSelectedTags = list; mDirty = true; checkDirtyStatus(); } void IncidenceCategories::load( const KCalCore::Incidence::Ptr &incidence ) { mLoadedIncidence = incidence; mDirty = false; if ( mLoadedIncidence ) { checkForUnknownCategories( mLoadedIncidence->categories() ); } else { mSelectedTags.clear(); } mWasDirty = false; } void IncidenceCategories::load( const Akonadi::Item &item ) { mSelectedTags = item.tags(); mUi->mTagWidget->setSelection(item.tags()); } void IncidenceCategories::save( const KCalCore::Incidence::Ptr &incidence ) { Q_ASSERT( incidence ); incidence->setCategories( categories() ); } void IncidenceCategories::save( Akonadi::Item &item ) { if (item.tags() != mSelectedTags) { item.setTags(mSelectedTags); } } QStringList IncidenceCategories::categories() const { QStringList list; Q_FOREACH (const Akonadi::Tag &tag, mSelectedTags) { list << tag.name(); } return list; } bool IncidenceCategories::isDirty() const { return mDirty; } void IncidenceCategories::printDebugInfo() const { kDebug() << "mSelectedCategories = " << categories(); kDebug() << "mLoadedIncidence->categories() = " << mLoadedIncidence->categories(); } void IncidenceCategories::checkForUnknownCategories( const QStringList &categoriesToCheck ) { - foreach ( const QString &category, categoriesToCheck ) { - Akonadi::TagCreateJob *tagCreateJob = new Akonadi::TagCreateJob(Akonadi::Tag::genericTag(category), this); - tagCreateJob->setMergeIfExisting(true); - //TODO add the missing tags to the item and add them to the list of selected tags in the widget - } + Akonadi::TagFetchJob *fetchJob = new Akonadi::TagFetchJob(); + fetchJob->setProperty("categoriesToCheck", categoriesToCheck); + connect(fetchJob, SIGNAL(tagsReceived(Akonadi::Tag::List)), this, SLOT(onTagsReceived(Akonadi::Tag::List))); +} + +void IncidenceCategories::onTagsReceived(const Akonadi::Tag::List &tags) +{ + QStringList tagsToCheck = sender()->property("categoriesToCheck").toStringList(); + foreach (const Akonadi::Tag &tag, tags) { + tagsToCheck.removeAll(tag.gid()); + tagsToCheck.removeAll(tag.name()); + } + foreach ( const QString &category, tagsToCheck ) { + kDebug() << "Creating tag: " << category; + Akonadi::Tag tag = Akonadi::Tag::genericTag(category); + tag.setGid(category.toUtf8()); + Akonadi::TagCreateJob *tagCreateJob = new Akonadi::TagCreateJob(tag, this); + connect(tagCreateJob, SIGNAL(result(KJob*)), this, SLOT(onTagCreated(KJob*))); + tagCreateJob->setMergeIfExisting(true); + } +} + +void IncidenceCategories::onTagCreated(KJob *job) +{ + const Akonadi::Tag createdTag = static_cast(job)->tag(); + kDebug() << "Created tag: " << createdTag; + mSelectedTags << createdTag; + mUi->mTagWidget->setSelection(mSelectedTags); + onSelectionChanged(mSelectedTags); } diff --git a/incidenceeditor-ng/incidencecategories.h b/incidenceeditor-ng/incidencecategories.h index bb25321551..66e9f01afe 100644 --- a/incidenceeditor-ng/incidencecategories.h +++ b/incidenceeditor-ng/incidencecategories.h @@ -1,73 +1,75 @@ /* Copyright (c) 2010 Bertjan Broeksema Copyright (c) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef INCIDENCEEDITOR_INCIDENCECATEGORIES_H #define INCIDENCEEDITOR_INCIDENCECATEGORIES_H #include "incidenceeditor-ng.h" namespace Ui { class EventOrTodoDesktop; class EventOrTodoMore; } namespace IncidenceEditorNG { class INCIDENCEEDITORS_NG_EXPORT IncidenceCategories : public IncidenceEditor { Q_OBJECT public: explicit IncidenceCategories( Ui::EventOrTodoDesktop *ui ); virtual void load( const KCalCore::Incidence::Ptr &incidence ); virtual void load( const Akonadi::Item &item ); virtual void save( const KCalCore::Incidence::Ptr &incidence ); virtual void save( Akonadi::Item &item ); /** * Returns the list of currently selected categories. */ QStringList categories() const; virtual bool isDirty() const; /**reimp*/ void printDebugInfo() const; private slots: void onSelectionChanged(const Akonadi::Tag::List &); + void onTagsReceived(const Akonadi::Tag::List &); + void onTagCreated(KJob *job); private: /** If the incidence comes from outside of KDE it can contain unknown categories. * KOrganizer usually checks for these, but it can happen that it checks before the * items are in the ETM, due to akonadi's async nature. * So we make the check inside the editor, and add new categories to config. This way * the editor can be used standalone too. * */ void checkForUnknownCategories( const QStringList &categoriesToCheck ); Ui::EventOrTodoDesktop *mUi; Akonadi::Tag::List mSelectedTags; bool mDirty; }; } #endif diff --git a/libkdepim/widgets/tagwidgets.cpp b/libkdepim/widgets/tagwidgets.cpp index 2c516e7ac0..d94d6390e5 100644 --- a/libkdepim/widgets/tagwidgets.cpp +++ b/libkdepim/widgets/tagwidgets.cpp @@ -1,168 +1,175 @@ /* Copyright (c) 2014 Christian Mollekopf This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2, as published by the Free Software Foundation. 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 "tagwidgets.h" #include #include #include #include #include #include #include #include #include #include #include #include using namespace KPIM; TagWidget::TagWidget(QWidget* parent) : QWidget(parent) { mTagWidget = new Akonadi::TagWidget(this); connect(mTagWidget, SIGNAL(selectionChanged(Akonadi::Tag::List)), this, SLOT(onSelectionChanged(Akonadi::Tag::List))); QHBoxLayout *l = new QHBoxLayout; l->addWidget(mTagWidget); setLayout(l); } void TagWidget::onSelectionChanged(const Akonadi::Tag::List &tags) { Q_UNUSED(tags); mCachedTagNames.clear(); Q_FOREACH (const Akonadi::Tag &tag, mTagWidget->selection()) { mCachedTagNames << tag.name(); } emit selectionChanged(mCachedTagNames); emit selectionChanged(tags); } void TagWidget::setSelection(const QStringList &tagNames) { mTagList.clear(); mCachedTagNames = tagNames; - foreach (const QString &name, tagNames) { - //TODO fetch by GID instead, we don't really want to create tags here - Akonadi::TagCreateJob *tagCreateJob = new Akonadi::TagCreateJob(Akonadi::Tag::genericTag(name), this); - tagCreateJob->setMergeIfExisting(true); - connect(tagCreateJob, SIGNAL(result(KJob*)), this, SLOT(onTagCreated(KJob*))); - } + Akonadi::TagFetchJob *fetchJob = new Akonadi::TagFetchJob(); + connect(fetchJob, SIGNAL(tagsReceived(Akonadi::Tag::List)), this, SLOT(onTagsReceived(Akonadi::Tag::List))); + fetchJob->start(); } -void TagWidget::onTagCreated(KJob *job) +void TagWidget::onTagsReceived(const Akonadi::Tag::List &tags) { - if (job->error()) { - kWarning() << "Failed to create tag " << job->errorString(); - return; + foreach (const Akonadi::Tag &tag, tags) { + if (mCachedTagNames.contains(QString::fromUtf8(tag.gid()))) { + mTagList << tag; + } } - Akonadi::TagCreateJob *createJob = static_cast(job); - mTagList << createJob->tag(); mTagWidget->setSelection(mTagList); } QStringList TagWidget::selection() const { return mCachedTagNames; } TagSelectionDialog::TagSelectionDialog(QWidget* parent) : Akonadi::TagSelectionDialog(parent) { } void TagSelectionDialog::setSelection(const QStringList &tagNames) { mTagList.clear(); - foreach (const QString &name, tagNames) { - //TODO fetch by GID instead, we don't really want to create tags here - Akonadi::TagCreateJob *tagCreateJob = new Akonadi::TagCreateJob(Akonadi::Tag::genericTag(name), this); - tagCreateJob->setMergeIfExisting(true); - connect(tagCreateJob, SIGNAL(result(KJob*)), this, SLOT(onTagCreated(KJob*))); + Akonadi::TagFetchJob *fetchJob = new Akonadi::TagFetchJob(); + fetchJob->setProperty("categoriesToCheck", tagNames); + connect(fetchJob, SIGNAL(tagsReceived(Akonadi::Tag::List)), this, SLOT(onTagsReceived(Akonadi::Tag::List))); +} + +void TagSelectionDialog::onTagsReceived(const Akonadi::Tag::List &tags) +{ + QStringList tagsToCheck = sender()->property("categoriesToCheck").toStringList(); + foreach (const Akonadi::Tag &tag, tags) { + tagsToCheck.removeAll(QString::fromUtf8(tag.gid())); + tagsToCheck.removeAll(tag.name()); + } + foreach (const QString &tagName, tagsToCheck) { + kDebug() << "Creating tag: " << tagName; + Akonadi::Tag tag = Akonadi::Tag::genericTag(tagName); + tag.setGid(tagName.toUtf8()); + Akonadi::TagCreateJob *tagCreateJob = new Akonadi::TagCreateJob(tag, this); + connect(tagCreateJob, SIGNAL(result(KJob*)), this, SLOT(onTagCreated(KJob*))); + tagCreateJob->setMergeIfExisting(true); } } void TagSelectionDialog::onTagCreated(KJob *job) { - if (job->error()) { - kWarning() << "Failed to create tag " << job->errorString(); - return; - } - Akonadi::TagCreateJob *createJob = static_cast(job); - mTagList << createJob->tag(); + const Akonadi::Tag createdTag = static_cast(job)->tag(); + kDebug() << "Created tag: " << createdTag; + mTagList << createdTag; Akonadi::TagSelectionDialog::setSelection(mTagList); } QStringList TagSelectionDialog::selection() const { QStringList list; Q_FOREACH (const Akonadi::Tag &tag, Akonadi::TagSelectionDialog::selection()) { list << tag.name(); } return list; } Akonadi::Tag::List TagSelectionDialog::tagSelection() const { return Akonadi::TagSelectionDialog::selection(); } class MatchingCheckableProxyModel : public KCheckableProxyModel { public: MatchingCheckableProxyModel(QObject* parent = 0): KCheckableProxyModel(parent) {} virtual QModelIndexList match(const QModelIndex& start, int role, const QVariant& value, int hits = 1, Qt::MatchFlags flags = Qt::MatchExactly) const { if (role == Qt::CheckStateRole) { return selectionModel()->selectedRows(); } return KCheckableProxyModel::match(start, role, value, hits, flags); } }; TagSelectionCombo::TagSelectionCombo(QWidget* parent) : KPIM::KCheckComboBox(parent) { Akonadi::Monitor *monitor = new Akonadi::Monitor(this); monitor->setTypeMonitored(Akonadi::Monitor::Tags); Akonadi::TagModel *model = new Akonadi::TagModel(monitor, this); QItemSelectionModel *selectionModel = new QItemSelectionModel(model, this); KCheckableProxyModel *checkableProxy = new MatchingCheckableProxyModel( this ); checkableProxy->setSourceModel( model ); checkableProxy->setSelectionModel( selectionModel ); setModel(checkableProxy); //We need to reconnect from the constructor of KCheckComboBox to the new model connect(checkableProxy, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(updateCheckedItems(QModelIndex,QModelIndex)) ); } TagCombo::TagCombo(QWidget* parent) : KComboBox(parent) { Akonadi::Monitor *monitor = new Akonadi::Monitor(this); monitor->setTypeMonitored(Akonadi::Monitor::Tags); Akonadi::TagModel *model = new Akonadi::TagModel(monitor, this); setModel(model); } diff --git a/libkdepim/widgets/tagwidgets.h b/libkdepim/widgets/tagwidgets.h index 9b58682e8e..b485f92646 100644 --- a/libkdepim/widgets/tagwidgets.h +++ b/libkdepim/widgets/tagwidgets.h @@ -1,90 +1,91 @@ /* Copyright (c) 2014 Christian Mollekopf This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2, as published by the Free Software Foundation. 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 */ #ifndef KDEPIM_TAGWIDGETS_H #define KDEPIM_TAGWIDGETS_H #include "kdepim_export.h" #include #include #include #include #include #include "kcheckcombobox.h" class KJob; namespace Akonadi { class TagWidget; } namespace KPIM { class KDEPIM_EXPORT TagWidget: public QWidget { Q_OBJECT public: explicit TagWidget(QWidget* parent = 0); void setSelection(const QStringList &); QStringList selection() const; signals: void selectionChanged(const QStringList &); void selectionChanged(const Akonadi::Tag::List &); private slots: - void onTagCreated(KJob*); void onSelectionChanged(const Akonadi::Tag::List &); + void onTagsReceived(const Akonadi::Tag::List &); private: Akonadi::TagWidget *mTagWidget; Akonadi::Tag::List mTagList; QStringList mCachedTagNames; }; class KDEPIM_EXPORT TagSelectionDialog : public Akonadi::TagSelectionDialog { Q_OBJECT public: explicit TagSelectionDialog(QWidget* parent = 0); void setSelection(const QStringList &); QStringList selection() const; Akonadi::Tag::List tagSelection() const; private slots: + void onTagsReceived(const Akonadi::Tag::List &); void onTagCreated(KJob*); private: Akonadi::Tag::List mTagList; }; class KDEPIM_EXPORT TagSelectionCombo : public KPIM::KCheckComboBox { Q_OBJECT public: explicit TagSelectionCombo(QWidget* parent = 0); }; class KDEPIM_EXPORT TagCombo : public KComboBox { Q_OBJECT public: explicit TagCombo(QWidget* parent = 0); }; } #endif diff --git a/mailcommon/tag/tag.cpp b/mailcommon/tag/tag.cpp index e3a09f4895..30a2c4266d 100644 --- a/mailcommon/tag/tag.cpp +++ b/mailcommon/tag/tag.cpp @@ -1,156 +1,160 @@ /* Copyright 2010 Thomas McGuire Copyright 2012 Laurent Montel 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) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. 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, see . */ #include "tag.h" #include #include #include #include using namespace MailCommon; Tag::Ptr Tag::createDefaultTag(const QString& name) { Tag::Ptr tag( new Tag() ); tag->tagName = name; tag->iconName = QLatin1String("mail-tagged"); tag->priority = -1; tag->inToolbar = false; tag->isImmutable = false; return tag; } Tag::Ptr Tag::fromAkonadi(const Akonadi::Tag& akonadiTag) { Tag::Ptr tag( new Tag() ); tag->tagName = akonadiTag.name(); tag->mTag = akonadiTag; tag->priority = -1; tag->iconName = QLatin1String("mail-tagged"); tag->inToolbar = false; tag->isImmutable = akonadiTag.isImmutable(); Akonadi::TagAttribute *attr = akonadiTag.attribute(); if (attr) { if (!attr->iconName().isEmpty()) { tag->iconName = attr->iconName(); } tag->inToolbar = attr->inToolbar(); tag->shortcut = KShortcut(attr->shortcut()); tag->textColor = attr->textColor(); tag->backgroundColor = attr->backgroundColor(); if (!attr->font().isEmpty()) { tag->textFont.fromString( attr->font() ); } tag->priority = attr->priority(); } return tag; } Akonadi::Tag Tag::saveToAkonadi(Tag::SaveFlags saveFlags) const { Akonadi::Tag tag = mTag; if (tag.gid().isEmpty()) { - tag.setGid(QUuid::createUuid().toByteArray().mid(1, 36)); + if (isImmutable) { + tag.setGid(tagName.toUtf8()); + } else { + tag.setGid(QUuid::createUuid().toByteArray().mid(1, 36)); + } } if (isImmutable) { tag.setType(Akonadi::Tag::PLAIN); } else { tag.setType(Akonadi::Tag::GENERIC); } Akonadi::TagAttribute *attr = tag.attribute(Akonadi::AttributeEntity::AddIfMissing); attr->setDisplayName( tagName ); attr->setIconName( iconName ); attr->setInToolbar( inToolbar ); attr->setShortcut( shortcut.toString() ); attr->setPriority( priority ); if ( textColor.isValid() && (saveFlags & TextColor) ) attr->setTextColor( textColor ); else attr->setTextColor( QColor() ); if ( backgroundColor.isValid() && (saveFlags & BackgroundColor) ) attr->setBackgroundColor( backgroundColor ); else attr->setBackgroundColor( QColor() ); if ( (textFont != QFont()) && (saveFlags & Font) ) attr->setFont( textFont.toString() ); else attr->setFont( QString() ); tag.addAttribute(attr); return tag; } bool Tag::compare( Tag::Ptr &tag1, Tag::Ptr &tag2 ) { if ( tag1->priority < tag2->priority ) return true; else if (tag1->priority == tag2->priority) return ( tag1->tagName < tag2->tagName ); else return false; } bool Tag::compareName( Tag::Ptr &tag1, Tag::Ptr &tag2 ) { return ( tag1->tagName < tag2->tagName ); } bool Tag::operator==( const Tag &other ) const { #if 0 if (mTag.isValid()) { return id() == other.id(); } #endif return tagName == other.tagName && textColor == other.textColor && backgroundColor == other.backgroundColor && textFont == other.textFont && iconName == other.iconName && inToolbar == other.inToolbar && shortcut.toString() == other.shortcut.toString() && priority == other.priority; } bool Tag::operator!=( const Tag &other ) const { return !( *this == other ); } qint64 Tag::id() const { return mTag.id(); } QString Tag::name() const { return mTag.name(); } Akonadi::Tag Tag::tag() const { return mTag; }