Skip to content

Commit

Permalink
Added ability to set up project-specific custom commands
Browse files Browse the repository at this point in the history
Also added a %projectpath variable for use when setting up a command.

Issue #1665
  • Loading branch information
bjorn committed May 22, 2020
1 parent 65e251f commit a46a21d
Show file tree
Hide file tree
Showing 16 changed files with 599 additions and 419 deletions.
83 changes: 41 additions & 42 deletions src/tiled/command.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "commandmanager.h"
#include "documentmanager.h"
#include "logginginterface.h"
#include "mainwindow.h"
#include "mapdocument.h"
#include "mapobject.h"
#include "worlddocument.h"
Expand Down Expand Up @@ -72,16 +73,13 @@ static QString replaceVariables(const QString &string, bool quoteValues = true)
// Perform variable replacement
if (Document *document = DocumentManager::instance()->currentDocument()) {
const QString fileName = document->fileName();

finalString.replace(QLatin1String("%mapfile"),
replaceString.arg(fileName));

QFileInfo fileInfo(fileName);
QString mapPath = fileInfo.absolutePath();
const QString mapPath = fileInfo.absolutePath();
const QString projectPath = QFileInfo(MainWindow::instance()->project().fileName()).absolutePath();

finalString.replace(
QLatin1String("%mappath"),
replaceString.arg(mapPath));
finalString.replace(QLatin1String("%mapfile"), replaceString.arg(fileName));
finalString.replace(QLatin1String("%mappath"), replaceString.arg(mapPath));
finalString.replace(QLatin1String("%projectpath"), replaceString.arg(projectPath));

if (MapDocument *mapDocument = qobject_cast<MapDocument*>(document)) {
if (const Layer *layer = mapDocument->currentLayer()) {
Expand Down Expand Up @@ -151,53 +149,54 @@ void Command::execute(bool inTerminal) const
/**
* Stores this command in a QVariant.
*/
QVariant Command::toQVariant() const
QVariantHash Command::toVariant() const
{
return QVariantHash {
{ QLatin1String("Enabled"), isEnabled },
{ QLatin1String("Name"), name },
{ QLatin1String("Command"), executable },
{ QLatin1String("Arguments"), arguments },
{ QLatin1String("WorkingDirectory"), workingDirectory },
{ QLatin1String("Shortcut"), shortcut },
{ QLatin1String("ShowOutput"), showOutput },
{ QLatin1String("SaveBeforeExecute"), saveBeforeExecute },
{ QStringLiteral("arguments"), arguments },
{ QStringLiteral("command"), executable },
{ QStringLiteral("enabled"), isEnabled },
{ QStringLiteral("name"), name },
{ QStringLiteral("saveBeforeExecute"), saveBeforeExecute },
{ QStringLiteral("shortcut"), shortcut },
{ QStringLiteral("showOutput"), showOutput },
{ QStringLiteral("workingDirectory"), workingDirectory },
};
}

/**
* Generates a command from a QVariant.
*/
Command Command::fromQVariant(const QVariant &variant)
Command Command::fromVariant(const QVariant &variant)
{
const auto hash = variant.toHash();

const QString namePref = QLatin1String("Name");
const QString executablePref = QLatin1String("Command");
const QString argumentsPref = QLatin1String("Arguments");
const QString workingDirectoryPref = QLatin1String("WorkingDirectory");
const QString enablePref = QLatin1String("Enabled");
const QString shortcutPref = QLatin1String("Shortcut");
const QString showOutputPref = QLatin1String("ShowOutput");
const QString saveBeforeExecutePref = QLatin1String("SaveBeforeExecute");
auto read = [&] (const QString &prop) {
if (hash.contains(prop))
return hash.value(prop);

QString oldProp = prop.at(0).toUpper() + prop.mid(1);
return hash.value(oldProp);
};

const QVariant arguments = read(QStringLiteral("arguments"));
const QVariant enable = read(QStringLiteral("enabled"));
const QVariant executable = read(QStringLiteral("command"));
const QVariant name = read(QStringLiteral("name"));
const QVariant saveBeforeExecute = read(QStringLiteral("saveBeforeExecute"));
const QVariant shortcut = read(QStringLiteral("shortcut"));
const QVariant showOutput = read(QStringLiteral("showOutput"));
const QVariant workingDirectory = read(QStringLiteral("workingDirectory"));

Command command;
if (hash.contains(enablePref))
command.isEnabled = hash[enablePref].toBool();
if (hash.contains(namePref))
command.name = hash[namePref].toString();
if (hash.contains(executablePref))
command.executable = hash[executablePref].toString();
if (hash.contains(argumentsPref))
command.arguments = hash[argumentsPref].toString();
if (hash.contains(workingDirectoryPref))
command.workingDirectory = hash[workingDirectoryPref].toString();
if (hash.contains(shortcutPref))
command.shortcut = hash[shortcutPref].value<QKeySequence>();
if (hash.contains(showOutputPref))
command.showOutput = hash[showOutputPref].toBool();
if (hash.contains(saveBeforeExecutePref))
command.saveBeforeExecute = hash[saveBeforeExecutePref].toBool();

command.arguments = arguments.toString();
command.isEnabled = enable.toBool();
command.executable = executable.toString();
command.name = name.toString();
command.saveBeforeExecute = saveBeforeExecute.toBool();
command.shortcut = shortcut.value<QKeySequence>();
command.showOutput = showOutput.toBool();
command.workingDirectory = workingDirectory.toString();

return command;
}
Expand Down
4 changes: 2 additions & 2 deletions src/tiled/command.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ struct Command

void execute(bool inTerminal = false) const;

QVariant toQVariant() const;
static Command fromQVariant(const QVariant &variant);
QVariantHash toVariant() const;
static Command fromVariant(const QVariant &variant);
};

} // namespace Tiled
29 changes: 14 additions & 15 deletions src/tiled/commandbutton.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,24 +48,23 @@ CommandButton::CommandButton(QWidget *parent)

void CommandButton::runCommand()
{
if (auto command = CommandManager::instance()->firstEnabledCommand()) {
command->execute();
} else {
QMessageBox warning(QMessageBox::Warning,
tr("Error Executing Command"),
tr("You do not have any commands setup."),
QMessageBox::Ok,
window());
if (CommandManager::instance()->executeDefaultCommand())
return;

const auto editButton = warning.addButton(tr("Edit Commands..."), QMessageBox::ActionRole);
warning.setDefaultButton(QMessageBox::Ok);
warning.setEscapeButton(QMessageBox::Ok);
QMessageBox warning(QMessageBox::Warning,
tr("Error Executing Command"),
tr("You do not have any commands setup."),
QMessageBox::Ok,
window());

connect(editButton, &QAbstractButton::clicked, CommandManager::instance(), &CommandManager::showDialog);
const auto editButton = warning.addButton(tr("Edit Commands..."), QMessageBox::ActionRole);
warning.setDefaultButton(QMessageBox::Ok);
warning.setEscapeButton(QMessageBox::Ok);

warning.exec();
return;
}
connect(editButton, &QAbstractButton::clicked,
CommandManager::instance(), &CommandManager::showDialog);

warning.exec();
}

void CommandButton::changeEvent(QEvent *event)
Expand Down
155 changes: 16 additions & 139 deletions src/tiled/commanddialog.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/*
* commanddialog.cpp
* Copyright 2010, Jeff Bland <[email protected]>
* Copyright 2017, Ketan Gupta <[email protected]>
* Copyright 2020, Thorbjørn Lindeijer <[email protected]>
*
* This file is part of Tiled.
*
Expand All @@ -23,68 +25,35 @@

#include "commanddatamodel.h"
#include "commandmanager.h"
#include "commandsedit.h"
#include "utils.h"

#include <QShortcut>
#include <QMenu>
#include <QContextMenuEvent>
#include <QModelIndex>
#include <QFileDialog>
#include <QStandardPaths>

using namespace Tiled;

CommandDialog::CommandDialog(const QVector<Command> &commands, QWidget *parent)
CommandDialog::CommandDialog(QWidget *parent)
: QDialog(parent)
, mUi(new Ui::CommandDialog)
, mModel(new CommandDataModel(this))
{
mUi->setupUi(this);
resize(Utils::dpiScaled(size()));
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
#endif

mModel->setCommands(commands);
mUi->treeView->setModel(mModel);
auto *commandManager = CommandManager::instance();

// Setup resizing so the command column stretches
QHeaderView *h = mUi->treeView->header();
h->resizeSection(0, Utils::dpiScaled(200));
h->setStretchLastSection(false);
h->setSectionResizeMode(CommandDataModel::NameColumn, QHeaderView::Stretch);
h->setSectionResizeMode(CommandDataModel::ShortcutColumn, QHeaderView::Fixed);
h->setSectionResizeMode(CommandDataModel::EnabledColumn, QHeaderView::ResizeToContents);
mGlobalCommandsEdit = new CommandsEdit(commandManager->globalCommands());
mProjectCommandsEdit = new CommandsEdit(commandManager->projectCommands());

setWindowTitle(tr("Edit Commands"));
Utils::restoreGeometry(this);

connect(mUi->saveBox, &QCheckBox::stateChanged,
this, &CommandDialog::setSaveBeforeExecute);

connect(mUi->outputBox, &QCheckBox::stateChanged,
this, &CommandDialog::setShowOutput);

connect(mUi->keySequenceEdit, &QKeySequenceEdit::keySequenceChanged,
this, &CommandDialog::setShortcut);

connect(mUi->executableEdit, &QLineEdit::textChanged,
this, &CommandDialog::setExecutable);
mUi->tabWidget->addTab(mGlobalCommandsEdit, tr("Global Commands"));
mUi->tabWidget->addTab(mProjectCommandsEdit, tr("Project Commands"));

connect(mUi->argumentsEdit, &QLineEdit::textChanged,
this, &CommandDialog::setArguments);

connect(mUi->workingDirectoryEdit, &QLineEdit::textChanged,
this, &CommandDialog::setWorkingDirectory);

connect(mUi->treeView->selectionModel(), &QItemSelectionModel::currentChanged,
this, &CommandDialog::updateWidgets);

connect(mUi->exBrowseButton, &QPushButton::clicked,
this, &CommandDialog::browseExecutable);

connect(mUi->wdBrowseButton, &QPushButton::clicked,
this, &CommandDialog::browseWorkingDirectory);
Utils::restoreGeometry(this);
}

CommandDialog::~CommandDialog()
Expand All @@ -93,103 +62,14 @@ CommandDialog::~CommandDialog()
delete mUi;
}

const QVector<Command> &CommandDialog::commands() const
const QVector<Command> &CommandDialog::globalCommands() const
{
return mModel->commands();
return mGlobalCommandsEdit->commands();
}

void CommandDialog::setShortcut(const QKeySequence &keySequence)
{
const QModelIndex &current = mUi->treeView->currentIndex();
if (current.row() < mModel->rowCount())
mModel->setShortcut(current, keySequence);
}

void CommandDialog::setSaveBeforeExecute(int state)
{
const QModelIndex &current = mUi->treeView->currentIndex();
if (current.row() < mModel->rowCount())
mModel->setSaveBeforeExecute(current, state);
}

void CommandDialog::setShowOutput(int state)
{
const QModelIndex &current = mUi->treeView->currentIndex();
if (current.row() < mModel->rowCount())
mModel->setShowOutput(current, state);
}


void CommandDialog::setExecutable(const QString &text)
const QVector<Command> &CommandDialog::projectCommands() const
{
const QModelIndex &current = mUi->treeView->currentIndex();
if (current.row() < mModel->rowCount())
mModel->setExecutable(current, text);
}

void CommandDialog::setArguments(const QString &text)
{
const QModelIndex &current = mUi->treeView->currentIndex();
if (current.row() < mModel->rowCount())
mModel->setArguments(current, text);
}

void CommandDialog::setWorkingDirectory(const QString &text)
{
const QModelIndex &current = mUi->treeView->currentIndex();
if (current.row() < mModel->rowCount())
mModel->setWorkingDirectory(current, text);
}

void CommandDialog::updateWidgets(const QModelIndex &current)
{
bool enable = (current.row() < mModel->rowCount() - 1);

mUi->saveBox->setEnabled(enable);
mUi->executableEdit->setEnabled(enable);
mUi->argumentsEdit->setEnabled(enable);
mUi->workingDirectoryEdit->setEnabled(enable);
mUi->exBrowseButton->setEnabled(enable);
mUi->wdBrowseButton->setEnabled(enable);
mUi->keySequenceEdit->setEnabled(enable);
mUi->clearButton->setEnabled(enable);
mUi->outputBox->setEnabled(enable);

if (enable) {
const Command command = mModel->command(current);
mUi->executableEdit->setText(command.executable);
mUi->argumentsEdit->setText(command.arguments);
mUi->workingDirectoryEdit->setText(command.workingDirectory);
mUi->keySequenceEdit->setKeySequence(command.shortcut);
mUi->saveBox->setChecked(command.saveBeforeExecute);
mUi->outputBox->setChecked(command.showOutput);
} else {
mUi->executableEdit->clear();
mUi->argumentsEdit->clear();
mUi->workingDirectoryEdit->clear();
mUi->keySequenceEdit->clear();
}
}

void CommandDialog::browseExecutable()
{
QString caption = tr("Select Executable");
QString dir = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation);
QString executableName = QFileDialog::getOpenFileName(this, caption, dir);

if (!executableName.isEmpty())
mUi->executableEdit->setText(executableName);
}

void CommandDialog::browseWorkingDirectory()
{
QString caption = tr("Select Working Directory");
QString dir = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
QString workingDirectoryName = QFileDialog::getExistingDirectory(this, caption, dir,
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);

if (!workingDirectoryName.isEmpty())
mUi->workingDirectoryEdit->setText(workingDirectoryName);
return mProjectCommandsEdit->commands();
}


Expand Down Expand Up @@ -239,11 +119,8 @@ void CommandTreeView::rowsAboutToBeRemoved(const QModelIndex &parent, int start,
return;

int selectedRow = currentIndex().row();
if (selectedRow >= start && selectedRow <= end && end < model()->rowCount() - 1) {
selectionModel()->select(model()->index(end + 1, 0),
QItemSelectionModel::ClearAndSelect |
QItemSelectionModel::Rows);
}
if (selectedRow >= start && selectedRow <= end && end < model()->rowCount() - 1)
setCurrentIndex(model()->index(end + 1, 0));

QTreeView::rowsAboutToBeRemoved(parent, start, end);
}
Expand Down
Loading

0 comments on commit a46a21d

Please sign in to comment.