diff --git a/src/browserwidget.cpp b/src/browserwidget.cpp index 44abbd3..c6fafab 100644 --- a/src/browserwidget.cpp +++ b/src/browserwidget.cpp @@ -1,689 +1,689 @@ /* Copyright (c) 2007 Volker Krause 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 "browserwidget.h" #include "collectionattributespage.h" #include "collectioninternalspage.h" #include "collectionaclpage.h" #include "dbaccess.h" #include "akonadibrowsermodel.h" #include "tagpropertiesdialog.h" #include "config-akonadiconsole.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "akonadiconsole_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef ENABLE_CONTENTVIEWS #include -#include +#include #include #include #endif using namespace Akonadi; AKONADI_COLLECTION_PROPERTIES_PAGE_FACTORY(CollectionAttributePageFactory, CollectionAttributePage) AKONADI_COLLECTION_PROPERTIES_PAGE_FACTORY(CollectionInternalsPageFactory, CollectionInternalsPage) AKONADI_COLLECTION_PROPERTIES_PAGE_FACTORY(CollectionAclPageFactory, CollectionAclPage) Q_DECLARE_METATYPE(QSet) BrowserWidget::BrowserWidget(KXmlGuiWindow *xmlGuiWindow, QWidget *parent) : QWidget(parent), mAttrModel(nullptr), mStdActionManager(nullptr), mMonitor(nullptr), mCacheOnlyAction(nullptr) { Q_ASSERT(xmlGuiWindow); QVBoxLayout *layout = new QVBoxLayout(this); QSplitter *splitter = new QSplitter(Qt::Horizontal, this); splitter->setObjectName(QStringLiteral("collectionSplitter")); layout->addWidget(splitter); QSplitter *splitter2 = new QSplitter(Qt::Vertical, this); splitter2->setObjectName(QStringLiteral("ffvSplitter")); mCollectionView = new Akonadi::EntityTreeView(xmlGuiWindow, this); mCollectionView->setObjectName(QStringLiteral("CollectionView")); mCollectionView->setSelectionMode(QAbstractItemView::ExtendedSelection); splitter2->addWidget(mCollectionView); EntityListView *favoritesView = new EntityListView(xmlGuiWindow, this); //favoritesView->setViewMode( QListView::IconMode ); splitter2->addWidget(favoritesView); splitter->addWidget(splitter2); ChangeRecorder *tagRecorder = new ChangeRecorder(this); tagRecorder->setObjectName(QStringLiteral("tagRecorder")); tagRecorder->setTypeMonitored(Monitor::Tags); tagRecorder->setChangeRecordingEnabled(false); mTagView = new QTreeView(this); mTagModel = new Akonadi::TagModel(tagRecorder, this); mTagView->setModel(mTagModel); splitter2->addWidget(mTagView); mTagView->setContextMenuPolicy(Qt::CustomContextMenu); connect(mTagView, &QTreeView::customContextMenuRequested, this, &BrowserWidget::tagViewContextMenuRequested); connect(mTagView, &QTreeView::doubleClicked, this, &BrowserWidget::tagViewDoubleClicked); Session *session = new Session(("AkonadiConsole Browser Widget"), this); // monitor collection changes mBrowserMonitor = new ChangeRecorder(this); mBrowserMonitor->setObjectName(QStringLiteral("collectionMonitor")); mBrowserMonitor->setSession(session); mBrowserMonitor->setCollectionMonitored(Collection::root()); mBrowserMonitor->fetchCollection(true); mBrowserMonitor->setAllMonitored(true); // TODO: Only fetch the envelope etc if possible. mBrowserMonitor->itemFetchScope().fetchFullPayload(true); mBrowserMonitor->itemFetchScope().setCacheOnly(true); mBrowserMonitor->itemFetchScope().setFetchGid(true); mBrowserModel = new AkonadiBrowserModel(mBrowserMonitor, this); mBrowserModel->setItemPopulationStrategy(EntityTreeModel::LazyPopulation); mBrowserModel->setShowSystemEntities(true); mBrowserModel->setListFilter(CollectionFetchScope::Display); // new ModelTest( mBrowserModel ); EntityMimeTypeFilterModel *collectionFilter = new EntityMimeTypeFilterModel(this); collectionFilter->setSourceModel(mBrowserModel); collectionFilter->addMimeTypeInclusionFilter(Collection::mimeType()); collectionFilter->setHeaderGroup(EntityTreeModel::CollectionTreeHeaders); collectionFilter->setDynamicSortFilter(true); collectionFilter->setSortCaseSensitivity(Qt::CaseInsensitive); statisticsProxyModel = new Akonadi::StatisticsProxyModel(this); statisticsProxyModel->setToolTipEnabled(true); statisticsProxyModel->setSourceModel(collectionFilter); mCollectionView->setModel(statisticsProxyModel); Akonadi::SelectionProxyModel *selectionProxyModel = new Akonadi::SelectionProxyModel(mCollectionView->selectionModel(), this); selectionProxyModel->setSourceModel(mBrowserModel); selectionProxyModel->setFilterBehavior(KSelectionProxyModel::ChildrenOfExactSelection); EntityMimeTypeFilterModel *itemFilter = new EntityMimeTypeFilterModel(this); itemFilter->setSourceModel(selectionProxyModel); itemFilter->addMimeTypeExclusionFilter(Collection::mimeType()); itemFilter->setHeaderGroup(EntityTreeModel::ItemListHeaders); const KConfigGroup group = KSharedConfig::openConfig()->group("FavoriteCollectionsModel"); connect(mBrowserModel, &AkonadiBrowserModel::columnsChanged, itemFilter, &EntityMimeTypeFilterModel::invalidate); AkonadiBrowserSortModel *sortModel = new AkonadiBrowserSortModel(mBrowserModel, this); sortModel->setDynamicSortFilter(true); sortModel->setSourceModel(itemFilter); FavoriteCollectionsModel *favoritesModel = new FavoriteCollectionsModel(mBrowserModel, group, this); favoritesView->setModel(favoritesModel); QSplitter *splitter3 = new QSplitter(Qt::Vertical, this); splitter3->setObjectName(QStringLiteral("itemSplitter")); splitter->addWidget(splitter3); QWidget *itemViewParent = new QWidget(this); itemUi.setupUi(itemViewParent); itemUi.modelBox->addItem(QStringLiteral("Generic")); itemUi.modelBox->addItem(QStringLiteral("Mail")); itemUi.modelBox->addItem(QStringLiteral("Contacts")); itemUi.modelBox->addItem(QStringLiteral("Calendar/Tasks")); connect(itemUi.modelBox, static_cast(&KComboBox::activated), this, &BrowserWidget::modelChanged); QTimer::singleShot(0, this, &BrowserWidget::modelChanged); itemUi.itemView->setXmlGuiClient(xmlGuiWindow); itemUi.itemView->setModel(sortModel); itemUi.itemView->setSelectionMode(QAbstractItemView::ExtendedSelection); connect(itemUi.itemView->selectionModel(), &QItemSelectionModel::currentChanged, this, &BrowserWidget::currentChanged); splitter3->addWidget(itemViewParent); itemViewParent->layout()->setContentsMargins(0, 0, 0, 0); QWidget *contentViewParent = new QWidget(this); contentUi.setupUi(contentViewParent); contentUi.saveButton->setEnabled(false); connect(contentUi.saveButton, &QPushButton::clicked, this, &BrowserWidget::save); splitter3->addWidget(contentViewParent); #ifdef ENABLE_CONTENTVIEWS auto w = new QWidget; w->setLayout(new QVBoxLayout); w->layout()->addWidget(mContactView = new Akonadi::ContactViewer); contentUi.stack->addWidget(w); w = new QWidget; w->setLayout(new QVBoxLayout); w->layout()->addWidget(mContactGroupView = new Akonadi::ContactGroupViewer); contentUi.stack->addWidget(w); w = new QWidget; w->setLayout(new QVBoxLayout); w->layout()->addWidget(mIncidenceView = new CalendarSupport::IncidenceViewer); contentUi.stack->addWidget(w); w = new QWidget; w->setLayout(new QVBoxLayout); w->layout()->addWidget(mMailView = new MessageViewer::Viewer(this)); contentUi.stack->addWidget(w); #endif connect(contentUi.attrAddButton, &QPushButton::clicked, this, &BrowserWidget::addAttribute); connect(contentUi.attrDeleteButton, &QPushButton::clicked, this, &BrowserWidget::delAttribute); connect(contentUi.flags, &KEditListWidget::changed, this, &BrowserWidget::contentViewChanged); connect(contentUi.tags, &KEditListWidget::changed, this, &BrowserWidget::contentViewChanged); connect(contentUi.remoteId, &QLineEdit::textChanged, this, &BrowserWidget::contentViewChanged); connect(contentUi.gid, &QLineEdit::textChanged, this, &BrowserWidget::contentViewChanged); CollectionPropertiesDialog::registerPage(new CollectionAclPageFactory()); CollectionPropertiesDialog::registerPage(new CollectionAttributePageFactory()); CollectionPropertiesDialog::registerPage(new CollectionInternalsPageFactory()); ControlGui::widgetNeedsAkonadi(this); mStdActionManager = new StandardActionManager(xmlGuiWindow->actionCollection(), xmlGuiWindow); mStdActionManager->setCollectionSelectionModel(mCollectionView->selectionModel()); mStdActionManager->setItemSelectionModel(itemUi.itemView->selectionModel()); mStdActionManager->setFavoriteCollectionsModel(favoritesModel); mStdActionManager->setFavoriteSelectionModel(favoritesView->selectionModel()); mStdActionManager->createAllActions(); mCacheOnlyAction = new KToggleAction(QStringLiteral("Cache only retrieval"), xmlGuiWindow); mCacheOnlyAction->setChecked(true); xmlGuiWindow->actionCollection()->addAction(QStringLiteral("akonadiconsole_cacheonly"), mCacheOnlyAction); connect(mCacheOnlyAction, &KToggleAction::toggled, this, &BrowserWidget::updateItemFetchScope); m_stateMaintainer = new KViewStateMaintainer(KSharedConfig::openConfig()->group("CollectionViewState"), this); m_stateMaintainer->setView(mCollectionView); m_stateMaintainer->restoreState(); } BrowserWidget::~BrowserWidget() { m_stateMaintainer->saveState(); } void BrowserWidget::clear() { contentUi.stack->setCurrentWidget(contentUi.unsupportedTypePage); contentUi.dataView->clear(); contentUi.id->clear(); contentUi.remoteId->clear(); contentUi.gid->clear(); contentUi.mimeType->clear(); contentUi.revision->clear(); contentUi.size->clear(); contentUi.modificationtime->clear(); contentUi.flags->clear(); contentUi.tags->clear(); contentUi.attrView->setModel(nullptr); } void BrowserWidget::currentChanged(const QModelIndex &index) { const Item item = index.sibling(index.row(), 0).data(EntityTreeModel::ItemRole).value< Item >(); if (!item.isValid()) { clear(); return; } ItemFetchJob *job = new ItemFetchJob(item, this); job->fetchScope().fetchFullPayload(); job->fetchScope().fetchAllAttributes(); job->fetchScope().setFetchTags(true); auto &tfs = job->fetchScope().tagFetchScope(); tfs.setFetchIdOnly(false); tfs.fetchAllAttributes(); connect(job, &ItemFetchJob::result, this, &BrowserWidget::itemFetchDone); } void BrowserWidget::itemFetchDone(KJob *job) { ItemFetchJob *fetch = static_cast(job); if (job->error()) { qCWarning(AKONADICONSOLE_LOG) << "Item fetch failed: " << job->errorString(); } else if (fetch->items().isEmpty()) { qCWarning(AKONADICONSOLE_LOG) << "No item found!"; } else { const Item item = fetch->items().first(); setItem(item); } } void BrowserWidget::contentViewChanged() { contentUi.saveButton->setEnabled(true); } void BrowserWidget::setItem(const Akonadi::Item &item) { mCurrentItem = item; #ifdef ENABLE_CONTENTVIEWS if (item.hasPayload()) { mContactView->setItem(item); contentUi.stack->setCurrentWidget(mContactView->parentWidget()); } else if (item.hasPayload()) { mContactGroupView->setItem(item); contentUi.stack->setCurrentWidget(mContactGroupView->parentWidget()); } else if (item.hasPayload()) { mIncidenceView->setItem(item); contentUi.stack->setCurrentWidget(mIncidenceView->parentWidget()); } else if (item.mimeType() == QLatin1String("message/rfc822") || item.mimeType() == QLatin1String("message/news")) { mMailView->setMessageItem(item, MimeTreeParser::Force); contentUi.stack->setCurrentWidget(mMailView->parentWidget()); } else #endif if (item.hasPayload()) { contentUi.imageView->setPixmap(item.payload()); contentUi.stack->setCurrentWidget(contentUi.imageViewPage); } else { contentUi.stack->setCurrentWidget(contentUi.unsupportedTypePage); } contentUi.saveButton->setEnabled(false); QByteArray data = item.payloadData(); // Note that this is true for *all* items as soon as the binary format is enabled. // Independently from how they are actually stored in the database. if (item.hasPayload()) { quint32 magic; QDataStream input(data); input >> magic; KCalendarCore::ICalFormat format; if (magic == KCalendarCore::IncidenceBase::magicSerializationIdentifier()) { // Binary format isn't readable, show KCalendarCore string instead. auto incidence = item.payload(); data = "(converted from binary format)\n" + format.toRawString(incidence); } } contentUi.dataView->setPlainText(QString::fromLatin1(data)); contentUi.id->setText(QString::number(item.id())); contentUi.remoteId->setText(item.remoteId()); contentUi.gid->setText(item.gid()); contentUi.mimeType->setText(item.mimeType()); contentUi.revision->setText(QString::number(item.revision())); contentUi.size->setText(QString::number(item.size())); contentUi.modificationtime->setText(item.modificationTime().toString() + QStringLiteral(" UTC")); QStringList flags; foreach (const Item::Flag &f, item.flags()) { flags << QString::fromUtf8(f); } contentUi.flags->setItems(flags); QStringList tags; foreach (const Tag &tag, item.tags()) { tags << QLatin1String(tag.gid()); } contentUi.tags->setItems(tags); Attribute::List list = item.attributes(); delete mAttrModel; mAttrModel = new QStandardItemModel(list.count(), 2); QStringList labels; labels << QStringLiteral("Attribute") << QStringLiteral("Value"); mAttrModel->setHorizontalHeaderLabels(labels); for (int i = 0; i < list.count(); ++i) { QModelIndex index = mAttrModel->index(i, 0); Q_ASSERT(index.isValid()); mAttrModel->setData(index, QString::fromLatin1(list[i]->type())); index = mAttrModel->index(i, 1); Q_ASSERT(index.isValid()); mAttrModel->setData(index, QString::fromLatin1(list[i]->serialized())); mAttrModel->itemFromIndex(index)->setFlags(Qt::ItemIsEditable | mAttrModel->flags(index)); } contentUi.attrView->setModel(mAttrModel); if (mMonitor) { mMonitor->deleteLater(); // might be the one calling us } mMonitor = new Monitor(this); mMonitor->setObjectName(QStringLiteral("itemMonitor")); mMonitor->setItemMonitored(item); mMonitor->itemFetchScope().fetchFullPayload(); mMonitor->itemFetchScope().fetchAllAttributes(); qRegisterMetaType >(); connect(mMonitor, &Akonadi::Monitor::itemChanged, this, &BrowserWidget::setItem, Qt::QueuedConnection); contentUi.saveButton->setEnabled(false); } void BrowserWidget::modelChanged() { switch (itemUi.modelBox->currentIndex()) { case 1: mBrowserModel->setItemDisplayMode(AkonadiBrowserModel::MailMode); break; case 2: mBrowserModel->setItemDisplayMode(AkonadiBrowserModel::ContactsMode); break; case 3: mBrowserModel->setItemDisplayMode(AkonadiBrowserModel::CalendarMode); break; default: mBrowserModel->setItemDisplayMode(AkonadiBrowserModel::GenericMode); } } void BrowserWidget::save() { Q_ASSERT(mAttrModel); const QByteArray data = contentUi.dataView->toPlainText().toUtf8(); Item item = mCurrentItem; item.setRemoteId(contentUi.remoteId->text()); item.setGid(contentUi.gid->text()); foreach (const Item::Flag &f, mCurrentItem.flags()) { item.clearFlag(f); } foreach (const QString &s, contentUi.flags->items()) { item.setFlag(s.toUtf8()); } foreach (const Tag &tag, mCurrentItem.tags()) { item.clearTag(tag); } foreach (const QString &s, contentUi.tags->items()) { Tag tag; tag.setGid(s.toLatin1()); item.setTag(tag); } item.setPayloadFromData(data); item.clearAttributes(); for (int i = 0; i < mAttrModel->rowCount(); ++i) { const QModelIndex typeIndex = mAttrModel->index(i, 0); Q_ASSERT(typeIndex.isValid()); const QModelIndex valueIndex = mAttrModel->index(i, 1); Q_ASSERT(valueIndex.isValid()); Attribute *attr = AttributeFactory::createAttribute(mAttrModel->data(typeIndex).toString().toLatin1()); Q_ASSERT(attr); attr->deserialize(mAttrModel->data(valueIndex).toString().toLatin1()); item.addAttribute(attr); } ItemModifyJob *store = new ItemModifyJob(item, this); connect(store, &ItemModifyJob::result, this, &BrowserWidget::saveResult); } void BrowserWidget::saveResult(KJob *job) { if (job->error()) { KMessageBox::error(this, QStringLiteral("Failed to save changes: %1").arg(job->errorString())); } else { contentUi.saveButton->setEnabled(false); } } void BrowserWidget::addAttribute() { if (!mAttrModel || contentUi.attrName->text().isEmpty()) { return; } const int row = mAttrModel->rowCount(); mAttrModel->insertRow(row); QModelIndex index = mAttrModel->index(row, 0); Q_ASSERT(index.isValid()); mAttrModel->setData(index, contentUi.attrName->text()); contentUi.attrName->clear(); contentUi.saveButton->setEnabled(true); } void BrowserWidget::delAttribute() { if (!mAttrModel) { return; } QModelIndexList selection = contentUi.attrView->selectionModel()->selectedRows(); if (selection.count() != 1) { return; } mAttrModel->removeRow(selection.first().row()); contentUi.saveButton->setEnabled(true); } void BrowserWidget::dumpToXml() { const Collection root = currentCollection(); if (!root.isValid()) { return; } const QString fileName = QFileDialog::getSaveFileName(this, QStringLiteral("Select XML file"), QString(), QStringLiteral("*.xml")); if (fileName.isEmpty()) { return; } XmlWriteJob *job = new XmlWriteJob(root, fileName, this); connect(job, &XmlWriteJob::result, this, &BrowserWidget::dumpToXmlResult); } void BrowserWidget::dumpToXmlResult(KJob *job) { if (job->error()) { KMessageBox::error(this, job->errorString()); } } void BrowserWidget::clearCache() { const Collection coll = currentCollection(); if (!coll.isValid()) { return; } const auto ridCount = QStringLiteral("SELECT COUNT(*) FROM PimItemTable WHERE collectionId=%1 AND remoteId=''").arg(coll.id()); QSqlQuery query(DbAccess::database()); if (!query.exec(ridCount)) { qCWarning(AKONADICONSOLE_LOG) << "Failed to execute query" << ridCount << ":" << query.lastError().text(); KMessageBox::error(this, query.lastError().text()); return; } query.next(); const int emptyRidCount = query.value(0).toInt(); if (emptyRidCount > 0) { if (KMessageBox::warningContinueCancel(this, QStringLiteral( "The collection '%1' contains %2 items without Remote ID. " "Those items were likely never uploaded to the destination server, " "so clearing this collection means that those data will be lost. " "Are you sure you want to proceed?").arg(coll.id()).arg(emptyRidCount), QStringLiteral("POSSIBLE DATA LOSS!")) == KMessageBox::Cancel) { return; } } QString str = QStringLiteral("DELETE FROM PimItemTable WHERE collectionId=%1").arg(coll.id()); qCDebug(AKONADICONSOLE_LOG) << str; query = QSqlQuery(str, DbAccess::database()); if (query.exec()) { if (query.lastError().isValid()) { qCDebug(AKONADICONSOLE_LOG) << query.lastError(); KMessageBox::error(this, query.lastError().text()); } } // TODO: Clear external parts // TODO: Reset Akonadi's internall collection statistics cache // TODO: Notify all clients EXCEPT FOR THE RESOURCE about the deletion? // TODO: Clear search index // TODO: ??? KMessageBox::information(this, QStringLiteral("Collection cache cleared. You should restart Akonadi now.")); } Akonadi::Collection BrowserWidget::currentCollection() const { return mCollectionView->currentIndex().data(EntityTreeModel::CollectionRole).value(); } void BrowserWidget::updateItemFetchScope() { mBrowserMonitor->itemFetchScope().setCacheOnly(mCacheOnlyAction->isChecked()); } void BrowserWidget::tagViewContextMenuRequested(const QPoint &pos) { const QModelIndex index = mTagView->indexAt(pos); QMenu *menu = new QMenu(this); connect(menu, &QMenu::aboutToHide, menu, &QMenu::deleteLater); menu->addAction(QIcon::fromTheme(QStringLiteral("list-add")), QStringLiteral("&Add tag..."), this, &BrowserWidget::addTagRequested); if (index.isValid()) { menu->addAction(QStringLiteral("Add &subtag..."), this, &BrowserWidget::addSubTagRequested); menu->addAction(QIcon::fromTheme(QStringLiteral("document-edit")), QStringLiteral("&Edit tag..."), this, &BrowserWidget::editTagRequested, QKeySequence(Qt::Key_Return)); menu->addAction(QIcon::fromTheme(QStringLiteral("edit-delete")), QStringLiteral("&Delete tag..."), this, &BrowserWidget::removeTagRequested, QKeySequence::Delete); menu->setProperty("Tag", index.data(TagModel::TagRole)); } menu->popup(mTagView->mapToGlobal(pos)); } void BrowserWidget::addTagRequested() { TagPropertiesDialog *dlg = new TagPropertiesDialog(this); connect(dlg, &TagPropertiesDialog::accepted, this, &BrowserWidget::createTag); connect(dlg, &TagPropertiesDialog::rejected, dlg, &TagPropertiesDialog::deleteLater); dlg->show(); } void BrowserWidget::addSubTagRequested() { QAction *action = qobject_cast(sender()); const Akonadi::Tag parentTag = action->parent()->property("Tag").value(); Akonadi::Tag tag; tag.setParent(parentTag); TagPropertiesDialog *dlg = new TagPropertiesDialog(tag, this); connect(dlg, &TagPropertiesDialog::accepted, this, &BrowserWidget::createTag); connect(dlg, &TagPropertiesDialog::rejected, dlg, &TagPropertiesDialog::deleteLater); dlg->show(); } void BrowserWidget::editTagRequested() { QAction *action = qobject_cast(sender()); const Akonadi::Tag tag = action->parent()->property("Tag").value(); TagPropertiesDialog *dlg = new TagPropertiesDialog(tag, this); connect(dlg, &TagPropertiesDialog::accepted, this, &BrowserWidget::modifyTag); connect(dlg, &TagPropertiesDialog::rejected, dlg, &TagPropertiesDialog::deleteLater); dlg->show(); } void BrowserWidget::tagViewDoubleClicked(const QModelIndex &index) { if (!index.isValid()) { addTagRequested(); return; } const Akonadi::Tag tag = mTagModel->data(index, TagModel::TagRole).value(); Q_ASSERT(tag.isValid()); TagPropertiesDialog *dlg = new TagPropertiesDialog(tag, this); connect(dlg, &TagPropertiesDialog::accepted, this, &BrowserWidget::modifyTag); connect(dlg, &TagPropertiesDialog::rejected, dlg, &TagPropertiesDialog::deleteLater); dlg->show(); } void BrowserWidget::removeTagRequested() { if (KMessageBox::questionYesNo(this, QStringLiteral("Do you really want to remove selected tag?"), QStringLiteral("Delete tag?"), KStandardGuiItem::del(), KStandardGuiItem::cancel(), QString(), KMessageBox::Dangerous) == KMessageBox::No) { return; } QAction *action = qobject_cast(sender()); const Akonadi::Tag tag = action->parent()->property("Tag").value(); new Akonadi::TagDeleteJob(tag, this); } void BrowserWidget::createTag() { TagPropertiesDialog *dlg = qobject_cast(sender()); Q_ASSERT(dlg); if (dlg->changed()) { new TagCreateJob(dlg->tag(), this); } } void BrowserWidget::modifyTag() { TagPropertiesDialog *dlg = qobject_cast(sender()); Q_ASSERT(dlg); if (dlg->changed()) { new TagModifyJob(dlg->tag(), this); } }