/*
* Copyright (c) 2012-2020 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "jobqueue.h"
#include
#include
#include "settings.h"
#ifdef Q_OS_WIN
#include "windowstools.h"
#endif
JobQueue::JobQueue(QObject *parent) :
QStandardItemModel(0, COLUMN_COUNT, parent),
m_paused(false)
{
}
JobQueue &JobQueue::singleton(QObject *parent)
{
static JobQueue *instance = 0;
if (!instance)
instance = new JobQueue(parent);
return *instance;
}
void JobQueue::cleanup()
{
QMutexLocker locker(&m_mutex);
foreach (AbstractJob *job, m_jobs) {
if (job->state() == QProcess::Running) {
job->stop();
break;
}
}
qDeleteAll(m_jobs);
}
AbstractJob *JobQueue::add(AbstractJob *job)
{
QList items;
QIcon icon = QIcon::fromTheme("run-build", QIcon(":/icons/oxygen/32x32/actions/run-build.png"));
items << new QStandardItem(icon, "");
QStandardItem *item = new QStandardItem(job->label());
items << item;
item = new QStandardItem(tr("pending"));
#ifdef Q_OS_MAC
QFont font = QFontDatabase::systemFont(QFontDatabase::FixedFont);
font.setPointSize(QGuiApplication::font().pointSize());
item->setFont(font);
#endif
item->setToolTip(tr("Estimated Hours:Minutes:Seconds"));
items << item;
appendRow(items);
job->setParent(this);
job->setStandardItem(item);
connect(job, SIGNAL(progressUpdated(QStandardItem *, int)), SLOT(onProgressUpdated(QStandardItem *,
int)));
connect(job, SIGNAL(finished(AbstractJob *, bool, QString)), SLOT(onFinished(AbstractJob *, bool,
QString)));
m_mutex.lock();
m_jobs.append(job);
m_mutex.unlock();
emit jobAdded();
startNextJob();
return job;
}
void JobQueue::onProgressUpdated(QStandardItem *standardItem, int percent)
{
if (standardItem) {
AbstractJob *job = m_jobs.at(standardItem->row());
if (job) {
QString remaining = "--:--:--";
if (percent > 2)
remaining = job->estimateRemaining(percent).toString();
standardItem->setText(QString("%1% (%2)").arg(percent).arg(remaining));
}
}
#ifdef Q_OS_WIN
WindowsTaskbarButton::getInstance().setProgress(percent);
#endif
}
void JobQueue::onFinished(AbstractJob *job, bool isSuccess, QString time)
{
QStandardItem *item = job->standardItem();
if (item) {
QIcon icon;
if (isSuccess) {
const QTime &time = QTime::fromMSecsSinceStartOfDay(job->time().elapsed());
item->setText(time.toString());
item->setToolTip(tr("Elapsed Hours:Minutes:Seconds"));
icon = QIcon(":/icons/oxygen/32x32/status/task-complete.png");
} else if (job->stopped()) {
item->setText(tr("stopped"));
icon = QIcon(":/icons/oxygen/32x32/status/task-attempt.png");
} else {
item->setText(tr("failed").append(' ').append(time));
icon = QIcon(":/icons/oxygen/32x32/status/task-reject.png");
}
// Remove any touched or incomplete pending proxy files
if (job->stopped() || !isSuccess)
if (job->objectName().contains("proxies") && job->objectName().contains(".pending")) {
QFile::remove(job->objectName());
}
item = JOBS.item(item->row(), JobQueue::COLUMN_ICON);
if (item)
item->setIcon(icon);
}
#ifdef Q_OS_WIN
WindowsTaskbarButton::getInstance().resetProgress();
#endif
startNextJob();
}
void JobQueue::startNextJob()
{
if (m_paused) return;
QMutexLocker locker(&m_mutex);
if (!m_jobs.isEmpty()) {
foreach (AbstractJob *job, m_jobs) {
// if there is already a job started or running, then exit
if (job->ran() && job->state() != QProcess::NotRunning)
break;
// otherwise, start first non-started job and exit
if (!job->ran()) {
job->start();
break;
}
}
}
}
AbstractJob *JobQueue::jobFromIndex(const QModelIndex &index) const
{
return m_jobs.at(index.row());
}
void JobQueue::pause()
{
m_paused = true;
}
void JobQueue::resume()
{
m_paused = false;
startNextJob();
}
bool JobQueue::isPaused() const
{
return m_paused;
}
bool JobQueue::hasIncomplete() const
{
foreach (AbstractJob *job, m_jobs) {
if (!job->ran() || job->state() == QProcess::Running)
return true;
}
return false;
}
void JobQueue::remove(const QModelIndex &index)
{
int row = index.row();
removeRow(index.row());
m_mutex.lock();
AbstractJob *job = m_jobs.at(row);
m_jobs.removeOne(job);
delete job;
m_mutex.unlock();
}
void JobQueue::removeFinished()
{
QMutexLocker locker(&m_mutex);
auto row = 0;
foreach (AbstractJob *job, m_jobs) {
if (job->ran() && job->state() != QProcess::Running) {
removeRow(row);
m_jobs.removeOne(job);
delete job;
} else {
++row;
}
}
}