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.
This commit is contained in:
Clinton Stimpson 2007-11-03 10:30:52 -04:00
parent c139a096c7
commit 77ad85a6ab
13 changed files with 461 additions and 264 deletions

View File

@ -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)

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

Before

Width:  |  Height:  |  Size: 358 B

After

Width:  |  Height:  |  Size: 358 B

View File

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

View File

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

View File

@ -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);
@ -183,18 +265,16 @@ 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));
}
}
void CMakeSetupDialog::error(const QString& title, const QString& message, bool* cancel)
{
@ -206,4 +286,17 @@ void CMakeSetupDialog::error(const QString& title, const QString& message, bool*
}
}
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);
}

View File

@ -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;
};

View File

@ -1,21 +1,14 @@
<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" >
@ -25,7 +18,7 @@
<property name="frameShadow" >
<enum>QFrame::Raised</enum>
</property>
<layout class="QGridLayout" >
<layout class="QVBoxLayout" >
<property name="leftMargin" >
<number>0</number>
</property>
@ -38,6 +31,8 @@
<property name="bottomMargin" >
<number>0</number>
</property>
<item>
<layout class="QGridLayout" >
<item row="0" column="0" >
<widget class="QLabel" name="label" >
<property name="text" >
@ -76,7 +71,13 @@
</property>
</widget>
</item>
<item row="2" column="0" colspan="3" >
</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,30 +86,46 @@
<enum>QAbstractItemView::SelectRows</enum>
</property>
</widget>
<widget class="QTextEdit" name="Output" >
<property name="lineWrapMode" >
<enum>QTextEdit::NoWrap</enum>
</property>
<property name="readOnly" >
<bool>true</bool>
</property>
</widget>
</widget>
</item>
<item row="3" column="0" colspan="3" >
<item>
<layout class="QHBoxLayout" >
<item>
<widget class="QPushButton" name="configureButton" >
<widget class="QPushButton" name="ConfigureButton" >
<property name="text" >
<string>Configure</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="generateButton" >
<widget class="QPushButton" name="GenerateButton" >
<property name="text" >
<string>Ok</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="cancelButton" >
<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" >
@ -129,18 +146,6 @@
</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>

View File

@ -118,10 +118,18 @@ 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);
if(percent >= 0)
{
emit self->progressChanged(msg, percent);
}
else
{
emit self->outputMessage(msg);
}
QCoreApplication::processEvents();
}

View File

@ -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;

View File

@ -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
@ -83,10 +124,25 @@ QVariant QCMakeCacheModel::data ( const QModelIndex & index, int role ) const
{
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))
{
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;
@ -110,12 +166,15 @@ 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";
@ -125,11 +184,18 @@ QVariant QCMakeCacheModel::headerData ( int section, Qt::Orientation orient, int
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;
f |= Qt::ItemIsEditable;
// booleans are editable in place
if(this->Properties[index.row()].Type == QCMakeCacheProperty::BOOL)
{
f |= Qt::ItemIsUserCheckable;
}
return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
}
return f;
}
@ -138,10 +204,20 @@ bool QCMakeCacheModel::setData ( const QModelIndex & index, const QVariant& valu
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;
}
@ -159,22 +235,23 @@ 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();
}

View File

@ -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