Browse Source

ENH: Add interrupt button near progress bar.
Implement help button.
Implement cancel button.
Add scrollable output window.
Replace ON/OFF & combobox editors with checkboxes.
Tab/backtab in cache table jumps between values (not names and values)
Add tooltips to show help strings.
Add application icon and qtmain for Windows.

BUG: Fix save of cache values on configure.

Clinton Stimpson 18 years ago
parent
commit
77ad85a6ab

+ 5 - 4
Source/QtDialog/CMakeLists.txt

@@ -6,7 +6,6 @@ IF(NOT QT4_FOUND)
   MESSAGE(SEND_ERROR "Failed to find Qt 4.3 or greater.")
 ELSE(NOT QT4_FOUND)
 
-  SET(QT_USE_QTMAIN TRUE)
   INCLUDE(${QT_USE_FILE})
 
   SET(SRCS
@@ -18,7 +17,6 @@ ELSE(NOT QT4_FOUND)
     QCMakeCacheView.cxx
     QCMakeCacheView.h
     )
-
   QT4_WRAP_UI(UI_SRCS 
     CMakeSetupDialog.ui
     )
@@ -30,13 +28,16 @@ ELSE(NOT QT4_FOUND)
   QT4_ADD_RESOURCES(RC_SRCS CMakeSetup.qrc)
 
   SET(SRCS ${SRCS} ${UI_SRCS} ${MOC_SRCS} ${RC_SRCS})
+  IF(WIN32)
+    SET(SRCS ${SRCS} CMakeSetup.rc)
+  ENDIF(WIN32)
+  # TODO  Mac OS X icon
 
   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)
+  TARGET_LINK_LIBRARIES(QtDialog CMakeLib ${QT_QTMAIN_LIBRARY} ${QT_LIBRARIES})
 
 ENDIF(NOT QT4_FOUND)
 

+ 1 - 0
Source/QtDialog/CMakeSetup.cxx

@@ -26,6 +26,7 @@ int main(int argc, char** argv)
   QApplication app(argc, argv);
   app.setApplicationName("CMakeSetup");
   app.setOrganizationName("Kitware");
+  app.setWindowIcon(QIcon(":/Icons/CMakeSetup.png"));
 
   // TODO handle CMake args
     

BIN
Source/QtDialog/CMakeSetup.ico


+ 0 - 0
Source/QtDialog/CMakeSetupDialog.png → Source/QtDialog/CMakeSetup.png


+ 1 - 1
Source/QtDialog/CMakeSetup.qrc

@@ -1,5 +1,5 @@
 <RCC>
     <qresource prefix="/Icons" >
-        <file>CMakeSetupDialog.png</file>
+        <file>CMakeSetup.png</file>
     </qresource>
 </RCC>

+ 1 - 0
Source/QtDialog/CMakeSetup.rc

@@ -0,0 +1 @@
+IDI_ICON1   ICON  DISCARDABLE  "CMakeSetup.ico"

+ 133 - 40
Source/QtDialog/CMakeSetupDialog.cxx

@@ -21,6 +21,9 @@
 #include <QThread>
 #include <QProgressBar>
 #include <QMessageBox>
+#include <QStatusBar>
+#include <QToolButton>
+#include <QDialogButtonBox>
 
 #include "QCMake.h"
 #include "QCMakeCacheView.h"
@@ -44,9 +47,17 @@ protected:
 CMakeSetupDialog::CMakeSetupDialog()
 {
   // create the GUI
-  this->setupUi(this);
+  this->resize(700, 500);
+  QWidget* cont = new QWidget(this);
+  this->setupUi(cont);
+  this->setCentralWidget(cont);
   this->ProgressBar = new QProgressBar();
   this->ProgressBar->setRange(0,100);
+  this->InterruptButton = new QToolButton();
+  this->InterruptButton->setEnabled(false);
+  this->InterruptButton->setIcon(
+    this->style()->standardPixmap(QStyle::SP_DialogCancelButton));
+  this->statusBar()->addPermanentWidget(this->InterruptButton);
   this->statusBar()->addPermanentWidget(this->ProgressBar);
   
   // start the cmake worker thread
@@ -64,36 +75,26 @@ void CMakeSetupDialog::initialize()
       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)),
