/* This file is part of Mitsuba, a physically based rendering system. Copyright (c) 2007-2011 by Wenzel Jakob and others. Mitsuba is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License Version 3 as published by the Free Software Foundation. Mitsuba is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "xmltreemodel.h" // ==================================================================== // TreeItem implementation // ==================================================================== TreeItem::TreeItem(const QString &name, const QString &readableName, const QVariant &data, const QVariant &defaultValue, TreeItem *parent, int importance) { m_parentItem = parent; m_itemName = name; m_readableName = readableName; m_itemValue = data; m_itemDefault = defaultValue; m_importance = importance; } TreeItem::~TreeItem() { qDeleteAll(m_childItems); } void TreeItem::appendChild(TreeItem *item) { m_childItems.append(item); } void TreeItem::removeChild(TreeItem *item) { m_childItems.removeOne(item); } TreeItem *TreeItem::child(int row) { return m_childItems.value(row); } int TreeItem::childCount() const { return m_childItems.count(); } int TreeItem::columnCount() const { if (isCategory()) return 1; else return 2; } QVariant TreeItem::data(int column) const { if (column == 0) return QVariant(m_readableName); else if (column == 1 && !isCategory()) return m_itemValue; else return QVariant(); } int TreeItem::row() const { if (m_parentItem) return m_parentItem->m_childItems.indexOf(const_cast(this)); return 0; } TreeItem *TreeItem::parent() { return m_parentItem; } void TreeItem::setToolTip(const QString &str) { m_toolTip = str; } const QString &TreeItem::toolTip() const { return m_toolTip; } bool TreeItem::isCategory() const { return m_itemValue.isNull(); } bool TreeItem::setData(int column, const QVariant &value) { if (isCategory() || column != 1) return false; m_itemValue = value; return true; } void TreeItem::setProperty(const std::string &name, const Properties &props) { QVariant data; switch (props.getType(name)) { case Properties::EBoolean: data = QVariant(props.getBoolean(name)); break; case Properties::EInteger: data = QVariant(props.getInteger(name)); break; case Properties::EFloat: data = QVariant((double) props.getFloat(name)); break; case Properties::EString: data = QVariant(props.getString(name).c_str()); break; default: SLog(EError, "TreeItem::getProperties(): \"%s\": Unable to handle elements of type %i", name.c_str(), props.getType(name)); } bool found = false; for (int i=0; im_itemName == name.c_str()) { m_childItems[i]->setData(1, data); found = true; break; } } if (!found) SLog(EWarn, "TreeItem::getProperties(): \"%s\": Unable to find element in tree!", name.c_str()); } void TreeItem::setProperties(const Properties &props) { std::vector propertyNames; props.putPropertyNames(propertyNames); for (std::vector::const_iterator it = propertyNames.begin(); it != propertyNames.end(); ++it) setProperty(*it, props); } void TreeItem::putProperties(Properties &props) const { for (int i=0; im_itemName.toStdString()); const QVariant &value(item->m_itemValue); if (value == item->m_itemDefault) continue; switch (value.type()) { case QVariant::Int: props.setInteger(name, value.toInt()); break; case QVariant::Double: props.setFloat(name, (Float) value.toDouble()); break; case QVariant::String: props.setString(name, value.toString().toStdString()); break; case QVariant::Bool: props.setBoolean(name, value.toBool()); break; default: SLog(EError, "TreeItem::putProperties(): \"%s\": Unable to handle elements of type %i", name.c_str(), value.type()); } } } // ==================================================================== // XMLTreeModel implementation // ==================================================================== XMLTreeModel::XMLTreeModel(QDomElement docRoot, const QPalette &palette, QObject *parent) : QAbstractItemModel(parent), m_docRoot(docRoot) { m_rootItem = new TreeItem("Root", "Root"); m_unimportantColor = QVariant(palette.color(QPalette::Disabled, QPalette::Text)); } TreeItem *XMLTreeModel::registerClass(const QString &_className, const QString &readableName) { QString className(_className); if (className == "") return NULL; TreeItem *parent = new TreeItem(className, readableName, QVariant(), QVariant(), m_rootItem); populate(className, parent); if (parent->childCount() == 0) { delete parent; return NULL; } beginInsertRows(QModelIndex(), m_rootItem->childCount(), m_rootItem->childCount()); m_rootItem->appendChild(parent); endInsertRows(); emit layoutChanged(); return parent; } TreeItem *XMLTreeModel::updateClass(TreeItem *prev, const QString &className, const QString &readableName) { if (prev != NULL) { beginRemoveRows(QModelIndex(), prev->row(), prev->row()); m_rootItem->removeChild(prev); endRemoveRows(); if (className == prev->getName()) { /* Just re-insert */ m_rootItem->removeChild(prev); beginInsertRows(QModelIndex(), m_rootItem->childCount(), m_rootItem->childCount()); m_rootItem->appendChild(prev); endInsertRows(); emit layoutChanged(); return prev; } } return registerClass(className, readableName); } void XMLTreeModel::setProperties(TreeItem *item, const Properties &props) { if (item == NULL) return; item->setProperties(props); int childCount = item->childCount(); if (childCount > 0) { emit dataChanged(createIndex(0, 0, item->child(0)), createIndex(item->row(), 1, item->child(childCount-1))); } } void XMLTreeModel::populate(const QString &className, TreeItem *parent) { QDomElement plugin; for (QDomElement e = m_docRoot.firstChildElement("plugin"); !e.isNull(); e = e.nextSiblingElement("plugin")) { if (e.attribute("className") == className) { plugin = e; break; } } if (plugin.hasAttribute("extends")) populate(plugin.attribute("extends"), parent); if (plugin.isNull()) return; for (QDomElement e = plugin.firstChildElement("param"); !e.isNull(); e = e.nextSiblingElement("param")) { QDomDocument tooltipDoc; QDomElement root = tooltipDoc.createElement("p"); tooltipDoc.appendChild(root); for (QDomNode child = e.firstChild(); !child.isNull(); child = child.nextSibling()) { root.appendChild(tooltipDoc.importNode(child, true)); } QString toolTip = tooltipDoc.toString(), type = e.attribute("type"), value = e.attribute("default"), name = e.attribute("name"), readableName = e.attribute("readableName"), importanceValue = e.attribute("importance"); QVariant variantValue, defaultValue; bool ok = true; if (type == "string") variantValue = QVariant(value); else if (type == "integer" || type == "long") { variantValue = QVariant((int) value.toInt(&ok)); } else if (type == "float") variantValue = QVariant(value.toDouble(&ok)); else if (type == "boolean") { value = value.toLower(); if (value == "true") variantValue = QVariant(true); else if (value == "false") variantValue = QVariant(false); else ok = false; } else { SLog(EError, "Unexpected property type!"); } if (!ok) SLog(EError, "Error in number conversion!"); int importance = 10; if (importanceValue != "") importance = importanceValue.toInt(); TreeItem *item = new TreeItem(name, readableName, variantValue, variantValue, parent, importance); item->setToolTip(toolTip); parent->appendChild(item); } } XMLTreeModel::~XMLTreeModel() { delete m_rootItem; } int XMLTreeModel::columnCount(const QModelIndex &) const { return 2; } QVariant XMLTreeModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); TreeItem *item = static_cast(index.internalPointer()); if (role == Qt::DisplayRole || role == Qt::EditRole) return item->data(index.column()); else if (role == Qt::BackgroundColorRole && item->isCategory()) return QVariant(QColor(qRgb(0x90, 0x90, 0x90))); else if (role == Qt::TextColorRole && item->isCategory()) return QVariant(QColor(qRgb(0xff, 0xff, 0xff))); else if (role == Qt::ToolTipRole) return QVariant(item->toolTip()); else if (role == Qt::ForegroundRole && index.column() == 0) return item->getImportance() < 5 ? m_unimportantColor : QVariant(); else return QVariant(); } bool XMLTreeModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (role != Qt::EditRole) return false; TreeItem *item = static_cast(index.internalPointer()); bool result = item->setData(index.column(), value); if (result) emit dataChanged(index, index); return result; } Qt::ItemFlags XMLTreeModel::flags(const QModelIndex &index) const { if (!index.isValid()) return 0; TreeItem *item = static_cast(index.internalPointer()); if (item->isCategory()) return Qt::ItemIsEnabled; else if (index.column() == 0) return Qt::ItemIsEnabled | Qt::ItemIsSelectable; else return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable; } QVariant XMLTreeModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { if (section == 0) return QVariant(tr("Property")); else return QVariant(tr("Value")); } return QVariant(); } QModelIndex XMLTreeModel::index(int row, int column, const QModelIndex &parent) const { if (!hasIndex(row, column, parent)) return QModelIndex(); TreeItem *parentItem; if (!parent.isValid()) parentItem = m_rootItem; else parentItem = static_cast(parent.internalPointer()); TreeItem *childItem = parentItem->child(row); if (childItem) return createIndex(row, column, childItem); else return QModelIndex(); } QModelIndex XMLTreeModel::parent(const QModelIndex &index) const { if (!index.isValid()) return QModelIndex(); TreeItem *childItem = static_cast(index.internalPointer()); TreeItem *parentItem = childItem->parent(); if (parentItem == m_rootItem) return QModelIndex(); return createIndex(parentItem->row(), 0, parentItem); } int XMLTreeModel::rowCount(const QModelIndex &parent) const { TreeItem *parentItem; if (parent.column() > 0) return 0; if (!parent.isValid()) parentItem = m_rootItem; else parentItem = static_cast(parent.internalPointer()); return parentItem->childCount(); }