From fe2dd584fc5ce998e50aa667044505193256f45f Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Thu, 13 Sep 2012 11:27:15 +0200 Subject: [PATCH] Fix error handling for custom executables. Prevent dialog showing "No executable" when canceling the prompt for the executable when pressing 'Run' / 'Debug' on a library project. Introduce new LocalApplicationRunControl::ensureConfigured() to ensure the configuration is complete and detect cancel via empty/non-null strings. Change-Id: I9bd4a296e7c995d26d6ad265519e7ebd3f98d6fe Reviewed-by: Tobias Hunger Reviewed-by: Daniel Teske --- src/plugins/debugger/debuggerrunner.cpp | 6 +- .../localapplicationruncontrol.cpp | 5 +- .../projectexplorer/projectexplorer.cpp | 6 +- .../projectexplorer/runconfiguration.cpp | 10 ++ .../projectexplorer/runconfiguration.h | 2 + .../customexecutablerunconfiguration.cpp | 144 ++++++++++++------ .../customexecutablerunconfiguration.h | 3 + 7 files changed, 127 insertions(+), 49 deletions(-) diff --git a/src/plugins/debugger/debuggerrunner.cpp b/src/plugins/debugger/debuggerrunner.cpp index e0ddc0122a3..c9c3e7aa07b 100644 --- a/src/plugins/debugger/debuggerrunner.cpp +++ b/src/plugins/debugger/debuggerrunner.cpp @@ -478,6 +478,8 @@ static DebuggerStartParameters localStartParameters(RunConfiguration *runConfigu LocalApplicationRunConfiguration *rc = qobject_cast(runConfiguration); QTC_ASSERT(rc, return sp); + if (!rc->ensureConfigured(errorMessage)) + return sp; Target *target = runConfiguration->target(); Kit *kit = target ? target->kit() : KitManager::instance()->defaultKit(); @@ -541,7 +543,6 @@ static DebuggerStartParameters localStartParameters(RunConfiguration *runConfigu RunControl *DebuggerRunControlFactory::create (RunConfiguration *runConfiguration, RunMode mode, QString *errorMessage) { - Q_UNUSED(errorMessage) QTC_ASSERT(mode == DebugRunMode || mode == DebugRunModeWithBreakOnMain, return 0); DebuggerStartParameters sp = localStartParameters(runConfiguration, errorMessage); if (sp.startMode == NoStartMode) @@ -621,6 +622,9 @@ DebuggerRunControl *DebuggerRunControlFactory::createAndScheduleRun (const DebuggerStartParameters &sp, RunConfiguration *runConfiguration) { QString errorMessage; + if (runConfiguration && !runConfiguration->ensureConfigured(&errorMessage)) + ProjectExplorer::ProjectExplorerPlugin::showRunErrorMessage(errorMessage); + DebuggerRunControl *rc = doCreate(sp, runConfiguration, &errorMessage); if (!rc) { ProjectExplorer::ProjectExplorerPlugin::showRunErrorMessage(errorMessage); diff --git a/src/plugins/projectexplorer/localapplicationruncontrol.cpp b/src/plugins/projectexplorer/localapplicationruncontrol.cpp index 0f9ef7d1ebe..3ce17199c76 100644 --- a/src/plugins/projectexplorer/localapplicationruncontrol.cpp +++ b/src/plugins/projectexplorer/localapplicationruncontrol.cpp @@ -66,11 +66,8 @@ RunControl *LocalApplicationRunControlFactory::create(RunConfiguration *runConfi QTC_ASSERT(canRun(runConfiguration, mode), return 0); LocalApplicationRunConfiguration *localRunConfiguration = qobject_cast(runConfiguration); // Force the dialog about executables at this point and fail if there is none - if (localRunConfiguration->executable().isEmpty()) { - if (errorMessage) - *errorMessage = tr("No executable"); + if (!localRunConfiguration->ensureConfigured(errorMessage)) return 0; - } return new LocalApplicationRunControl(localRunConfiguration, mode); } diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index 9a5c381be01..81889433808 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -1565,10 +1565,14 @@ void ProjectExplorerPlugin::buildStateChanged(Project * pro) void ProjectExplorerPlugin::executeRunConfiguration(RunConfiguration *runConfiguration, RunMode runMode) { + QString errorMessage; + if (!runConfiguration->ensureConfigured(&errorMessage)) { + showRunErrorMessage(errorMessage); + return; + } if (IRunControlFactory *runControlFactory = findRunControlFactory(runConfiguration, runMode)) { emit aboutToExecuteProject(runConfiguration->target()->project(), runMode); - QString errorMessage; RunControl *control = runControlFactory->create(runConfiguration, runMode, &errorMessage); if (!control) { showRunErrorMessage(errorMessage); diff --git a/src/plugins/projectexplorer/runconfiguration.cpp b/src/plugins/projectexplorer/runconfiguration.cpp index e7a34e0c0fa..c1784518429 100644 --- a/src/plugins/projectexplorer/runconfiguration.cpp +++ b/src/plugins/projectexplorer/runconfiguration.cpp @@ -322,6 +322,16 @@ bool RunConfiguration::isConfigured() const return true; } +bool RunConfiguration::ensureConfigured(QString *errorMessage) +{ + if (isConfigured()) + return true; + if (errorMessage) + *errorMessage = tr("Unknown error."); + return false; +} + + /*! \fn virtual QWidget *ProjectExplorer::RunConfiguration::createConfigurationWidget() diff --git a/src/plugins/projectexplorer/runconfiguration.h b/src/plugins/projectexplorer/runconfiguration.h index b476024668b..e8fedc8b268 100644 --- a/src/plugins/projectexplorer/runconfiguration.h +++ b/src/plugins/projectexplorer/runconfiguration.h @@ -151,6 +151,8 @@ public: virtual QString disabledReason() const; virtual QWidget *createConfigurationWidget() = 0; virtual bool isConfigured() const; + // Pop up configuration dialog in case for example the executable is missing. + virtual bool ensureConfigured(QString *errorMessage = 0); Target *target() const; diff --git a/src/plugins/qtsupport/customexecutablerunconfiguration.cpp b/src/plugins/qtsupport/customexecutablerunconfiguration.cpp index 2de78f45a4a..b02f5e1da47 100644 --- a/src/plugins/qtsupport/customexecutablerunconfiguration.cpp +++ b/src/plugins/qtsupport/customexecutablerunconfiguration.cpp @@ -45,6 +45,7 @@ #include #include +#include #include #include @@ -119,51 +120,106 @@ void CustomExecutableRunConfiguration::activeBuildConfigurationChanged() } } +// Dialog embedding the CustomExecutableConfigurationWidget +// prompting the user to complete the configuration. +class CustomExecutableDialog : public QDialog +{ + Q_OBJECT +public: + explicit CustomExecutableDialog(CustomExecutableRunConfiguration *rc, QWidget *parent = 0); + +private slots: + void changed() + { + setOkButtonEnabled(m_runConfiguration->isConfigured()); + } + +private: + inline void setOkButtonEnabled(bool e) + { + m_dialogButtonBox->button(QDialogButtonBox::Ok)->setEnabled(e); + } + + QDialogButtonBox *m_dialogButtonBox; + CustomExecutableRunConfiguration *m_runConfiguration; +}; + +CustomExecutableDialog::CustomExecutableDialog(CustomExecutableRunConfiguration *rc, QWidget *parent) + : QDialog(parent) + , m_dialogButtonBox(new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel)) + , m_runConfiguration(rc) +{ + connect(rc, SIGNAL(changed()), this, SLOT(changed())); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + QVBoxLayout *layout = new QVBoxLayout(this); + QLabel *label = new QLabel(tr("Could not find the executable, please specify one.")); + label->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + layout->addWidget(label); + QWidget *configWidget = rc->createConfigurationWidget(); + configWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + layout->addWidget(configWidget); + setOkButtonEnabled(false); + connect(m_dialogButtonBox, SIGNAL(accepted()), this, SLOT(accept())); + connect(m_dialogButtonBox, SIGNAL(rejected()), this, SLOT(reject())); + layout->addWidget(m_dialogButtonBox); + layout->setSizeConstraint(QLayout::SetMinAndMaxSize); +} + +bool CustomExecutableRunConfiguration::ensureConfigured(QString *errorMessage) +{ + if (isConfigured()) + return validateExecutable(0, errorMessage); + CustomExecutableDialog dialog(this, Core::ICore::mainWindow()); + dialog.setWindowTitle(displayName()); + const QString oldExecutable = m_executable; + const QString oldWorkingDirectory = m_workingDirectory; + const QString oldCmdArguments = m_cmdArguments; + if (dialog.exec() == QDialog::Accepted) + return validateExecutable(0, errorMessage); + // User canceled: Hack: Silence the error dialog. + if (errorMessage) + *errorMessage = QLatin1String(""); + // Restore values changed by the configuration widget. + if (m_executable != oldExecutable + || m_workingDirectory != oldWorkingDirectory + || m_cmdArguments != oldCmdArguments) { + m_executable = oldExecutable; + m_workingDirectory = oldWorkingDirectory; + m_cmdArguments = oldCmdArguments; + emit changed(); + } + return false; +} + +// Search the executable in the path. +bool CustomExecutableRunConfiguration::validateExecutable(QString *executable, QString *errorMessage) const +{ + if (executable) + executable->clear(); + if (m_executable.isEmpty()) { + if (errorMessage) + *errorMessage = tr("No executable."); + return false; + } + const Utils::Environment env = environment(); + const QString exec = env.searchInPath(Utils::expandMacros(m_executable, macroExpander()), + QStringList(workingDirectory())); + if (exec.isEmpty()) { + if (errorMessage) + *errorMessage = tr("The executable\n%1\ncannot be found in the path."). + arg(QDir::toNativeSeparators(m_executable)); + return false; + } + if (executable) + *executable = QDir::cleanPath(exec); + return true; +} + QString CustomExecutableRunConfiguration::executable() const { - Utils::Environment env = environment(); - QString exec = env.searchInPath(Utils::expandMacros(m_executable, macroExpander()), - QStringList() << workingDirectory()); - - if (exec.isEmpty() || !QFileInfo(exec).exists()) { - // Oh the executable doesn't exists, ask the user. - CustomExecutableRunConfiguration *that = const_cast(this); - QWidget *confWidget = that->createConfigurationWidget(); - confWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); - QDialog dialog(Core::ICore::mainWindow()); - dialog.setWindowTitle(displayName()); - dialog.setWindowFlags(dialog.windowFlags() & ~Qt::WindowContextHelpButtonHint); - dialog.setLayout(new QVBoxLayout()); - QLabel *label = new QLabel(tr("Could not find the executable, please specify one.")); - label->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); - dialog.layout()->addWidget(label); - dialog.layout()->addWidget(confWidget); - QDialogButtonBox *dbb = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); - connect(dbb, SIGNAL(accepted()), &dialog, SLOT(accept())); - connect(dbb, SIGNAL(rejected()), &dialog, SLOT(reject())); - dialog.layout()->addWidget(dbb); - dialog.layout()->setSizeConstraint(QLayout::SetMinAndMaxSize); - - QString oldExecutable = m_executable; - QString oldWorkingDirectory = m_workingDirectory; - QString oldCmdArguments = m_cmdArguments; - - if (dialog.exec() == QDialog::Accepted) { - return executable(); - } else { - // Restore values changed by the configuration widget. - if (that->m_executable != oldExecutable - || that->m_workingDirectory != oldWorkingDirectory - || that->m_cmdArguments != oldCmdArguments) { - that->m_executable = oldExecutable; - that->m_workingDirectory = oldWorkingDirectory; - that->m_cmdArguments = oldCmdArguments; - emit that->changed(); - } - return QString(); - } - } - return QDir::cleanPath(exec); + QString result; + validateExecutable(&result); + return result; } QString CustomExecutableRunConfiguration::rawExecutable() const @@ -426,3 +482,5 @@ QString CustomExecutableRunConfigurationFactory::displayNameForId(const Core::Id return tr("Custom Executable"); return QString(); } + +#include "customexecutablerunconfiguration.moc" diff --git a/src/plugins/qtsupport/customexecutablerunconfiguration.h b/src/plugins/qtsupport/customexecutablerunconfiguration.h index a0531fb0ee5..73182c30acf 100644 --- a/src/plugins/qtsupport/customexecutablerunconfiguration.h +++ b/src/plugins/qtsupport/customexecutablerunconfiguration.h @@ -80,6 +80,8 @@ public: QVariantMap toMap() const; + bool ensureConfigured(QString *errorMessage); + signals: void changed(); @@ -116,6 +118,7 @@ private: QString baseWorkingDirectory() const; void setUserName(const QString &name); void setRunMode(ProjectExplorer::LocalApplicationRunConfiguration::RunMode runMode); + bool validateExecutable(QString *executable = 0, QString *errorMessage = 0) const; QString m_executable; QString m_workingDirectory;