View Issue Details [ Jump to Notes ] | [ Print ] | ||||||||
ID | Project | Category | View Status | Date Submitted | Last Update | ||||
0008260 | CMake | QtDialog | public | 2008-12-08 14:10 | 2016-06-10 14:30 | ||||
Reporter | Alex Neundorf | ||||||||
Assigned To | Clinton Stimpson | ||||||||
Priority | normal | Severity | feature | Reproducibility | always | ||||
Status | closed | Resolution | moved | ||||||
Platform | OS | OS Version | |||||||
Product Version | CMake-2-6 | ||||||||
Target Version | Fixed in Version | ||||||||
Summary | 0008260: add "build" button to cmake-gui ? | ||||||||
Description | Hi, now that cmake also has a command line option to build stuff, would it be possible to add a button for that in cmake-gui ? Then I could configure/generate/build just inside cmake-gui. Alex | ||||||||
Tags | No tags attached. | ||||||||
Attached Files | 0001-ENH-Improve-cmake-gui-v1.patch [^] (35,921 bytes) 2010-01-18 04:39 [Show Content] [Hide Content]From e3c36ec72dbb1028211188f8be0d101917b8d277 Mon Sep 17 00:00:00 2001 From: Michael Wild <themiwi@users.sourceforge.net> Date: Fri, 15 Jan 2010 17:49:40 +0100 Subject: [PATCH] ENH: Improve cmake-gui * Add menu-entries/buttons to open source and binary directories in file browser. * Replace "Configure" and "Generate" buttons with a single button with a delayed popup-menu, alowing one to configure, generate, build, install and clean the project. * Read the cache to find CMAKE_CONFIGURATION_TYPES and offer the available build types in the menu and a combo-box next to the "action" button. Requires, however, that all available build types are stored in the cache. * TODO: For IDE-projects add a button to open the project file. * TODO: Somehow capture stdout/stderr when building/installing/cleaning and display in output widget. * TODO: Parse output from Makefile-builds and drive progress-bar instead of the generic "busy" display. Signed-off-by: Michael Wild <themiwi@users.sourceforge.net> --- Source/QtDialog/ActionButtonEventFilter.cxx | 72 ++++++ Source/QtDialog/ActionButtonEventFilter.h | 53 ++++ Source/QtDialog/CMakeLists.txt | 2 + Source/QtDialog/CMakeSetup.qrc | 1 + Source/QtDialog/CMakeSetupDialog.cxx | 346 +++++++++++++++++++++++---- Source/QtDialog/CMakeSetupDialog.h | 28 ++- Source/QtDialog/CMakeSetupDialog.ui | 44 +++- Source/QtDialog/QCMake.cxx | 68 +++++- Source/QtDialog/QCMake.h | 14 +- Source/QtDialog/emblem-symbolic-link.png | Bin 0 -> 724 bytes 10 files changed, 569 insertions(+), 59 deletions(-) create mode 100644 Source/QtDialog/ActionButtonEventFilter.cxx create mode 100644 Source/QtDialog/ActionButtonEventFilter.h create mode 100644 Source/QtDialog/emblem-symbolic-link.png diff --git a/Source/QtDialog/ActionButtonEventFilter.cxx b/Source/QtDialog/ActionButtonEventFilter.cxx new file mode 100644 index 0000000..a35a2e2 --- /dev/null +++ b/Source/QtDialog/ActionButtonEventFilter.cxx @@ -0,0 +1,72 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2009-2009 Michael Wild <themiwi@users.sourceforge.net> + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "ActionButtonEventFilter.h" +#include <QWidget> +#include <QStyle> +#include <QMouseEvent> + +ActionButtonEventFilter::ActionButtonEventFilter(QWidget* parent) + : QObject(parent) + , Delay(600) +{ + this->Timer = new QTimer(this); + this->Timer->setSingleShot(true); + // chain the timer's timeout() signal to our showMenu() signal + QObject::connect(this->Timer, SIGNAL(timeout()), this, SIGNAL(showMenu())); + if(parent) + { + // use the style-sheet defined popup-delay + this->setDelay(parent->style()->styleHint( + QStyle::SH_ToolButton_PopupDelay, 0, parent)); + } +} + +int ActionButtonEventFilter::getDelay() const +{ + return this->Delay; +} + +void ActionButtonEventFilter::setDelay(int delay) +{ + this->Delay = delay; +} + +bool ActionButtonEventFilter::eventFilter(QObject *obj, QEvent *event) +{ + // only handle mouse-button press and release + if(event->type() == QEvent::MouseButtonPress || + event->type() == QEvent::MouseButtonRelease) + { + QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event); + // only handle the left mouse-button + if(mouseEvent->button() == Qt::LeftButton) + { + if(mouseEvent->type() == QEvent::MouseButtonPress) + { + // on press, start timer and eat event + this->Timer->start(this->Delay); + return true; + } + else if(this->Timer->isActive()) + { + // on release, stop timer, fire default action and eat event + this->Timer->stop(); + emit this->activateDefaultAction(); + return true; + } + } + } + // normal processing + return QObject::eventFilter(obj, event); +} diff --git a/Source/QtDialog/ActionButtonEventFilter.h b/Source/QtDialog/ActionButtonEventFilter.h new file mode 100644 index 0000000..7c546ba --- /dev/null +++ b/Source/QtDialog/ActionButtonEventFilter.h @@ -0,0 +1,53 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2009-2009 Michael Wild <themiwi@users.sourceforge.net> + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef ActionButtonEventFilter_h +#define ActionButtonEventFilter_h + +#include <QEvent> +#include <QTimer> + +class QWidget; + +/// Event filter to create QToolButton::DelayedPopupMode behavior for a +/// QPushButton +class ActionButtonEventFilter : public QObject +{ + Q_OBJECT + + /// Popup-delay, defaults to the style-defined delay of the parent if not + /// NULL, 600ms otherwise. + Q_PROPERTY(int Delay READ getDelay WRITE setDelay) + +public: + ActionButtonEventFilter(QWidget* parent); + + int getDelay() const; + void setDelay(int delay); + +signals: + /// Emitted when the default action should be used + void activateDefaultAction(); + /// Emitted when the button menu should be displayed + void showMenu(); + +protected: + bool eventFilter(QObject *obj, QEvent *event); + +private: + int Delay; + QTimer* Timer; + +}; + +#endif // ActionButtonEventFilter_h diff --git a/Source/QtDialog/CMakeLists.txt b/Source/QtDialog/CMakeLists.txt index 4e7e357..d0a3769 100644 --- a/Source/QtDialog/CMakeLists.txt +++ b/Source/QtDialog/CMakeLists.txt @@ -27,6 +27,7 @@ ELSE(NOT QT4_FOUND) "WARNING: QtDialog requires a static built qt for installation.") ENDIF(WIN32 AND NOT QT_CONFIG MATCHES "static") SET(SRCS + ActionButtonEventFilter.cxx AddCacheEntry.cxx AddCacheEntry.h CMakeSetup.cxx @@ -51,6 +52,7 @@ ELSE(NOT QT4_FOUND) MacInstallDialog.ui ) QT4_WRAP_CPP(MOC_SRCS + ActionButtonEventFilter.h AddCacheEntry.h Compilers.h CMakeSetupDialog.h diff --git a/Source/QtDialog/CMakeSetup.qrc b/Source/QtDialog/CMakeSetup.qrc index 5ceb1df..fd21ab3 100644 --- a/Source/QtDialog/CMakeSetup.qrc +++ b/Source/QtDialog/CMakeSetup.qrc @@ -1,5 +1,6 @@ <RCC> <qresource prefix="/Icons" > + <file>emblem-symbolic-link.png</file> <file>CMakeSetup.png</file> <file>Delete16.png</file> <file>Plus16.png</file> diff --git a/Source/QtDialog/CMakeSetupDialog.cxx b/Source/QtDialog/CMakeSetupDialog.cxx index eb82f2a..a4842b5 100644 --- a/Source/QtDialog/CMakeSetupDialog.cxx +++ b/Source/QtDialog/CMakeSetupDialog.cxx @@ -26,12 +26,14 @@ #include <QMimeData> #include <QUrl> #include <QShortcut> +#include <QDesktopServices> #include <QMacInstallDialog.h> #include "QCMake.h" #include "QCMakeCacheView.h" #include "AddCacheEntry.h" #include "FirstConfigure.h" +#include "ActionButtonEventFilter.h" #include "cmVersion.h" QCMakeThread::QCMakeThread(QObject* p) @@ -55,7 +57,8 @@ void QCMakeThread::run() } CMakeSetupDialog::CMakeSetupDialog() - : ExitAfterGenerate(true), CacheModified(false), CurrentState(Interrupting) + : ExitAfterGenerate(true), CacheModified(false), CurrentState(Interrupting), + CurrentConfiguration() { QString title = QString(tr("CMake %1")); title = title.arg(cmVersion::GetCMakeVersion()); @@ -94,6 +97,16 @@ CMakeSetupDialog::CMakeSetupDialog() this->DeleteCacheAction = FileMenu->addAction(tr("&Delete Cache")); QObject::connect(this->DeleteCacheAction, SIGNAL(triggered(bool)), this, SLOT(doDeleteCache())); + FileMenu->addSeparator(); + this->OpenSourceDirectoryAction = FileMenu->addAction(tr("Open &Source Directory")); + QObject::connect(this->OpenSourceDirectoryAction, SIGNAL(triggered(bool)), + this, SLOT(doSourceOpen())); + this->OpenBuildDirectoryAction = FileMenu->addAction(tr("Open &Build Directory")); + QObject::connect(this->OpenBuildDirectoryAction, SIGNAL(triggered(bool)), + this, SLOT(doBinaryOpen())); +#if !defined(Q_WS_MAC) + FileMenu->addSeparator(); +#endif this->ExitAction = FileMenu->addAction(tr("E&xit")); QObject::connect(this->ExitAction, SIGNAL(triggered(bool)), this, SLOT(close())); @@ -104,9 +117,27 @@ CMakeSetupDialog::CMakeSetupDialog() this->ConfigureAction->setMenuRole(QAction::NoRole); QObject::connect(this->ConfigureAction, SIGNAL(triggered(bool)), this, SLOT(doConfigure())); + this->StopAction = ToolsMenu->addAction(tr("&Stop")); + this->StopAction->setEnabled(false); + this->StopAction->setVisible(false); + QObject::connect(this->StopAction, SIGNAL(triggered(bool)), + this, SLOT(doInterrupt())); this->GenerateAction = ToolsMenu->addAction(tr("&Generate")); QObject::connect(this->GenerateAction, SIGNAL(triggered(bool)), this, SLOT(doGenerate())); + ToolsMenu->addSeparator(); + this->BuildAction = ToolsMenu->addAction(tr("&Build Project")); + QObject::connect(this->BuildAction, SIGNAL(triggered(bool)), + this, SLOT(doBuild())); + this->InstallAction = ToolsMenu->addAction(tr("&Install Project")); + QObject::connect(this->InstallAction, SIGNAL(triggered(bool)), + this, SLOT(doInstall())); + this->CleanAction = ToolsMenu->addAction(tr("&Clean Project")); + QObject::connect(this->CleanAction, SIGNAL(triggered(bool)), + this, SLOT(doClean())); + ToolsMenu->addSeparator(); + this->ActiveConfigurationMenu = ToolsMenu->addMenu(tr("Active Configuration")); + ToolsMenu->addSeparator(); QAction* showChangesAction = ToolsMenu->addAction(tr("&Show My Changes")); QObject::connect(showChangesAction, SIGNAL(triggered(bool)), this, SLOT(showUserChanges())); @@ -159,6 +190,25 @@ CMakeSetupDialog::CMakeSetupDialog() this->Output->setFont(outputFont); this->ErrorFormat.setForeground(QBrush(Qt::red)); + // set up the action-button + QMenu* ActionMenu = new QMenu(this->ActionButton); + ActionMenu->addAction(this->ConfigureAction); + ActionMenu->addAction(this->StopAction); + ActionMenu->addAction(this->GenerateAction); + ActionMenu->addAction(this->BuildAction); + ActionMenu->addAction(this->InstallAction); + ActionMenu->addAction(this->CleanAction); + this->ActionButton->setMenu(ActionMenu); + ActionButtonEventFilter* actionEventFilter = new ActionButtonEventFilter(this->ActionButton); + QObject::connect(actionEventFilter, SIGNAL(activateDefaultAction()), + this, SLOT(doDefaultAction())); + QObject::connect(actionEventFilter, SIGNAL(showMenu()), + this->ActionButton, SLOT(showMenu())); + this->ActionButton->installEventFilter(actionEventFilter); + this->setDefaultAction(this->ConfigureAction); + + this->ActiveConfigurationPopup->setVisible(false); + // start the cmake worker thread this->CMakeThread = new QCMakeThread(this); QObject::connect(this->CMakeThread, SIGNAL(cmakeInitialized()), @@ -176,22 +226,30 @@ void CMakeSetupDialog::initialize() this->CacheValues->cacheModel(), SLOT(setProperties(const QCMakePropertyList&))); - QObject::connect(this->ConfigureButton, SIGNAL(clicked(bool)), - this, SLOT(doConfigure())); QObject::connect(this->CMakeThread->cmakeInstance(), SIGNAL(configureDone(int)), this, SLOT(finishConfigure(int))); QObject::connect(this->CMakeThread->cmakeInstance(), SIGNAL(generateDone(int)), this, SLOT(finishGenerate(int))); - - QObject::connect(this->GenerateButton, SIGNAL(clicked(bool)), - this, SLOT(doGenerate())); + QObject::connect(this->CMakeThread->cmakeInstance(), + SIGNAL(buildDone(int)), + this, SLOT(finishBuild(int))); + QObject::connect(this->CMakeThread->cmakeInstance(), + SIGNAL(installDone(int)), + this, SLOT(finishInstall(int))); + QObject::connect(this->CMakeThread->cmakeInstance(), + SIGNAL(cleanDone(int)), + this, SLOT(finishClean(int))); QObject::connect(this->BrowseSourceDirectoryButton, SIGNAL(clicked(bool)), this, SLOT(doSourceBrowse())); + QObject::connect(this->OpenSourceDirectoryButton, SIGNAL(clicked(bool)), + this, SLOT(doSourceOpen())); QObject::connect(this->BrowseBinaryDirectoryButton, SIGNAL(clicked(bool)), this, SLOT(doBinaryBrowse())); + QObject::connect(this->OpenBinaryDirectoryButton, SIGNAL(clicked(bool)), + this, SLOT(doBinaryOpen())); QObject::connect(this->BinaryDirectory, SIGNAL(editTextChanged(QString)), this, SLOT(onBinaryDirectoryChanged(QString))); @@ -266,16 +324,16 @@ CMakeSetupDialog::~CMakeSetupDialog() this->CMakeThread->quit(); this->CMakeThread->wait(2000); } - -void CMakeSetupDialog::doConfigure() + +void CMakeSetupDialog::doDefaultAction() { - if(this->CurrentState == Configuring) - { - // stop configure - doInterrupt(); - return; - } + // doing it this way so we don't have to disconnect/connect all the time when + // changing the default action + this->DefaultAction->trigger(); +} +void CMakeSetupDialog::doConfigure() +{ // make sure build directory exists QString bindir = this->CMakeThread->cmakeInstance()->binaryDirectory(); QDir dir(bindir); @@ -332,11 +390,24 @@ void CMakeSetupDialog::finishConfigure(int err) if(0 == err && !this->CacheValues->cacheModel()->newPropertyCount()) { this->enterState(ReadyGenerate); + QCMakePropertyList props = this->CacheValues->cacheModel()->properties(); + QStringList configs; + foreach(QCMakeProperty p, props) + { + if(p.Key == "CMAKE_CONFIGURATION_TYPES") + { + // p.Strings seems to be always empty... + configs = p.Value.toString().split(";"); + break; + } + } + this->manageConfigs(configs); } else { this->enterState(ReadyConfigure); this->CacheValues->scrollToTop(); + this->manageConfigs(QStringList()); } if(err != 0) @@ -349,7 +420,7 @@ void CMakeSetupDialog::finishConfigure(int err) void CMakeSetupDialog::finishGenerate(int err) { - this->enterState(ReadyConfigure); + this->enterState(ReadyBuild); if(err != 0) { QMessageBox::critical(this, tr("Error"), @@ -358,6 +429,43 @@ void CMakeSetupDialog::finishGenerate(int err) } } +void CMakeSetupDialog::finishBuild(int err) +{ + this->ProgressBar->setMaximum(100); + this->enterState(ReadyInstall); + if(err != 0) + { + QMessageBox::critical(this, tr("Error"), + tr("Error in build process"), + QMessageBox::Ok); + } +} + +void CMakeSetupDialog::finishInstall(int err) +{ + this->ProgressBar->setMaximum(100); + this->enterState(ReadyInstall); + if(err != 0) + { + QMessageBox::critical(this, tr("Error"), + tr("Error in installation process"), + QMessageBox::Ok); + } +} + +void CMakeSetupDialog::finishClean(int err) +{ + this->ProgressBar->setMaximum(100); + this->enterState(ReadyInstall); + this->enterState(ReadyBuild); + if(err != 0) + { + QMessageBox::critical(this, tr("Error"), + tr("Error in cleaning process"), + QMessageBox::Ok); + } +} + void CMakeSetupDialog::doInstallForCommandLine() { QMacInstallDialog setupdialog(0); @@ -366,17 +474,35 @@ void CMakeSetupDialog::doInstallForCommandLine() void CMakeSetupDialog::doGenerate() { - if(this->CurrentState == Generating) - { - // stop generate - doInterrupt(); - return; - } this->enterState(Generating); QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(), "generate", Qt::QueuedConnection); } - + +void CMakeSetupDialog::doBuild() +{ + this->enterState(Building); + this->ProgressBar->setMaximum(0); + QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(), + "build", Qt::QueuedConnection, Q_ARG(QString, this->CurrentConfiguration)); +} + +void CMakeSetupDialog::doInstall() +{ + this->enterState(Installing); + this->ProgressBar->setMaximum(0); + QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(), + "install", Qt::QueuedConnection, Q_ARG(QString, this->CurrentConfiguration)); +} + +void CMakeSetupDialog::doClean() +{ + this->enterState(Cleaning); + this->ProgressBar->setMaximum(0); + QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(), + "clean", Qt::QueuedConnection, Q_ARG(QString, this->CurrentConfiguration)); +} + void CMakeSetupDialog::closeEvent(QCloseEvent* e) { // prompt for close if there are unsaved changes, and we're not busy @@ -475,6 +601,15 @@ void CMakeSetupDialog::doSourceBrowse() } } +void CMakeSetupDialog::doSourceOpen() +{ + QString srcdir = this->SourceDirectory->text(); + if(!srcdir.isEmpty()) + { + QDesktopServices::openUrl(QUrl("file:/"+srcdir, QUrl::TolerantMode)); + } +} + void CMakeSetupDialog::updateSourceDirectory(const QString& dir) { if(this->SourceDirectory->text() != dir) @@ -505,6 +640,15 @@ void CMakeSetupDialog::doBinaryBrowse() } } +void CMakeSetupDialog::doBinaryOpen() +{ + QString bindir = this->BinaryDirectory->currentText(); + if(!bindir.isEmpty()) + { + QDesktopServices::openUrl(QUrl("file:/"+bindir, QUrl::TolerantMode)); + } +} + void CMakeSetupDialog::setBinaryDirectory(const QString& dir) { this->BinaryDirectory->setEditText(dir); @@ -530,6 +674,8 @@ void CMakeSetupDialog::onBinaryDirectoryChanged(const QString& dir) this->Output->clear(); QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(), "setBinaryDirectory", Qt::QueuedConnection, Q_ARG(QString, dir)); + + this->enterState(ReadyConfigure); } void CMakeSetupDialog::setSourceDirectory(const QString& dir) @@ -556,7 +702,7 @@ void CMakeSetupDialog::message(const QString& msg) void CMakeSetupDialog::setEnabledState(bool enabled) { - // disable parts of the GUI during configure/generate + // disable parts of the GUI during configure/generate/build/install/clean this->CacheValues->cacheModel()->setEditEnabled(enabled); this->SourceDirectory->setEnabled(enabled); this->BrowseSourceDirectoryButton->setEnabled(enabled); @@ -566,6 +712,13 @@ void CMakeSetupDialog::setEnabledState(bool enabled) this->DeleteCacheAction->setEnabled(enabled); this->ExitAction->setEnabled(enabled); this->ConfigureAction->setEnabled(enabled); + this->ActionButton->setEnabled(true); + this->GenerateAction->setEnabled(false); + this->BuildAction->setEnabled(false); + this->InstallAction->setEnabled(false); + this->CleanAction->setEnabled(false); + this->StopAction->setVisible(false); + this->StopAction->setEnabled(false); this->AddEntry->setEnabled(enabled); this->RemoveEntry->setEnabled(false); // let selection re-enable it } @@ -744,7 +897,9 @@ void CMakeSetupDialog::addBinaryPath(const QString& path) void CMakeSetupDialog::dragEnterEvent(QDragEnterEvent* e) { if(!(this->CurrentState == ReadyConfigure || - this->CurrentState == ReadyGenerate)) + this->CurrentState == ReadyGenerate || + this->CurrentState == ReadyBuild || + this->CurrentState == ReadyInstall)) { e->ignore(); return; @@ -768,7 +923,9 @@ void CMakeSetupDialog::dragEnterEvent(QDragEnterEvent* e) void CMakeSetupDialog::dropEvent(QDropEvent* e) { if(!(this->CurrentState == ReadyConfigure || - this->CurrentState == ReadyGenerate)) + this->CurrentState == ReadyGenerate || + this->CurrentState == ReadyBuild || + this->CurrentState == ReadyInstall)) { return; } @@ -854,7 +1011,9 @@ void CMakeSetupDialog::selectionChanged() QModelIndexList idxs = this->CacheValues->selectionModel()->selectedRows(); if(idxs.count() && (this->CurrentState == ReadyConfigure || - this->CurrentState == ReadyGenerate) ) + this->CurrentState == ReadyGenerate || + this->CurrentState == ReadyBuild || + this->CurrentState == ReadyInstall) ) { this->RemoveEntry->setEnabled(true); } @@ -875,44 +1034,117 @@ void CMakeSetupDialog::enterState(CMakeSetupDialog::State s) if(s == Interrupting) { - this->ConfigureButton->setEnabled(false); - this->GenerateButton->setEnabled(false); + this->ActionButton->setEnabled(false); } - else if(s == Configuring) + else if(s == Configuring || + s == Generating) { this->Output->clear(); this->setEnabledState(false); - this->GenerateButton->setEnabled(false); - this->GenerateAction->setEnabled(false); - this->ConfigureButton->setText(tr("&Stop")); + this->StopAction->setEnabled(true); + this->StopAction->setVisible(true); + this->setDefaultAction(this->StopAction); } - else if(s == Generating) + else if(s == Building || + s == Installing || + s == Cleaning) { - this->CacheModified = false; + this->Output->clear(); this->setEnabledState(false); - this->ConfigureButton->setEnabled(false); - this->GenerateAction->setEnabled(false); - this->GenerateButton->setText(tr("&Stop")); + this->ActionButton->setEnabled(false); } else if(s == ReadyConfigure) { this->ProgressBar->reset(); this->setEnabledState(true); - this->GenerateButton->setEnabled(false); - this->GenerateAction->setEnabled(false); - this->ConfigureButton->setEnabled(true); - this->ConfigureButton->setText(tr("&Configure")); - this->GenerateButton->setText(tr("&Generate")); + this->setDefaultAction(this->ConfigureAction); } else if(s == ReadyGenerate) { this->ProgressBar->reset(); this->setEnabledState(true); - this->GenerateButton->setEnabled(true); this->GenerateAction->setEnabled(true); - this->ConfigureButton->setEnabled(true); - this->ConfigureButton->setText(tr("&Configure")); - this->GenerateButton->setText(tr("&Generate")); + this->CleanAction->setEnabled(false); + this->setDefaultAction(this->GenerateAction); + } + else if(s == ReadyBuild) + { + this->ProgressBar->reset(); + this->setEnabledState(true); + this->GenerateAction->setEnabled(true); + this->BuildAction->setEnabled(true); + this->CleanAction->setEnabled(true); + this->setDefaultAction(this->BuildAction); + } + else if(s == ReadyInstall) + { + this->ProgressBar->reset(); + this->setEnabledState(true); + this->GenerateAction->setEnabled(true); + this->BuildAction->setEnabled(true); + this->InstallAction->setEnabled(true); + this->CleanAction->setEnabled(true); + this->setDefaultAction(this->InstallAction); + } +} + +void CMakeSetupDialog::setDefaultAction(QAction* action) +{ + this->DefaultAction = action; + this->ActionButton->setText(action->text()); +} + +void CMakeSetupDialog::manageConfigs(const QStringList& configs) +{ + // always rebuild menu and combobox from scratch (easier...) + // first check that we have in fact a multi-config generator + if(configs.length() > 0) + { + // figure out what the index of the current configuration is in the new set + int curIdx = -1; + if(!this->CurrentConfiguration.isEmpty()) + { + curIdx = configs.indexOf(this->CurrentConfiguration); + } + if(curIdx < 0) + { + this->CurrentConfiguration = ""; + curIdx = 0; + } + // now clear out the menu, the action group and the combo box + this->ActiveConfigurationMenu->clear(); + this->ActiveConfigurationPopup->clear(); + this->ConfigurationActions.clear(); + // this also deletes the actions (they are children of the old ActionGroup)! + this->ConfigurationActionsGroup = QSharedPointer<QActionGroup>(new QActionGroup(NULL)); + this->ConfigurationActionsGroup->setExclusive(true); + // now populate + size_t i = 0; + foreach(QString c, configs) + { + QAction* act = new QAction(c, this->ConfigurationActionsGroup.data()); + act->setCheckable(true); + this->ConfigurationActionsGroup->addAction(act); + this->ConfigurationActions.append(act); + this->ActiveConfigurationMenu->addAction(act); + this->ActiveConfigurationPopup->addItem(c); + QObject::connect(act, SIGNAL(triggered(bool)), this, SLOT(activeConfigurationChanged(bool))); + ++i; + } + this->ConfigurationActions[curIdx]->setChecked(true); + this->ActiveConfigurationMenu->setEnabled(true); + this->ActiveConfigurationPopup->setCurrentIndex(curIdx); + QObject::connect(this->ActiveConfigurationPopup, SIGNAL(currentIndexChanged(int)), + this, SLOT(activeConfigurationChanged(int))); + this->ActiveConfigurationPopup->setVisible(true); + } + else + { + // nope, disable + this->ActiveConfigurationMenu->setEnabled(false); + this->ActiveConfigurationPopup->clear(); + this->ActiveConfigurationPopup->setVisible(false); + this->CurrentConfiguration = ""; } } @@ -1048,4 +1280,26 @@ void CMakeSetupDialog::setSearchFilter(const QString& str) this->CacheValues->setSearchFilter(str); } +void CMakeSetupDialog::activeConfigurationChanged(bool checked) +{ + if(checked) + { + for(int i=0; i < this->ConfigurationActions.length(); ++i) + if(this->ConfigurationActions[i]->isChecked()) + { + this->activeConfigurationChanged(i); + break; + } + } +} + +void CMakeSetupDialog::activeConfigurationChanged(int index) +{ + if(index > -1) + { + this->ConfigurationActions[index]->setChecked(true); + this->ActiveConfigurationPopup->setCurrentIndex(index); + this->CurrentConfiguration = this->ActiveConfigurationPopup->currentText(); + } +} diff --git a/Source/QtDialog/CMakeSetupDialog.h b/Source/QtDialog/CMakeSetupDialog.h index de7922a..700b8c1 100644 --- a/Source/QtDialog/CMakeSetupDialog.h +++ b/Source/QtDialog/CMakeSetupDialog.h @@ -16,6 +16,7 @@ #include "QCMake.h" #include <QMainWindow> #include <QThread> +#include <QSharedPointer> #include "ui_CMakeSetupDialog.h" class QCMakeThread; @@ -37,19 +38,28 @@ public slots: protected slots: void initialize(); + void doDefaultAction(); void doConfigure(); void doGenerate(); + void doBuild(); + void doInstall(); + void doClean(); void doInstallForCommandLine(); void doHelp(); void doAbout(); void doInterrupt(); void finishConfigure(int error); void finishGenerate(int error); + void finishBuild(int error); + void finishInstall(int error); + void finishClean(int error); void error(const QString& message); void message(const QString& message); void doSourceBrowse(); + void doSourceOpen(); void doBinaryBrowse(); + void doBinaryOpen(); void doReloadCache(); void doDeleteCache(); void updateSourceDirectory(const QString& dir); @@ -73,11 +83,16 @@ protected slots: void setViewType(int); void showUserChanges(); void setSearchFilter(const QString& str); + void activeConfigurationChanged(bool checked); + void activeConfigurationChanged(int index); protected: - enum State { Interrupting, ReadyConfigure, ReadyGenerate, Configuring, Generating }; + enum State { Interrupting, ReadyConfigure, ReadyGenerate, ReadyBuild, + ReadyInstall, Configuring, Generating, Building, Installing, Cleaning }; void enterState(State s); + void setDefaultAction(QAction* action); + void manageConfigs(const QStringList& configs); void closeEvent(QCloseEvent*); void dragEnterEvent(QDragEnterEvent*); @@ -88,12 +103,23 @@ protected: bool CacheModified; QAction* ReloadCacheAction; QAction* DeleteCacheAction; + QAction* OpenSourceDirectoryAction; + QAction* OpenBuildDirectoryAction; QAction* ExitAction; QAction* ConfigureAction; + QAction* StopAction; QAction* GenerateAction; + QAction* BuildAction; + QAction* CleanAction; + QAction* InstallAction; + QAction* DefaultAction; + QMenu* ActiveConfigurationMenu; + QSharedPointer<QActionGroup> ConfigurationActionsGroup; QAction* SuppressDevWarningsAction; QAction* InstallForCommandLineAction; State CurrentState; + QList<QAction*> ConfigurationActions; + QString CurrentConfiguration; QTextCharFormat ErrorFormat; QTextCharFormat MessageFormat; diff --git a/Source/QtDialog/CMakeSetupDialog.ui b/Source/QtDialog/CMakeSetupDialog.ui index ae0dca2..2738304 100644 --- a/Source/QtDialog/CMakeSetupDialog.ui +++ b/Source/QtDialog/CMakeSetupDialog.ui @@ -37,7 +37,7 @@ <item row="0" column="2" > <widget class="QPushButton" name="BrowseSourceDirectoryButton" > <property name="text" > - <string>Browse &Source...</string> + <string>Choose &Source...</string> </property> </widget> </item> @@ -64,7 +64,35 @@ <item row="1" column="2" > <widget class="QPushButton" name="BrowseBinaryDirectoryButton" > <property name="text" > - <string>Browse &Build...</string> + <string>Choose &Build...</string> + </property> + </widget> + </item> + <item row="0" column="3"> + <widget class="QToolButton" name="OpenSourceDirectoryButton"> + <property name="toolTip"> + <string>Open Source Directory</string> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset resource="CMakeSetup.qrc"> + <normaloff>:/Icons/emblem-symbolic-link.png</normaloff>:/Icons/emblem-symbolic-link.png</iconset> + </property> + </widget> + </item> + <item row="1" column="3"> + <widget class="QToolButton" name="OpenBinaryDirectoryButton"> + <property name="toolTip"> + <string>Open Build Directory</string> + </property> + <property name="text"> + <string>...</string> + </property> + <property name="icon"> + <iconset resource="CMakeSetup.qrc"> + <normaloff>:/Icons/emblem-symbolic-link.png</normaloff>:/Icons/emblem-symbolic-link.png</iconset> </property> </widget> </item> @@ -228,18 +256,14 @@ <number>6</number> </property> <item> - <widget class="QPushButton" name="ConfigureButton" > - <property name="text" > - <string>&Configure</string> + <widget class="QPushButton" name="ActionButton"> + <property name="text"> + <string/> </property> </widget> </item> <item> - <widget class="QPushButton" name="GenerateButton" > - <property name="text" > - <string>&Generate</string> - </property> - </widget> + <widget class="QComboBox" name="ActiveConfigurationPopup"/> </item> <item> <widget class="QLabel" name="Generator" > diff --git a/Source/QtDialog/QCMake.cxx b/Source/QtDialog/QCMake.cxx index dc31fad..9905e24 100644 --- a/Source/QtDialog/QCMake.cxx +++ b/Source/QtDialog/QCMake.cxx @@ -193,7 +193,73 @@ void QCMake::generate() emit this->generateDone(err); } - + +void QCMake::build(QString config) +{ +#ifdef Q_OS_WIN + UINT lastErrorMode = SetErrorMode(0); +#endif + + cmSystemTools::ResetErrorOccuredFlag(); + int err = this->CMakeInstance->Build( + this->CMakeInstance->GetStartOutputDirectory(), + "", + config.toAscii().data(), + std::vector<std::string>(), + false + ); + +#ifdef Q_OS_WIN + SetErrorMode(lastErrorMode); +#endif + + emit this->buildDone(err); +} + +void QCMake::install(QString config) +{ +#ifdef Q_OS_WIN + UINT lastErrorMode = SetErrorMode(0); +#endif + + cmSystemTools::ResetErrorOccuredFlag(); + int err = this->CMakeInstance->Build( + this->CMakeInstance->GetStartOutputDirectory(), + "install", + config.toAscii().data(), + std::vector<std::string>(), + false + ); + +#ifdef Q_OS_WIN + SetErrorMode(lastErrorMode); +#endif + + emit this->installDone(err); +} + +void QCMake::clean(QString config) +{ +#ifdef Q_OS_WIN + UINT lastErrorMode = SetErrorMode(0); +#endif + + cmSystemTools::ResetErrorOccuredFlag(); + int err = this->CMakeInstance->Build( + this->CMakeInstance->GetStartOutputDirectory(), + "clean", + config.toAscii().data(), + std::vector<std::string>(), + false + ); + +#ifdef Q_OS_WIN + SetErrorMode(lastErrorMode); +#endif + + emit this->cleanDone(err); +} + void QCMake::setProperties(const QCMakePropertyList& newProps) { QCMakePropertyList props = newProps; diff --git a/Source/QtDialog/QCMake.h b/Source/QtDialog/QCMake.h index bbfb3d7..2ddc132 100644 --- a/Source/QtDialog/QCMake.h +++ b/Source/QtDialog/QCMake.h @@ -76,9 +76,15 @@ public slots: void configure(); /// generate the files void generate(); + /// build the project + void build(QString config); + /// install the project + void install(QString config); + /// clean the project + void clean(QString config); /// set the property values void setProperties(const QCMakePropertyList&); - /// interrupt the configure or generate process + /// interrupt the configure, generate, build or install process void interrupt(); /// delete the cache in binary directory void deleteCache(); @@ -119,6 +125,12 @@ signals: void configureDone(int error); /// signal when generate is done void generateDone(int error); + /// signal when build is done + void buildDone(int error); + /// signal when install is done + void installDone(int error); + /// signal when clean is done + void cleanDone(int error); /// signal when there is an output message void outputMessage(const QString& msg); /// signal when there is an error message diff --git a/Source/QtDialog/emblem-symbolic-link.png b/Source/QtDialog/emblem-symbolic-link.png new file mode 100644 index 0000000000000000000000000000000000000000..7e53be23d15a3289c5856e67497929e92c28886f GIT binary patch literal 724 zcmV;_0xSKAP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00004b3#c}2nYxW zd<bNS00009a7bBm000F3000F30lb7RF#rGn8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H1026dYSaeirbZlh+Q+aJ-Z)|B}AWUg)Yaa;2<NyEw6m&&cbU}4= zXm4@=N?~htVjxp=Wod3@a_0N@F8}}lA#_DpbW?A2a${uxXmoUNIxjD3X>Dy`V=irV zb7^B}VQg$JV|r<3<6Zy&0p&?VK~y-6eUr_O5<wJ&&#i7mz;?*+BR_+K31(*8PF#}k z68Z+bhAw#kmn_}L3$SA^3!O2H3wMTQOl<oPt7B%Msq4b&wj0n&Zn_rte0A!cE@Ebk z$K#isPUp1{qDn;g-(qF}2o{UQ+g`8tiiX1>b-Ud!hG9HqW)Knn1!3m60LaN?a;gKU z=JWYF00<#cfFvj>W@cowS)))WywLyj5hca@T-==sW`<HK>e2M*q<w%0k`zaKAp`(m z2OxyN&Fu|LbB-X8$Yk!}?DQFQT}K#(u&nQ>Rv*Pz)FX?LSSf{ziw}@e#`bOd2To4T zFrR<L!NUX8YW3KiNQ#9JxcWRptyahR`E#^dk3mFOuh*E(K4H0Bg0==7UxA3g%&3$r zXt$q$h;Y<ALb+VQ<>kk}$t2Q>9Ym1TYK7zDcI<|kaeckmi8fiO8RT-g)G(#|iDsjT zM!f;06qp$g3?mhB3`j;3(GL$xD3!`kN@26vq`t8RK*-FSeLu~1_S-$o{7dxvec!UI zZ<?m1nw3&<ZM^{y5q#hGr_<>>4Z!^%2<&39*b2jtJkOJ^>q^IQq;1>Mwr%M+j&xmD zdY&ge&y%KUE=Hr#o55i4p6*^?7C;d|X2<LPwFclb^X+fco(EIauqmYg0000<MNUMn GLSTaT_C7WM literal 0 HcmV?d00001 -- 1.6.5.2 0001-ENH-Improve-cmake-gui-v2.patch [^] (37,410 bytes) 2010-01-19 02:26 [Show Content] [Hide Content] From f220ffe7cd4bb68f29ba619e88b22fb9b9144707 Mon Sep 17 00:00:00 2001 From: Michael Wild <themiwi@users.sourceforge.net> Date: Fri, 15 Jan 2010 17:49:40 +0100 Subject: [PATCH] ENH: Improve cmake-gui * Add menu-entries/buttons to open source and binary directories in file browser. * Replace "Configure" and "Generate" buttons with a single button with a delayed popup-menu, alowing one to configure, generate, build, install and clean the project. * Read the cache to find CMAKE_CONFIGURATION_TYPES and offer the available build types in the menu and a combo-box next to the "action" button. Requires, however, that all available build types are stored in the cache. * TODO: For IDE-projects add a button to open the project file. * TODO: Somehow capture stdout/stderr when building/installing/cleaning and display in output widget. * TODO: Parse output from Makefile-builds and drive progress-bar instead of the generic "busy" display. Signed-off-by: Michael Wild <themiwi@users.sourceforge.net> --- Source/QtDialog/ActionButtonEventFilter.cxx | 72 +++++ Source/QtDialog/ActionButtonEventFilter.h | 53 ++++ Source/QtDialog/CMakeLists.txt | 2 + Source/QtDialog/CMakeSetup.qrc | 1 + Source/QtDialog/CMakeSetupDialog.cxx | 412 +++++++++++++++++++++++---- Source/QtDialog/CMakeSetupDialog.h | 28 ++- Source/QtDialog/CMakeSetupDialog.ui | 44 +++- Source/QtDialog/QCMake.cxx | 68 +++++- Source/QtDialog/QCMake.h | 14 +- Source/QtDialog/emblem-symbolic-link.png | Bin 0 -> 724 bytes 10 files changed, 623 insertions(+), 71 deletions(-) create mode 100644 Source/QtDialog/ActionButtonEventFilter.cxx create mode 100644 Source/QtDialog/ActionButtonEventFilter.h create mode 100644 Source/QtDialog/emblem-symbolic-link.png diff --git a/Source/QtDialog/ActionButtonEventFilter.cxx b/Source/QtDialog/ActionButtonEventFilter.cxx new file mode 100644 index 0000000..a35a2e2 --- /dev/null +++ b/Source/QtDialog/ActionButtonEventFilter.cxx @@ -0,0 +1,72 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2009-2009 Michael Wild <themiwi@users.sourceforge.net> + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "ActionButtonEventFilter.h" +#include <QWidget> +#include <QStyle> +#include <QMouseEvent> + +ActionButtonEventFilter::ActionButtonEventFilter(QWidget* parent) + : QObject(parent) + , Delay(600) +{ + this->Timer = new QTimer(this); + this->Timer->setSingleShot(true); + // chain the timer's timeout() signal to our showMenu() signal + QObject::connect(this->Timer, SIGNAL(timeout()), this, SIGNAL(showMenu())); + if(parent) + { + // use the style-sheet defined popup-delay + this->setDelay(parent->style()->styleHint( + QStyle::SH_ToolButton_PopupDelay, 0, parent)); + } +} + +int ActionButtonEventFilter::getDelay() const +{ + return this->Delay; +} + +void ActionButtonEventFilter::setDelay(int delay) +{ + this->Delay = delay; +} + +bool ActionButtonEventFilter::eventFilter(QObject *obj, QEvent *event) +{ + // only handle mouse-button press and release + if(event->type() == QEvent::MouseButtonPress || + event->type() == QEvent::MouseButtonRelease) + { + QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event); + // only handle the left mouse-button + if(mouseEvent->button() == Qt::LeftButton) + { + if(mouseEvent->type() == QEvent::MouseButtonPress) + { + // on press, start timer and eat event + this->Timer->start(this->Delay); + return true; + } + else if(this->Timer->isActive()) + { + // on release, stop timer, fire default action and eat event + this->Timer->stop(); + emit this->activateDefaultAction(); + return true; + } + } + } + // normal processing + return QObject::eventFilter(obj, event); +} diff --git a/Source/QtDialog/ActionButtonEventFilter.h b/Source/QtDialog/ActionButtonEventFilter.h new file mode 100644 index 0000000..7c546ba --- /dev/null +++ b/Source/QtDialog/ActionButtonEventFilter.h @@ -0,0 +1,53 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2009-2009 Michael Wild <themiwi@users.sourceforge.net> + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef ActionButtonEventFilter_h +#define ActionButtonEventFilter_h + +#include <QEvent> +#include <QTimer> + +class QWidget; + +/// Event filter to create QToolButton::DelayedPopupMode behavior for a +/// QPushButton +class ActionButtonEventFilter : public QObject +{ + Q_OBJECT + + /// Popup-delay, defaults to the style-defined delay of the parent if not + /// NULL, 600ms otherwise. + Q_PROPERTY(int Delay READ getDelay WRITE setDelay) + +public: + ActionButtonEventFilter(QWidget* parent); + + int getDelay() const; + void setDelay(int delay); + +signals: + /// Emitted when the default action should be used + void activateDefaultAction(); + /// Emitted when the button menu should be displayed + void showMenu(); + +protected: + bool eventFilter(QObject *obj, QEvent *event); + +private: + int Delay; + QTimer* Timer; + +}; + +#endif // ActionButtonEventFilter_h diff --git a/Source/QtDialog/CMakeLists.txt b/Source/QtDialog/CMakeLists.txt index 4e7e357..d0a3769 100644 --- a/Source/QtDialog/CMakeLists.txt +++ b/Source/QtDialog/CMakeLists.txt @@ -27,6 +27,7 @@ ELSE(NOT QT4_FOUND) "WARNING: QtDialog requires a static built qt for installation.") ENDIF(WIN32 AND NOT QT_CONFIG MATCHES "static") SET(SRCS + ActionButtonEventFilter.cxx AddCacheEntry.cxx AddCacheEntry.h CMakeSetup.cxx @@ -51,6 +52,7 @@ ELSE(NOT QT4_FOUND) MacInstallDialog.ui ) QT4_WRAP_CPP(MOC_SRCS + ActionButtonEventFilter.h AddCacheEntry.h Compilers.h CMakeSetupDialog.h diff --git a/Source/QtDialog/CMakeSetup.qrc b/Source/QtDialog/CMakeSetup.qrc index 5ceb1df..fd21ab3 100644 --- a/Source/QtDialog/CMakeSetup.qrc +++ b/Source/QtDialog/CMakeSetup.qrc @@ -1,5 +1,6 @@ <RCC> <qresource prefix="/Icons" > + <file>emblem-symbolic-link.png</file> <file>CMakeSetup.png</file> <file>Delete16.png</file> <file>Plus16.png</file> diff --git a/Source/QtDialog/CMakeSetupDialog.cxx b/Source/QtDialog/CMakeSetupDialog.cxx index eb82f2a..588192f 100644 --- a/Source/QtDialog/CMakeSetupDialog.cxx +++ b/Source/QtDialog/CMakeSetupDialog.cxx @@ -26,12 +26,14 @@ #include <QMimeData> #include <QUrl> #include <QShortcut> +#include <QDesktopServices> #include <QMacInstallDialog.h> #include "QCMake.h" #include "QCMakeCacheView.h" #include "AddCacheEntry.h" #include "FirstConfigure.h" +#include "ActionButtonEventFilter.h" #include "cmVersion.h" QCMakeThread::QCMakeThread(QObject* p) @@ -55,7 +57,8 @@ void QCMakeThread::run() } CMakeSetupDialog::CMakeSetupDialog() - : ExitAfterGenerate(true), CacheModified(false), CurrentState(Interrupting) + : ExitAfterGenerate(true), CacheModified(false), CurrentState(Interrupting), + CurrentConfiguration() { QString title = QString(tr("CMake %1")); title = title.arg(cmVersion::GetCMakeVersion()); @@ -94,6 +97,16 @@ CMakeSetupDialog::CMakeSetupDialog() this->DeleteCacheAction = FileMenu->addAction(tr("&Delete Cache")); QObject::connect(this->DeleteCacheAction, SIGNAL(triggered(bool)), this, SLOT(doDeleteCache())); + FileMenu->addSeparator(); + this->OpenSourceDirectoryAction = FileMenu->addAction(tr("Open &Source Directory")); + QObject::connect(this->OpenSourceDirectoryAction, SIGNAL(triggered(bool)), + this, SLOT(doSourceOpen())); + this->OpenBuildDirectoryAction = FileMenu->addAction(tr("Open &Build Directory")); + QObject::connect(this->OpenBuildDirectoryAction, SIGNAL(triggered(bool)), + this, SLOT(doBinaryOpen())); +#if !defined(Q_WS_MAC) + FileMenu->addSeparator(); +#endif this->ExitAction = FileMenu->addAction(tr("E&xit")); QObject::connect(this->ExitAction, SIGNAL(triggered(bool)), this, SLOT(close())); @@ -104,9 +117,32 @@ CMakeSetupDialog::CMakeSetupDialog() this->ConfigureAction->setMenuRole(QAction::NoRole); QObject::connect(this->ConfigureAction, SIGNAL(triggered(bool)), this, SLOT(doConfigure())); + this->StopAction = ToolsMenu->addAction(tr("&Stop")); + this->StopAction->setEnabled(false); + this->StopAction->setVisible(false); + QObject::connect(this->StopAction, SIGNAL(triggered(bool)), + this, SLOT(doInterrupt())); this->GenerateAction = ToolsMenu->addAction(tr("&Generate")); QObject::connect(this->GenerateAction, SIGNAL(triggered(bool)), this, SLOT(doGenerate())); + ToolsMenu->addSeparator(); + this->BuildAction = ToolsMenu->addAction(tr("&Build Project")); + QObject::connect(this->BuildAction, SIGNAL(triggered(bool)), + this, SLOT(doBuild())); + this->InstallAction = ToolsMenu->addAction(tr("&Install Project")); + QObject::connect(this->InstallAction, SIGNAL(triggered(bool)), + this, SLOT(doInstall())); + this->CleanAction = ToolsMenu->addAction(tr("&Clean Project")); + QObject::connect(this->CleanAction, SIGNAL(triggered(bool)), + this, SLOT(doClean())); + ToolsMenu->addSeparator(); + this->ActiveConfigurationMenu = ToolsMenu->addMenu(tr("Active Configuration")); +#if !(defined(QT_MAC_USE_COCOA) && QT_VERSION == 0x040503) + // Don't disable the menu when using Qt-Cocoa 4.5.3: + // http://bugreports.qt.nokia.com/browse/QTBUG-5313 + this->ActiveConfigurationMenu->setEnabled(false); +#endif + ToolsMenu->addSeparator(); QAction* showChangesAction = ToolsMenu->addAction(tr("&Show My Changes")); QObject::connect(showChangesAction, SIGNAL(triggered(bool)), this, SLOT(showUserChanges())); @@ -159,6 +195,25 @@ CMakeSetupDialog::CMakeSetupDialog() this->Output->setFont(outputFont); this->ErrorFormat.setForeground(QBrush(Qt::red)); + // set up the action-button + QMenu* ActionMenu = new QMenu(this->ActionButton); + ActionMenu->addAction(this->ConfigureAction); + ActionMenu->addAction(this->StopAction); + ActionMenu->addAction(this->GenerateAction); + ActionMenu->addAction(this->BuildAction); + ActionMenu->addAction(this->InstallAction); + ActionMenu->addAction(this->CleanAction); + this->ActionButton->setMenu(ActionMenu); + ActionButtonEventFilter* actionEventFilter = new ActionButtonEventFilter(this->ActionButton); + QObject::connect(actionEventFilter, SIGNAL(activateDefaultAction()), + this, SLOT(doDefaultAction())); + QObject::connect(actionEventFilter, SIGNAL(showMenu()), + this->ActionButton, SLOT(showMenu())); + this->ActionButton->installEventFilter(actionEventFilter); + this->setDefaultAction(this->ConfigureAction); + + this->ActiveConfigurationPopup->setVisible(false); + // start the cmake worker thread this->CMakeThread = new QCMakeThread(this); QObject::connect(this->CMakeThread, SIGNAL(cmakeInitialized()), @@ -176,22 +231,30 @@ void CMakeSetupDialog::initialize() this->CacheValues->cacheModel(), SLOT(setProperties(const QCMakePropertyList&))); - QObject::connect(this->ConfigureButton, SIGNAL(clicked(bool)), - this, SLOT(doConfigure())); QObject::connect(this->CMakeThread->cmakeInstance(), SIGNAL(configureDone(int)), this, SLOT(finishConfigure(int))); QObject::connect(this->CMakeThread->cmakeInstance(), SIGNAL(generateDone(int)), this, SLOT(finishGenerate(int))); - - QObject::connect(this->GenerateButton, SIGNAL(clicked(bool)), - this, SLOT(doGenerate())); + QObject::connect(this->CMakeThread->cmakeInstance(), + SIGNAL(buildDone(int)), + this, SLOT(finishBuild(int))); + QObject::connect(this->CMakeThread->cmakeInstance(), + SIGNAL(installDone(int)), + this, SLOT(finishInstall(int))); + QObject::connect(this->CMakeThread->cmakeInstance(), + SIGNAL(cleanDone(int)), + this, SLOT(finishClean(int))); QObject::connect(this->BrowseSourceDirectoryButton, SIGNAL(clicked(bool)), this, SLOT(doSourceBrowse())); + QObject::connect(this->OpenSourceDirectoryButton, SIGNAL(clicked(bool)), + this, SLOT(doSourceOpen())); QObject::connect(this->BrowseBinaryDirectoryButton, SIGNAL(clicked(bool)), this, SLOT(doBinaryBrowse())); + QObject::connect(this->OpenBinaryDirectoryButton, SIGNAL(clicked(bool)), + this, SLOT(doBinaryOpen())); QObject::connect(this->BinaryDirectory, SIGNAL(editTextChanged(QString)), this, SLOT(onBinaryDirectoryChanged(QString))); @@ -266,16 +329,20 @@ CMakeSetupDialog::~CMakeSetupDialog() this->CMakeThread->quit(); this->CMakeThread->wait(2000); } - -void CMakeSetupDialog::doConfigure() + +void CMakeSetupDialog::doDefaultAction() { - if(this->CurrentState == Configuring) + // doing it this way so we don't have to disconnect/connect all the time when + // changing the default action. also prevent the action from triggering if it + // is disabled. + if(this->DefaultAction->isEnabled()) { - // stop configure - doInterrupt(); - return; + this->DefaultAction->trigger(); } +} +void CMakeSetupDialog::doConfigure() +{ // make sure build directory exists QString bindir = this->CMakeThread->cmakeInstance()->binaryDirectory(); QDir dir(bindir); @@ -349,15 +416,67 @@ void CMakeSetupDialog::finishConfigure(int err) void CMakeSetupDialog::finishGenerate(int err) { - this->enterState(ReadyConfigure); - if(err != 0) + if(err == 0) + { + this->enterState(ReadyBuild); + } + else { + this->enterState(ReadyConfigure); QMessageBox::critical(this, tr("Error"), tr("Error in generation process, project files may be invalid"), QMessageBox::Ok); } } +void CMakeSetupDialog::finishBuild(int err) +{ + this->ProgressBar->setMaximum(100); + if(err == 0) + { + this->enterState(ReadyInstall); + } + else + { + this->enterState(ReadyConfigure); + QMessageBox::critical(this, tr("Error"), + tr("Error in build process"), + QMessageBox::Ok); + } +} + +void CMakeSetupDialog::finishInstall(int err) +{ + this->ProgressBar->setMaximum(100); + if(err == 0) + { + this->enterState(ReadyInstall); + } + else + { + this->enterState(ReadyConfigure); + QMessageBox::critical(this, tr("Error"), + tr("Error in installation process"), + QMessageBox::Ok); + } +} + +void CMakeSetupDialog::finishClean(int err) +{ + this->ProgressBar->setMaximum(100); + if(err == 0) + { + this->enterState(ReadyBuild); + } + else + { + this->enterState(ReadyConfigure); + QMessageBox::critical(this, tr("Error"), + tr("Error in cleaning process"), + QMessageBox::Ok); + } +} + void CMakeSetupDialog::doInstallForCommandLine() { QMacInstallDialog setupdialog(0); @@ -366,17 +485,35 @@ void CMakeSetupDialog::doInstallForCommandLine() void CMakeSetupDialog::doGenerate() { - if(this->CurrentState == Generating) - { - // stop generate - doInterrupt(); - return; - } this->enterState(Generating); QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(), "generate", Qt::QueuedConnection); } - + +void CMakeSetupDialog::doBuild() +{ + this->enterState(Building); + this->ProgressBar->setMaximum(0); + QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(), + "build", Qt::QueuedConnection, Q_ARG(QString, this->CurrentConfiguration)); +} + +void CMakeSetupDialog::doInstall() +{ + this->enterState(Installing); + this->ProgressBar->setMaximum(0); + QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(), + "install", Qt::QueuedConnection, Q_ARG(QString, this->CurrentConfiguration)); +} + +void CMakeSetupDialog::doClean() +{ + this->enterState(Cleaning); + this->ProgressBar->setMaximum(0); + QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(), + "clean", Qt::QueuedConnection, Q_ARG(QString, this->CurrentConfiguration)); +} + void CMakeSetupDialog::closeEvent(QCloseEvent* e) { // prompt for close if there are unsaved changes, and we're not busy @@ -475,6 +612,15 @@ void CMakeSetupDialog::doSourceBrowse() } } +void CMakeSetupDialog::doSourceOpen() +{ + QString srcdir = this->SourceDirectory->text(); + if(!srcdir.isEmpty()) + { + QDesktopServices::openUrl(QUrl("file:/"+srcdir, QUrl::TolerantMode)); + } +} + void CMakeSetupDialog::updateSourceDirectory(const QString& dir) { if(this->SourceDirectory->text() != dir) @@ -505,6 +651,15 @@ void CMakeSetupDialog::doBinaryBrowse() } } +void CMakeSetupDialog::doBinaryOpen() +{ + QString bindir = this->BinaryDirectory->currentText(); + if(!bindir.isEmpty()) + { + QDesktopServices::openUrl(QUrl("file:/"+bindir, QUrl::TolerantMode)); + } +} + void CMakeSetupDialog::setBinaryDirectory(const QString& dir) { this->BinaryDirectory->setEditText(dir); @@ -530,6 +685,8 @@ void CMakeSetupDialog::onBinaryDirectoryChanged(const QString& dir) this->Output->clear(); QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(), "setBinaryDirectory", Qt::QueuedConnection, Q_ARG(QString, dir)); + + this->enterState(ReadyConfigure); } void CMakeSetupDialog::setSourceDirectory(const QString& dir) @@ -556,7 +713,7 @@ void CMakeSetupDialog::message(const QString& msg) void CMakeSetupDialog::setEnabledState(bool enabled) { - // disable parts of the GUI during configure/generate + // disable parts of the GUI during configure/generate/build/install/clean this->CacheValues->cacheModel()->setEditEnabled(enabled); this->SourceDirectory->setEnabled(enabled); this->BrowseSourceDirectoryButton->setEnabled(enabled); @@ -566,8 +723,32 @@ void CMakeSetupDialog::setEnabledState(bool enabled) this->DeleteCacheAction->setEnabled(enabled); this->ExitAction->setEnabled(enabled); this->ConfigureAction->setEnabled(enabled); + this->ActionButton->setEnabled(true); + this->GenerateAction->setEnabled(false); + this->BuildAction->setEnabled(false); + this->InstallAction->setEnabled(false); + this->CleanAction->setEnabled(false); + this->StopAction->setVisible(false); + this->StopAction->setEnabled(false); this->AddEntry->setEnabled(enabled); this->RemoveEntry->setEnabled(false); // let selection re-enable it + if(enabled) + { + this->manageConfigs(); + } + else + { + this->ActiveConfigurationPopup->setEnabled(false); +#if !(defined(QT_MAC_USE_COCOA) && QT_VERSION == 0x040503) + // don't disable the menu when using Qt-Cocoa 4.5.3: + // http://bugreports.qt.nokia.com/browse/QTBUG-5313 + this->ActiveConfigurationMenu->setEnabled(false); +#else + // do it the dedious way: disable all child-actions + foreach(QAction* a, this->ActiveConfigurationMenu->actions()) + a->setEnabled(false); +#endif + } } bool CMakeSetupDialog::setupFirstConfigure() @@ -744,7 +925,9 @@ void CMakeSetupDialog::addBinaryPath(const QString& path) void CMakeSetupDialog::dragEnterEvent(QDragEnterEvent* e) { if(!(this->CurrentState == ReadyConfigure || - this->CurrentState == ReadyGenerate)) + this->CurrentState == ReadyGenerate || + this->CurrentState == ReadyBuild || + this->CurrentState == ReadyInstall)) { e->ignore(); return; @@ -768,7 +951,9 @@ void CMakeSetupDialog::dragEnterEvent(QDragEnterEvent* e) void CMakeSetupDialog::dropEvent(QDropEvent* e) { if(!(this->CurrentState == ReadyConfigure || - this->CurrentState == ReadyGenerate)) + this->CurrentState == ReadyGenerate || + this->CurrentState == ReadyBuild || + this->CurrentState == ReadyInstall)) { return; } @@ -854,7 +1039,9 @@ void CMakeSetupDialog::selectionChanged() QModelIndexList idxs = this->CacheValues->selectionModel()->selectedRows(); if(idxs.count() && (this->CurrentState == ReadyConfigure || - this->CurrentState == ReadyGenerate) ) + this->CurrentState == ReadyGenerate || + this->CurrentState == ReadyBuild || + this->CurrentState == ReadyInstall) ) { this->RemoveEntry->setEnabled(true); } @@ -873,46 +1060,133 @@ void CMakeSetupDialog::enterState(CMakeSetupDialog::State s) this->CurrentState = s; - if(s == Interrupting) + switch(s) { - this->ConfigureButton->setEnabled(false); - this->GenerateButton->setEnabled(false); + case Interrupting: + this->ActionButton->setEnabled(false); + break; + case Configuring: + case Generating: + this->Output->clear(); + this->setEnabledState(false); + this->StopAction->setEnabled(true); + this->StopAction->setVisible(true); + this->setDefaultAction(this->StopAction); + break; + case Building: + case Installing: + case Cleaning: + this->Output->clear(); + this->setEnabledState(false); + this->ActionButton->setEnabled(false); + break; + case ReadyConfigure: + this->ProgressBar->reset(); + this->setEnabledState(true); + this->setDefaultAction(this->ConfigureAction); + break; + case ReadyGenerate: + this->ProgressBar->reset(); + this->setEnabledState(true); + this->GenerateAction->setEnabled(true); + this->CleanAction->setEnabled(false); + this->setDefaultAction(this->GenerateAction); + break; + case ReadyBuild: + this->ProgressBar->reset(); + this->setEnabledState(true); + this->GenerateAction->setEnabled(true); + this->BuildAction->setEnabled(true); + this->CleanAction->setEnabled(true); + this->setDefaultAction(this->BuildAction); + break; + case ReadyInstall: + this->ProgressBar->reset(); + this->setEnabledState(true); + this->GenerateAction->setEnabled(true); + this->BuildAction->setEnabled(true); + this->InstallAction->setEnabled(true); + this->CleanAction->setEnabled(true); + this->setDefaultAction(this->InstallAction); + break; } - else if(s == Configuring) - { - this->Output->clear(); - this->setEnabledState(false); - this->GenerateButton->setEnabled(false); - this->GenerateAction->setEnabled(false); - this->ConfigureButton->setText(tr("&Stop")); - } - else if(s == Generating) +} + +void CMakeSetupDialog::setDefaultAction(QAction* action) +{ + this->DefaultAction = action; + this->ActionButton->setText(action->text()); +} + +void CMakeSetupDialog::manageConfigs() +{ + QCMakePropertyList props = this->CacheValues->cacheModel()->properties(); + QStringList configs; + foreach(QCMakeProperty p, props) { - this->CacheModified = false; - this->setEnabledState(false); - this->ConfigureButton->setEnabled(false); - this->GenerateAction->setEnabled(false); - this->GenerateButton->setText(tr("&Stop")); + if(p.Key == "CMAKE_CONFIGURATION_TYPES") + { + // p.Strings seems to be always empty... + configs = p.Value.toString().split(";"); + break; + } } - else if(s == ReadyConfigure) + // always rebuild menu and combobox from scratch (easier...) + // first check that we have in fact a multi-config generator + if(configs.length() > 0) { - this->ProgressBar->reset(); - this->setEnabledState(true); - this->GenerateButton->setEnabled(false); - this->GenerateAction->setEnabled(false); - this->ConfigureButton->setEnabled(true); - this->ConfigureButton->setText(tr("&Configure")); - this->GenerateButton->setText(tr("&Generate")); + // figure out what the index of the current configuration is in the new set + int curIdx = -1; + if(!this->CurrentConfiguration.isEmpty()) + { + curIdx = configs.indexOf(this->CurrentConfiguration); + } + if(curIdx < 0) + { + this->CurrentConfiguration = ""; + curIdx = 0; + } + // now clear out the menu, the action group and the combo box + this->ActiveConfigurationMenu->clear(); + this->ActiveConfigurationPopup->clear(); + this->ConfigurationActions.clear(); + // this also deletes the actions (they are children of the old ActionGroup)! + this->ConfigurationActionsGroup = QSharedPointer<QActionGroup>(new QActionGroup(NULL)); + this->ConfigurationActionsGroup->setExclusive(true); + // now populate + size_t i = 0; + foreach(QString c, configs) + { + QAction* act = new QAction(c, this->ConfigurationActionsGroup.data()); + act->setCheckable(true); + this->ConfigurationActionsGroup->addAction(act); + this->ConfigurationActions.append(act); + this->ActiveConfigurationMenu->addAction(act); + this->ActiveConfigurationPopup->addItem(c); + QObject::connect(act, SIGNAL(triggered(bool)), this, SLOT(activeConfigurationChanged(bool))); + ++i; + } + this->ConfigurationActions[curIdx]->setChecked(true); + this->ActiveConfigurationPopup->setCurrentIndex(curIdx); + QObject::connect(this->ActiveConfigurationPopup, SIGNAL(currentIndexChanged(int)), + this, SLOT(activeConfigurationChanged(int))); + this->ActiveConfigurationPopup->setVisible(true); + this->ActiveConfigurationPopup->setEnabled(true); + this->ActiveConfigurationMenu->setEnabled(true); } - else if(s == ReadyGenerate) + else { - this->ProgressBar->reset(); - this->setEnabledState(true); - this->GenerateButton->setEnabled(true); - this->GenerateAction->setEnabled(true); - this->ConfigureButton->setEnabled(true); - this->ConfigureButton->setText(tr("&Configure")); - this->GenerateButton->setText(tr("&Generate")); + // nope, disable + this->ActiveConfigurationPopup->clear(); + this->ActiveConfigurationMenu->clear(); + this->ActiveConfigurationPopup->setVisible(false); + this->ActiveConfigurationPopup->setEnabled(false); +#if !(defined(QT_MAC_USE_COCOA) && QT_VERSION == 0x040503) + // Don't disable the menu when using Qt-Cocoa 4.5.3: + // http://bugreports.qt.nokia.com/browse/QTBUG-5313 + this->ActiveConfigurationMenu->setEnabled(false); +#endif + this->CurrentConfiguration = ""; } } @@ -1048,4 +1322,26 @@ void CMakeSetupDialog::setSearchFilter(const QString& str) this->CacheValues->setSearchFilter(str); } +void CMakeSetupDialog::activeConfigurationChanged(bool checked) +{ + if(checked) + { + for(int i=0; i < this->ConfigurationActions.length(); ++i) + if(this->ConfigurationActions[i]->isChecked()) + { + this->activeConfigurationChanged(i); + break; + } + } +} + +void CMakeSetupDialog::activeConfigurationChanged(int index) +{ + if(index > -1) + { + this->ConfigurationActions[index]->setChecked(true); + this->ActiveConfigurationPopup->setCurrentIndex(index); + this->CurrentConfiguration = this->ActiveConfigurationPopup->currentText(); + } +} diff --git a/Source/QtDialog/CMakeSetupDialog.h b/Source/QtDialog/CMakeSetupDialog.h index de7922a..2f386d2 100644 --- a/Source/QtDialog/CMakeSetupDialog.h +++ b/Source/QtDialog/CMakeSetupDialog.h @@ -16,6 +16,7 @@ #include "QCMake.h" #include <QMainWindow> #include <QThread> +#include <QSharedPointer> #include "ui_CMakeSetupDialog.h" class QCMakeThread; @@ -37,19 +38,28 @@ public slots: protected slots: void initialize(); + void doDefaultAction(); void doConfigure(); void doGenerate(); + void doBuild(); + void doInstall(); + void doClean(); void doInstallForCommandLine(); void doHelp(); void doAbout(); void doInterrupt(); void finishConfigure(int error); void finishGenerate(int error); + void finishBuild(int error); + void finishInstall(int error); + void finishClean(int error); void error(const QString& message); void message(const QString& message); void doSourceBrowse(); + void doSourceOpen(); void doBinaryBrowse(); + void doBinaryOpen(); void doReloadCache(); void doDeleteCache(); void updateSourceDirectory(const QString& dir); @@ -73,11 +83,16 @@ protected slots: void setViewType(int); void showUserChanges(); void setSearchFilter(const QString& str); + void activeConfigurationChanged(bool checked); + void activeConfigurationChanged(int index); protected: - enum State { Interrupting, ReadyConfigure, ReadyGenerate, Configuring, Generating }; + enum State { Interrupting, ReadyConfigure, ReadyGenerate, ReadyBuild, + ReadyInstall, Configuring, Generating, Building, Installing, Cleaning }; void enterState(State s); + void setDefaultAction(QAction* action); + void manageConfigs(); void closeEvent(QCloseEvent*); void dragEnterEvent(QDragEnterEvent*); @@ -88,12 +103,23 @@ protected: bool CacheModified; QAction* ReloadCacheAction; QAction* DeleteCacheAction; + QAction* OpenSourceDirectoryAction; + QAction* OpenBuildDirectoryAction; QAction* ExitAction; QAction* ConfigureAction; + QAction* StopAction; QAction* GenerateAction; + QAction* BuildAction; + QAction* CleanAction; + QAction* InstallAction; + QAction* DefaultAction; + QMenu* ActiveConfigurationMenu; + QSharedPointer<QActionGroup> ConfigurationActionsGroup; QAction* SuppressDevWarningsAction; QAction* InstallForCommandLineAction; State CurrentState; + QList<QAction*> ConfigurationActions; + QString CurrentConfiguration; QTextCharFormat ErrorFormat; QTextCharFormat MessageFormat; diff --git a/Source/QtDialog/CMakeSetupDialog.ui b/Source/QtDialog/CMakeSetupDialog.ui index ae0dca2..2738304 100644 --- a/Source/QtDialog/CMakeSetupDialog.ui +++ b/Source/QtDialog/CMakeSetupDialog.ui @@ -37,7 +37,7 @@ <item row="0" column="2" > <widget class="QPushButton" name="BrowseSourceDirectoryButton" > <property name="text" > - <string>Browse &Source...</string> + <string>Choose &Source...</string> </property> </widget> </item> @@ -64,7 +64,35 @@ <item row="1" column="2" > <widget class="QPushButton" name="BrowseBinaryDirectoryButton" > <property name="text" > - <string>Browse &Build...</string> + <string>Choose &Build...</string> + </property> + </widget> + </item> + <item row="0" column="3"> + <widget class="QToolButton" name="OpenSourceDirectoryButton"> + <property name="toolTip"> + <string>Open Source Directory</string> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset resource="CMakeSetup.qrc"> + <normaloff>:/Icons/emblem-symbolic-link.png</normaloff>:/Icons/emblem-symbolic-link.png</iconset> + </property> + </widget> + </item> + <item row="1" column="3"> + <widget class="QToolButton" name="OpenBinaryDirectoryButton"> + <property name="toolTip"> + <string>Open Build Directory</string> + </property> + <property name="text"> + <string>...</string> + </property> + <property name="icon"> + <iconset resource="CMakeSetup.qrc"> + <normaloff>:/Icons/emblem-symbolic-link.png</normaloff>:/Icons/emblem-symbolic-link.png</iconset> </property> </widget> </item> @@ -228,18 +256,14 @@ <number>6</number> </property> <item> - <widget class="QPushButton" name="ConfigureButton" > - <property name="text" > - <string>&Configure</string> + <widget class="QPushButton" name="ActionButton"> + <property name="text"> + <string/> </property> </widget> </item> <item> - <widget class="QPushButton" name="GenerateButton" > - <property name="text" > - <string>&Generate</string> - </property> - </widget> + <widget class="QComboBox" name="ActiveConfigurationPopup"/> </item> <item> <widget class="QLabel" name="Generator" > diff --git a/Source/QtDialog/QCMake.cxx b/Source/QtDialog/QCMake.cxx index dc31fad..9905e24 100644 --- a/Source/QtDialog/QCMake.cxx +++ b/Source/QtDialog/QCMake.cxx @@ -193,7 +193,73 @@ void QCMake::generate() emit this->generateDone(err); } - + +void QCMake::build(QString config) +{ +#ifdef Q_OS_WIN + UINT lastErrorMode = SetErrorMode(0); +#endif + + cmSystemTools::ResetErrorOccuredFlag(); + int err = this->CMakeInstance->Build( + this->CMakeInstance->GetStartOutputDirectory(), + "", + config.toAscii().data(), + std::vector<std::string>(), + false + ); + +#ifdef Q_OS_WIN + SetErrorMode(lastErrorMode); +#endif + + emit this->buildDone(err); +} + +void QCMake::install(QString config) +{ +#ifdef Q_OS_WIN + UINT lastErrorMode = SetErrorMode(0); +#endif + + cmSystemTools::ResetErrorOccuredFlag(); + int err = this->CMakeInstance->Build( + this->CMakeInstance->GetStartOutputDirectory(), + "install", + config.toAscii().data(), + std::vector<std::string>(), + false + ); + +#ifdef Q_OS_WIN + SetErrorMode(lastErrorMode); +#endif + + emit this->installDone(err); +} + +void QCMake::clean(QString config) +{ +#ifdef Q_OS_WIN + UINT lastErrorMode = SetErrorMode(0); +#endif + + cmSystemTools::ResetErrorOccuredFlag(); + int err = this->CMakeInstance->Build( + this->CMakeInstance->GetStartOutputDirectory(), + "clean", + config.toAscii().data(), + std::vector<std::string>(), + false + ); + +#ifdef Q_OS_WIN + SetErrorMode(lastErrorMode); +#endif + + emit this->cleanDone(err); +} + void QCMake::setProperties(const QCMakePropertyList& newProps) { QCMakePropertyList props = newProps; diff --git a/Source/QtDialog/QCMake.h b/Source/QtDialog/QCMake.h index bbfb3d7..2ddc132 100644 --- a/Source/QtDialog/QCMake.h +++ b/Source/QtDialog/QCMake.h @@ -76,9 +76,15 @@ public slots: void configure(); /// generate the files void generate(); + /// build the project + void build(QString config); + /// install the project + void install(QString config); + /// clean the project + void clean(QString config); /// set the property values void setProperties(const QCMakePropertyList&); - /// interrupt the configure or generate process + /// interrupt the configure, generate, build or install process void interrupt(); /// delete the cache in binary directory void deleteCache(); @@ -119,6 +125,12 @@ signals: void configureDone(int error); /// signal when generate is done void generateDone(int error); + /// signal when build is done + void buildDone(int error); + /// signal when install is done + void installDone(int error); + /// signal when clean is done + void cleanDone(int error); /// signal when there is an output message void outputMessage(const QString& msg); /// signal when there is an error message diff --git a/Source/QtDialog/emblem-symbolic-link.png b/Source/QtDialog/emblem-symbolic-link.png new file mode 100644 index 0000000000000000000000000000000000000000..7e53be23d15a3289c5856e67497929e92c28886f GIT binary patch literal 724 zcmV;_0xSKAP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00004b3#c}2nYxW zd<bNS00009a7bBm000F3000F30lb7RF#rGn8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H1026dYSaeirbZlh+Q+aJ-Z)|B}AWUg)Yaa;2<NyEw6m&&cbU}4= zXm4@=N?~htVjxp=Wod3@a_0N@F8}}lA#_DpbW?A2a${uxXmoUNIxjD3X>Dy`V=irV zb7^B}VQg$JV|r<3<6Zy&0p&?VK~y-6eUr_O5<wJ&&#i7mz;?*+BR_+K31(*8PF#}k z68Z+bhAw#kmn_}L3$SA^3!O2H3wMTQOl<oPt7B%Msq4b&wj0n&Zn_rte0A!cE@Ebk z$K#isPUp1{qDn;g-(qF}2o{UQ+g`8tiiX1>b-Ud!hG9HqW)Knn1!3m60LaN?a;gKU z=JWYF00<#cfFvj>W@cowS)))WywLyj5hca@T-==sW`<HK>e2M*q<w%0k`zaKAp`(m z2OxyN&Fu|LbB-X8$Yk!}?DQFQT}K#(u&nQ>Rv*Pz)FX?LSSf{ziw}@e#`bOd2To4T zFrR<L!NUX8YW3KiNQ#9JxcWRptyahR`E#^dk3mFOuh*E(K4H0Bg0==7UxA3g%&3$r zXt$q$h;Y<ALb+VQ<>kk}$t2Q>9Ym1TYK7zDcI<|kaeckmi8fiO8RT-g)G(#|iDsjT zM!f;06qp$g3?mhB3`j;3(GL$xD3!`kN@26vq`t8RK*-FSeLu~1_S-$o{7dxvec!UI zZ<?m1nw3&<ZM^{y5q#hGr_<>>4Z!^%2<&39*b2jtJkOJ^>q^IQq;1>Mwr%M+j&xmD zdY&ge&y%KUE=Hr#o55i4p6*^?7C;d|X2<LPwFclb^X+fco(EIauqmYg0000<MNUMn GLSTaT_C7WM literal 0 HcmV?d00001 -- 1.6.5.2 0001-ENH-Improve-cmake-gui-v3.patch [^] (45,601 bytes) 2010-01-22 04:23 [Show Content] [Hide Content] From 85e4200c66155f65e5d27f00f1f7b8da3c52c815 Mon Sep 17 00:00:00 2001 From: Michael Wild <themiwi@users.sourceforge.net> Date: Fri, 15 Jan 2010 17:49:40 +0100 Subject: [PATCH] ENH: Improve cmake-gui * Add menu-entries/buttons to open source and binary directories in file browser. * Replace "Configure" and "Generate" buttons with a single button with a delayed popup-menu, alowing one to configure, generate, build, install and clean the project. * Read the cache to find CMAKE_CONFIGURATION_TYPES and offer the available build types in the menu and a combo-box next to the "action" button. Requires, however, that all available build types are stored in the cache. * Capture build/install/clean output, display it in the message window and parse progress for "Unix Makefiles" generators, driving the progress indicator. * The build/install/clean steps can also be interrupted. For this a new callback function to cmSystemTools to stop cmSystemTools::RunSingleCommand had to be added. For consistency it would be nice to implement this also for cmSystemTools::RunCommand, but for the functionality implemented here it is not necessary and since the UNIX implementation uses popen this can't be done easily (popen doesn't return the child process id). * TODO: For IDE-projects add a button to open the project file. * TODO: For non-"Unix Makefiles" generators find a way to infer the progress. Signed-off-by: Michael Wild <themiwi@users.sourceforge.net> --- Source/QtDialog/ActionButtonEventFilter.cxx | 72 +++++ Source/QtDialog/ActionButtonEventFilter.h | 53 ++++ Source/QtDialog/CMakeLists.txt | 2 + Source/QtDialog/CMakeSetup.qrc | 1 + Source/QtDialog/CMakeSetupDialog.cxx | 401 +++++++++++++++++++++++---- Source/QtDialog/CMakeSetupDialog.h | 28 ++- Source/QtDialog/CMakeSetupDialog.ui | 44 +++- Source/QtDialog/QCMake.cxx | 142 ++++++++++- Source/QtDialog/QCMake.h | 20 ++- Source/QtDialog/emblem-symbolic-link.png | Bin 0 -> 724 bytes Source/cmSystemTools.cxx | 25 ++- Source/cmSystemTools.h | 18 ++ 12 files changed, 732 insertions(+), 74 deletions(-) create mode 100644 Source/QtDialog/ActionButtonEventFilter.cxx create mode 100644 Source/QtDialog/ActionButtonEventFilter.h create mode 100644 Source/QtDialog/emblem-symbolic-link.png diff --git a/Source/QtDialog/ActionButtonEventFilter.cxx b/Source/QtDialog/ActionButtonEventFilter.cxx new file mode 100644 index 0000000..a35a2e2 --- /dev/null +++ b/Source/QtDialog/ActionButtonEventFilter.cxx @@ -0,0 +1,72 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2009-2009 Michael Wild <themiwi@users.sourceforge.net> + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "ActionButtonEventFilter.h" +#include <QWidget> +#include <QStyle> +#include <QMouseEvent> + +ActionButtonEventFilter::ActionButtonEventFilter(QWidget* parent) + : QObject(parent) + , Delay(600) +{ + this->Timer = new QTimer(this); + this->Timer->setSingleShot(true); + // chain the timer's timeout() signal to our showMenu() signal + QObject::connect(this->Timer, SIGNAL(timeout()), this, SIGNAL(showMenu())); + if(parent) + { + // use the style-sheet defined popup-delay + this->setDelay(parent->style()->styleHint( + QStyle::SH_ToolButton_PopupDelay, 0, parent)); + } +} + +int ActionButtonEventFilter::getDelay() const +{ + return this->Delay; +} + +void ActionButtonEventFilter::setDelay(int delay) +{ + this->Delay = delay; +} + +bool ActionButtonEventFilter::eventFilter(QObject *obj, QEvent *event) +{ + // only handle mouse-button press and release + if(event->type() == QEvent::MouseButtonPress || + event->type() == QEvent::MouseButtonRelease) + { + QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event); + // only handle the left mouse-button + if(mouseEvent->button() == Qt::LeftButton) + { + if(mouseEvent->type() == QEvent::MouseButtonPress) + { + // on press, start timer and eat event + this->Timer->start(this->Delay); + return true; + } + else if(this->Timer->isActive()) + { + // on release, stop timer, fire default action and eat event + this->Timer->stop(); + emit this->activateDefaultAction(); + return true; + } + } + } + // normal processing + return QObject::eventFilter(obj, event); +} diff --git a/Source/QtDialog/ActionButtonEventFilter.h b/Source/QtDialog/ActionButtonEventFilter.h new file mode 100644 index 0000000..7c546ba --- /dev/null +++ b/Source/QtDialog/ActionButtonEventFilter.h @@ -0,0 +1,53 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2009-2009 Michael Wild <themiwi@users.sourceforge.net> + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef ActionButtonEventFilter_h +#define ActionButtonEventFilter_h + +#include <QEvent> +#include <QTimer> + +class QWidget; + +/// Event filter to create QToolButton::DelayedPopupMode behavior for a +/// QPushButton +class ActionButtonEventFilter : public QObject +{ + Q_OBJECT + + /// Popup-delay, defaults to the style-defined delay of the parent if not + /// NULL, 600ms otherwise. + Q_PROPERTY(int Delay READ getDelay WRITE setDelay) + +public: + ActionButtonEventFilter(QWidget* parent); + + int getDelay() const; + void setDelay(int delay); + +signals: + /// Emitted when the default action should be used + void activateDefaultAction(); + /// Emitted when the button menu should be displayed + void showMenu(); + +protected: + bool eventFilter(QObject *obj, QEvent *event); + +private: + int Delay; + QTimer* Timer; + +}; + +#endif // ActionButtonEventFilter_h diff --git a/Source/QtDialog/CMakeLists.txt b/Source/QtDialog/CMakeLists.txt index 4e7e357..d0a3769 100644 --- a/Source/QtDialog/CMakeLists.txt +++ b/Source/QtDialog/CMakeLists.txt @@ -27,6 +27,7 @@ ELSE(NOT QT4_FOUND) "WARNING: QtDialog requires a static built qt for installation.") ENDIF(WIN32 AND NOT QT_CONFIG MATCHES "static") SET(SRCS + ActionButtonEventFilter.cxx AddCacheEntry.cxx AddCacheEntry.h CMakeSetup.cxx @@ -51,6 +52,7 @@ ELSE(NOT QT4_FOUND) MacInstallDialog.ui ) QT4_WRAP_CPP(MOC_SRCS + ActionButtonEventFilter.h AddCacheEntry.h Compilers.h CMakeSetupDialog.h diff --git a/Source/QtDialog/CMakeSetup.qrc b/Source/QtDialog/CMakeSetup.qrc index 5ceb1df..fd21ab3 100644 --- a/Source/QtDialog/CMakeSetup.qrc +++ b/Source/QtDialog/CMakeSetup.qrc @@ -1,5 +1,6 @@ <RCC> <qresource prefix="/Icons" > + <file>emblem-symbolic-link.png</file> <file>CMakeSetup.png</file> <file>Delete16.png</file> <file>Plus16.png</file> diff --git a/Source/QtDialog/CMakeSetupDialog.cxx b/Source/QtDialog/CMakeSetupDialog.cxx index eb82f2a..a93c2ce 100644 --- a/Source/QtDialog/CMakeSetupDialog.cxx +++ b/Source/QtDialog/CMakeSetupDialog.cxx @@ -26,12 +26,14 @@ #include <QMimeData> #include <QUrl> #include <QShortcut> +#include <QDesktopServices> #include <QMacInstallDialog.h> #include "QCMake.h" #include "QCMakeCacheView.h" #include "AddCacheEntry.h" #include "FirstConfigure.h" +#include "ActionButtonEventFilter.h" #include "cmVersion.h" QCMakeThread::QCMakeThread(QObject* p) @@ -55,7 +57,8 @@ void QCMakeThread::run() } CMakeSetupDialog::CMakeSetupDialog() - : ExitAfterGenerate(true), CacheModified(false), CurrentState(Interrupting) + : ExitAfterGenerate(true), CacheModified(false), CurrentState(Interrupting), + CurrentConfiguration() { QString title = QString(tr("CMake %1")); title = title.arg(cmVersion::GetCMakeVersion()); @@ -94,6 +97,16 @@ CMakeSetupDialog::CMakeSetupDialog() this->DeleteCacheAction = FileMenu->addAction(tr("&Delete Cache")); QObject::connect(this->DeleteCacheAction, SIGNAL(triggered(bool)), this, SLOT(doDeleteCache())); + FileMenu->addSeparator(); + this->OpenSourceDirectoryAction = FileMenu->addAction(tr("Open &Source Directory")); + QObject::connect(this->OpenSourceDirectoryAction, SIGNAL(triggered(bool)), + this, SLOT(doSourceOpen())); + this->OpenBuildDirectoryAction = FileMenu->addAction(tr("Open &Build Directory")); + QObject::connect(this->OpenBuildDirectoryAction, SIGNAL(triggered(bool)), + this, SLOT(doBinaryOpen())); +#if !defined(Q_WS_MAC) + FileMenu->addSeparator(); +#endif this->ExitAction = FileMenu->addAction(tr("E&xit")); QObject::connect(this->ExitAction, SIGNAL(triggered(bool)), this, SLOT(close())); @@ -104,9 +117,32 @@ CMakeSetupDialog::CMakeSetupDialog() this->ConfigureAction->setMenuRole(QAction::NoRole); QObject::connect(this->ConfigureAction, SIGNAL(triggered(bool)), this, SLOT(doConfigure())); + this->StopAction = ToolsMenu->addAction(tr("&Stop")); + this->StopAction->setEnabled(false); + this->StopAction->setVisible(false); + QObject::connect(this->StopAction, SIGNAL(triggered(bool)), + this, SLOT(doInterrupt())); this->GenerateAction = ToolsMenu->addAction(tr("&Generate")); QObject::connect(this->GenerateAction, SIGNAL(triggered(bool)), this, SLOT(doGenerate())); + ToolsMenu->addSeparator(); + this->BuildAction = ToolsMenu->addAction(tr("&Build Project")); + QObject::connect(this->BuildAction, SIGNAL(triggered(bool)), + this, SLOT(doBuild())); + this->InstallAction = ToolsMenu->addAction(tr("&Install Project")); + QObject::connect(this->InstallAction, SIGNAL(triggered(bool)), + this, SLOT(doInstall())); + this->CleanAction = ToolsMenu->addAction(tr("&Clean Project")); + QObject::connect(this->CleanAction, SIGNAL(triggered(bool)), + this, SLOT(doClean())); + ToolsMenu->addSeparator(); + this->ActiveConfigurationMenu = ToolsMenu->addMenu(tr("Active Configuration")); +#if !(defined(QT_MAC_USE_COCOA) && QT_VERSION == 0x040503) + // Don't disable the menu when using Qt-Cocoa 4.5.3: + // http://bugreports.qt.nokia.com/browse/QTBUG-5313 + this->ActiveConfigurationMenu->setEnabled(false); +#endif + ToolsMenu->addSeparator(); QAction* showChangesAction = ToolsMenu->addAction(tr("&Show My Changes")); QObject::connect(showChangesAction, SIGNAL(triggered(bool)), this, SLOT(showUserChanges())); @@ -159,6 +195,25 @@ CMakeSetupDialog::CMakeSetupDialog() this->Output->setFont(outputFont); this->ErrorFormat.setForeground(QBrush(Qt::red)); + // set up the action-button + QMenu* ActionMenu = new QMenu(this->ActionButton); + ActionMenu->addAction(this->ConfigureAction); + ActionMenu->addAction(this->StopAction); + ActionMenu->addAction(this->GenerateAction); + ActionMenu->addAction(this->BuildAction); + ActionMenu->addAction(this->InstallAction); + ActionMenu->addAction(this->CleanAction); + this->ActionButton->setMenu(ActionMenu); + ActionButtonEventFilter* actionEventFilter = new ActionButtonEventFilter(this->ActionButton); + QObject::connect(actionEventFilter, SIGNAL(activateDefaultAction()), + this, SLOT(doDefaultAction())); + QObject::connect(actionEventFilter, SIGNAL(showMenu()), + this->ActionButton, SLOT(showMenu())); + this->ActionButton->installEventFilter(actionEventFilter); + this->setDefaultAction(this->ConfigureAction); + + this->ActiveConfigurationPopup->setVisible(false); + // start the cmake worker thread this->CMakeThread = new QCMakeThread(this); QObject::connect(this->CMakeThread, SIGNAL(cmakeInitialized()), @@ -176,22 +231,30 @@ void CMakeSetupDialog::initialize() this->CacheValues->cacheModel(), SLOT(setProperties(const QCMakePropertyList&))); - QObject::connect(this->ConfigureButton, SIGNAL(clicked(bool)), - this, SLOT(doConfigure())); QObject::connect(this->CMakeThread->cmakeInstance(), SIGNAL(configureDone(int)), this, SLOT(finishConfigure(int))); QObject::connect(this->CMakeThread->cmakeInstance(), SIGNAL(generateDone(int)), this, SLOT(finishGenerate(int))); - - QObject::connect(this->GenerateButton, SIGNAL(clicked(bool)), - this, SLOT(doGenerate())); + QObject::connect(this->CMakeThread->cmakeInstance(), + SIGNAL(buildDone(int)), + this, SLOT(finishBuild(int))); + QObject::connect(this->CMakeThread->cmakeInstance(), + SIGNAL(installDone(int)), + this, SLOT(finishInstall(int))); + QObject::connect(this->CMakeThread->cmakeInstance(), + SIGNAL(cleanDone(int)), + this, SLOT(finishClean(int))); QObject::connect(this->BrowseSourceDirectoryButton, SIGNAL(clicked(bool)), this, SLOT(doSourceBrowse())); + QObject::connect(this->OpenSourceDirectoryButton, SIGNAL(clicked(bool)), + this, SLOT(doSourceOpen())); QObject::connect(this->BrowseBinaryDirectoryButton, SIGNAL(clicked(bool)), this, SLOT(doBinaryBrowse())); + QObject::connect(this->OpenBinaryDirectoryButton, SIGNAL(clicked(bool)), + this, SLOT(doBinaryOpen())); QObject::connect(this->BinaryDirectory, SIGNAL(editTextChanged(QString)), this, SLOT(onBinaryDirectoryChanged(QString))); @@ -266,16 +329,20 @@ CMakeSetupDialog::~CMakeSetupDialog() this->CMakeThread->quit(); this->CMakeThread->wait(2000); } - -void CMakeSetupDialog::doConfigure() + +void CMakeSetupDialog::doDefaultAction() { - if(this->CurrentState == Configuring) + // doing it this way so we don't have to disconnect/connect all the time when + // changing the default action. also prevent the action from triggering if it + // is disabled. + if(this->DefaultAction->isEnabled()) { - // stop configure - doInterrupt(); - return; + this->DefaultAction->trigger(); } +} +void CMakeSetupDialog::doConfigure() +{ // make sure build directory exists QString bindir = this->CMakeThread->cmakeInstance()->binaryDirectory(); QDir dir(bindir); @@ -349,15 +416,59 @@ void CMakeSetupDialog::finishConfigure(int err) void CMakeSetupDialog::finishGenerate(int err) { - this->enterState(ReadyConfigure); - if(err != 0) + if(err == 0) { + this->enterState(ReadyBuild); + } + else + { + this->enterState(ReadyConfigure); QMessageBox::critical(this, tr("Error"), tr("Error in generation process, project files may be invalid"), QMessageBox::Ok); } } +void CMakeSetupDialog::finishBuild(int err) +{ + this->ProgressBar->setMaximum(100); + if(err == 0) + { + this->enterState(ReadyInstall); + } + else + { + this->enterState(ReadyBuild); + QMessageBox::critical(this, tr("Error"), + tr("Error in build process"), + QMessageBox::Ok); + } +} + +void CMakeSetupDialog::finishInstall(int err) +{ + this->ProgressBar->setMaximum(100); + if(err != 0) + { + QMessageBox::critical(this, tr("Error"), + tr("Error in installation process"), + QMessageBox::Ok); + } + this->enterState(ReadyInstall); +} + +void CMakeSetupDialog::finishClean(int err) +{ + this->ProgressBar->setMaximum(100); + if(err != 0) + { + QMessageBox::critical(this, tr("Error"), + tr("Error in cleaning process"), + QMessageBox::Ok); + } + this->enterState(ReadyBuild); +} + void CMakeSetupDialog::doInstallForCommandLine() { QMacInstallDialog setupdialog(0); @@ -366,17 +477,35 @@ void CMakeSetupDialog::doInstallForCommandLine() void CMakeSetupDialog::doGenerate() { - if(this->CurrentState == Generating) - { - // stop generate - doInterrupt(); - return; - } this->enterState(Generating); QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(), "generate", Qt::QueuedConnection); } - + +void CMakeSetupDialog::doBuild() +{ + this->enterState(Building); + this->ProgressBar->setMaximum(0); + QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(), + "build", Qt::QueuedConnection, Q_ARG(QString, this->CurrentConfiguration)); +} + +void CMakeSetupDialog::doInstall() +{ + this->enterState(Installing); + this->ProgressBar->setMaximum(0); + QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(), + "install", Qt::QueuedConnection, Q_ARG(QString, this->CurrentConfiguration)); +} + +void CMakeSetupDialog::doClean() +{ + this->enterState(Cleaning); + this->ProgressBar->setMaximum(0); + QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(), + "clean", Qt::QueuedConnection, Q_ARG(QString, this->CurrentConfiguration)); +} + void CMakeSetupDialog::closeEvent(QCloseEvent* e) { // prompt for close if there are unsaved changes, and we're not busy @@ -475,6 +604,15 @@ void CMakeSetupDialog::doSourceBrowse() } } +void CMakeSetupDialog::doSourceOpen() +{ + QString srcdir = this->SourceDirectory->text(); + if(!srcdir.isEmpty()) + { + QDesktopServices::openUrl(QUrl("file:/"+srcdir, QUrl::TolerantMode)); + } +} + void CMakeSetupDialog::updateSourceDirectory(const QString& dir) { if(this->SourceDirectory->text() != dir) @@ -505,6 +643,15 @@ void CMakeSetupDialog::doBinaryBrowse() } } +void CMakeSetupDialog::doBinaryOpen() +{ + QString bindir = this->BinaryDirectory->currentText(); + if(!bindir.isEmpty()) + { + QDesktopServices::openUrl(QUrl("file:/"+bindir, QUrl::TolerantMode)); + } +} + void CMakeSetupDialog::setBinaryDirectory(const QString& dir) { this->BinaryDirectory->setEditText(dir); @@ -530,6 +677,8 @@ void CMakeSetupDialog::onBinaryDirectoryChanged(const QString& dir) this->Output->clear(); QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(), "setBinaryDirectory", Qt::QueuedConnection, Q_ARG(QString, dir)); + + this->enterState(ReadyConfigure); } void CMakeSetupDialog::setSourceDirectory(const QString& dir) @@ -539,6 +688,7 @@ void CMakeSetupDialog::setSourceDirectory(const QString& dir) void CMakeSetupDialog::showProgress(const QString& /*msg*/, float percent) { + this->ProgressBar->setMaximum(100); this->ProgressBar->setValue(qRound(percent * 100)); } @@ -556,7 +706,7 @@ void CMakeSetupDialog::message(const QString& msg) void CMakeSetupDialog::setEnabledState(bool enabled) { - // disable parts of the GUI during configure/generate + // disable parts of the GUI during configure/generate/build/install/clean this->CacheValues->cacheModel()->setEditEnabled(enabled); this->SourceDirectory->setEnabled(enabled); this->BrowseSourceDirectoryButton->setEnabled(enabled); @@ -566,8 +716,32 @@ void CMakeSetupDialog::setEnabledState(bool enabled) this->DeleteCacheAction->setEnabled(enabled); this->ExitAction->setEnabled(enabled); this->ConfigureAction->setEnabled(enabled); + this->ActionButton->setEnabled(true); + this->GenerateAction->setEnabled(false); + this->BuildAction->setEnabled(false); + this->InstallAction->setEnabled(false); + this->CleanAction->setEnabled(false); + this->StopAction->setVisible(false); + this->StopAction->setEnabled(false); this->AddEntry->setEnabled(enabled); this->RemoveEntry->setEnabled(false); // let selection re-enable it + if(enabled) + { + this->manageConfigs(); + } + else + { + this->ActiveConfigurationPopup->setEnabled(false); +#if !(defined(QT_MAC_USE_COCOA) && QT_VERSION == 0x040503) + // don't disable the menu when using Qt-Cocoa 4.5.3: + // http://bugreports.qt.nokia.com/browse/QTBUG-5313 + this->ActiveConfigurationMenu->setEnabled(false); +#else + // do it the dedious way: disable all child-actions + foreach(QAction* a, this->ActiveConfigurationMenu->actions()) + a->setEnabled(false); +#endif + } } bool CMakeSetupDialog::setupFirstConfigure() @@ -744,7 +918,9 @@ void CMakeSetupDialog::addBinaryPath(const QString& path) void CMakeSetupDialog::dragEnterEvent(QDragEnterEvent* e) { if(!(this->CurrentState == ReadyConfigure || - this->CurrentState == ReadyGenerate)) + this->CurrentState == ReadyGenerate || + this->CurrentState == ReadyBuild || + this->CurrentState == ReadyInstall)) { e->ignore(); return; @@ -768,7 +944,9 @@ void CMakeSetupDialog::dragEnterEvent(QDragEnterEvent* e) void CMakeSetupDialog::dropEvent(QDropEvent* e) { if(!(this->CurrentState == ReadyConfigure || - this->CurrentState == ReadyGenerate)) + this->CurrentState == ReadyGenerate || + this->CurrentState == ReadyBuild || + this->CurrentState == ReadyInstall)) { return; } @@ -854,7 +1032,9 @@ void CMakeSetupDialog::selectionChanged() QModelIndexList idxs = this->CacheValues->selectionModel()->selectedRows(); if(idxs.count() && (this->CurrentState == ReadyConfigure || - this->CurrentState == ReadyGenerate) ) + this->CurrentState == ReadyGenerate || + this->CurrentState == ReadyBuild || + this->CurrentState == ReadyInstall) ) { this->RemoveEntry->setEnabled(true); } @@ -873,46 +1053,129 @@ void CMakeSetupDialog::enterState(CMakeSetupDialog::State s) this->CurrentState = s; - if(s == Interrupting) - { - this->ConfigureButton->setEnabled(false); - this->GenerateButton->setEnabled(false); - } - else if(s == Configuring) + switch(s) { - this->Output->clear(); - this->setEnabledState(false); - this->GenerateButton->setEnabled(false); - this->GenerateAction->setEnabled(false); - this->ConfigureButton->setText(tr("&Stop")); + case Interrupting: + this->ActionButton->setEnabled(false); + break; + case Configuring: + case Generating: + case Building: + case Installing: + case Cleaning: + this->Output->clear(); + this->setEnabledState(false); + this->StopAction->setEnabled(true); + this->StopAction->setVisible(true); + this->setDefaultAction(this->StopAction); + break; + case ReadyConfigure: + this->ProgressBar->reset(); + this->setEnabledState(true); + this->setDefaultAction(this->ConfigureAction); + break; + case ReadyGenerate: + this->ProgressBar->reset(); + this->setEnabledState(true); + this->GenerateAction->setEnabled(true); + this->CleanAction->setEnabled(false); + this->setDefaultAction(this->GenerateAction); + break; + case ReadyBuild: + this->ProgressBar->reset(); + this->setEnabledState(true); + this->GenerateAction->setEnabled(true); + this->BuildAction->setEnabled(true); + this->CleanAction->setEnabled(true); + this->setDefaultAction(this->BuildAction); + break; + case ReadyInstall: + this->ProgressBar->reset(); + this->setEnabledState(true); + this->GenerateAction->setEnabled(true); + this->BuildAction->setEnabled(true); + this->InstallAction->setEnabled(true); + this->CleanAction->setEnabled(true); + this->setDefaultAction(this->InstallAction); + break; } - else if(s == Generating) +} + +void CMakeSetupDialog::setDefaultAction(QAction* action) +{ + this->DefaultAction = action; + this->ActionButton->setText(action->text()); +} + +void CMakeSetupDialog::manageConfigs() +{ + QCMakePropertyList props = this->CacheValues->cacheModel()->properties(); + QStringList configs; + foreach(QCMakeProperty p, props) { - this->CacheModified = false; - this->setEnabledState(false); - this->ConfigureButton->setEnabled(false); - this->GenerateAction->setEnabled(false); - this->GenerateButton->setText(tr("&Stop")); + if(p.Key == "CMAKE_CONFIGURATION_TYPES") + { + // p.Strings seems to be always empty... + configs = p.Value.toString().split(";"); + break; + } } - else if(s == ReadyConfigure) + // always rebuild menu and combobox from scratch (easier...) + // first check that we have in fact a multi-config generator + if(configs.length() > 0) { - this->ProgressBar->reset(); - this->setEnabledState(true); - this->GenerateButton->setEnabled(false); - this->GenerateAction->setEnabled(false); - this->ConfigureButton->setEnabled(true); - this->ConfigureButton->setText(tr("&Configure")); - this->GenerateButton->setText(tr("&Generate")); + // figure out what the index of the current configuration is in the new set + int curIdx = -1; + if(!this->CurrentConfiguration.isEmpty()) + { + curIdx = configs.indexOf(this->CurrentConfiguration); + } + if(curIdx < 0) + { + this->CurrentConfiguration = ""; + curIdx = 0; + } + // now clear out the menu, the action group and the combo box + this->ActiveConfigurationMenu->clear(); + this->ActiveConfigurationPopup->clear(); + this->ConfigurationActions.clear(); + // this also deletes the actions (they are children of the old ActionGroup)! + this->ConfigurationActionsGroup = QSharedPointer<QActionGroup>(new QActionGroup(NULL)); + this->ConfigurationActionsGroup->setExclusive(true); + // now populate + size_t i = 0; + foreach(QString c, configs) + { + QAction* act = new QAction(c, this->ConfigurationActionsGroup.data()); + act->setCheckable(true); + this->ConfigurationActionsGroup->addAction(act); + this->ConfigurationActions.append(act); + this->ActiveConfigurationMenu->addAction(act); + this->ActiveConfigurationPopup->addItem(c); + QObject::connect(act, SIGNAL(triggered(bool)), this, SLOT(activeConfigurationChanged(bool))); + ++i; + } + this->ConfigurationActions[curIdx]->setChecked(true); + this->ActiveConfigurationPopup->setCurrentIndex(curIdx); + QObject::connect(this->ActiveConfigurationPopup, SIGNAL(currentIndexChanged(int)), + this, SLOT(activeConfigurationChanged(int))); + this->ActiveConfigurationPopup->setVisible(true); + this->ActiveConfigurationPopup->setEnabled(true); + this->ActiveConfigurationMenu->setEnabled(true); } - else if(s == ReadyGenerate) + else { - this->ProgressBar->reset(); - this->setEnabledState(true); - this->GenerateButton->setEnabled(true); - this->GenerateAction->setEnabled(true); - this->ConfigureButton->setEnabled(true); - this->ConfigureButton->setText(tr("&Configure")); - this->GenerateButton->setText(tr("&Generate")); + // nope, disable + this->ActiveConfigurationPopup->clear(); + this->ActiveConfigurationMenu->clear(); + this->ActiveConfigurationPopup->setVisible(false); + this->ActiveConfigurationPopup->setEnabled(false); +#if !(defined(QT_MAC_USE_COCOA) && QT_VERSION == 0x040503) + // Don't disable the menu when using Qt-Cocoa 4.5.3: + // http://bugreports.qt.nokia.com/browse/QTBUG-5313 + this->ActiveConfigurationMenu->setEnabled(false); +#endif + this->CurrentConfiguration = ""; } } @@ -1048,4 +1311,26 @@ void CMakeSetupDialog::setSearchFilter(const QString& str) this->CacheValues->setSearchFilter(str); } +void CMakeSetupDialog::activeConfigurationChanged(bool checked) +{ + if(checked) + { + for(int i=0; i < this->ConfigurationActions.length(); ++i) + if(this->ConfigurationActions[i]->isChecked()) + { + this->activeConfigurationChanged(i); + break; + } + } +} + +void CMakeSetupDialog::activeConfigurationChanged(int index) +{ + if(index > -1) + { + this->ConfigurationActions[index]->setChecked(true); + this->ActiveConfigurationPopup->setCurrentIndex(index); + this->CurrentConfiguration = this->ActiveConfigurationPopup->currentText(); + } +} diff --git a/Source/QtDialog/CMakeSetupDialog.h b/Source/QtDialog/CMakeSetupDialog.h index de7922a..2f386d2 100644 --- a/Source/QtDialog/CMakeSetupDialog.h +++ b/Source/QtDialog/CMakeSetupDialog.h @@ -16,6 +16,7 @@ #include "QCMake.h" #include <QMainWindow> #include <QThread> +#include <QSharedPointer> #include "ui_CMakeSetupDialog.h" class QCMakeThread; @@ -37,19 +38,28 @@ public slots: protected slots: void initialize(); + void doDefaultAction(); void doConfigure(); void doGenerate(); + void doBuild(); + void doInstall(); + void doClean(); void doInstallForCommandLine(); void doHelp(); void doAbout(); void doInterrupt(); void finishConfigure(int error); void finishGenerate(int error); + void finishBuild(int error); + void finishInstall(int error); + void finishClean(int error); void error(const QString& message); void message(const QString& message); void doSourceBrowse(); + void doSourceOpen(); void doBinaryBrowse(); + void doBinaryOpen(); void doReloadCache(); void doDeleteCache(); void updateSourceDirectory(const QString& dir); @@ -73,11 +83,16 @@ protected slots: void setViewType(int); void showUserChanges(); void setSearchFilter(const QString& str); + void activeConfigurationChanged(bool checked); + void activeConfigurationChanged(int index); protected: - enum State { Interrupting, ReadyConfigure, ReadyGenerate, Configuring, Generating }; + enum State { Interrupting, ReadyConfigure, ReadyGenerate, ReadyBuild, + ReadyInstall, Configuring, Generating, Building, Installing, Cleaning }; void enterState(State s); + void setDefaultAction(QAction* action); + void manageConfigs(); void closeEvent(QCloseEvent*); void dragEnterEvent(QDragEnterEvent*); @@ -88,12 +103,23 @@ protected: bool CacheModified; QAction* ReloadCacheAction; QAction* DeleteCacheAction; + QAction* OpenSourceDirectoryAction; + QAction* OpenBuildDirectoryAction; QAction* ExitAction; QAction* ConfigureAction; + QAction* StopAction; QAction* GenerateAction; + QAction* BuildAction; + QAction* CleanAction; + QAction* InstallAction; + QAction* DefaultAction; + QMenu* ActiveConfigurationMenu; + QSharedPointer<QActionGroup> ConfigurationActionsGroup; QAction* SuppressDevWarningsAction; QAction* InstallForCommandLineAction; State CurrentState; + QList<QAction*> ConfigurationActions; + QString CurrentConfiguration; QTextCharFormat ErrorFormat; QTextCharFormat MessageFormat; diff --git a/Source/QtDialog/CMakeSetupDialog.ui b/Source/QtDialog/CMakeSetupDialog.ui index ae0dca2..2738304 100644 --- a/Source/QtDialog/CMakeSetupDialog.ui +++ b/Source/QtDialog/CMakeSetupDialog.ui @@ -37,7 +37,7 @@ <item row="0" column="2" > <widget class="QPushButton" name="BrowseSourceDirectoryButton" > <property name="text" > - <string>Browse &Source...</string> + <string>Choose &Source...</string> </property> </widget> </item> @@ -64,7 +64,35 @@ <item row="1" column="2" > <widget class="QPushButton" name="BrowseBinaryDirectoryButton" > <property name="text" > - <string>Browse &Build...</string> + <string>Choose &Build...</string> + </property> + </widget> + </item> + <item row="0" column="3"> + <widget class="QToolButton" name="OpenSourceDirectoryButton"> + <property name="toolTip"> + <string>Open Source Directory</string> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset resource="CMakeSetup.qrc"> + <normaloff>:/Icons/emblem-symbolic-link.png</normaloff>:/Icons/emblem-symbolic-link.png</iconset> + </property> + </widget> + </item> + <item row="1" column="3"> + <widget class="QToolButton" name="OpenBinaryDirectoryButton"> + <property name="toolTip"> + <string>Open Build Directory</string> + </property> + <property name="text"> + <string>...</string> + </property> + <property name="icon"> + <iconset resource="CMakeSetup.qrc"> + <normaloff>:/Icons/emblem-symbolic-link.png</normaloff>:/Icons/emblem-symbolic-link.png</iconset> </property> </widget> </item> @@ -228,18 +256,14 @@ <number>6</number> </property> <item> - <widget class="QPushButton" name="ConfigureButton" > - <property name="text" > - <string>&Configure</string> + <widget class="QPushButton" name="ActionButton"> + <property name="text"> + <string/> </property> </widget> </item> <item> - <widget class="QPushButton" name="GenerateButton" > - <property name="text" > - <string>&Generate</string> - </property> - </widget> + <widget class="QComboBox" name="ActiveConfigurationPopup"/> </item> <item> <widget class="QLabel" name="Generator" > diff --git a/Source/QtDialog/QCMake.cxx b/Source/QtDialog/QCMake.cxx index dc31fad..042154d 100644 --- a/Source/QtDialog/QCMake.cxx +++ b/Source/QtDialog/QCMake.cxx @@ -50,6 +50,8 @@ QCMake::QCMake(QObject* p) cmSystemTools::DisableRunCommandOutput(); cmSystemTools::SetRunCommandHideConsole(true); cmSystemTools::SetErrorCallback(QCMake::errorCallback, this); + cmSystemTools::SetStdoutCallback(QCMake::stdoutCallback, this); + cmSystemTools::SetKillChildCallback(QCMake::killChildCallback, this); cmSystemTools::FindExecutableDirectory(cmakeCommand.toAscii().data()); this->CMakeInstance = new cmake; @@ -77,6 +79,7 @@ QCMake::QCMake(QObject* p) } this->AvailableGenerators.append(iter->c_str()); } + this->ShouldKillChild = false; } QCMake::~QCMake() @@ -146,6 +149,8 @@ void QCMake::setGenerator(const QString& gen) if(this->Generator != gen) { this->Generator = gen; + // only parse progress from Unix Makefile generators + this->CanParseProgress = this->Generator.indexOf("Unix Makefiles") != -1; emit this->generatorChanged(this->Generator); } } @@ -174,6 +179,7 @@ void QCMake::configure() SetErrorMode(lastErrorMode); #endif + this->ShouldKillChild = false; emit this->propertiesChanged(this->properties()); emit this->configureDone(err); } @@ -191,9 +197,82 @@ void QCMake::generate() SetErrorMode(lastErrorMode); #endif + this->ShouldKillChild = false; emit this->generateDone(err); } - + +void QCMake::build(QString config) +{ +#ifdef Q_OS_WIN + UINT lastErrorMode = SetErrorMode(0); +#endif + + cmSystemTools::ResetErrorOccuredFlag(); + int err = this->CMakeInstance->Build( + this->CMakeInstance->GetStartOutputDirectory(), + "", + config.toAscii().data(), + std::vector<std::string>(), + false + ); + +#ifdef Q_OS_WIN + SetErrorMode(lastErrorMode); +#endif + + this->flushOutputBuffer(); + this->ShouldKillChild = false; + emit this->buildDone(err); +} + +void QCMake::install(QString config) +{ +#ifdef Q_OS_WIN + UINT lastErrorMode = SetErrorMode(0); +#endif + + cmSystemTools::ResetErrorOccuredFlag(); + int err = this->CMakeInstance->Build( + this->CMakeInstance->GetStartOutputDirectory(), + "install", + config.toAscii().data(), + std::vector<std::string>(), + false + ); + +#ifdef Q_OS_WIN + SetErrorMode(lastErrorMode); +#endif + + this->flushOutputBuffer(); + this->ShouldKillChild = false; + emit this->installDone(err); +} + +void QCMake::clean(QString config) +{ +#ifdef Q_OS_WIN + UINT lastErrorMode = SetErrorMode(0); +#endif + + cmSystemTools::ResetErrorOccuredFlag(); + int err = this->CMakeInstance->Build( + this->CMakeInstance->GetStartOutputDirectory(), + "clean", + config.toAscii().data(), + std::vector<std::string>(), + false + ); + +#ifdef Q_OS_WIN + SetErrorMode(lastErrorMode); +#endif + + this->flushOutputBuffer(); + this->ShouldKillChild = false; + emit this->cleanDone(err); +} + void QCMake::setProperties(const QCMakePropertyList& newProps) { QCMakePropertyList props = newProps; @@ -329,6 +408,7 @@ QCMakePropertyList QCMake::properties() const void QCMake::interrupt() { + this->ShouldKillChild = true; cmSystemTools::SetFatalErrorOccured(); } @@ -354,6 +434,66 @@ void QCMake::errorCallback(const char* msg, const char* /*title*/, QCoreApplication::processEvents(); } +void QCMake::stdoutCallback(const char* data, int length, void* cd) +{ + QCMake* self = reinterpret_cast<QCMake*>(cd); + if(length) + { + // sometimes data still contains NULL and QString doesn't have a + // constructor QString(char*, int) + std::string tmp(data, length); + std::string::iterator i = tmp.begin(), e = tmp.end(); + for(; i != e; ++i) + if(*i == '\0') + *i = ' '; + // fromUtf8 seems to work for all LC_CTYPES for me... + QString qtmp = QString::fromUtf8(tmp.c_str()); + // if possible, parse progress information + if(self->CanParseProgress) + { + QRegExp rx("^\\[\\s*(\\d+)%\\]"); + foreach(QString line, qtmp.split("\n", QString::SkipEmptyParts)) + { + if(rx.indexIn(line) != -1) + { + bool ok; + float percent = rx.cap(1).toFloat(&ok); + if(ok) + { + emit self->progressChanged(QString(), percent/100); + } + break; + } + } + } + // append to buffer + self->OutputBuffer += qtmp; + // find the last newline + int idx = self->OutputBuffer.lastIndexOf("\n"); + if(idx > 0) + { + // output text up to the newline (the widget adds a newline itself, the + // whole reason for the buffering) + emit self->outputMessage(self->OutputBuffer.left(idx)); + // chop buffer + self->OutputBuffer.remove(0, idx+1); + } + } + QCoreApplication::processEvents(); +} + +bool QCMake::killChildCallback(void* cd) +{ + QCMake* self = reinterpret_cast<QCMake*>(cd); + return self->ShouldKillChild; +} + +void QCMake::flushOutputBuffer() +{ + if(!this->OutputBuffer.isEmpty()) + emit this->outputMessage(this->OutputBuffer); +} + QString QCMake::binaryDirectory() const { return this->BinaryDirectory; diff --git a/Source/QtDialog/QCMake.h b/Source/QtDialog/QCMake.h index bbfb3d7..73eabfd 100644 --- a/Source/QtDialog/QCMake.h +++ b/Source/QtDialog/QCMake.h @@ -76,9 +76,15 @@ public slots: void configure(); /// generate the files void generate(); + /// build the project + void build(QString config); + /// install the project + void install(QString config); + /// clean the project + void clean(QString config); /// set the property values void setProperties(const QCMakePropertyList&); - /// interrupt the configure or generate process + /// interrupt the configure, generate, build or install process void interrupt(); /// delete the cache in binary directory void deleteCache(); @@ -119,6 +125,12 @@ signals: void configureDone(int error); /// signal when generate is done void generateDone(int error); + /// signal when build is done + void buildDone(int error); + /// signal when install is done + void installDone(int error); + /// signal when clean is done + void cleanDone(int error); /// signal when there is an output message void outputMessage(const QString& msg); /// signal when there is an error message @@ -132,12 +144,18 @@ protected: static void progressCallback(const char* msg, float percent, void* cd); static void errorCallback(const char* msg, const char* title, bool&, void* cd); + static void stdoutCallback(const char* data, int length, void* cd); + static bool killChildCallback(void* cd); + void flushOutputBuffer(); bool SuppressDevWarnings; QString SourceDirectory; QString BinaryDirectory; QString Generator; QStringList AvailableGenerators; QString CMakeExecutable; + QString OutputBuffer; + bool CanParseProgress; + bool ShouldKillChild; }; #endif // __QCMake_h diff --git a/Source/QtDialog/emblem-symbolic-link.png b/Source/QtDialog/emblem-symbolic-link.png new file mode 100644 index 0000000000000000000000000000000000000000..7e53be23d15a3289c5856e67497929e92c28886f GIT binary patch literal 724 zcmV;_0xSKAP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00004b3#c}2nYxW zd<bNS00009a7bBm000F3000F30lb7RF#rGn8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H1026dYSaeirbZlh+Q+aJ-Z)|B}AWUg)Yaa;2<NyEw6m&&cbU}4= zXm4@=N?~htVjxp=Wod3@a_0N@F8}}lA#_DpbW?A2a${uxXmoUNIxjD3X>Dy`V=irV zb7^B}VQg$JV|r<3<6Zy&0p&?VK~y-6eUr_O5<wJ&&#i7mz;?*+BR_+K31(*8PF#}k z68Z+bhAw#kmn_}L3$SA^3!O2H3wMTQOl<oPt7B%Msq4b&wj0n&Zn_rte0A!cE@Ebk z$K#isPUp1{qDn;g-(qF}2o{UQ+g`8tiiX1>b-Ud!hG9HqW)Knn1!3m60LaN?a;gKU z=JWYF00<#cfFvj>W@cowS)))WywLyj5hca@T-==sW`<HK>e2M*q<w%0k`zaKAp`(m z2OxyN&Fu|LbB-X8$Yk!}?DQFQT}K#(u&nQ>Rv*Pz)FX?LSSf{ziw}@e#`bOd2To4T zFrR<L!NUX8YW3KiNQ#9JxcWRptyahR`E#^dk3mFOuh*E(K4H0Bg0==7UxA3g%&3$r zXt$q$h;Y<ALb+VQ<>kk}$t2Q>9Ym1TYK7zDcI<|kaeckmi8fiO8RT-g)G(#|iDsjT zM!f;06qp$g3?mhB3`j;3(GL$xD3!`kN@26vq`t8RK*-FSeLu~1_S-$o{7dxvec!UI zZ<?m1nw3&<ZM^{y5q#hGr_<>>4Z!^%2<&39*b2jtJkOJ^>q^IQq;1>Mwr%M+j&xmD zdY&ge&y%KUE=Hr#o55i4p6*^?7C;d|X2<LPwFclb^X+fco(EIauqmYg0000<MNUMn GLSTaT_C7WM literal 0 HcmV?d00001 diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx index 031bfc3..9306f2f 100644 --- a/Source/cmSystemTools.cxx +++ b/Source/cmSystemTools.cxx @@ -123,8 +123,10 @@ const char* cmSystemTools::GetWindows9xComspecSubstitute() void (*cmSystemTools::s_ErrorCallback)(const char*, const char*, bool&, void*); void (*cmSystemTools::s_StdoutCallback)(const char*, int len, void*); +bool (*cmSystemTools::s_KillChildCallback)(void*); void* cmSystemTools::s_ErrorCallbackClientData = 0; void* cmSystemTools::s_StdoutCallbackClientData = 0; +void* cmSystemTools::s_KillChildCallbackClientData = 0; // replace replace with with as many times as it shows up in source. // write the result into source. @@ -297,6 +299,21 @@ void cmSystemTools::Stdout(const char* s, int length) } } +void cmSystemTools::SetKillChildCallback(KillChildCallback f, void* clientData) +{ + s_KillChildCallback = f; + s_KillChildCallbackClientData = clientData; +} + +bool cmSystemTools::ExecuteKillChildCallback() +{ + if(s_KillChildCallback) + { + return (*s_KillChildCallback)(s_KillChildCallbackClientData); + } + return false; +} + void cmSystemTools::Message(const char* m1, const char *title) { if(s_DisableMessages) @@ -611,8 +628,6 @@ bool cmSystemTools::RunSingleCommand(std::vector<cmStdString>const& command, std::vector<char> tempOutput; char* data; int length; - if ( output || verbose ) - { while(cmsysProcess_WaitForData(cp, &data, &length, 0)) { if(output || verbose) @@ -636,7 +651,11 @@ bool cmSystemTools::RunSingleCommand(std::vector<cmStdString>const& command, { cmSystemTools::Stdout(data, length); } - } + if(cmSystemTools::ExecuteKillChildCallback()) + { + cmsysProcess_Kill(cp); + break; + } } cmsysProcess_WaitForExit(cp, 0); diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h index 679884e..747327c 100644 --- a/Source/cmSystemTools.h +++ b/Source/cmSystemTools.h @@ -81,6 +81,22 @@ public: typedef void (*StdoutCallback)(const char*, int length, void*); static void SetStdoutCallback(StdoutCallback, void* clientData=0); + /** Execute the callback function while running a child processes with + * RunSingleCommand. If it returns \c true, the child process should be + * killed. + */ + static bool ExecuteKillChildCallback(); + typedef bool (*KillChildCallback)(void*); + /** + * Set a callback function which gets periodically called during the + * execution of RunSingleCommand. If the callback returns \c true, the child + * should be killed. + * + * NB: Not implemented for RunCommand because the Unix implementation + * (RunCommandViaPopen) uses popen which can't be killed easily. + */ + static void SetKillChildCallback(KillChildCallback, void* clientData=0); + ///! Return true if there was an error at any point. static bool GetErrorOccuredFlag() { @@ -422,8 +438,10 @@ private: static bool s_DisableRunCommandOutput; static ErrorCallback s_ErrorCallback; static StdoutCallback s_StdoutCallback; + static KillChildCallback s_KillChildCallback; static void* s_ErrorCallbackClientData; static void* s_StdoutCallbackClientData; + static void* s_KillChildCallbackClientData; static std::string s_Windows9xComspecSubstitute; }; -- 1.6.5.2 | ||||||||
Relationships | ||||||
|
Relationships |
Notes | |
(0015718) Alex Neundorf (developer) 2009-03-17 17:16 |
I still think this would be a good idea, but I'm not completely sure what it should do. So, with the Makefile generators, it should probably run make. With e.g. the CodeBlocks generator, which is also Makefile-based, should it run make or start CodeBlocks with the generated project ? (probably the second) For the Xcode and Visual Studio generators it should probably start the IDE instead of executing the builtool (e.g. xcode-build or what the name was). What do you think ? We can add code to the cmake scripts so that they search e.g. for the codeblocks executable when the codeblocks generator is used, so that this information would be available to cmake-gui. Alex |
(0015719) Clinton Stimpson (developer) 2009-03-17 18:20 |
I'm not sure what it should do either. Bug 0008664 might overlap with this. It seems the intent is a quick way to start compiling after cmake has created the build files. If that is it, then it seems there are more way to do this. Also, if one should be able to close cmake-gui and still have the IDE or terminal alive, then I don't think it can be a child process of cmake-gui. |
(0015728) Alex Neundorf (developer) 2009-03-18 18:41 |
Yes, it's more or less the same as 0008664. Alex |
(0015748) Philip Lowman (developer) 2009-03-21 20:31 |
I suggest calling the button "Open". Please do not make the default behavior to start the build upon the IDE being opened although this might be possible as a user option for certain IDEs that support it. For Makefile based generators that do not use an IDE the Open button could spawn a terminal in the build directory. |
(0019141) Michael Wild (reporter) 2010-01-14 14:50 |
I think the user should have all of these options: - Open the project in the IDE corresponding to the generator (if available/applicable/implementable). This is what 0008664 is about. - Have a drop-down menu to select the build configuration for multi-configuration generators. The same drop-down could be used to select the build target (like in Xcode) - Have buttons (possibly a dropdown-button, also like in Xcode) to build, clean and install the project. If it's a dropdown-button the default action could change from "build" to "install" after a successful build. - Don't need a button for this, but it would be nice to have a menu-entry to open the source and binary directories. |
(0019160) Michael Wild (reporter) 2010-01-18 04:42 |
The attached patch * adds menu-entries/buttons to open source and binary directories in file browser. Not too sure about the button-icons (grabbed it from tango, actually is an overlay to indicate symlinks) * replaces "Configure" and "Generate" buttons with a single button with a delayed popup-menu, alowing one to configure, generate, build, install and clean the project. * reads the cache to find CMAKE_CONFIGURATION_TYPES and offer the available build types in the menu and a combo-box next to the "action" button. Requires, however, that all available build types are stored in the cache. TODO: * For IDE-projects add a button to open the project file. * Somehow capture stdout/stderr when building/installing/cleaning and display in output widget. * Parse output from Makefile-builds and drive progress-bar instead of using the generic "busy" display. |
(0019180) Clinton Stimpson (developer) 2010-01-18 22:06 |
A few comments: cmSystemTools has a way to get the stdout when building, installing and cleaning. QCMake doesn't install a message handler for that. We should have a way to interrupt the build, install and clean. I'm not sure if sending SIGINT to a child process is supported with kwsys, or at least getting the pid for the child process. Hitting the build project, while it is building, actually queues another build after the first is done. I guess enabling the "stop" would get around that. Going back to configure doesn't remove the combo box that shows up (for selecting build config) after a generate. It gives the option of installing even if the build failed. Did you have thoughts on progress display when not using makefiles? Did you have plans to do a bit more work on this to fix issues? Or were you hoping someone else would? |
(0019187) Michael Wild (reporter) 2010-01-19 01:20 |
I tried having a look at cmSystemTools and kwsys, but couldn't figure out how to do things. IMHO the whole thing is only useful if stdout/stderr can be captured. Yes, aborting would also be required, but again, I need some help here how to do this with cmSystemTools. Indeed, I never thought of hitting the disabled button... ;-) Will fix ASAP, should be pretty easy to do. I didn't want to enable the "stop" button, because currently I don't know how to do this as mentioned above. Should the combo-box be removed? After first hitting "configure", the project IS configured, and the available configurations presumably don't change after a generate/build/install. Will fix the issue with the enabled install-button after a build-failure. Didn't think of that too... About the progress for non-makefile generators, I have no idea whatsoever. AFAIK this information is not available to CMake, but I might be wrong. Will get started on the easy issues (not-really disabled build-button and enabled install-button after build-failure). For the other issues I probably need some help/guidance from somebody knowing cmSystemTools/kwsys. |
(0019188) Michael Wild (reporter) 2010-01-19 03:30 edited on: 2010-01-19 03:31 |
Attached a new version of the patch with the following changes: * Prevent the "action"-button from triggering if disabled * If any of the actions fails, go back to the configure-step * Also disable the "Active Configuration" menu. Disable both, the menu and the combo-box, when an action is running. On Qt-Cocoa 4.5.3 work around QTBUG-5313. Thanks for the feedback, Clinton! |
(0019225) Clinton Stimpson (developer) 2010-01-20 14:58 |
To interrupt the build/clean/install (maybe add package too?), it can be spawned off in a separate process. It might work using kwsysProcess to run "cmake --build" for example. But I don't see how to get a message to the thread QCMake is on to interrupt it. QProcess should work though. To redirect stdout from cmake, one could add a call to cmSystemTool::SetStdoutCallback in QCMake.cxx much like we do for errors, but that only works if cmSystemTools is used to run these commands. For the QProcess/kwsysProcess approach, they have their own methods for reading the stdout of the child process. |
(0019238) Michael Wild (reporter) 2010-01-21 08:19 |
I'd prefer not to fork "cmake --build", since there is cmake::Build(...). Concerning capturing of the output, having traced function calls and studied the code a bit more, there actually is cmSystemTools::SetStdoutCallback(...) allowing one to install a callback function which receives chunks of output from the child process. Will check out whether that works as expected. Stopping the child process will need some further investigation though. Perhaps the above-mentioned call-back function could be used to check back whether the process should be stopped, and then set a flag in the cmSystemTools class which causes it to kill the child. But this will require changes to the cmSystemTools class. |
(0019263) Michael Wild (reporter) 2010-01-22 04:28 |
Added an updated patch (-v3) which (in addition to above mentioned capabilities) * Captures build/install/clean output, displays it in the message window and parses progress for "Unix Makefiles" generators, driving the progress indicator. * Enables the "stop" action for the build/install/clean step. For this a new callback function to stop cmSystemTools::RunSingleCommand had to be added to cmSystemTools. For consistency it would be nice to implement this also for cmSystemTools::RunCommand, but for the functionality implemented here it is not necessary and since the UNIX implementation uses popen this can't be done easily (popen doesn't return the child process id). On the TODO list are still the button to open the project file for IDE-projects and a way to infer the progress from non-Makefile generators. |
(0041478) Kitware Robot (administrator) 2016-06-10 14:27 |
Resolving issue as `moved`. This issue tracker is no longer used. Further discussion of this issue may take place in the current CMake Issues page linked in the banner at the top of this page. |
Notes |
Issue History | |||
Date Modified | Username | Field | Change |
2008-12-08 14:10 | Alex Neundorf | New Issue | |
2008-12-15 10:04 | Bill Hoffman | Status | new => assigned |
2008-12-15 10:04 | Bill Hoffman | Assigned To | => Clinton Stimpson |
2009-03-17 17:16 | Alex Neundorf | Note Added: 0015718 | |
2009-03-17 18:20 | Clinton Stimpson | Note Added: 0015719 | |
2009-03-18 18:41 | Alex Neundorf | Note Added: 0015728 | |
2009-03-21 20:31 | Philip Lowman | Note Added: 0015748 | |
2010-01-14 14:50 | Michael Wild | Note Added: 0019141 | |
2010-01-18 04:39 | Michael Wild | File Added: 0001-ENH-Improve-cmake-gui-v1.patch | |
2010-01-18 04:42 | Michael Wild | Note Added: 0019160 | |
2010-01-18 22:06 | Clinton Stimpson | Note Added: 0019180 | |
2010-01-19 01:20 | Michael Wild | Note Added: 0019187 | |
2010-01-19 02:26 | Michael Wild | File Added: 0001-ENH-Improve-cmake-gui-v2.patch | |
2010-01-19 03:30 | Michael Wild | Note Added: 0019188 | |
2010-01-19 03:31 | Michael Wild | Note Edited: 0019188 | |
2010-01-20 14:58 | Clinton Stimpson | Note Added: 0019225 | |
2010-01-21 08:19 | Michael Wild | Note Added: 0019238 | |
2010-01-22 04:23 | Michael Wild | File Added: 0001-ENH-Improve-cmake-gui-v3.patch | |
2010-01-22 04:28 | Michael Wild | Note Added: 0019263 | |
2011-01-18 12:11 | David Cole | Relationship added | related to 0008664 |
2012-08-13 23:23 | Clinton Stimpson | Status | assigned => backlog |
2016-06-10 14:27 | Kitware Robot | Note Added: 0041478 | |
2016-06-10 14:27 | Kitware Robot | Status | backlog => resolved |
2016-06-10 14:27 | Kitware Robot | Resolution | open => moved |
2016-06-10 14:30 | Kitware Robot | Status | resolved => closed |
Issue History |
Copyright © 2000 - 2018 MantisBT Team |