From 800cbd0550466794cf853c6fb4dc0349e245220c Mon Sep 17 00:00:00 2001
From: Clinton Stimpson <clinton@elemtech.com>
Date: Fri, 2 Nov 2007 11:50:17 -0400
Subject: ENH:  Beginnings of a Qt UI for CMake.

---
 Source/QtDialog/CMakeLists.txt       |  37 +++++++
 Source/QtDialog/CMakeSetup.cxx       |  21 ++++
 Source/QtDialog/CMakeSetup.qrc       |   5 +
 Source/QtDialog/CMakeSetupDialog.cxx | 193 +++++++++++++++++++++++++++++++++++
 Source/QtDialog/CMakeSetupDialog.h   |  47 +++++++++
 Source/QtDialog/CMakeSetupDialog.png | Bin 0 -> 358 bytes
 Source/QtDialog/CMakeSetupDialog.ui  | 155 ++++++++++++++++++++++++++++
 Source/QtDialog/QCMake.cxx           | 179 ++++++++++++++++++++++++++++++++
 Source/QtDialog/QCMake.h             |  78 ++++++++++++++
 Source/QtDialog/QCMakeCacheView.cxx  | 192 ++++++++++++++++++++++++++++++++++
 Source/QtDialog/QCMakeCacheView.h    | 100 ++++++++++++++++++
 11 files changed, 1007 insertions(+)
 create mode 100644 Source/QtDialog/CMakeLists.txt
 create mode 100644 Source/QtDialog/CMakeSetup.cxx
 create mode 100644 Source/QtDialog/CMakeSetup.qrc
 create mode 100644 Source/QtDialog/CMakeSetupDialog.cxx
 create mode 100644 Source/QtDialog/CMakeSetupDialog.h
 create mode 100644 Source/QtDialog/CMakeSetupDialog.png
 create mode 100644 Source/QtDialog/CMakeSetupDialog.ui
 create mode 100644 Source/QtDialog/QCMake.cxx
 create mode 100644 Source/QtDialog/QCMake.h
 create mode 100644 Source/QtDialog/QCMakeCacheView.cxx
 create mode 100644 Source/QtDialog/QCMakeCacheView.h

