diff options
author | Christian Kandeler <christian.kandeler@digia.com> | 2013-08-08 18:11:06 +0200 |
---|---|---|
committer | Christian Kandeler <christian.kandeler@digia.com> | 2013-08-12 10:07:21 +0200 |
commit | 35182b6576585f3b2f38c66350fe33a12b031f58 (patch) | |
tree | 80f659f8b6561b127e724cb9f1faf863b4e54aa6 /src/app/config-ui | |
parent | 0f9b7cbbbe7c4f7335b6a851b133dec4f9c72d73 (diff) |
Add editing capabilities to config-ui application.
Configuration entries can be added and removed, and their values
as well as their names can be changed. The latter feature
means the GUI interface is now actually more powerful than
the command-line version.
Change-Id: Ic95954fe7bcae727448d25cfba833792ba47b5cb
Reviewed-by: Joerg Bornemann <joerg.bornemann@digia.com>
Diffstat (limited to 'src/app/config-ui')
-rw-r--r-- | src/app/config-ui/mainwindow.cpp | 64 | ||||
-rw-r--r-- | src/app/config-ui/mainwindow.h | 4 | ||||
-rw-r--r-- | src/app/config-ui/settingsmodel.cpp | 156 | ||||
-rw-r--r-- | src/app/config-ui/settingsmodel.h | 9 |
4 files changed, 218 insertions, 15 deletions
diff --git a/src/app/config-ui/mainwindow.cpp b/src/app/config-ui/mainwindow.cpp index 1ea3729b5..2b997fe65 100644 --- a/src/app/config-ui/mainwindow.cpp +++ b/src/app/config-ui/mainwindow.cpp @@ -35,13 +35,20 @@ #include <QKeySequence> #include <QMenu> #include <QMenuBar> +#include <QMessageBox> +#include <QModelIndex> +#include <QPoint> +#include <QString> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); m_model = new SettingsModel(this); ui->treeView->setModel(m_model); + ui->treeView->setContextMenuPolicy(Qt::CustomContextMenu); connect(ui->treeView, SIGNAL(expanded(QModelIndex)), SLOT(adjustColumns())); + connect(ui->treeView, SIGNAL(customContextMenuRequested(QPoint)), + SLOT(provideContextMenu(QPoint))); adjustColumns(); QMenu * const fileMenu = menuBar()->addMenu(tr("&File")); @@ -50,6 +57,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi QAction * const reloadAction = new QAction(tr("&Reload"), this); reloadAction->setShortcut(Qt::CTRL | Qt::Key_R); connect(reloadAction, SIGNAL(triggered()), SLOT(reloadSettings())); + QAction * const saveAction = new QAction(tr("&Save"), this); + saveAction->setShortcut(Qt::CTRL | Qt::Key_S); + connect(saveAction, SIGNAL(triggered()), SLOT(saveSettings())); QAction * const expandAllAction = new QAction(tr("&Expand All"), this); expandAllAction->setShortcut(Qt::CTRL | Qt::Key_E); connect(expandAllAction, SIGNAL(triggered()), SLOT(expandAll())); @@ -58,9 +68,10 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi connect(collapseAllAction, SIGNAL(triggered()), SLOT(collapseAll())); QAction * const exitAction = new QAction(tr("E&xit"), this); exitAction->setShortcut(Qt::CTRL | Qt::Key_Q); - connect(exitAction, SIGNAL(triggered()), qApp, SLOT(quit())); + connect(exitAction, SIGNAL(triggered()), SLOT(exit())); fileMenu->addAction(reloadAction); + fileMenu->addAction(saveAction); fileMenu->addSeparator(); fileMenu->addAction(exitAction); @@ -93,5 +104,56 @@ void MainWindow::collapseAll() void MainWindow::reloadSettings() { + if (m_model->hasUnsavedChanges()) { + const QMessageBox::StandardButton answer = QMessageBox::question(this, + tr("Unsaved Changes"), + tr("You have unsaved changes. Do you want to discard them?")); + if (answer != QMessageBox::Yes) + return; + } m_model->reload(); } + +void MainWindow::saveSettings() +{ + m_model->save(); +} + +void MainWindow::exit() +{ + if (m_model->hasUnsavedChanges()) { + const QMessageBox::StandardButton answer = QMessageBox::question(this, + tr("Unsaved Changes"), + tr("You have unsaved changes. Do you want to save them now?")); + if (answer == QMessageBox::Yes) + m_model->save(); + } + qApp->quit(); +} + +void MainWindow::provideContextMenu(const QPoint &pos) +{ + const QModelIndex index = ui->treeView->indexAt(pos); + if (index.isValid() && index.column() != m_model->keyColumn()) + return; + const QString settingsKey = m_model->data(index).toString(); + + QMenu contextMenu; + QAction addKeyAction(this); + QAction removeKeyAction(this); + if (index.isValid()) { + addKeyAction.setText(tr("Add new key below '%1'").arg(settingsKey)); + removeKeyAction.setText(tr("Remove key '%1' and all its sub-keys").arg(settingsKey)); + contextMenu.addAction(&addKeyAction); + contextMenu.addAction(&removeKeyAction); + } else { + addKeyAction.setText(tr("Add new top-level key")); + contextMenu.addAction(&addKeyAction); + } + + const QAction *action = contextMenu.exec(ui->treeView->mapToGlobal(pos)); + if (action == &addKeyAction) + m_model->addNewKey(index); + else if (action == &removeKeyAction) + m_model->removeKey(index); +} diff --git a/src/app/config-ui/mainwindow.h b/src/app/config-ui/mainwindow.h index e793f69dd..cd365c86e 100644 --- a/src/app/config-ui/mainwindow.h +++ b/src/app/config-ui/mainwindow.h @@ -33,6 +33,7 @@ QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } +class QPoint; QT_END_NAMESPACE class SettingsModel; @@ -50,6 +51,9 @@ private slots: void expandAll(); void collapseAll(); void reloadSettings(); + void saveSettings(); + void exit(); + void provideContextMenu(const QPoint &pos); private: Ui::MainWindow *ui; diff --git a/src/app/config-ui/settingsmodel.cpp b/src/app/config-ui/settingsmodel.cpp index 1eef5073f..9f58ac121 100644 --- a/src/app/config-ui/settingsmodel.cpp +++ b/src/app/config-ui/settingsmodel.cpp @@ -38,6 +38,9 @@ struct Node Node() : parent(0) {} ~Node() { qDeleteAll(children); } + QString uniqueChildName() const; + bool hasDirectChildWithName(const QString &name) const; + QString name; QString value; Node *parent; @@ -48,15 +51,19 @@ class SettingsModel::SettingsModelPrivate { public: void readSettings(); - void addNode(qbs::Settings *settings, Node *parentNode, const QString &fullyQualifiedName); + void addNode(Node *parentNode, const QString &fullyQualifiedName); + void doSave(const Node *node, const QString &prefix); Node *indexToNode(const QModelIndex &index); Node rootNode; + SettingsPtr settings; + bool dirty; }; SettingsModel::SettingsModel(QObject *parent) : QAbstractItemModel(parent), d(new SettingsModelPrivate) { + d->settings = qbsSettings(); d->readSettings(); } @@ -72,11 +79,63 @@ void SettingsModel::reload() endResetModel(); } +void SettingsModel::save() +{ + if (!d->dirty) + return; + d->settings->clear(); + d->doSave(&d->rootNode, QString()); + d->dirty = false; +} + +void SettingsModel::addNewKey(const QModelIndex &parent) +{ + Node *parentNode = d->indexToNode(parent); + if (!parentNode) + return; + Node * const newNode = new Node; + newNode->parent = parentNode; + newNode->name = parentNode->uniqueChildName(); + beginInsertRows(parent, parentNode->children.count(), parentNode->children.count()); + parentNode->children << newNode; + endInsertRows(); + d->dirty = true; +} + +void SettingsModel::removeKey(const QModelIndex &index) +{ + Node * const node = d->indexToNode(index); + if (!node || node == &d->rootNode) + return; + const int positionInParent = node->parent->children.indexOf(node); + beginRemoveRows(parent(index), positionInParent, positionInParent); + node->parent->children.removeAt(positionInParent); + delete node; + endRemoveRows(); + d->dirty = true; +} + +bool SettingsModel::hasUnsavedChanges() const +{ + return d->dirty; +} + Qt::ItemFlags SettingsModel::flags(const QModelIndex &index) const { if (!index.isValid()) return Qt::ItemFlags(); - return Qt::ItemIsEnabled | Qt::ItemIsSelectable; + const Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable; + if (index.column() == keyColumn()) + return flags | Qt::ItemIsEditable; + if (index.column() == valueColumn()) { + const Node * const node = d->indexToNode(index); + if (!node) + return Qt::ItemFlags(); + + // Only leaf nodes have values. + return node->children.isEmpty() ? flags | Qt::ItemIsEditable : flags; + } + return Qt::ItemFlags(); } QVariant SettingsModel::headerData(int section, Qt::Orientation orientation, int role) const @@ -85,9 +144,11 @@ QVariant SettingsModel::headerData(int section, Qt::Orientation orientation, int return QVariant(); if (role != Qt::DisplayRole) return QVariant(); - if (section == 0) + if (section == keyColumn()) return tr("Key"); - return tr("Value"); + if (section == valueColumn()) + return tr("Value"); + return QVariant(); } int SettingsModel::columnCount(const QModelIndex &parent) const @@ -107,13 +168,40 @@ int SettingsModel::rowCount(const QModelIndex &parent) const QVariant SettingsModel::data(const QModelIndex &index, int role) const { - if (role != Qt::DisplayRole) + if (role != Qt::DisplayRole && role != Qt::EditRole) return QVariant(); - const Node * const node = static_cast<Node *>(index.internalPointer()); - Q_ASSERT(node); - if (index.column() == 0) + const Node * const node = d->indexToNode(index); + if (!node) + return QVariant(); + if (index.column() == keyColumn()) return node->name; - return node->value; + if (index.column() == valueColumn() && node->children.isEmpty()) + return node->value; + return QVariant(); +} + +bool SettingsModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (!index.isValid() || role != Qt::EditRole) + return false; + Node * const node = d->indexToNode(index); + if (!node) + return false; + const QString valueString = value.toString(); + QString *toChange = 0; + if (index.column() == keyColumn() && !valueString.isEmpty() + && !node->parent->hasDirectChildWithName(valueString)) { + toChange = &node->name; + } else if (index.column() == valueColumn() && valueString != node->value) { + toChange = &node->value; + } + + if (toChange) { + *toChange = valueString; + emit dataChanged(index, index); + d->dirty = true; + } + return toChange; } QModelIndex SettingsModel::index(int row, int column, const QModelIndex &parent) const @@ -142,24 +230,64 @@ void SettingsModel::SettingsModelPrivate::readSettings() { qDeleteAll(rootNode.children); rootNode.children.clear(); - SettingsPtr settings = qbsSettings(); foreach (const QString &topLevelKey, settings->directChildren(QString())) - addNode(settings.data(), &rootNode, topLevelKey); + addNode(&rootNode, topLevelKey); + dirty = false; } -void SettingsModel::SettingsModelPrivate::addNode(qbs::Settings *settings, Node *parentNode, +void SettingsModel::SettingsModelPrivate::addNode(Node *parentNode, const QString &fullyQualifiedName) { Node * const node = new Node; node->name = fullyQualifiedName.mid(fullyQualifiedName.lastIndexOf(QLatin1Char('.')) + 1); - node->value = settings->value(fullyQualifiedName).toStringList().join(QLatin1String(",")); + node->value = settingsValueToRepresentation(settings->value(fullyQualifiedName)); node->parent = parentNode; parentNode->children << node; foreach (const QString &childKey, settings->directChildren(fullyQualifiedName)) - addNode(settings, node, fullyQualifiedName + QLatin1Char('.') + childKey); + addNode(node, fullyQualifiedName + QLatin1Char('.') + childKey); + dirty = true; +} + +void SettingsModel::SettingsModelPrivate::doSave(const Node *node, const QString &prefix) +{ + if (node->children.isEmpty()) { + settings->setValue(prefix + node->name, representationToSettingsValue(node->value)); + return; + } + + const QString newPrefix = prefix + node->name + QLatin1Char('.'); + foreach (const Node * const child, node->children) + doSave(child, newPrefix); } Node *SettingsModel::SettingsModelPrivate::indexToNode(const QModelIndex &index) { return index.isValid() ? static_cast<Node *>(index.internalPointer()) : &rootNode; } + + +QString Node::uniqueChildName() const +{ + QString newName = QLatin1String("newkey"); + bool unique; + do { + unique = true; + foreach (const Node *childNode, children) { + if (childNode->name == newName) { + unique = false; + newName += QLatin1Char('_'); + break; + } + } + } while (!unique); + return newName; +} + +bool Node::hasDirectChildWithName(const QString &name) const +{ + foreach (const Node * const child, children) { + if (child->name == name) + return true; + } + return false; +} diff --git a/src/app/config-ui/settingsmodel.h b/src/app/config-ui/settingsmodel.h index 1052196e2..510ff1f5d 100644 --- a/src/app/config-ui/settingsmodel.h +++ b/src/app/config-ui/settingsmodel.h @@ -35,13 +35,22 @@ public: SettingsModel(QObject *parent = 0); ~SettingsModel(); + int keyColumn() const { return 0; } + int valueColumn() const { return 1; } + bool hasUnsavedChanges() const; + void reload(); + void save(); + + void addNewKey(const QModelIndex &parent); + void removeKey(const QModelIndex &index); Qt::ItemFlags flags(const QModelIndex &index) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; int columnCount(const QModelIndex &parent = QModelIndex()) const; int rowCount(const QModelIndex &parent = QModelIndex()) const; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; QModelIndex parent(const QModelIndex &child) const; |