+  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)),
+  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)),
+  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)),
+  QObject::connect(this->BinaryDirectory, SIGNAL(editTextChanged(QString)),
                    this->CMakeThread->CMakeInstance, SLOT(setBinaryDirectory(QString)));
 
   QObject::connect(this->CMakeThread->CMakeInstance, SIGNAL(sourceDirChanged(QString)),
@@ -105,6 +106,16 @@ void CMakeSetupDialog::initialize()
   QObject::connect(this->CMakeThread->CMakeInstance, SIGNAL(error(QString, QString, bool*)),
                    this, SLOT(error(QString,QString,bool*)), Qt::BlockingQueuedConnection);
 
+  QObject::connect(this->InterruptButton, SIGNAL(clicked(bool)),
+                   this->CMakeThread->CMakeInstance, SLOT(interrupt()));
+  QObject::connect(this->InterruptButton, SIGNAL(clicked(bool)),
+                   this, SLOT(doInterrupt()));
+  
+  QObject::connect(this->CMakeThread->CMakeInstance, SIGNAL(outputMessage(QString)),
+                   this->Output, SLOT(append(QString)));
+  
+  QObject::connect(this->HelpButton, SIGNAL(clicked(bool)),
+                   this, SLOT(doHelp()));
 }
 
 CMakeSetupDialog::~CMakeSetupDialog()
