Don't use const'ref for QChar
[kdepim.git] / akonadiconsole / agentwidget.cpp
blob1fda2b495654a359224f84a7751c84ba5b0215d8
1 /*
2 This file is part of Akonadi.
4 Copyright (c) 2006 Tobias Koenig <tokoe@kde.org>
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
19 USA.
22 #include "agentwidget.h"
23 #include "agentconfigdialog.h"
24 #include "akonadiconsole_debug.h"
25 #include "kpimtextedit/plaintexteditorwidget.h"
26 #include <AkonadiWidgets/agenttypedialog.h>
27 #include <AkonadiWidgets/agentinstancewidget.h>
28 #include <AkonadiCore/agentmanager.h>
29 #include <AkonadiCore/AgentFilterProxyModel>
30 #include <AkonadiCore/agentinstancecreatejob.h>
31 #include <AkonadiWidgets/controlgui.h>
33 #include <KLocalizedString>
34 #include <KMessageBox>
35 #include <KStandardGuiItem>
36 #include <KTextEdit>
37 #include <QLineEdit>
38 #include <QIcon>
40 #include <QFile>
41 #include <QPointer>
42 #include <QMenu>
43 #include <QPushButton>
44 #include <QDBusInterface>
45 #include <QDBusMessage>
46 #include <QDBusReply>
47 #include <QMetaMethod>
48 #include <QResizeEvent>
49 #include <KGuiItem>
50 #include <QDialogButtonBox>
51 #include <KConfigGroup>
52 #include <QVBoxLayout>
54 class TextDialog : public QDialog
56 public:
57 TextDialog(QWidget *parent = Q_NULLPTR)
58 : QDialog(parent)
60 QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok);
61 QVBoxLayout *mainLayout = new QVBoxLayout;
62 setLayout(mainLayout);
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, &TextDialog::accept);
67 connect(buttonBox, &QDialogButtonBox::rejected, this, &TextDialog::reject);
69 mText = new KPIMTextEdit::PlainTextEditorWidget;
70 mainLayout->addWidget(mText);
71 mainLayout->addWidget(buttonBox);
72 mText->setReadOnly(true);
73 resize(QSize(400, 600));
76 void setText(const QString &text)
78 mText->setPlainText(text);
81 private:
82 KPIMTextEdit::PlainTextEditorWidget *mText;
85 using namespace Akonadi;
87 AgentWidget::AgentWidget(QWidget *parent)
88 : QWidget(parent)
90 ui.setupUi(this);
92 connect(ui.instanceWidget, &Akonadi::AgentInstanceWidget::doubleClicked, this, &AgentWidget::configureAgent);
93 connect(ui.instanceWidget, &Akonadi::AgentInstanceWidget::currentChanged, this, &AgentWidget::currentChanged);
94 connect(ui.instanceWidget, &Akonadi::AgentInstanceWidget::customContextMenuRequested, this, &AgentWidget::showContextMenu);
96 connect(ui.instanceWidget->view()->selectionModel(), &QItemSelectionModel::selectionChanged, this, &AgentWidget::selectionChanged);
97 connect(ui.instanceWidget->view()->selectionModel(), &QItemSelectionModel::currentChanged, this, &AgentWidget::selectionChanged);
98 connect(ui.instanceWidget->view()->model(), &QAbstractItemModel::dataChanged, this, &AgentWidget::slotDataChanged);
100 currentChanged();
102 KGuiItem::assign(ui.addButton, KStandardGuiItem::add());
103 connect(ui.addButton, &QPushButton::clicked, this, &AgentWidget::addAgent);
105 KGuiItem::assign(ui.removeButton, KStandardGuiItem::remove());
106 connect(ui.removeButton, &QPushButton::clicked, this, &AgentWidget::removeAgent);
108 mConfigMenu = new QMenu(QStringLiteral("Configure"), this);
109 mConfigMenu->addAction(QStringLiteral("Configure Natively..."), this, SLOT(configureAgent()));
110 mConfigMenu->addAction(QStringLiteral("Configure Remotely..."), this, SLOT(configureAgentRemote()));
111 mConfigMenu->setIcon(KStandardGuiItem::configure().icon());
112 KGuiItem::assign(ui.configButton, KStandardGuiItem::configure());
113 ui.configButton->setMenu(mConfigMenu);
114 connect(ui.configButton, &QPushButton::clicked, this, &AgentWidget::configureAgent);
116 mSyncMenu = new QMenu(QStringLiteral("Synchronize"), this);
117 mSyncMenu->addAction(QStringLiteral("Synchronize All"), this, SLOT(synchronizeAgent()));
118 mSyncMenu->addAction(QStringLiteral("Synchronize Collection Tree"), this, SLOT(synchronizeTree()));
119 mSyncMenu->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
120 ui.syncButton->setMenu(mSyncMenu);
121 ui.syncButton->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
122 connect(ui.syncButton, &QPushButton::clicked, this, &AgentWidget::synchronizeAgent);
124 ui.abortButton->setIcon(QIcon::fromTheme(QStringLiteral("dialog-cancel")));
125 connect(ui.abortButton, &QPushButton::clicked, this, &AgentWidget::abortAgent);
126 ui.restartButton->setIcon(QIcon::fromTheme(QStringLiteral("system-reboot"))); //FIXME: Is using system-reboot icon here a good idea?
127 connect(ui.restartButton, &QPushButton::clicked, this, &AgentWidget::restartAgent);
129 ui.mFilterAccount->setProxy(ui.instanceWidget->agentFilterProxyModel());
130 ui.mFilterAccount->installEventFilter(this);
131 ControlGui::widgetNeedsAkonadi(this);
134 bool AgentWidget::eventFilter(QObject *obj, QEvent *event)
136 if (event->type() == QEvent::KeyPress && obj == ui.mFilterAccount) {
137 QKeyEvent *key = static_cast<QKeyEvent *>(event);
138 if ((key->key() == Qt::Key_Enter) || (key->key() == Qt::Key_Return)) {
139 event->accept();
140 return true;
143 return QWidget::eventFilter(obj, event);
146 void AgentWidget::addAgent()
148 QPointer<Akonadi::AgentTypeDialog> dlg = new Akonadi::AgentTypeDialog(this);
149 if (dlg->exec()) {
150 const AgentType agentType = dlg->agentType();
152 if (agentType.isValid()) {
153 AgentInstanceCreateJob *job = new AgentInstanceCreateJob(agentType, this);
154 job->configure(this);
155 job->start(); // TODO: check result
158 delete dlg;
161 void AgentWidget::selectionChanged()
163 const bool multiSelection = ui.instanceWidget->view()->selectionModel()->selectedRows().size() > 1;
164 // Only agent removal, sync and restart is possible when multiple items are selected.
165 ui.configButton->setDisabled(multiSelection);
167 // Restarting an agent is not possible if it's in Running status... (see AgentProcessInstance::restartWhenIdle)
168 AgentInstance agent = ui.instanceWidget->currentAgentInstance();
169 ui.restartButton->setEnabled(agent.isValid() && agent.status() != 1);
172 void AgentWidget::slotDataChanged(const QModelIndex &topLeft, const QModelIndex & /*bottomRight*/)
174 QList<QModelIndex> selectedRows = ui.instanceWidget->view()->selectionModel()->selectedRows();
175 if (selectedRows.isEmpty()) {
176 selectedRows.append(ui.instanceWidget->view()->selectionModel()->currentIndex());
178 QList<int> rows;
179 Q_FOREACH (const QModelIndex &index, selectedRows) {
180 rows.append(index.row());
182 qSort(rows);
183 // Assume topLeft.row == bottomRight.row
184 if (topLeft.row() >= rows.first() && topLeft.row() <= rows.last()) {
185 selectionChanged(); // depends on status
186 currentChanged();
190 void AgentWidget::removeAgent()
192 AgentInstance::List list = ui.instanceWidget->selectedAgentInstances();
193 if (!list.isEmpty()) {
194 if (KMessageBox::questionYesNo(this,
195 i18np("Do you really want to delete the selected agent instance?",
196 "Do you really want to delete these %1 agent instances?",
197 list.size()),
198 list.size() == 1 ? QStringLiteral("Agent Deletion") : QStringLiteral("Multiple Agent Deletion"),
199 KStandardGuiItem::del(),
200 KStandardGuiItem::cancel(),
201 QString(),
202 KMessageBox::Dangerous)
203 == KMessageBox::Yes) {
204 foreach (const AgentInstance &agent, list) {
205 AgentManager::self()->removeInstance(agent);
211 void AgentWidget::configureAgent()
213 AgentInstance agent = ui.instanceWidget->currentAgentInstance();
214 if (agent.isValid()) {
215 agent.configure(this);
219 void AgentWidget::configureAgentRemote()
221 AgentInstance agent = ui.instanceWidget->currentAgentInstance();
222 if (agent.isValid()) {
223 QPointer<AgentConfigDialog> dlg = new AgentConfigDialog(this);
224 dlg->setAgentInstance(agent);
225 dlg->exec();
226 delete dlg;
230 void AgentWidget::synchronizeAgent()
232 AgentInstance::List list = ui.instanceWidget->selectedAgentInstances();
233 if (!list.isEmpty())
234 foreach (AgentInstance agent, list) {
235 agent.synchronize();
239 void AgentWidget::toggleOnline()
241 AgentInstance agent = ui.instanceWidget->currentAgentInstance();
242 if (agent.isValid()) {
243 agent.setIsOnline(!agent.isOnline());
247 void AgentWidget::showTaskList()
249 AgentInstance agent = ui.instanceWidget->currentAgentInstance();
250 if (!agent.isValid()) {
251 return;
254 QDBusInterface iface(QStringLiteral("org.freedesktop.Akonadi.Agent.%1").arg(agent.identifier()),
255 QStringLiteral("/Debug"), QString());
257 QDBusReply<QString> reply = iface.call(QStringLiteral("dumpToString"));
258 QString txt;
259 if (reply.isValid()) {
260 txt = reply.value();
261 } else {
262 txt = reply.error().message();
265 QPointer<TextDialog> dlg = new TextDialog(this);
266 dlg->setWindowTitle(QStringLiteral("Resource Task List"));
267 dlg->setText(txt);
268 dlg->exec();
269 delete dlg;
272 void AgentWidget::showChangeNotifications()
274 AgentInstance agent = ui.instanceWidget->currentAgentInstance();
275 if (!agent.isValid()) {
276 return;
279 QDBusInterface iface(QStringLiteral("org.freedesktop.Akonadi.Agent.%1").arg(agent.identifier()),
280 QStringLiteral("/Debug"), QString());
282 QDBusReply<QString> reply = iface.call(QStringLiteral("dumpNotificationListToString"));
283 QString txt;
284 if (reply.isValid()) {
285 txt = reply.value();
286 } else {
287 txt = reply.error().message();
290 QPointer<TextDialog> dlg = new TextDialog(this);
291 dlg->setWindowTitle(QStringLiteral("Change Notification Log"));
292 dlg->setText(txt);
294 dlg->exec();
295 delete dlg;
298 void AgentWidget::synchronizeTree()
300 AgentInstance::List list = ui.instanceWidget->selectedAgentInstances();
301 if (!list.isEmpty())
302 foreach (AgentInstance agent, list) {
303 agent.synchronizeCollectionTree();
307 void AgentWidget::abortAgent()
309 AgentInstance::List list = ui.instanceWidget->selectedAgentInstances();
310 if (!list.isEmpty())
311 foreach (const AgentInstance &agent, list) {
312 agent.abortCurrentTask();
316 void AgentWidget::restartAgent()
318 AgentInstance agent = ui.instanceWidget->currentAgentInstance();
319 if (agent.isValid()) {
320 agent.restart();
324 void AgentWidget::cloneAgent()
326 mCloneSource = ui.instanceWidget->currentAgentInstance();
327 if (!mCloneSource.isValid()) {
328 return;
330 const AgentType agentType = mCloneSource.type();
331 if (agentType.isValid()) {
332 AgentInstanceCreateJob *job = new AgentInstanceCreateJob(agentType, this);
333 connect(job, SIGNAL(result(KJob*)), SLOT(cloneAgent(KJob*)));
334 job->start();
335 } else {
336 qCWarning(AKONADICONSOLE_LOG) << "WTF?";
340 void AgentWidget::cloneAgent(KJob *job)
342 if (job->error()) {
343 KMessageBox::error(this, QStringLiteral("Cloning agent failed: %1.").arg(job->errorText()));
344 return;
347 AgentInstance cloneTarget = static_cast<AgentInstanceCreateJob *>(job)->instance();
348 Q_ASSERT(cloneTarget.isValid());
349 Q_ASSERT(mCloneSource.isValid());
351 QDBusInterface sourceIface(QStringLiteral("org.freedesktop.Akonadi.Agent.%1").arg(mCloneSource.identifier()),
352 QStringLiteral("/Settings"));
353 if (!sourceIface.isValid()) {
354 qCritical() << "Unable to obtain KConfigXT D-Bus interface of source agent" << mCloneSource.identifier();
355 return;
358 QDBusInterface targetIface(QStringLiteral("org.freedesktop.Akonadi.Agent.%1").arg(cloneTarget.identifier()),
359 QStringLiteral("/Settings"));
360 if (!targetIface.isValid()) {
361 qCritical() << "Unable to obtain KConfigXT D-Bus interface of target agent" << cloneTarget.identifier();
362 return;
365 cloneTarget.setName(mCloneSource.name() + QStringLiteral(" (Clone)"));
367 // iterate over all getter methods in the source interface and call the
368 // corresponding setter in the target interface
369 for (int i = 0; i < sourceIface.metaObject()->methodCount(); ++i) {
370 const QMetaMethod method = sourceIface.metaObject()->method(i);
371 if (QByteArray(method.typeName()).isEmpty()) { // returns void
372 continue;
374 const QByteArray signature(method.methodSignature());
375 if (signature.isEmpty()) {
376 continue;
378 if (signature.startsWith("set") || !signature.contains("()")) { // setter or takes parameters // krazy:exclude=strings
379 continue;
381 if (signature.startsWith("Introspect")) { // D-Bus stuff // krazy:exclude=strings
382 continue;
384 const QString methodName = QLatin1String(signature.left(signature.indexOf('(')));
385 const QDBusMessage reply = sourceIface.call(methodName);
386 if (reply.arguments().count() != 1) {
387 qCritical() << "call to method" << signature << "failed: " << reply.arguments() << reply.errorMessage();
388 continue;
390 const QString setterName = QStringLiteral("set") + methodName.at(0).toUpper() + methodName.mid(1);
391 targetIface.call(setterName, reply.arguments().at(0));
394 cloneTarget.reconfigure();
397 void AgentWidget::currentChanged()
399 AgentInstance instance = ui.instanceWidget->currentAgentInstance();
400 ui.removeButton->setEnabled(instance.isValid());
401 ui.configButton->setEnabled(instance.isValid());
402 ui.syncButton->setEnabled(instance.isValid());
403 ui.restartButton->setEnabled(instance.isValid());
405 if (instance.isValid()) {
406 ui.identifierLabel->setText(instance.identifier());
407 ui.typeLabel->setText(instance.type().name());
408 QString onlineStatus = (instance.isOnline() ? QStringLiteral("Online") : QStringLiteral("Offline"));
409 QString agentStatus;
410 switch (instance.status()) {
411 case AgentInstance::Idle: agentStatus =
412 i18nc("agent is in an idle state", "Idle");
413 break;
414 case AgentInstance::Running: agentStatus =
415 i18nc("agent is running", "Running (%1%)", instance.progress());
416 break;
417 case AgentInstance::Broken: agentStatus =
418 i18nc("agent is broken somehow", "Broken");
419 break;
420 case AgentInstance::NotConfigured: agentStatus =
421 i18nc("agent is not yet configured", "Not Configured");
422 break;
424 ui.statusLabel->setText(
425 i18nc("Two statuses, for example \"Online, Running (66%)\" or \"Offline, Broken\"",
426 "%1, %2", onlineStatus, agentStatus));
427 ui.statusMessageLabel->setText(instance.statusMessage());
428 ui.capabilitiesLabel->setText(instance.type().capabilities().join(QStringLiteral(", ")));
429 ui.mimeTypeLabel->setText(instance.type().mimeTypes().join(QStringLiteral(", ")));
430 } else {
431 ui.identifierLabel->setText(QString());
432 ui.typeLabel->setText(QString());
433 ui.statusLabel->setText(QString());
434 ui.capabilitiesLabel->setText(QString());
435 ui.mimeTypeLabel->setText(QString());
439 void AgentWidget::showContextMenu(const QPoint &pos)
441 QMenu menu(this);
442 menu.addAction(QIcon::fromTheme(QStringLiteral("list-add")), QStringLiteral("Add Agent..."), this, SLOT(addAgent()));
443 menu.addAction(QIcon::fromTheme(QStringLiteral("edit-copy")), QStringLiteral("Clone Agent"), this, SLOT(cloneAgent()));
444 menu.addSeparator();
445 menu.addMenu(mSyncMenu);
446 menu.addAction(QIcon::fromTheme(QStringLiteral("dialog-cancel")), QStringLiteral("Abort Activity"), this, SLOT(abortAgent()));
447 menu.addAction(QIcon::fromTheme(QStringLiteral("system-reboot")), QStringLiteral("Restart Agent"), this, SLOT(restartAgent())); //FIXME: Is using system-reboot icon here a good idea?
448 menu.addAction(QIcon::fromTheme(QStringLiteral("network-disconnect")), QStringLiteral("Toggle Online/Offline"), this, SLOT(toggleOnline()));
449 menu.addAction(QStringLiteral("Show task list"), this, SLOT(showTaskList()));
450 menu.addAction(QStringLiteral("Show change-notification log"), this, SLOT(showChangeNotifications()));
451 menu.addMenu(mConfigMenu);
452 menu.addAction(QIcon::fromTheme(QStringLiteral("list-remove")), QStringLiteral("Remove Agent"), this, SLOT(removeAgent()));
453 menu.exec(ui.instanceWidget->mapToGlobal(pos));
456 void AgentWidget::resizeEvent(QResizeEvent *event)
458 ui.detailsBox->setVisible(event->size().height() > 400);