1 /*
2  * Copyright (c) 2012-2020 Meltytech, LLC
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program 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
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "jobsdock.h"
19 #include "ui_jobsdock.h"
20 #include "jobqueue.h"
21 #include <QtWidgets>
22 #include <Logger.h>
23 #include "dialogs/textviewerdialog.h"
24 
JobsDock(QWidget * parent)25 JobsDock::JobsDock(QWidget *parent) :
26     QDockWidget(parent),
27     ui(new Ui::JobsDock)
28 {
29     LOG_DEBUG() << "begin";
30     ui->setupUi(this);
31     toggleViewAction()->setIcon(windowIcon());
32     ui->treeView->setModel(&JOBS);
33     QHeaderView* header = ui->treeView->header();
34     header->setStretchLastSection(false);
35     header->setSectionResizeMode(JobQueue::COLUMN_ICON, QHeaderView::Fixed);
36     ui->treeView->setColumnWidth(JobQueue::COLUMN_ICON, 24);
37     header->setSectionResizeMode(JobQueue::COLUMN_OUTPUT, QHeaderView::Stretch);
38     header->setSectionResizeMode(JobQueue::COLUMN_STATUS, QHeaderView::ResizeToContents);
39     ui->cleanButton->hide();
40     LOG_DEBUG() << "end";
41 }
42 
~JobsDock()43 JobsDock::~JobsDock()
44 {
45     JOBS.cleanup();
46     delete ui;
47 }
48 
currentJob() const49 AbstractJob *JobsDock::currentJob() const
50 {
51     QModelIndex index = ui->treeView->currentIndex();
52     if (!index.isValid()) return 0;
53     return JOBS.jobFromIndex(index);
54 }
55 
onJobAdded()56 void JobsDock::onJobAdded()
57 {
58     QModelIndex index = JOBS.index(JOBS.rowCount() - 1, JobQueue::COLUMN_OUTPUT);
59     QProgressBar* progressBar = new QProgressBar;
60     progressBar->setMinimum(0);
61     progressBar->setMaximum(100);
62     progressBar->setAutoFillBackground(true);
63     progressBar->setTextVisible(false);
64     QHBoxLayout* layout = new QHBoxLayout(progressBar);
65     QLabel* label = new QLabel;
66     layout->addWidget(label);
67     layout->setContentsMargins(0, 0, 0, 0);
68     ui->treeView->setIndexWidget(index, progressBar);
69     ui->treeView->resizeColumnToContents(JobQueue::COLUMN_STATUS);
70     label->setToolTip(JOBS.data(index).toString());
71     label->setText(label->fontMetrics().elidedText(
72         JOBS.data(index).toString(), Qt::ElideMiddle, ui->treeView->columnWidth(JobQueue::COLUMN_OUTPUT)));
73     connect(JOBS.jobFromIndex(index), SIGNAL(progressUpdated(QStandardItem*, int)), SLOT(onProgressUpdated(QStandardItem*, int)));
74     show();
75     raise();
76 }
77 
onProgressUpdated(QStandardItem * item,int percent)78 void JobsDock::onProgressUpdated(QStandardItem* item, int percent)
79 {
80     if (item) {
81         QModelIndex index = JOBS.index(item->row(), JobQueue::COLUMN_OUTPUT);
82         QProgressBar* progressBar = qobject_cast<QProgressBar*>(ui->treeView->indexWidget(index));
83         if (progressBar)
84             progressBar->setValue(percent);
85     }
86 }
87 
resizeEvent(QResizeEvent * event)88 void JobsDock::resizeEvent(QResizeEvent *event)
89 {
90     QDockWidget::resizeEvent(event);
91     foreach (QLabel* label, ui->treeView->findChildren<QLabel*>()) {
92         label->setText(label->fontMetrics().elidedText(
93             label->toolTip(), Qt::ElideMiddle, ui->treeView->columnWidth(JobQueue::COLUMN_OUTPUT)));
94     }
95 
96 }
97 
on_treeView_customContextMenuRequested(const QPoint & pos)98 void JobsDock::on_treeView_customContextMenuRequested(const QPoint &pos)
99 {
100     QModelIndex index = ui->treeView->currentIndex();
101     QMenu menu(this);
102     AbstractJob* job = index.isValid()? JOBS.jobFromIndex(index) : nullptr;
103     if (job) {
104         if (job->ran() && job->state() == QProcess::NotRunning && job->exitStatus() == QProcess::NormalExit) {
105             menu.addActions(job->successActions());
106         }
107         if (job->stopped() || (JOBS.isPaused() && !job->ran()))
108             menu.addAction(ui->actionRun);
109         if (job->state() == QProcess::Running)
110             menu.addAction(ui->actionStopJob);
111         else
112             menu.addAction(ui->actionRemove);
113         if (job->ran())
114             menu.addAction(ui->actionViewLog);
115         menu.addActions(job->standardActions());
116     }
117     for (auto job : JOBS.jobs()) {
118         if (job->ran() && job->state() != QProcess::Running) {
119             menu.addAction(ui->actionRemoveFinished);
120             break;
121         }
122     }
123     menu.exec(mapToGlobal(pos));
124 }
125 
on_actionStopJob_triggered()126 void JobsDock::on_actionStopJob_triggered()
127 {
128     QModelIndex index = ui->treeView->currentIndex();
129     if (!index.isValid()) return;
130     AbstractJob* job = JOBS.jobFromIndex(index);
131     if (job) job->stop();
132 }
133 
on_actionViewLog_triggered()134 void JobsDock::on_actionViewLog_triggered()
135 {
136     QModelIndex index = ui->treeView->currentIndex();
137     if (!index.isValid()) return;
138     AbstractJob* job = JOBS.jobFromIndex(index);
139     if (job) {
140         TextViewerDialog dialog(this);
141         dialog.setWindowTitle(tr("Job Log"));
142         dialog.setText(job->log());
143         dialog.exec();
144     }
145 }
146 
on_pauseButton_toggled(bool checked)147 void JobsDock::on_pauseButton_toggled(bool checked)
148 {
149     if (checked)
150         JOBS.pause();
151     else
152         JOBS.resume();
153 }
154 
on_actionRun_triggered()155 void JobsDock::on_actionRun_triggered()
156 {
157     QModelIndex index = ui->treeView->currentIndex();
158     if (!index.isValid()) return;
159     AbstractJob* job = JOBS.jobFromIndex(index);
160     if (job) job->start();
161 }
162 
on_menuButton_clicked()163 void JobsDock::on_menuButton_clicked()
164 {
165     on_treeView_customContextMenuRequested(ui->menuButton->mapToParent(QPoint(0, 0)));
166 }
167 
on_treeView_doubleClicked(const QModelIndex & index)168 void JobsDock::on_treeView_doubleClicked(const QModelIndex &index)
169 {
170     AbstractJob* job = JOBS.jobFromIndex(index);
171     if (job && job->ran() && job->state() == QProcess::NotRunning && job->exitStatus() == QProcess::NormalExit) {
172         foreach (QAction* action, job->successActions()) {
173             if (action->text() == "Open") {
174                 action->trigger();
175                 break;
176             }
177         }
178     }
179 }
180 
on_actionRemove_triggered()181 void JobsDock::on_actionRemove_triggered()
182 {
183     QModelIndex index = ui->treeView->currentIndex();
184     if (!index.isValid()) return;
185     JOBS.remove(index);
186 }
187 
on_actionRemoveFinished_triggered()188 void JobsDock::on_actionRemoveFinished_triggered()
189 {
190     JOBS.removeFinished();
191 }
192 
on_JobsDock_visibilityChanged(bool visible)193 void JobsDock::on_JobsDock_visibilityChanged(bool visible)
194 {
195     if (visible) {
196         foreach (QLabel* label, ui->treeView->findChildren<QLabel*>()) {
197             label->setText(label->fontMetrics().elidedText(
198                                label->toolTip(), Qt::ElideMiddle, ui->treeView->columnWidth(JobQueue::COLUMN_OUTPUT)));
199         }
200     }
201 }
202