Don't use const'ref for QChar
[kdepim.git] / akonadiconsole / tagpropertiesdialog.cpp
blob2485300fae6ac53aa380562ee04d3c573ec492b1
1 /*
2 * Copyright (C) 2014 Daniel Vrátil <dvratil@redhat.com>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 #include "tagpropertiesdialog.h"
21 #include "dbaccess.h"
23 #include <AkonadiCore/AttributeFactory>
24 #include <QSqlQuery>
25 #include <QSqlError>
26 #include <KConfigGroup>
27 #include <QDialogButtonBox>
29 using namespace Akonadi;
31 TagPropertiesDialog::TagPropertiesDialog(QWidget *parent)
32 : QDialog(parent)
33 , mChanged(false)
35 setupUi();
38 TagPropertiesDialog::TagPropertiesDialog(const Akonadi::Tag &tag, QWidget *parent)
39 : QDialog(parent)
40 , mTag(tag)
41 , mChanged(false)
43 setupUi();
46 TagPropertiesDialog::~TagPropertiesDialog()
50 Tag TagPropertiesDialog::tag() const
52 return mTag;
55 bool TagPropertiesDialog::changed() const
57 return mChanged;
60 void TagPropertiesDialog::setupUi()
62 QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
63 QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok);
64 okButton->setDefault(true);
65 okButton->setShortcut(Qt::CTRL | Qt::Key_Return);
66 connect(buttonBox, &QDialogButtonBox::accepted, this, &TagPropertiesDialog::slotAccept);
67 connect(buttonBox, &QDialogButtonBox::rejected, this, &TagPropertiesDialog::reject);
69 QWidget *widget = new QWidget(this);
70 ui.setupUi(widget);
71 QVBoxLayout *mainLayout = new QVBoxLayout;
72 setLayout(mainLayout);
73 mainLayout->addWidget(widget);
74 mainLayout->addWidget(buttonBox);
76 connect(ui.addAttrButton, &QPushButton::clicked, this, &TagPropertiesDialog::addAttributeClicked);
77 connect(ui.deleteAttrButton, &QPushButton::clicked, this, &TagPropertiesDialog::deleteAttributeClicked);
79 connect(ui.addRIDButton, &QPushButton::clicked, this, &TagPropertiesDialog::addRIDClicked);
80 connect(ui.deleteRIDButton, &QPushButton::clicked, this, &TagPropertiesDialog::deleteRIDClicked);
82 Attribute::List attributes = mTag.attributes();
83 mAttributesModel = new QStandardItemModel(attributes.size(), 2, this);
84 connect(mAttributesModel, &QStandardItemModel::itemChanged, this, &TagPropertiesDialog::attributeChanged);
85 QStringList labels;
86 labels << QStringLiteral("Attribute") << QStringLiteral("Value");
87 mAttributesModel->setHorizontalHeaderLabels(labels);
89 mRemoteIdsModel = new QStandardItemModel(this);
90 connect(mRemoteIdsModel, &QStandardItemModel::itemChanged, this, &TagPropertiesDialog::remoteIdChanged);
91 mRemoteIdsModel->setColumnCount(2);
92 labels.clear();
93 labels << QStringLiteral("Resource") << QStringLiteral("Remote ID");
94 mRemoteIdsModel->setHorizontalHeaderLabels(labels);
95 ui.ridsView->setModel(mRemoteIdsModel);
97 if (mTag.isValid()) {
98 ui.idLabel->setText(QString::number(mTag.id()));
99 ui.typeEdit->setText(QLatin1String(mTag.type()));
100 ui.gidEdit->setText(QLatin1String(mTag.gid()));
101 ui.parentIdLabel->setText(QString::number(mTag.parent().id()));
103 for (int i = 0; i < attributes.count(); ++i) {
104 QModelIndex index = mAttributesModel->index(i, 0);
105 Q_ASSERT(index.isValid());
106 mAttributesModel->setData(index, QLatin1String(attributes[i]->type()));
107 mAttributesModel->item(i, 0)->setEditable(false);
108 index = mAttributesModel->index(i, 1);
109 Q_ASSERT(index.isValid());
110 mAttributesModel->setData(index, QLatin1String(attributes[i]->serialized()));
111 mAttributesModel->item(i, 1)->setEditable(true);
115 // There is (intentionally) no way to retrieve Tag RID for another
116 // resource than in the current context. Since Akonadi Console has
117 // not resource context at all, we need to retrieve the IDs the hard way
118 QSqlQuery query(DbAccess::database());
119 query.prepare(QStringLiteral("SELECT ResourceTable.name, TagRemoteIdResourceRelationTable.remoteId "
120 "FROM TagRemoteIdResourceRelationTable "
121 "LEFT JOIN ResourceTable ON ResourceTable.id = TagRemoteIdResourceRelationTable.resourceId "
122 "WHERE TagRemoteIdResourceRelationTable.tagid = ?"));
123 query.addBindValue(mTag.id());
124 if (query.exec()) {
125 while (query.next()) {
126 QList<QStandardItem *> items;
127 QStandardItem *item = new QStandardItem(query.value(0).toString());
128 item->setEditable(false);
129 items << item;
130 item = new QStandardItem(query.value(1).toString());
131 item->setEditable(true);
132 items << item;
133 mRemoteIdsModel->appendRow(items);
135 } else {
136 qCritical() << query.executedQuery();
137 qCritical() << query.lastError().text();
140 } else {
141 ui.idLabel->setVisible(false);
142 ui.idLabelBuddy->setVisible(false);
143 if (mTag.parent().isValid()) {
144 ui.parentIdLabel->setText(QString::number(mTag.parent().id()));
145 } else {
146 ui.parentIdLabel->setVisible(false);
147 ui.parentIdLabelBuddy->setVisible(false);
149 // Since we are using direct SQL to update RIDs, we cannot do this
150 // when creating a new Tag, because the tag is created by caller after
151 // this dialog is closed
152 ui.tabWidget->setTabEnabled(2, false);
155 ui.attrsView->setModel(mAttributesModel);
158 void TagPropertiesDialog::addAttributeClicked()
160 const QString newType = ui.newAttrEdit->text();
161 if (newType.isEmpty()) {
162 return;
164 ui.newAttrEdit->clear();
166 mChangedAttrs.insert(newType);
167 mRemovedAttrs.remove(newType);
168 mChanged = true;
170 const int row = mAttributesModel->rowCount();
171 mAttributesModel->insertRow(row);
172 const QModelIndex index = mAttributesModel->index(row, 0);
173 Q_ASSERT(index.isValid());
174 mAttributesModel->setData(index, newType);
175 mAttributesModel->item(row, 0)->setEditable(false);
176 mAttributesModel->setItem(row, 1, new QStandardItem);
177 mAttributesModel->item(row, 1)->setEditable(true);
180 void TagPropertiesDialog::deleteAttributeClicked()
182 const QModelIndexList selection = ui.attrsView->selectionModel()->selectedRows();
183 if (selection.count() != 1) {
184 return;
186 const QString attr = selection.first().data().toString();
187 mChangedAttrs.remove(attr);
188 mRemovedAttrs.insert(attr);
189 mChanged = true;
190 mAttributesModel->removeRow(selection.first().row());
193 void TagPropertiesDialog::attributeChanged(QStandardItem *item)
195 const QString attr = mAttributesModel->data(mAttributesModel->index(item->row(), 0)).toString();
196 mRemovedAttrs.remove(attr);
197 mChangedAttrs.insert(attr);
198 mChanged = true;
201 void TagPropertiesDialog::addRIDClicked()
203 const QString newResource = ui.newRIDEdit->text();
204 if (newResource.isEmpty()) {
205 return;
207 ui.newRIDEdit->clear();
209 mChangedRIDs.insert(newResource);
210 mRemovedRIDs.remove(newResource);
211 // Don't change mChanged here, we will handle this internally
213 const int row = mRemoteIdsModel->rowCount();
214 mRemoteIdsModel->insertRow(row);
215 const QModelIndex index = mRemoteIdsModel->index(row, 0);
216 Q_ASSERT(index.isValid());
217 mRemoteIdsModel->setData(index, newResource);
218 mRemoteIdsModel->item(row, 0)->setEditable(false);
219 mRemoteIdsModel->setItem(row, 1, new QStandardItem);
220 mRemoteIdsModel->item(row, 1)->setEditable(true);
223 void TagPropertiesDialog::deleteRIDClicked()
225 const QModelIndexList selection = ui.ridsView->selectionModel()->selectedRows();
226 if (selection.count() != 1) {
227 return;
229 const QString res = selection.first().data().toString();
230 mChangedRIDs.remove(res);
231 mRemovedRIDs.insert(res);
232 // Don't change mChanged here, we will handle this internally
233 mRemoteIdsModel->removeRow(selection.first().row());
236 void TagPropertiesDialog::remoteIdChanged(QStandardItem *item)
238 const QString res = mRemoteIdsModel->data(mRemoteIdsModel->index(item->row(), 0)).toString();
239 mRemovedRIDs.remove(res);
240 mChangedRIDs.insert(res);
241 // Don't change mChanged here, we will handle this internally
244 void TagPropertiesDialog::slotAccept()
246 mChanged |= (mTag.type() != ui.typeEdit->text().toLatin1());
247 mChanged |= (mTag.gid() != ui.gidEdit->text().toLatin1());
249 if (!mChanged && mChangedRIDs.isEmpty() && mRemovedRIDs.isEmpty()) {
250 QDialog::accept();
251 return;
254 mTag.setType(ui.typeEdit->text().toLatin1());
255 mTag.setGid(ui.gidEdit->text().toLatin1());
257 Q_FOREACH (const QString &removedAttr, mRemovedAttrs) {
258 mTag.removeAttribute(removedAttr.toLatin1());
260 for (int i = 0; i < mAttributesModel->rowCount(); ++i) {
261 const QModelIndex typeIndex = mAttributesModel->index(i, 0);
262 Q_ASSERT(typeIndex.isValid());
263 if (!mChangedAttrs.contains(typeIndex.data().toString())) {
264 continue;
266 const QModelIndex valueIndex = mAttributesModel->index(i, 1);
267 Attribute *attr = AttributeFactory::createAttribute(mAttributesModel->data(typeIndex).toString().toLatin1());
268 if (!attr) {
269 continue;
271 attr->deserialize(mAttributesModel->data(valueIndex).toString().toLatin1());
272 mTag.addAttribute(attr);
275 bool queryOK = true;
276 if (mTag.isValid() && (!mRemovedRIDs.isEmpty() || !mChangedRIDs.isEmpty())) {
277 DbAccess::database().transaction();
280 if (mTag.isValid() && !mRemovedRIDs.isEmpty()) {
281 QSqlQuery query(DbAccess::database());
282 QString queryStr = QStringLiteral("DELETE FROM TagRemoteIdResourceRelationTable "
283 "WHERE tagId = ? AND "
284 "resourceId IN (SELECT id "
285 "FROM ResourceTable "
286 "WHERE ");
287 QStringList conds;
288 for (int i = 0; i < mRemovedRIDs.count(); ++i) {
289 conds << QStringLiteral("name = ?");
291 queryStr += conds.join(QStringLiteral(" OR ")) + QLatin1String(")");
292 query.prepare(queryStr);
293 query.addBindValue(mTag.id());
294 Q_FOREACH (const QString &removedRid, mRemovedRIDs) {
295 query.addBindValue(removedRid);
297 if (!query.exec()) {
298 qCritical() << query.executedQuery();
299 qCritical() << query.lastError().text();
300 queryOK = false;
303 if (queryOK && mTag.isValid() && !mChangedRIDs.isEmpty()) {
304 QMap<QString, qint64> resourceNameToIdMap;
305 QVector<qint64> existingResourceRecords;
307 QSqlQuery query(DbAccess::database());
308 QString queryStr = QStringLiteral("SELECT id, name FROM ResourceTable WHERE ");
309 QStringList conds;
310 for (int i = 0; i < mChangedRIDs.count(); ++i) {
311 conds << QStringLiteral("name = ?");
313 queryStr += conds.join(QStringLiteral(" OR "));
314 query.prepare(queryStr);
315 Q_FOREACH (const QString &res, mChangedRIDs) {
316 query.addBindValue(res);
318 if (!query.exec()) {
319 qCritical() << query.executedQuery();
320 qCritical() << query.lastError().text();
321 queryOK = false;
324 while (query.next()) {
325 resourceNameToIdMap[query.value(1).toString()] = query.value(0).toLongLong();
329 // This is a workaround for PSQL not supporting UPSERTs
331 QSqlQuery query(DbAccess::database());
332 query.prepare(QStringLiteral("SELECT resourceId FROM TagRemoteIdResourceRelationTable WHERE tagId = ?"));
333 query.addBindValue(mTag.id());
334 if (!query.exec()) {
335 qCritical() << query.executedQuery();
336 qCritical() << query.lastError().text();
337 queryOK = false;
340 while (query.next()) {
341 existingResourceRecords << query.value(0).toLongLong();
345 for (int i = 0; i < mRemoteIdsModel->rowCount() && queryOK; ++i) {
346 const QModelIndex resIndex = mRemoteIdsModel->index(i, 0);
347 Q_ASSERT(resIndex.isValid());
348 if (!mChangedRIDs.contains(resIndex.data().toString())) {
349 continue;
351 const QModelIndex valueIndex = mRemoteIdsModel->index(i, 1);
353 QSqlQuery query(DbAccess::database());
354 const qlonglong resourceId = resourceNameToIdMap[resIndex.data().toString()];
355 if (existingResourceRecords.contains(resourceId)) {
356 query.prepare(QStringLiteral("UPDATE TagRemoteIdResourceRelationTable SET remoteId = ? WHERE tagId = ? AND resourceId = ?"));
357 query.addBindValue(valueIndex.data().toString());
358 query.addBindValue(mTag.id());
359 query.addBindValue(resourceId);
360 } else {
361 query.prepare(QStringLiteral("INSERT INTO TagRemoteIdResourceRelationTable (tagId, resourceId, remoteId) VALUES (?, ?, ?)"));
362 query.addBindValue(mTag.id());
363 query.addBindValue(resourceId);
364 query.addBindValue(valueIndex.data().toString());
366 if (!query.exec()) {
367 qCritical() << query.executedQuery();
368 qCritical() << query.lastError().text();
369 queryOK = false;
370 break;
375 if (mTag.isValid() && (!mRemovedRIDs.isEmpty() || !mChangedRIDs.isEmpty())) {
376 if (queryOK) {
377 DbAccess::database().commit();
378 } else {
379 DbAccess::database().rollback();
383 QDialog::accept();