1 /*
2 This file is part of Akonadi.
3
4 SPDX-FileCopyrightText: 2009 Till Adam <adam@kde.org>
5
6 SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
9 #include "jobtrackerwidget.h"
10 #include <QCheckBox>
11
12 #include "jobtrackerfilterproxymodel.h"
13 #include "jobtrackermodel.h"
14 #include "jobtrackersearchwidget.h"
15
16 #include <Akonadi/ControlGui>
17
18 #include <QApplication>
19 #include <QClipboard>
20 #include <QFile>
21 #include <QFileDialog>
22 #include <QHeaderView>
23 #include <QMenu>
24 #include <QPushButton>
25 #include <QTreeView>
26 #include <QVBoxLayout>
27
28 class JobTrackerWidgetPrivate
29 {
30 public:
31 JobTrackerModel *model = nullptr;
32 QTreeView *tv = nullptr;
33 JobTrackerFilterProxyModel *filterProxyModel = nullptr;
34 JobTrackerSearchWidget *searchLineEditWidget = nullptr;
35 };
36
JobTrackerWidget(const char * name,QWidget * parent,const QString & checkboxText)37 JobTrackerWidget::JobTrackerWidget(const char *name, QWidget *parent, const QString &checkboxText)
38 : QWidget(parent)
39 , d(new JobTrackerWidgetPrivate)
40 {
41 d->model = new JobTrackerModel(name, this);
42
43 auto layout = new QVBoxLayout(this);
44
45 auto enableCB = new QCheckBox(this);
46 enableCB->setText(checkboxText);
47 connect(enableCB, &QAbstractButton::toggled, d->model, &JobTrackerModel::setEnabled);
48 layout->addWidget(enableCB);
49
50 d->searchLineEditWidget = new JobTrackerSearchWidget(this);
51 layout->addWidget(d->searchLineEditWidget);
52 connect(d->searchLineEditWidget, &JobTrackerSearchWidget::searchTextChanged, this, &JobTrackerWidget::textFilterChanged);
53 connect(d->searchLineEditWidget, &JobTrackerSearchWidget::columnChanged, this, &JobTrackerWidget::searchColumnChanged);
54 connect(d->searchLineEditWidget, &JobTrackerSearchWidget::selectOnlyErrorChanged, this, &JobTrackerWidget::selectOnlyErrorChanged);
55 d->filterProxyModel = new JobTrackerFilterProxyModel(this);
56 d->filterProxyModel->setSourceModel(d->model);
57
58 d->tv = new QTreeView(this);
59 d->tv->setModel(d->filterProxyModel);
60 d->tv->expandAll();
61 d->tv->setAlternatingRowColors(true);
62 d->tv->setContextMenuPolicy(Qt::CustomContextMenu);
63 // too slow with many jobs:
64 // tv->header()->setResizeMode( QHeaderView::ResizeToContents );
65 connect(d->model, &JobTrackerModel::modelReset, d->tv, &QTreeView::expandAll);
66 connect(d->tv, &QTreeView::customContextMenuRequested, this, &JobTrackerWidget::contextMenu);
67 layout->addWidget(d->tv);
68 d->model->setEnabled(false); // since it can be slow, default to off
69
70 auto layout2 = new QHBoxLayout;
71 auto button = new QPushButton(QStringLiteral("Save to file..."), this);
72 connect(button, &QAbstractButton::clicked, this, &JobTrackerWidget::slotSaveToFile);
73 layout2->addWidget(button);
74 layout2->addStretch(1);
75 layout->addLayout(layout2);
76
77 Akonadi::ControlGui::widgetNeedsAkonadi(this);
78 }
79
80 JobTrackerWidget::~JobTrackerWidget() = default;
81
selectOnlyErrorChanged(bool state)82 void JobTrackerWidget::selectOnlyErrorChanged(bool state)
83 {
84 d->filterProxyModel->setShowOnlyFailed(state);
85 d->tv->expandAll();
86 }
87
searchColumnChanged(int index)88 void JobTrackerWidget::searchColumnChanged(int index)
89 {
90 d->filterProxyModel->setSearchColumn(index);
91 d->tv->expandAll();
92 }
93
textFilterChanged(const QString & str)94 void JobTrackerWidget::textFilterChanged(const QString &str)
95 {
96 d->filterProxyModel->setFilterFixedString(str);
97 d->tv->expandAll();
98 }
99
contextMenu(const QPoint &)100 void JobTrackerWidget::contextMenu(const QPoint & /*pos*/)
101 {
102 QMenu menu;
103 menu.addAction(QStringLiteral("Clear View"), d->model, &JobTrackerModel::resetTracker);
104 menu.addSeparator();
105 menu.addAction(QStringLiteral("Copy Info"), this, &JobTrackerWidget::copyJobInfo);
106 menu.addSeparator();
107 menu.addAction(QStringLiteral("Collapse All"), this, &JobTrackerWidget::collapseAll);
108 menu.addAction(QStringLiteral("Expand All"), this, &JobTrackerWidget::expandAll);
109
110 menu.exec(QCursor::pos());
111 }
112
expandAll()113 void JobTrackerWidget::expandAll()
114 {
115 d->tv->expandAll();
116 }
117
collapseAll()118 void JobTrackerWidget::collapseAll()
119 {
120 d->tv->collapseAll();
121 }
122
copyJobInfo()123 void JobTrackerWidget::copyJobInfo()
124 {
125 QModelIndex index = d->tv->currentIndex();
126 if (index.isValid()) {
127 const QModelIndex newIndex = d->model->index(index.row(), 6, index.parent());
128 QClipboard *cb = QApplication::clipboard();
129 cb->setText(newIndex.data().toString(), QClipboard::Clipboard);
130 }
131 }
132
slotSaveToFile()133 void JobTrackerWidget::slotSaveToFile()
134 {
135 const QString fileName = QFileDialog::getSaveFileName(this, QString(), QString(), QString());
136 if (fileName.isEmpty()) {
137 return;
138 }
139
140 QFile file(fileName);
141 if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
142 return;
143 }
144
145 file.write("Job ID\t\tCreated\t\tWait Time\tJob Duration\tJob Type\t\tState\tInfo\n");
146
147 writeRows(QModelIndex(), file, 0);
148
149 file.close();
150 }
151
writeRows(const QModelIndex & parent,QFile & file,int indentLevel)152 void JobTrackerWidget::writeRows(const QModelIndex &parent, QFile &file, int indentLevel)
153 {
154 for (int row = 0; row < d->model->rowCount(parent); ++row) {
155 QByteArray data;
156 for (int tabs = 0; tabs < indentLevel; ++tabs) {
157 data += '\t';
158 }
159 const int columnCount = d->model->columnCount(parent);
160 for (int column = 0; column < columnCount; ++column) {
161 const QModelIndex index = d->model->index(row, column, parent);
162 data += index.data().toByteArray();
163 if (column < columnCount - 1) {
164 data += '\t';
165 }
166 }
167 data += '\n';
168 file.write(data);
169
170 const QModelIndex index = d->model->index(row, 0, parent);
171 writeRows(index, file, indentLevel + 1);
172 }
173 }
174