tools/qconfig/featuretreemodel.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/qconfig/featuretreemodel.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,451 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "featuretreemodel.h"
+#include "feature.h"
+#include <QPalette>
+#include <QColor>
+#include <QApplication>
+#include <QtDebug>
+
+QT_BEGIN_NAMESPACE
+
+class Node
+{
+public:
+    Node(Feature *f, Node *p = 0) : feature(f), parent(p) {}
+    ~Node();
+    Node* find(const Feature *child) const;
+    bool contains(const Feature *child) const { return find(child) != 0; }
+    bool insert(Node *n);
+
+    Feature *feature;
+    Node *parent;
+    QList<Node*> children; // maybe convert to Map to get keys sorted
+};
+
+Node::~Node()
+{
+    while (!children.isEmpty())
+        delete children.takeFirst();
+}
+
+Node* Node::find(const Feature *f) const
+{
+    if (this->feature == f)
+	return const_cast<Node*>(this);
+
+    foreach (Node *n, children)
+	if (Node *m = n->find(f))
+	    return m;
+
+    return 0;
+}
+
+static bool nodePtrLessThan(const Node *n1, const Node *n2)
+{
+    return (n1->feature->key() < n2->feature->key());
+}
+
+/*
+  Try insert \a n into the tree with this node as root.
+  n is inserted as a child if it has a dependency to this node.
+  Returns true if child is inserted into the tree, false otherwise.
+*/
+bool Node::insert(Node *n)
+{
+    Feature *f = const_cast<Feature*>(n->feature);
+    if (feature->supports().contains(f)) {
+        children.append(n);
+	qSort(children.begin(), children.end(), nodePtrLessThan);
+	n->parent = this;
+        return true;
+    }
+    foreach (Node *child, children)
+        if (child->insert(n))
+            return true;
+    return false;
+}
+
+static bool isSection(const QModelIndex &index)
+{
+    return index.isValid() && (index.internalId() == 0);
+}
+
+FeatureTreeModel::FeatureTreeModel(QObject *parent)
+    : QAbstractItemModel(parent)
+{
+}
+
+FeatureTreeModel::~FeatureTreeModel()
+{
+    foreach (QString section, sections.keys())
+	while (!sections[section].isEmpty())
+	    delete sections[section].takeFirst();
+}
+
+/*
+  Returns true if the model already contains \a in \a section, false otherwise.
+*/
+bool FeatureTreeModel::contains(const QString &section, const Feature *f) const
+{
+    return (find(section, f) != 0);
+}
+
+Node* FeatureTreeModel::find(const QString &section, const Feature *f) const
+{
+    QList<Node*> roots = sections[section];
+    foreach (Node *root, roots)
+        if (Node *n = root->find(f))
+            return n;
+    return 0;
+}
+
+/*
+  Add new \a feature to the tree.
+  When all feature is added, buildTree() must be called to build the
+  dependency tree.
+*/
+void FeatureTreeModel::addFeature(Feature *feature)
+{
+    const QString section = feature->section();
+    Q_ASSERT(!contains(section, feature));
+
+    connect(feature, SIGNAL(changed()), this, SLOT(featureChanged()));
+
+    Node *node = new Node(feature, 0);
+
+    // try insert any toplevel nodes as child of this one
+    foreach (Node *n, sections[section])
+	if (node->insert(n))
+	    sections[section].removeAll(n);
+
+    // try insert this node as a child of any existing node
+    foreach (Node *n, sections[section])
+	if (n->insert(node)) {
+            emit layoutChanged();
+	    return;
+        }
+
+    // not a child, insert as a toplevel node
+    sections[section].append(node);
+    qSort(sections[section].begin(), sections[section].end(), nodePtrLessThan);
+    emit layoutChanged();
+}
+
+QModelIndex FeatureTreeModel::createIndex(int row, int column,
+					  const QModelIndex &parent,
+					  const Node *node) const
+{
+    QModelIndex index = QAbstractItemModel::createIndex(row, column,
+							(void*)node);
+    if (parent.isValid())
+	parentMap[index] = parent;
+    if (node)
+        featureIndexMap[node->feature] = index;
+    return index;
+}
+
+QModelIndex FeatureTreeModel::index(int row, int column,
+                                    const QModelIndex &parent) const
+{
+    if (!parent.isValid()) { // index is a section
+        if (row < sections.size() && column == 0)
+            return QAbstractItemModel::createIndex(row, column, 0);
+	return QModelIndex();
+    }
+
+    if (isSection(parent)) { // index is a toplevel feature
+        const int parentRow = parent.row();
+        if (parentRow < sections.size()) {
+            QString section = sections.keys().at(parentRow);
+	    QList<Node*> nodes = sections[section];
+            if (row < nodes.size() && column < 2)
+                return createIndex(row, column, parent, nodes.at(row));
+        }
+	return QModelIndex();
+    }
+
+    // parent is a feature
+    Node *parentNode = static_cast<Node*>(parent.internalPointer());
+    QList<Node*> children = parentNode->children;
+    if (row < children.size() && column < 2)
+	return createIndex(row, column, parent, children.at(row));
+
+    return QModelIndex();
+}
+
+QModelIndex FeatureTreeModel::index(const QModelIndex &parent,
+                                    const Feature *feature) const
+{
+    const int rows = rowCount(parent);
+    for (int i = 0; i < rows; ++i) {
+        QModelIndex child = index(i, 0, parent);
+        Node *node = static_cast<Node*>(child.internalPointer());
+        if (node && node->feature == feature)
+            return child;
+        QModelIndex childSearch = index(child, feature);
+        if (childSearch.isValid())
+            return childSearch;
+    }
+    return QModelIndex();
+}
+
+QModelIndex FeatureTreeModel::index(const Feature *feature) const
+{
+    if (featureIndexMap.contains(feature))
+        return featureIndexMap.value(feature);
+
+    // exhaustive search
+    int sectionRow = sections.keys().indexOf(feature->section());
+    QModelIndex sectionIndex = index(sectionRow, 0, QModelIndex());
+
+    return index(sectionIndex, feature);
+}
+
+QModelIndex FeatureTreeModel::parent(const QModelIndex &index) const
+{
+    if (!index.isValid())
+        return QModelIndex();
+
+    if (parentMap.contains(index))
+	return parentMap.value(index);
+    return QModelIndex();
+}
+
+int FeatureTreeModel::rowCount(const QModelIndex &parent) const
+{
+    if (!parent.isValid())
+        return sections.size();
+
+    if (isSection(parent)) {
+	const QString section = sections.keys().at(parent.row());
+	return sections[section].size();
+    }
+
+    const Node *node = static_cast<Node*>(parent.internalPointer());
+    return node->children.size();
+}
+
+int FeatureTreeModel::columnCount(const QModelIndex &parent) const
+{
+#if 0
+    if (!parent.isValid())
+        return 0;
+
+    if (isSection(parent))
+       return 1;
+#endif
+    Q_UNUSED(parent);
+    return 2; // Feature: [key, name]
+}
+
+QVariant FeatureTreeModel::data(const QModelIndex &index, int role) const
+{
+    if (!index.isValid())
+        return QVariant();
+
+    const Node *node = static_cast<Node*>(index.internalPointer());
+
+    switch (role) {
+	case Qt::DisplayRole: {
+	    if (node == 0)  // index is a section
+		return sections.keys().at(index.row());
+	    if (index.column() == 0)
+		return node->feature->key();
+	    Q_ASSERT(index.column() == 1);
+	    return node->feature->title();
+	}
+	case Qt::CheckStateRole: {
+	    if (node && index.column() == 0)
+		return (node->feature->enabled() ?
+			Qt::Checked : Qt::Unchecked);
+	    break;
+	}
+	case Qt::TextColorRole: {
+	    if (node && index.column() == 0)  // feature key
+		if (node->feature->selectable())
+		    return QApplication::palette().color(QPalette::Link);
+	    break;
+	}
+	case Qt::TextAlignmentRole:
+	case Qt::BackgroundColorRole:
+	case Qt::FontRole:
+	case Qt::ToolTipRole: // TODO
+	case Qt::StatusTipRole: // TODO
+	case Qt::WhatsThisRole: // TODO
+	case Qt::DecorationRole:
+	case Qt::EditRole:
+ 	default:
+	    break;
+    }
+    return QVariant();
+}
+
+bool FeatureTreeModel::setData(const QModelIndex &index,
+                               const QVariant &value, int role)
+{
+    if (!index.isValid())
+        return false;
+
+    Node *node = static_cast<Node*>(index.internalPointer());
+    if (!node)
+        return false;
+
+    if (role == Qt::CheckStateRole) {
+        Qt::CheckState state = static_cast<Qt::CheckState>(value.toInt());
+        if (state == Qt::Checked)
+            node->feature->setEnabled(true);
+        else if (state == Qt::Unchecked)
+            node->feature->setEnabled(false);
+        emit dataChanged(index, index);
+        return true;
+    }
+    return false;
+}
+
+Qt::ItemFlags FeatureTreeModel::flags(const QModelIndex &index) const
+{
+    if (!index.isValid() || index.internalPointer() == 0)
+	return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
+
+    const Node *node = static_cast<Node*>(index.internalPointer());
+    const Feature *feature = node->feature;
+    Qt::ItemFlags flags = Qt::ItemIsUserCheckable | Qt::ItemIsSelectable;
+
+    if (feature->selectable())
+	flags |= Qt::ItemIsEnabled;
+
+    return flags;
+}
+
+QVariant FeatureTreeModel::headerData(int section, Qt::Orientation orientation,
+				      int role) const
+{
+    if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
+        if (section == 0)
+            return QString("Id");
+        else if (section == 1)
+            return QString("Name");
+    }
+
+    return QVariant();
+}
+
+Feature* FeatureTreeModel::getFeature(const QModelIndex &index) const
+{
+    if (!index.isValid())
+	return 0;
+    if (isSection(index))
+	return 0;
+    Node *node = static_cast<Node*>(index.internalPointer());
+    return const_cast<Feature*>(node->feature);
+}
+
+void FeatureTreeModel::featureChanged()
+{
+    Feature *feature = qobject_cast<Feature*>(sender());
+    if (feature) {
+        QModelIndex featureIndex = index(feature);
+        emit dataChanged(featureIndex, featureIndex);
+    } else {
+        emit layoutChanged();
+    }
+}
+
+void FeatureTreeModel::readConfig(QTextStream &stream)
+{
+    static QRegExp regexp("\\s*#\\s*define\\s+QT_NO_(\\S+)\\s*");
+
+    while (!stream.atEnd()) {
+	QString line = stream.readLine();
+        if (regexp.exactMatch(line)) {
+            Feature *f = Feature::getInstance(regexp.cap(1));
+            f->setEnabled(false);
+        }
+    }
+}
+/*
+  Search for all disabled child features of \a parent.
+  Returns a list of feature keys for the disabled items.
+*/
+QStringList FeatureTreeModel::findDisabled(const QModelIndex &parent) const
+{
+    QStringList stringList;
+
+    const int rows = rowCount(parent);
+    for (int i = 0; i < rows; ++i) {
+        QModelIndex child = index(i, 0, parent);
+        Node *node = static_cast<Node*>(child.internalPointer());
+        if (node && node->feature && !node->feature->enabled())
+            stringList << node->feature->key();
+        stringList << findDisabled(child);
+    }
+    return stringList;
+}
+
+void FeatureTreeModel::writeConfig(QTextStream &stream) const
+{
+    const int sectionCount = rowCount(QModelIndex());
+
+    for (int i = 0; i < sectionCount; ++i) {
+        QModelIndex section = index(i, 0, QModelIndex());
+        QStringList disabled = findDisabled(section);
+        if (disabled.size() > 0) {
+            stream << '\n' << "/* " << sections.keys().at(i) << " */" << '\n';
+            foreach (QString feature, disabled)
+                stream << "#ifndef QT_NO_" << feature << '\n'
+                       << "#  define QT_NO_" << feature << '\n'
+                       << "#endif" << '\n';
+        }
+    }
+}
+
+void FeatureTreeModel::clear()
+{
+    Feature::clear();
+    sections.clear();
+    parentMap.clear();
+    featureIndexMap.clear();
+    emit layoutChanged();
+}
+
+QT_END_NAMESPACE