@@ -116,49 +127,119 @@ CMakeSetupDialog::~CMakeSetupDialog()
   
 void CMakeSetupDialog::doConfigure()
 {
-  emit this->propertiesChanged(this->CacheValues->cacheModel()->properties());
-  emit this->configure();
+  this->InterruptButton->setEnabled(true);
+  this->setEnabledState(false);
+  this->Output->clear();
+  QMetaObject::invokeMethod(this->CMakeThread->CMakeInstance,
+    "setProperties", Qt::QueuedConnection, 
+    Q_ARG(QCMakeCachePropertyList,
+      this->CacheValues->cacheModel()->properties()));
+  QMetaObject::invokeMethod(this->CMakeThread->CMakeInstance,
+    "configure", Qt::QueuedConnection);
 }
 
 void CMakeSetupDialog::finishConfigure(int error)
 {
+  this->InterruptButton->setEnabled(false);
+  this->setEnabledState(true);
   this->ProgressBar->reset();
-  this->statusBar()->showMessage("Configure Done", 2000);
+  this->statusBar()->showMessage(tr("Configure Done"), 2000);
   if(error != 0)
-  {
-    bool dummy;
-    this->error("Error", "Error in configuration process, project files may be invalid", &dummy);
-  }
+    {
+    QMessageBox::critical(this, tr("Error"), 
+      tr("Error in configuration process, project files may be invalid"), 
+      QMessageBox::Ok);
+    }
 }
 
 void CMakeSetupDialog::finishGenerate(int error)
 {
+  this->InterruptButton->setEnabled(false);
+  this->setEnabledState(true);
   this->ProgressBar->reset();
-  this->statusBar()->showMessage("Generate Done", 2000);
+  this->statusBar()->showMessage(tr("Generate Done"), 2000);
   if(error != 0)
-  {
-    bool dummy;
-    this->error("Error", "Error in generation process, project files may be invalid", &dummy);
-  }
+    {
+    QMessageBox::critical(this, tr("Error"), 
+      tr("Error in generation process, project files may be invalid"),
+      QMessageBox::Ok);
+    }
+  else
+    {
+    QApplication::quit();
+    }
 }
 
 void CMakeSetupDialog::doOk()
 {
-  emit this->ok();
+  this->InterruptButton->setEnabled(true);
+  this->setEnabledState(false);
+  this->Output->clear();
+  QMetaObject::invokeMethod(this->CMakeThread->CMakeInstance,
+    "generate", Qt::QueuedConnection);
 }
 
 void CMakeSetupDialog::doCancel()
 {
-  emit this->cancel();
+  if(this->CacheValues->cacheModel()->isDirty())
+    {
+    QString message = tr("You have changed options but not rebuilt, "
+                    "are you sure you want to exit?");
+    QString title = tr("Confirm Exit");
+    QMessageBox::StandardButton btn =
+      QMessageBox::critical(this, title, message, QMessageBox::Ok | QMessageBox::Cancel);
+    if(btn == QMessageBox::Cancel)
+      {
+      return;
+      }
+    }
+
+  QApplication::quit();
 }
 
 void CMakeSetupDialog::doHelp()
 {
+  QString msg = tr("CMake is used to configure and generate build files for"
+    "software projects.   The basic steps for configuring a project are as"
+    "follows:\r\n\r\n1. Select the source directory for the project.  This should"
+    "contain the CMakeLists.txt files for the project.\r\n\r\n2. Select the build"
+    "directory for the project.   This is the directory where the project will be"
+    "built.  It can be the same or a different directory than the source"
+    "directory.   For easy clean up, a separate build directory is recommended."
+    "CMake will create the directory if it does not exist.\r\n\r\n3. Once the"
+    "source and binary directories are selected, it is time to press the"
+    "Configure button.  This will cause CMake to read all of the input files and"
+    "discover all the variables used by the project.   The first time a variable"
+    "is displayed it will be in Red.   Users should inspect red variables making"
+    "sure the values are correct.   For some projects the Configure process can"
+    "be iterative, so continue to press the Configure button until there are no"
+    "longer red entries.\r\n\r\n4. Once there are no longer red entries, you"
+    "should click the OK button.  This will write the build files to the build"
+    "directory and exit CMake.");
+
+  QDialog dialog;
+  QVBoxLayout* l = new QVBoxLayout(&dialog);
+  QLabel* label = new QLabel(&dialog);
+  l->addWidget(label);
+  label->setText(msg);
+  label->setWordWrap(true);
+  QDialogButtonBox* btns = new QDialogButtonBox(QDialogButtonBox::Ok,
+                                                Qt::Horizontal, &dialog);
+  QObject::connect(btns, SIGNAL(accepted()), &dialog, SLOT(accept()));
+  l->addWidget(btns);
+  dialog.exec();
+}
+
+void CMakeSetupDialog::doInterrupt()
+{
+  this->InterruptButton->setEnabled(false);
+  this->statusBar()->showMessage(tr("Interrupting..."));
 }
 
 void CMakeSetupDialog::doSourceBrowse()
 {
-  QString dir = QFileDialog::getExistingDirectory(this, "TODO", this->SourceDirectory->text());
+  QString dir = QFileDialog::getExistingDirectory(this, 
+    tr("Enter Path to Source"), this->SourceDirectory->text());
   if(!dir.isEmpty())
     {
     this->updateSourceDirectory(dir);
@@ -172,7 +253,8 @@ void CMakeSetupDialog::updateSourceDirectory(const QString& dir)
 
 void CMakeSetupDialog::doBinaryBrowse()
 {
-  QString dir = QFileDialog::getExistingDirectory(this, "TODO", this->BinaryDirectory->currentText());
+  QString dir = QFileDialog::getExistingDirectory(this, 
+    tr("Enter Path to Build"), this->BinaryDirectory->currentText());
   if(!dir.isEmpty())
     {
     this->setBinaryDirectory(dir);
@@ -182,18 +264,16 @@ void CMakeSetupDialog::doBinaryBrowse()
 void CMakeSetupDialog::setBinaryDirectory(const QString& dir)
 {
   if(dir != this->BinaryDirectory->currentText())
-  {
+    {
+    this->Output->clear();
     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));
-  }
+  this->statusBar()->showMessage(msg);
+  this->ProgressBar->setValue(qRound(percent * 100));
 }
   
 void CMakeSetupDialog::error(const QString& title, const QString& message, bool* cancel)
@@ -201,9 +281,22 @@ void CMakeSetupDialog::error(const QString& title, const QString& message, bool*
   QMessageBox::StandardButton btn =
     QMessageBox::critical(this, title, message, QMessageBox::Ok | QMessageBox::Cancel);
   if(btn == QMessageBox::Cancel)
-  {
+    {
     *cancel = false;
-  }
+    }
+}
+
+void CMakeSetupDialog::setEnabledState(bool enabled)
+{
+  this->CacheValues->setEnabled(enabled);
+  this->SourceDirectory->setEnabled(enabled);
+  this->BrowseSourceDirectoryButton->setEnabled(enabled);
+  this->BinaryDirectory->setEnabled(enabled);
+  this->BrowseBinaryDirectoryButton->setEnabled(enabled);
+  this->ConfigureButton->setEnabled(enabled);
+  this->GenerateButton->setEnabled(enabled);
+  this->CancelButton->setEnabled(enabled);
+  this->HelpButton->setEnabled(enabled);
 }
 
 

+ 4 - 8
Source/QtDialog/CMakeSetupDialog.h

@@ -22,6 +22,7 @@
 class QCMakeThread;
 class CMakeCacheModel;
 class QProgressBar;
+class QToolButton;
 
 /// Qt user interface for CMake
 class CMakeSetupDialog : public QMainWindow, public Ui::CMakeSetupDialog
@@ -31,18 +32,13 @@ 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 doInterrupt();
   void finishConfigure(int error);
   void finishGenerate(int error);
   void error(const QString& title, const QString& message, bool* cancel);
@@ -51,13 +47,13 @@ protected slots:
   void doBinaryBrowse();
   void updateSourceDirectory(const QString& dir);
   void setBinaryDirectory(const QString& dir);
-
   void showProgress(const QString& msg, float percent);
+  void setEnabledState(bool);
 
 protected:
 
   QCMakeThread* CMakeThread;
   QProgressBar* ProgressBar;
-
+  QToolButton* InterruptButton;
 };
 

+ 129 - 124
Source/QtDialog/CMakeSetupDialog.ui

@@ -1,82 +1,83 @@
 <ui version="4.0" >
  <class>CMakeSetupDialog</class>
- <widget class="QMainWindow" name="CMakeSetupDialog" >
+ <widget class="QWidget" name="CMakeSetupDialog" >
   <property name="geometry" >
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>650</width>
-    <height>505</height>
+    <width>673</width>
+    <height>460</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>
+  <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="QVBoxLayout" >
+      <property name="leftMargin" >
+       <number>0</number>
       </property>
-      <property name="frameShadow" >
-       <enum>QFrame::Raised</enum>
+      <property name="topMargin" >
+       <number>0</number>
       </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" >
+      <property name="rightMargin" >
+       <number>0</number>
+      </property>
+      <property name="bottomMargin" >
+       <number>0</number>
+      </property>
+      <item>
+       <layout class="QGridLayout" >
+        <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>
+       </layout>
+      </item>
+      <item>
+       <widget class="QSplitter" name="splitter" >
+        <property name="orientation" >
+         <enum>Qt::Vertical</enum>
+        </property>
         <widget class="QCMakeCacheView" name="CacheValues" >
          <property name="alternatingRowColors" >
           <bool>true</bool>
@@ -85,61 +86,65 @@
           <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 class="QTextEdit" name="Output" >
+         <property name="lineWrapMode" >
+          <enum>QTextEdit::NoWrap</enum>
+         </property>
+         <property name="readOnly" >
+          <bool>true</bool>
+         </property>
+        </widget>
+       </widget>
+      </item>
+      <item>
+       <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>
+         <widget class="QPushButton" name="HelpButton" >
+          <property name="text" >
+           <string>Help</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>
  <customwidgets>
   <customwidget>

+ 18 - 10
Source/QtDialog/QCMake.cxx

@@ -118,9 +118,17 @@ void QCMake::setProperties(const QCMakeCachePropertyList& props)
     {
     if ( it.Find(prop.Key.toAscii().data()) )
       {
-      it.SetValue(prop.Value.toAscii().data());
+      if(prop.Value.type() == QVariant::Bool)
+        {
+        it.SetValue(prop.Value.toBool() ? "ON" : "OFF");
+        }
+      else
+        {
+        it.SetValue(prop.Value.toString().toAscii().data());
+        }
       }
     }
+  cachem->SaveCache(this->BinaryDirectory.toAscii().data());
 }
 
 QCMakeCachePropertyList QCMake::properties()
@@ -147,14 +155,7 @@ QCMakeCachePropertyList QCMake::properties()
     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");
-        }
+      prop.Value = cmSystemTools::IsOn(i.GetValue());
       }
     else if(i.GetType() == cmCacheManager::PATH)
       {
@@ -183,7 +184,14 @@ void QCMake::interrupt()
 void QCMake::progressCallback(const char* msg, float percent, void* cd)
 {
   QCMake* self = reinterpret_cast<QCMake*>(cd);
-  emit self->progressChanged(msg, percent);
+  if(percent >= 0)
+    {
+    emit self->progressChanged(msg, percent);
+    }
+  else
+    {
+    emit self->outputMessage(msg);
+    }
   QCoreApplication::processEvents();
 }
 

+ 31 - 7
Source/QtDialog/QCMake.h

@@ -20,17 +20,19 @@
 
 #include <QObject>
 #include <QString>
+#include <QVariant>
 #include <QList>
 #include <QMetaType>
 
 class cmake;
 
-// struct to represent cache properties in Qt
+/// struct to represent cache properties in Qt
+/// Value is of type String or Bool
 struct QCMakeCacheProperty
 {
   enum PropertyType { BOOL, PATH, FILEPATH, STRING };
   QString Key;
-  QString Value;
+  QVariant Value;
   QString Help;
   PropertyType Type;
   bool Advanced;
@@ -41,9 +43,9 @@ 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..
+/// 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
@@ -52,31 +54,53 @@ public:
   ~QCMake();
 
 public slots:
+  /// load the cache file in a directory
   void loadCache(const QString& dir);
+  /// set the source directory containing the source
   void setSourceDirectory(const QString& dir);
+  /// set the binary directory to build in
   void setBinaryDirectory(const QString& dir);
+  /// set the desired generator to use
   void setGenerator(const QString& generator);
+  /// do the configure step
   void configure();
+  /// generate the files
   void generate();
+  /// set the property values
   void setProperties(const QCMakeCachePropertyList&);
+  /// interrupt the configure or generate process
   void interrupt();
 
 public:
+  /// get the list of cache properties
   QCMakeCachePropertyList properties();
+  /// get the current binary directory
   QString binaryDirectory();
+  /// get the current source directory
   QString sourceDirectory();
+  /// get the current generator
   QString generator();
+  /// get the available generators
+  QStringList availableGenerators();
 
 signals:
+  /// signal when properties change (during read from disk or configure process)
   void propertiesChanged(const QCMakeCachePropertyList& vars);
+  /// signal when the generator changes
   void generatorChanged(const QString& gen);
+  /// signal when there is an error message
   void error(const QString& title, const QString& message, bool*);
+  /// signal when the source directory changes (binary directory already
+  /// containing a CMakeCache.txt file)
   void sourceDirChanged(const QString& dir);
+  /// signal for progress events
   void progressChanged(const QString& msg, float percent);
+  /// signal when configure is done
   void configureDone(int error);
+  /// signal when generate is done
   void generateDone(int error);
-  void configureReady();
-  void generateReady();
+  /// signal when there is an output message
+  void outputMessage(const QString& msg);
 
 protected:
   cmake* CMakeInstance;

+ 124 - 45
Source/QtDialog/QCMakeCacheView.cxx

@@ -24,7 +24,7 @@
 #include <QEvent>
 
 QCMakeCacheView::QCMakeCacheView(QWidget* p)
-  : QTableView(p)
+  : QTableView(p), Init(false)
 {
   QCMakeCacheModel* m = new QCMakeCacheModel(this);
   this->setModel(m);
@@ -35,16 +35,17 @@ QCMakeCacheView::QCMakeCacheView(QWidget* p)
   this->setItemDelegate(delegate);
 }
 
-bool QCMakeCacheView::event(QEvent* e)
+void QCMakeCacheView::showEvent(QShowEvent* e)
 {
-  if(e->type() == QEvent::Polish)
+  if(!this->Init)
     {
     // initialize the table view column size
     int colWidth = this->columnWidth(0) + this->columnWidth(1);
     this->setColumnWidth(0, colWidth/2);
     this->setColumnWidth(1, colWidth/2);
+    this->Init = true;
     }
-  return QTableView::event(e);
+  return QTableView::showEvent(e);
 }
   
 QCMakeCacheModel* QCMakeCacheView::cacheModel() const
@@ -52,8 +53,42 @@ QCMakeCacheModel* QCMakeCacheView::cacheModel() const
   return qobject_cast<QCMakeCacheModel*>(this->model());
 }
 
+QModelIndex QCMakeCacheView::moveCursor(CursorAction act, 
+  Qt::KeyboardModifiers mod)
+{
+  // tab through values only (not names)
+  QModelIndex current = this->currentIndex();
+  if(act == MoveNext)
+    {
+    if(!current.isValid())
+      {
+      return this->model()->index(0, 1);
+      }
+    else if(current.column() == 0)
+      {
+      return this->model()->index(current.row(), 1);
+      }
+    else
+      {
+      return this->model()->index(current.row()+1, 1);
+      }
+    }
+  else if(act == MovePrevious)
+    {
+    if(!current.isValid())
+      {
+      return this->model()->index(0, 1);
+      }
+    else
+      {
+      return this->model()->index(current.row()-1, 1);
+      }
+    }
+  return QTableView::moveCursor(act, mod);
+}
+
 QCMakeCacheModel::QCMakeCacheModel(QObject* p)
-  : QAbstractTableModel(p)
+  : QAbstractTableModel(p), IsDirty(false)
 {
 }
 
@@ -61,10 +96,16 @@ QCMakeCacheModel::~QCMakeCacheModel()
 {
 }
 
+bool QCMakeCacheModel::isDirty() const
+{
+  return this->IsDirty;
+}
+
 void QCMakeCacheModel::setProperties(const QCMakeCachePropertyList& props)
 {
   this->Properties = props;
   this->reset();
+  this->IsDirty = false;
 }
   
 QCMakeCachePropertyList QCMakeCacheModel::properties() const
@@ -80,25 +121,40 @@ int QCMakeCacheModel::columnCount ( const QModelIndex & parent ) const
 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() == 0 && role == Qt::ToolTipRole)
