Commit 800cbd05 authored by Clinton Stimpson's avatar Clinton Stimpson
Browse files

ENH: Beginnings of a Qt UI for CMake.

parent dcf21dd9
SET(QT_MIN_VERSION "4.3.0")
FIND_PACKAGE(Qt4 REQUIRED)
IF(NOT QT4_FOUND)
MESSAGE(SEND_ERROR "Failed to find Qt4.")
ELSE(NOT QT4_FOUND)
INCLUDE(${QT_USE_FILE})
SET(SRCS
CMakeSetup.cxx
CMakeSetupDialog.cxx
QCMake.cxx
QCMakeCacheView.cxx
)
QT4_WRAP_UI(UI_SRCS
CMakeSetupDialog.ui
)
QT4_WRAP_CPP(MOC_SRCS
CMakeSetupDialog.h
QCMake.h
QCMakeCacheView.h
)
QT4_ADD_RESOURCES(RC_SRCS CMakeSetup.qrc)
SET(SRCS ${SRCS} ${UI_SRCS} ${MOC_SRCS} ${RC_SRCS})
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
ADD_EXECUTABLE(QtDialog WIN32 MACOSX_BUNDLE ${SRCS})
TARGET_LINK_LIBRARIES(QtDialog CMakeLib ${QT_LIBRARIES})
ADD_DEPENDENCIES(QtDialog cmake)
ENDIF(NOT QT4_FOUND)
#include <QApplication>
#include "cmSystemTools.h"
#include "CMakeSetupDialog.h"
int main(int argc, char** argv)
{
QApplication app(argc, argv);
app.setApplicationName("CMakeSetup");
app.setOrganizationName("Kitware");
// TODO handle CMake args
CMakeSetupDialog dialog;
dialog.show();
return app.exec();
}
<RCC>
<qresource prefix="/Icons" >
<file>CMakeSetupDialog.png</file>
</qresource>
</RCC>
#include "CMakeSetupDialog.h"
#include <QFileDialog>
#include <QThread>
#include <QProgressBar>
#include <QMessageBox>
#include "QCMake.h"
#include "QCMakeCacheView.h"
// QCMake instance on a thread
class QCMakeThread : public QThread
{
public:
QCMakeThread(QObject* p) : QThread(p) { }
QCMake* CMakeInstance;
protected:
virtual void run()
{
this->CMakeInstance = new QCMake;
this->exec();
delete this->CMakeInstance;
}
};
CMakeSetupDialog::CMakeSetupDialog()
{
// create the GUI
this->setupUi(this);
this->ProgressBar = new QProgressBar();
this->ProgressBar->setRange(0,100);
this->statusBar()->addPermanentWidget(this->ProgressBar);
// start the cmake worker thread
this->CMakeThread = new QCMakeThread(this);
// TODO does this guarantee the QCMake instance is created before initialize is called?
QObject::connect(this->CMakeThread, SIGNAL(started()),
this, SLOT(initialize()));
this->CMakeThread->start();
}
void CMakeSetupDialog::initialize()
{
// now the cmake worker thread is running, lets make our connections to it
QObject::connect(this->CMakeThread->CMakeInstance,
SIGNAL(propertiesChanged(const QCMakeCachePropertyList&)),
this->CacheValues->cacheModel(),
SLOT(setProperties(const QCMakeCachePropertyList&)));
QObject::connect(this,
SIGNAL(propertiesChanged(const QCMakeCachePropertyList&)),
this->CMakeThread->CMakeInstance,
SLOT(setProperties(const QCMakeCachePropertyList&)));
QObject::connect(this->configureButton, SIGNAL(clicked(bool)),
this, SLOT(doConfigure()));
QObject::connect(this, SIGNAL(configure()),
this->CMakeThread->CMakeInstance, SLOT(configure()));
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(doOk()));
QObject::connect(this, SIGNAL(ok()),
this->CMakeThread->CMakeInstance, SLOT(generate()));
QObject::connect(this->cancelButton, SIGNAL(clicked(bool)),
this, SLOT(doCancel()));
QObject::connect(this, SIGNAL(cancel()),
this->CMakeThread->CMakeInstance, SLOT(interrupt()));
QObject::connect(this->BrowseSourceDirectoryButton, SIGNAL(clicked(bool)),
this, SLOT(doSourceBrowse()));
QObject::connect(this->BrowseBinaryDirectoryButton, SIGNAL(clicked(bool)),
this, SLOT(doBinaryBrowse()));
QObject::connect(this->BinaryDirectory, SIGNAL(textChanged(QString)),
this->CMakeThread->CMakeInstance, SLOT(setBinaryDirectory(QString)));
QObject::connect(this->CMakeThread->CMakeInstance, SIGNAL(sourceDirChanged(QString)),
this, SLOT(updateSourceDirectory(QString)));
QObject::connect(this->CMakeThread->CMakeInstance, SIGNAL(progressChanged(QString, float)),
this, SLOT(showProgress(QString,float)));
QObject::connect(this->CMakeThread->CMakeInstance, SIGNAL(error(QString, QString, bool*)),
this, SLOT(error(QString,QString,bool*)), Qt::BlockingQueuedConnection);
}
CMakeSetupDialog::~CMakeSetupDialog()
{
// wait for thread to stop
this->CMakeThread->quit();
this->CMakeThread->wait();
}
void CMakeSetupDialog::doConfigure()
{
emit this->propertiesChanged(this->CacheValues->cacheModel()->properties());
emit this->configure();
}
void CMakeSetupDialog::finishConfigure(int error)
{
this->ProgressBar->reset();
this->statusBar()->showMessage("Configure Done", 2000);
if(error != 0)
{
bool dummy;
this->error("Error", "Error in configuration process, project files may be invalid", &dummy);
}
}
void CMakeSetupDialog::finishGenerate(int error)
{
this->ProgressBar->reset();
this->statusBar()->showMessage("Generate Done", 2000);
if(error != 0)
{
bool dummy;
this->error("Error", "Error in generation process, project files may be invalid", &dummy);
}
}
void CMakeSetupDialog::doOk()
{
emit this->ok();
}
void CMakeSetupDialog::doCancel()
{
emit this->cancel();
}
void CMakeSetupDialog::doHelp()
{
}
void CMakeSetupDialog::doSourceBrowse()
{
QString dir = QFileDialog::getExistingDirectory(this, "TODO", this->SourceDirectory->text());
if(!dir.isEmpty())
{
this->updateSourceDirectory(dir);
}
}
void CMakeSetupDialog::updateSourceDirectory(const QString& dir)
{
this->SourceDirectory->setText(dir);
}
void CMakeSetupDialog::doBinaryBrowse()
{
QString dir = QFileDialog::getExistingDirectory(this, "TODO", this->BinaryDirectory->currentText());
if(!dir.isEmpty())
{
this->setBinaryDirectory(dir);
}
}
void CMakeSetupDialog::setBinaryDirectory(const QString& dir)
{
if(dir != this->BinaryDirectory->currentText())
{
this->BinaryDirectory->setEditText(dir);
}
}
void CMakeSetupDialog::showProgress(const QString& msg, float percent)
{
if(percent >= 0)
{
this->statusBar()->showMessage(msg);
this->ProgressBar->setValue(qRound(percent * 100));
}
}
void CMakeSetupDialog::error(const QString& title, const QString& message, bool* cancel)
{
QMessageBox::StandardButton btn =
QMessageBox::critical(this, title, message, QMessageBox::Ok | QMessageBox::Cancel);
if(btn == QMessageBox::Cancel)
{
*cancel = false;
}
}
#include <QMainWindow>
#include "ui_CMakeSetupDialog.h"
#include "QCMake.h"
class QCMakeThread;
class CMakeCacheModel;
class QProgressBar;
/// Qt user interface for CMake
class CMakeSetupDialog : public QMainWindow, public Ui::CMakeSetupDialog
{
Q_OBJECT
public:
CMakeSetupDialog();
~CMakeSetupDialog();
signals:
void configure();
void ok();
void cancel();
void propertiesChanged(const QCMakeCachePropertyList&);
protected slots:
void initialize();
void doConfigure();
void doOk();
void doCancel();
void doHelp();
void finishConfigure(int error);
void finishGenerate(int error);
void error(const QString& title, const QString& message, bool* cancel);
void doSourceBrowse();
void doBinaryBrowse();
void updateSourceDirectory(const QString& dir);
void setBinaryDirectory(const QString& dir);
void showProgress(const QString& msg, float percent);
protected:
QCMakeThread* CMakeThread;
QProgressBar* ProgressBar;
};
<ui version="4.0" >
<class>CMakeSetupDialog</class>
<widget class="QMainWindow" name="CMakeSetupDialog" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>650</width>
<height>505</height>
</rect>
</property>
<property name="windowTitle" >
<string>CMakeSetup</string>
</property>
<property name="windowIcon" >
<iconset resource="CMakeSetup.qrc" >:/Icons/CMakeSetupDialog.png</iconset>
</property>
<widget class="QWidget" name="centralwidget" >
<layout class="QGridLayout" >
<item row="0" column="0" >
<widget class="QFrame" name="frame" >
<property name="frameShape" >
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow" >
<enum>QFrame::Raised</enum>
</property>
<layout class="QGridLayout" >
<property name="leftMargin" >
<number>0</number>
</property>
<property name="topMargin" >
<number>0</number>
</property>
<property name="rightMargin" >
<number>0</number>
</property>
<property name="bottomMargin" >
<number>0</number>
</property>
<item row="0" column="0" >
<widget class="QLabel" name="label" >
<property name="text" >
<string>Where is the source code:</string>
</property>
</widget>
</item>
<item row="0" column="1" >
<widget class="QLineEdit" name="SourceDirectory" />
</item>
<item row="0" column="2" >
<widget class="QPushButton" name="BrowseSourceDirectoryButton" >
<property name="text" >
<string>Browse...</string>
</property>
</widget>
</item>
<item row="1" column="0" >
<widget class="QLabel" name="label_2" >
<property name="text" >
<string>Where to build the binaries:</string>
</property>
</widget>
</item>
<item row="1" column="1" >
<widget class="QComboBox" name="BinaryDirectory" >
<property name="editable" >
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="2" >
<widget class="QPushButton" name="BrowseBinaryDirectoryButton" >
<property name="text" >
<string>Browse...</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="3" >
<widget class="QCMakeCacheView" name="CacheValues" >
<property name="alternatingRowColors" >
<bool>true</bool>
</property>
<property name="selectionBehavior" >
<enum>QAbstractItemView::SelectRows</enum>
</property>
</widget>
</item>
<item row="3" column="0" colspan="3" >
<layout class="QHBoxLayout" >
<item>
<widget class="QPushButton" name="configureButton" >
<property name="text" >
<string>Configure</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="generateButton" >
<property name="text" >
<string>Ok</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="cancelButton" >
<property name="text" >
<string>Cancel</string>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" >
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>650</width>
<height>29</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar" />
</widget>
<customwidgets>
<customwidget>
<class>QCMakeCacheView</class>
<extends>QTableView</extends>
<header>QCMakeCacheView.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="CMakeSetup.qrc" />
</resources>
<connections/>
</ui>
#include "QCMake.h"
#include <QCoreApplication>
#include <QDir>
#include "cmake.h"
#include "cmCacheManager.h"
#include "cmSystemTools.h"
QCMake::QCMake(QObject* p)
: QObject(p)
{
static int metaId = qRegisterMetaType<QCMakeCacheProperty>();
static int metaIdList = qRegisterMetaType<QCMakeCachePropertyList>();
QDir appDir(QCoreApplication::applicationDirPath());
#if defined(Q_OS_WIN)
this->CMakeExecutable = appDir.filePath("cmake.exe");
#elif defined(Q_OS_MAC)
# error "need to implement for Mac OS X"
#else
this->CMakeExecutable = appDir.filePath("cmake");
#endif
// TODO: check for existence?
cmSystemTools::DisableRunCommandOutput();
cmSystemTools::SetRunCommandHideConsole(true);
cmSystemTools::SetErrorCallback(QCMake::errorCallback, this);
this->CMakeInstance = new cmake;
this->CMakeInstance->SetProgressCallback(QCMake::progressCallback, this);
}
QCMake::~QCMake()
{
delete this->CMakeInstance;
//cmDynamicLoader::FlushCache();
}
void QCMake::loadCache(const QString& dir)
{
this->setBinaryDirectory(dir);
}
void QCMake::setSourceDirectory(const QString& dir)
{
this->SourceDirectory = dir;
emit this->sourceDirChanged(dir);
}
void QCMake::setBinaryDirectory(const QString& dir)
{
cmCacheManager *cachem = this->CMakeInstance->GetCacheManager();
this->BinaryDirectory = dir;
this->CMakeInstance->GetCacheManager()->LoadCache(dir.toLocal8Bit().data());
QCMakeCachePropertyList props = this->properties();
emit this->propertiesChanged(props);
cmCacheManager::CacheIterator itm = cachem->NewIterator();
if ( itm.Find("CMAKE_HOME_DIRECTORY"))
{
setSourceDirectory(itm.GetValue());
}
}
void QCMake::setGenerator(const QString& generator)
{
}
void QCMake::configure()
{
this->CMakeInstance->SetHomeDirectory(this->SourceDirectory.toAscii().data());
this->CMakeInstance->SetStartDirectory(this->SourceDirectory.toAscii().data());
this->CMakeInstance->SetHomeOutputDirectory(this->BinaryDirectory.toAscii().data());
this->CMakeInstance->SetStartOutputDirectory(this->BinaryDirectory.toAscii().data());
this->CMakeInstance->SetGlobalGenerator(
this->CMakeInstance->CreateGlobalGenerator("Unix Makefiles")); // TODO
this->CMakeInstance->SetCMakeCommand(this->CMakeExecutable.toAscii().data());
this->CMakeInstance->LoadCache();
cmSystemTools::ResetErrorOccuredFlag();
int error = this->CMakeInstance->Configure();
emit this->propertiesChanged(this->properties());
emit this->configureDone(error);
}
void QCMake::generate()
{
cmSystemTools::ResetErrorOccuredFlag();
int error = this->CMakeInstance->Generate();
emit this->generateDone(error);
}
void QCMake::setProperties(const QCMakeCachePropertyList& props)
{
cmCacheManager *cachem = this->CMakeInstance->GetCacheManager();
cmCacheManager::CacheIterator it = cachem->NewIterator();
foreach(QCMakeCacheProperty prop, props)
{
if ( it.Find(prop.Key.toAscii().data()) )
{
it.SetValue(prop.Value.toAscii().data());
}
}
}
QCMakeCachePropertyList QCMake::properties()
{
QCMakeCachePropertyList ret;
cmCacheManager *cachem = this->CMakeInstance->GetCacheManager();
for(cmCacheManager::CacheIterator i = cachem->NewIterator();
!i.IsAtEnd(); i.Next())
{
if(i.GetType() == cmCacheManager::INTERNAL ||
i.GetType() == cmCacheManager::STATIC)
{
continue;
}
QCMakeCacheProperty prop;
prop.Key = i.GetName();
prop.Help = i.GetProperty("HELPSTRING");
prop.Value = i.GetValue();
prop.Advanced = i.GetPropertyAsBool("ADVANCED");
if(i.GetType() == cmCacheManager::BOOL)
{
prop.Type = QCMakeCacheProperty::BOOL;
if(cmSystemTools::IsOn(prop.Value.toAscii().data()))
{
prop.Value = QString("ON");
}
else
{
prop.Value = QString("OFF");
}
}
else if(i.GetType() == cmCacheManager::PATH)
{
prop.Type = QCMakeCacheProperty::PATH;
}
else if(i.GetType() == cmCacheManager::FILEPATH)
{
prop.Type = QCMakeCacheProperty::FILEPATH;
}