Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 91 additions & 1 deletion src/runtaskdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@
#include <QDir>
#include <QFileDialog>
#include <QFileInfo>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QStandardPaths>
#include <QVBoxLayout>

RunTaskDialog::RunTaskDialog(QWidget *parent) : QDialog(parent), ui(new Ui::RunTaskDialog)
{
Expand All @@ -37,11 +40,23 @@ RunTaskDialog::RunTaskDialog(QWidget *parent) : QDialog(parent), ui(new Ui::RunT

this->m_okButton = this->ui->buttonBox->button(QDialogButtonBox::Ok);

connect(this->ui->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
this->m_errorLabel = new QLabel(this);
this->m_errorLabel->setWordWrap(true);
this->m_errorLabel->setVisible(false);

auto *layout = qobject_cast<QVBoxLayout *>(this->layout());
if (layout)
{
const int buttonBoxIndex = layout->indexOf(this->ui->buttonBox);
layout->insertWidget(buttonBoxIndex, this->m_errorLabel);
}

connect(this->ui->buttonBox, &QDialogButtonBox::accepted, this, &RunTaskDialog::accept);
connect(this->ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
connect(this->ui->browseButton, &QPushButton::clicked, this, &RunTaskDialog::browseForCommand);
connect(this->ui->commandCombo->lineEdit(), &QLineEdit::textChanged, this, [this]()
{
this->m_errorLabel->setVisible(false);
this->updateOkButton();
});

Expand Down Expand Up @@ -79,6 +94,81 @@ void RunTaskDialog::updateOkButton()
this->m_okButton->setEnabled(!this->Command().isEmpty());
}

void RunTaskDialog::accept()
{
const QString command = this->Command();
if (command.isEmpty())
return;

const QString exe = RunTaskDialog::resolveExecutable(command);
if (exe.isEmpty())
{
// clamped to avoid blowing up the dialog
QString program = command.split(' ', Qt::SkipEmptyParts).value(0);
constexpr int maxLen = 40;
if (program.size() > maxLen)
program = program.left(maxLen) + QStringLiteral("\u2026"); // '…'
this->setError(tr("Program or file \u201c%1\u201d does not exist or could not be found.").arg(program));
return;
}

QDialog::accept();
}

void RunTaskDialog::setError(const QString &message)
{
if (!this->m_errorLabel)
return;
this->m_errorLabel->setText(QStringLiteral("\u26a0 ") + message);
if (!this->m_errorLabel->isVisible())
{
this->m_errorLabel->setVisible(true);
const QSize hint = this->sizeHint();
if (hint.height() > this->height())
this->resize(this->width(), hint.height());
}
}

QString RunTaskDialog::resolveExecutable(const QString &command)
{
if (command.trimmed().isEmpty())
return {};

// Grab the first token
QString token = command.trimmed();

// Handle single-quoted token
if (token.startsWith('\''))
{
const int end = token.indexOf('\'', 1);
token = (end > 0) ? token.mid(1, end - 1) : token.mid(1);
}
// Handle double-quoted token
else if (token.startsWith('"'))
{
const int end = token.indexOf('"', 1);
token = (end > 0) ? token.mid(1, end - 1) : token.mid(1);
}
else
{
// Take everything up to the first whitespace
token = token.split(' ', Qt::SkipEmptyParts).value(0);
}

if (token.isEmpty())
return {};

// Check if the token contains a path separator
if (token.contains('/') || token.startsWith('.'))
{
const QFileInfo fi(token);
return fi.exists() ? fi.absoluteFilePath() : QString{};
}

// Else look it up on PATH
return QStandardPaths::findExecutable(token);
}

QString RunTaskDialog::shellQuote(const QString &text)
{
QString escaped = text;
Expand Down
5 changes: 5 additions & 0 deletions src/runtaskdialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
QT_BEGIN_NAMESPACE
namespace Ui { class RunTaskDialog; }
class QPushButton;
class QLabel;
QT_END_NAMESPACE

class RunTaskDialog : public QDialog
Expand All @@ -37,12 +38,16 @@ class RunTaskDialog : public QDialog
QString Command() const;

private:
void accept() override;
void browseForCommand();
void updateOkButton();
void setError(const QString &message);
static QString shellQuote(const QString &text);
static QString resolveExecutable(const QString &command);

Ui::RunTaskDialog *ui { nullptr };
QPushButton *m_okButton { nullptr };
QLabel *m_errorLabel { nullptr };
};

#endif // RUNTASKDIALOG_H
Loading