+    {
+    return this->data(index, Qt::DisplayRole).toString() + "\n" +
+           this->data(index, QCMakeCacheModel::HelpRole).toString();
+    }
   else if(index.column() == 1 && (role == Qt::DisplayRole || role == Qt::EditRole))
-  {
-    return this->Properties[index.row()].Value;
-  }
+    {
+    if(this->Properties[index.row()].Type != QCMakeCacheProperty::BOOL)
+      {
+      return this->Properties[index.row()].Value;
+      }
+    }
+  else if(index.column() == 1 && role == Qt::CheckStateRole)
+    {
+    if(this->Properties[index.row()].Type == QCMakeCacheProperty::BOOL)
+      {
+      return this->Properties[index.row()].Value.toBool() ? Qt::Checked : Qt::Unchecked;
+      }
+    }
   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();
 }
 
@@ -110,39 +166,59 @@ QModelIndex QCMakeCacheModel::parent ( const QModelIndex & index ) const
 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
 {
+  // return header labels
   if(role == Qt::DisplayRole && orient == Qt::Horizontal)
-  {
+    {
     return section == 0 ? "Name" : "Value";
-  }
+    }
   return QVariant();
 }
   
 Qt::ItemFlags QCMakeCacheModel::flags ( const QModelIndex& index ) const
 {
+  Qt::ItemFlags f = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
+  // all column 1's are editable
   if(index.column() == 1)
-  {
-    return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled;
-  }
-  return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
+    {
+    f |= Qt::ItemIsEditable;
+    // booleans are editable in place
+    if(this->Properties[index.row()].Type == QCMakeCacheProperty::BOOL)
+      {
+      f |= Qt::ItemIsUserCheckable;
+      }
+    }
+  return f;
 }
 
 
 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();
