/****************************************************************************
**
** 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 §ion, const Feature *f) const
{
return (find(section, f) != 0);
}
Node* FeatureTreeModel::find(const QString §ion, 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