diff --git a/Source/QtDialog/CMakeLists.txt b/Source/QtDialog/CMakeLists.txt
new file mode 100644
index 0000000..b6cd7b1
--- /dev/null
+++ b/Source/QtDialog/CMakeLists.txt
@@ -0,0 +1,37 @@
+
+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)
+
diff --git a/Source/QtDialog/CMakeSetup.cxx b/Source/QtDialog/CMakeSetup.cxx
new file mode 100644
index 0000000..655a4cf
--- /dev/null
+++ b/Source/QtDialog/CMakeSetup.cxx
@@ -0,0 +1,21 @@
+
+#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();
+}
+
diff --git a/Source/QtDialog/CMakeSetup.qrc b/Source/QtDialog/CMakeSetup.qrc
new file mode 100644
index 0000000..14357e0
--- /dev/null
+++ b/Source/QtDialog/CMakeSetup.qrc
@@ -0,0 +1,5 @@
+<RCC>
+    <qresource prefix="/Icons" >
+        <file>CMakeSetupDialog.png</file>
+    </qresource>
+</RCC>
diff --git a/Source/QtDialog/CMakeSetupDialog.cxx b/Source/QtDialog/CMakeSetupDialog.cxx
new file mode 100644
index 0000000..7f2ab1d
--- /dev/null
+++ b/Source/QtDialog/CMakeSetupDialog.cxx
@@ -0,0 +1,193 @@
+
+#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;
+  }
+}
+
+
diff --git a/Source/QtDialog/CMakeSetupDialog.h b/Source/QtDialog/CMakeSetupDialog.h
new file mode 100644
index 0000000..71a51d9
--- /dev/null
+++ b/Source/QtDialog/CMakeSetupDialog.h
@@ -0,0 +1,47 @@
+
+#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;
+
+};
+
diff --git a/Source/QtDialog/CMakeSetupDialog.png b/Source/QtDialog/CMakeSetupDialog.png
new file mode 100644
index 0000000..7bbcee4
Binary files /dev/null and b/Source/QtDialog/CMakeSetupDialog.png differ
diff --git a/Source/QtDialog/CMakeSetupDialog.ui b/Source/QtDialog/CMakeSetupDialog.ui
new file mode 100644
index 0000000..9080f98
--- /dev/null
+++ b/Source/QtDialog/CMakeSetupDialog.ui
@@ -0,0 +1,155 @@
+<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>
diff --git a/Source/QtDialog/QCMake.cxx b/Source/QtDialog/QCMake.cxx
new file mode 100644
index 0000000..53519b4
--- /dev/null
+++ b/Source/QtDialog/QCMake.cxx
@@ -0,0 +1,179 @@
+
+#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;
+      }
+    else if(i.GetType() == cmCacheManager::STRING)
+      {
+      prop.Type = QCMakeCacheProperty::STRING;
+      }
+
+    ret.append(prop);
+    }
+
+  return ret;
+}
+  
+void QCMake::interrupt()
+{
+  cmSystemTools::SetFatalErrorOccured();
+}
+
+void QCMake::progressCallback(const char* msg, float percent, void* cd)
+{
+  QCMake* self = reinterpret_cast<QCMake*>(cd);
+  emit self->progressChanged(msg, percent);
+  QCoreApplication::processEvents();
+}
+
+void QCMake::errorCallback(const char* msg, const char* title, bool& stop, void* cd)
+{
+  QCMake* self = reinterpret_cast<QCMake*>(cd);
+  emit self->error(title, msg, &stop);
+}
+
diff --git a/Source/QtDialog/QCMake.h b/Source/QtDialog/QCMake.h
new file mode 100644
index 0000000..1e4b661
--- /dev/null
+++ b/Source/QtDialog/QCMake.h
@@ -0,0 +1,78 @@
+
+#ifndef __QCMake_h
+#define __QCMake_h
+
+#include <QObject>
+#include <QString>
+#include <QList>
+#include <QMetaType>
+
+class cmake;
+
+// struct to represent cache properties in Qt
+struct QCMakeCacheProperty
+{
+  enum PropertyType { BOOL, PATH, FILEPATH, STRING };
+  QString Key;
+  QString Value;
+  QString Help;
+  PropertyType Type;
+  bool Advanced;
+};
+
+// make types usable with QVariant
+Q_DECLARE_METATYPE(QCMakeCacheProperty)
+typedef QList<QCMakeCacheProperty> QCMakeCachePropertyList;
+Q_DECLARE_METATYPE(QCMakeCachePropertyList)
+
+// Qt API for CMake library.
+// Wrapper like class allows for easier integration with 
+// Qt features such as, signal/slot connections, multi-threading, etc..
+class QCMake : public QObject
+{
+  Q_OBJECT
+public:
+  QCMake(QObject* p=0);
+  ~QCMake();
+
+public slots:
+  void loadCache(const QString& dir);
+  void setSourceDirectory(const QString& dir);
+  void setBinaryDirectory(const QString& dir);
+  void setGenerator(const QString& generator);
+  void configure();
+  void generate();
+  void setProperties(const QCMakeCachePropertyList&);
+  void interrupt();
+
+public:
+  QCMakeCachePropertyList properties();
+  QString binaryDirectory();
+  QString sourceDirectory();
+  QString generator();
+
+signals:
+  void propertiesChanged(const QCMakeCachePropertyList& vars);
+  void generatorChanged(const QString& gen);
+  void error(const QString& title, const QString& message, bool*);
+  void sourceDirChanged(const QString& dir);
+  void progressChanged(const QString& msg, float percent);
+  void configureDone(int error);
+  void generateDone(int error);
+  void configureReady();
+  void generateReady();
+
+protected:
+  cmake* CMakeInstance;
+
+  static void progressCallback(const char* msg, float percent, void* cd);
+  static void errorCallback(const char* msg, const char* title, bool&, void* cd);
+
+  QString SourceDirectory;
+  QString BinaryDirectory;
+  QString Generator;
+  QString CMakeExecutable;
+};
+
+#endif // __QCMake_h
+
diff --git a/Source/QtDialog/QCMakeCacheView.cxx b/Source/QtDialog/QCMakeCacheView.cxx
new file mode 100644
index 0000000..f66f60f
--- /dev/null
+++ b/Source/QtDialog/QCMakeCacheView.cxx
@@ -0,0 +1,192 @@
+
+#include "QCMakeCacheView.h"
+
+#include <QToolButton>
+#include <QFileDialog>
+#include <QHBoxLayout>
+#include <QHeaderView>
+#include <QEvent>
+
+QCMakeCacheView::QCMakeCacheView(QWidget* p)
+  : QTableView(p)
+{
+  QCMakeCacheModel* m = new QCMakeCacheModel(this);
+  this->setModel(m);
+  this->horizontalHeader()->setStretchLastSection(true);
+  this->verticalHeader()->hide();
+
+  QCMakeCacheModelDelegate* delegate = new QCMakeCacheModelDelegate(this);
+  this->setItemDelegate(delegate);
+}
+
+bool QCMakeCacheView::event(QEvent* e)
+{
+  if(e->type() == QEvent::Polish)
+    {
+    // initialize the table view column size
+    int colWidth = this->columnWidth(0) + this->columnWidth(1);
+    this->setColumnWidth(0, colWidth/2);
+    this->setColumnWidth(1, colWidth/2);
+    }
+  return QTableView::event(e);
+}
+  
+QCMakeCacheModel* QCMakeCacheView::cacheModel() const
+{
+  return qobject_cast<QCMakeCacheModel*>(this->model());
+}
+
+QCMakeCacheModel::QCMakeCacheModel(QObject* p)
+  : QAbstractTableModel(p)
+{
+}
+
+QCMakeCacheModel::~QCMakeCacheModel()
+{
+}
+
+void QCMakeCacheModel::setProperties(const QCMakeCachePropertyList& props)
+{
+  this->Properties = props;
+  this->reset();
+}
+  
+QCMakeCachePropertyList QCMakeCacheModel::properties() const
+{
+  return this->Properties;
+}
+
+int QCMakeCacheModel::columnCount ( const QModelIndex & parent ) const
+{
+  return 2;
+}
+
+QVariant QCMakeCacheModel::data ( const QModelIndex & index, int role ) const
+{
+  if(index.column() == 0 && (role == Qt::DisplayRole || role == Qt::EditRole))
+  {
+    return this->Properties[index.row()].Key;
+  }
+  else if(index.column() == 1 && (role == Qt::DisplayRole || role == Qt::EditRole))
+  {
+    return this->Properties[index.row()].Value;
+  }
+  else if(role == QCMakeCacheModel::HelpRole)
+  {
+    return this->Properties[index.row()].Help;
+  }
+  else if(role == QCMakeCacheModel::TypeRole)
+  {
+    return this->Properties[index.row()].Type;
+  }
+  else if(role == QCMakeCacheModel::AdvancedRole)
+  {
+    return this->Properties[index.row()].Advanced;
+  }
+  return QVariant();
+}
+
+QModelIndex QCMakeCacheModel::parent ( const QModelIndex & index ) const
+{
+  return QModelIndex();
+}
+
+int QCMakeCacheModel::rowCount ( const QModelIndex & parent ) const
+{
+  if(parent.isValid())
+    return 0;
+  return this->Properties.count();
+}
+
+QVariant QCMakeCacheModel::headerData ( int section, Qt::Orientation orient, int role ) const
+{
+  if(role == Qt::DisplayRole && orient == Qt::Horizontal)
+  {
+    return section == 0 ? "Name" : "Value";
+  }
+  return QVariant();
+}
+  
+Qt::ItemFlags QCMakeCacheModel::flags ( const QModelIndex& index ) const
+{
+  if(index.column() == 1)
+  {
+    return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled;
+  }
+  return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
+}
+
+
+bool QCMakeCacheModel::setData ( const QModelIndex & index, const QVariant& value, int role )
+{
+  if(index.column() == 0 && (role == Qt::DisplayRole || role == Qt::EditRole))
+  {
+    this->Properties[index.row()].Key = value.toString();
+  }
+  else if(index.column() == 1 && (role == Qt::DisplayRole || role == Qt::EditRole))
+  {
+    this->Properties[index.row()].Value = value.toString();
+  }
+  return false;
+}
+
+
+
+QCMakeCacheModelDelegate::QCMakeCacheModelDelegate(QObject* p)
+  : QItemDelegate(p)
+{
+}
+
+QWidget* QCMakeCacheModelDelegate::createEditor(QWidget* parent, 
+    const QStyleOptionViewItem&, const QModelIndex& index) const
+{
+  QVariant type = index.data(QCMakeCacheModel::TypeRole);
+  if(type == QCMakeCacheProperty::BOOL)
+  {
+    return new QCMakeCacheBoolEditor(index.data().toString(), parent);
+  }
+  else if(type == QCMakeCacheProperty::PATH)
+  {
+    return new QCMakeCachePathEditor(index.data().toString(), parent);
+  }
+  else if(type == QCMakeCacheProperty::FILEPATH)
+  {
+  }
+
+  return new QLineEdit(parent);
+}
+
+
+QCMakeCachePathEditor::QCMakeCachePathEditor(const QString& file, QWidget* p)
+  : QWidget(p), LineEdit(this)
+{
+  QHBoxLayout* l = new QHBoxLayout(this);
+  l->setMargin(0);
+  l->setSpacing(0);
+  l->addWidget(&this->LineEdit);
+  QToolButton* tb = new QToolButton(this);
+  tb->setText("...");
+  l->addWidget(tb);
+  QObject::connect(tb, SIGNAL(clicked(bool)),
+                   this, SLOT(chooseFile()));
+  this->LineEdit.setText(file);
+  tb->setFocusProxy(&this->LineEdit);
+  this->setFocusProxy(&this->LineEdit);
+}
+
+void QCMakeCachePathEditor::chooseFile()
+{
+  QString path = QFileDialog::getExistingDirectory(this, "TODO", this->value());
+  if(!path.isEmpty())
+  {
+    this->LineEdit.setText(path);
+  }
+}
+
+QString QCMakeCachePathEditor::value() const
+{
+  return this->LineEdit.text();
+}
+
+
+
diff --git a/Source/QtDialog/QCMakeCacheView.h b/Source/QtDialog/QCMakeCacheView.h
new file mode 100644
index 0000000..a284ecd
--- /dev/null
+++ b/Source/QtDialog/QCMakeCacheView.h
@@ -0,0 +1,100 @@
+
+#ifndef QCMakeCacheView_h
+#define QCMakeCacheView_h
+
+#include <QTableView>
+#include <QAbstractTableModel>
+#include <QComboBox>
+#include <QLineEdit>
+#include <QItemDelegate>
+
+#include "QCMake.h"
+class QCMakeCacheModel;
+
+
+/// Qt view class for cache properties
+class QCMakeCacheView : public QTableView
+{
+  Q_OBJECT
+public:
+  QCMakeCacheView(QWidget* p);
+
+  QCMakeCacheModel* cacheModel() const;
+
+protected:
+  bool event(QEvent*);
+};
+
+/// Qt model class for cache properties
+class QCMakeCacheModel : public QAbstractTableModel
+{
+  Q_OBJECT
+public:
+  QCMakeCacheModel(QObject* parent);
+  ~QCMakeCacheModel();
+
+  enum { HelpRole = Qt::UserRole, TypeRole, AdvancedRole };
+
+public slots:
+  void setProperties(const QCMakeCachePropertyList& props);
+
+public:
+  int columnCount ( const QModelIndex & parent ) const;
+  QVariant data ( const QModelIndex & index, int role ) const;
+  QModelIndex parent ( const QModelIndex & index ) const;
+  int rowCount ( const QModelIndex & parent ) const;
+  QVariant headerData ( int section, Qt::Orientation orient, int role ) const;
+  Qt::ItemFlags flags ( const QModelIndex& index ) const;
+  bool setData ( const QModelIndex& index, const QVariant& value, int role );
+
+  QCMakeCachePropertyList properties() const;
+
+protected:
+  QCMakeCachePropertyList Properties;
+};
+
+/// Qt delegate class for interaction (or other customization) with cache properties
+class QCMakeCacheModelDelegate : public QItemDelegate
+{
+  Q_OBJECT
+public:
+  QCMakeCacheModelDelegate(QObject* p);
+  QWidget* createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index ) const;
+};
+
+/// Editor widget for editing paths
+class QCMakeCachePathEditor : public QWidget
+{
+  Q_OBJECT
+  Q_PROPERTY(QString value READ value USER true)
+public:
+  QCMakeCachePathEditor(const QString& file, QWidget* p);
+  QString value() const;
+protected slots:
+  void chooseFile();
+protected:
+  QLineEdit LineEdit;
+};
+
+/// Editor widget for editing file paths
+class QCMakeCacheFilePathEditor : public QWidget
+{
+};
+
+/// Editor widget for editing booleans
+class QCMakeCacheBoolEditor : public QComboBox
+{
+  Q_OBJECT
+  Q_PROPERTY(QString value READ currentText USER true)
+public:
+  QCMakeCacheBoolEditor(const QString& val, QWidget* p)
+    : QComboBox(p)
+  {
+    this->addItem("ON");
+    this->addItem("OFF");
+    this->setCurrentIndex(val == "ON" ? 0 : 1);
+  }
+};
+
+#endif
+
-- 
cgit v0.12