-  }
+    this->IsDirty = true;
+    emit this->dataChanged(index, index);
+    }
   else if(index.column() == 1 && (role == Qt::DisplayRole || role == Qt::EditRole))
-  {
+    {
     this->Properties[index.row()].Value = value.toString();
-  }
+    this->IsDirty = true;
+    emit this->dataChanged(index, index);
+    }
+  else if(index.column() == 1 && (role == Qt::CheckStateRole))
+    {
+    this->Properties[index.row()].Value = value.toInt() == Qt::Checked;
+    this->IsDirty = true;
+    emit this->dataChanged(index, index);
+    }
   return false;
 }
 
@@ -158,23 +234,24 @@ QWidget* QCMakeCacheModelDelegate::createEditor(QWidget* parent,
 {
   QVariant type = index.data(QCMakeCacheModel::TypeRole);
   if(type == QCMakeCacheProperty::BOOL)
-  {
-    return new QCMakeCacheBoolEditor(index.data().toString(), parent);
-  }
+    {
+    return NULL;
+    }
   else if(type == QCMakeCacheProperty::PATH)
-  {
-    return new QCMakeCachePathEditor(index.data().toString(), parent);
-  }
+    {
+    return new QCMakeCachePathEditor(index.data().toString(), false, parent);
+    }
   else if(type == QCMakeCacheProperty::FILEPATH)
-  {
-  }
+    {
+    return new QCMakeCachePathEditor(index.data().toString(), true, parent);
+    }
 
   return new QLineEdit(parent);
 }
 
 
-QCMakeCachePathEditor::QCMakeCachePathEditor(const QString& file, QWidget* p)
-  : QWidget(p), LineEdit(this)
+QCMakeCachePathEditor::QCMakeCachePathEditor(const QString& file, bool fp, QWidget* p)
+  : QWidget(p), LineEdit(this), IsFilePath(fp)
 {
   QHBoxLayout* l = new QHBoxLayout(this);
   l->setMargin(0);
@@ -192,17 +269,19 @@ QCMakeCachePathEditor::QCMakeCachePathEditor(const QString& file, QWidget* p)
 
 void QCMakeCachePathEditor::chooseFile()
 {
-  QString path = QFileDialog::getExistingDirectory(this, "TODO", this->value());
+  QString path;
+  if(this->IsFilePath)
+    {
+    path = QFileDialog::getOpenFileName(this, "TODO");
+    }
+  else
+    {
+    path = QFileDialog::getExistingDirectory(this, "TODO", this->value());
+    }
   if(!path.isEmpty())
-  {
+    {
     this->LineEdit.setText(path);
-  }
-}
-
-QString QCMakeCachePathEditor::value() const
-{
-  return this->LineEdit.text();
+    }
 }
 
 
-

+ 14 - 25
Source/QtDialog/QCMakeCacheView.h

@@ -20,7 +20,7 @@
 
 #include <QTableView>
 #include <QAbstractTableModel>
-#include <QComboBox>
+#include <QCheckBox>
 #include <QLineEdit>
 #include <QItemDelegate>
 
@@ -38,7 +38,9 @@ public:
   QCMakeCacheModel* cacheModel() const;
 
 protected:
-  bool event(QEvent*);
+  QModelIndex moveCursor(CursorAction, Qt::KeyboardModifiers);
+  void showEvent(QShowEvent* e);
+  bool Init;
 };
 
 /// Qt model class for cache properties
@@ -55,6 +57,7 @@ public slots:
   void setProperties(const QCMakeCachePropertyList& props);
 
 public:
+  // satisfy [pure] virtuals
   int columnCount ( const QModelIndex & parent ) const;
   QVariant data ( const QModelIndex & index, int role ) const;
   QModelIndex parent ( const QModelIndex & index ) const;
@@ -63,10 +66,14 @@ public:
   Qt::ItemFlags flags ( const QModelIndex& index ) const;
   bool setData ( const QModelIndex& index, const QVariant& value, int role );
 
+  // flag if a cache property has been modified
+  bool isDirty() const;
+  // get the properties
   QCMakeCachePropertyList properties() const;
 
 protected:
   QCMakeCachePropertyList Properties;
+  bool IsDirty;
 };
 
 /// Qt delegate class for interaction (or other customization) with cache properties
@@ -75,41 +82,23 @@ class QCMakeCacheModelDelegate : public QItemDelegate
   Q_OBJECT
 public:
   QCMakeCacheModelDelegate(QObject* p);
+  /// create our own editors for cache properties
   QWidget* createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index ) const;
 };
 
-/// Editor widget for editing paths
+/// Editor widget for editing paths or file 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;
+  QCMakeCachePathEditor(const QString& file, bool isFilePath, QWidget* p);
+  QString value() const { return this->LineEdit->text(); }
 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);
-  }
+  bool IsFilePath;
 };
 
 #endif