git-svn-id: file:///svnroot/family-shop-mgr@18 26eb2498-383b-47a6-be48-5d6f36779e85
[family-shop-mgr] / code / family-shop-mgr / ShoppingTreeModel.cpp
index 2de033f..4a485e8 100644 (file)
 \r
 #include "ShoppingTreeModel.h"\r
 \r
-ShoppingTreeModel::ShoppingTreeModel()\r
+#include "ShoppingTreeItem.h"\r
+#include <QFile>\r
+#include <QApplication>\r
+#include <QtGui>\r
+\r
+ShoppingTreeModel::ShoppingTreeModel(const QString &xmlFileName,\r
+                                     QObject *parent) :\r
+QAbstractItemModel(parent), m_document("ShoppingList")\r
+{\r
+    QString error;\r
+    int errLine;\r
+    int errColumn;\r
+\r
+    m_xmlFileName = QApplication::applicationDirPath() + "/" + xmlFileName;\r
+    QFile file(m_xmlFileName);\r
+    if(!file.open(QIODevice::ReadOnly))\r
+        return;\r
+    // Parse xml file\r
+    if(!m_document.setContent(&file, true, &error, &errLine, &errColumn))\r
+    {\r
+        emit xmlParseError(error, errLine, errColumn);\r
+        file.close();\r
+        return;\r
+    }\r
+    file.close();\r
+\r
+    QDomElement root = m_document.documentElement();\r
+    if(root.tagName() != "shoppingList" || !root.hasAttribute("version"))\r
+    {\r
+        emit invalidDocument();\r
+        return;\r
+    }\r
+    else if(root.attribute("version") == "1.0")\r
+    {\r
+        // set column titles\r
+        QVector<QVariant> rootData;\r
+        rootData << "Category/Item name"\r
+                << "Quantity" << "Store";\r
+\r
+        rootItem = new ShoppingTreeItem(rootData);\r
+    }\r
+    else\r
+    {\r
+        // upgrade document version if possible\r
+        ;\r
+    }\r
+\r
+    QDomElement child = root.firstChildElement("category");\r
+    while(!child.isNull())\r
+    {\r
+        // Parse all categories\r
+        parseCategoryElement(child);\r
+        child = child.nextSiblingElement("category");\r
+    }\r
+\r
+    child = root.firstChildElement("item");\r
+    while(!child.isNull())\r
+    {\r
+        // parse all items which don't have category\r
+        rootItem->insertChildren(\r
+                rootItem->childCount(), 1,\r
+                rootItem->columnCount());\r
+        QVector<QVariant> columnData =\r
+                getColumnsFromItemElement(child);\r
+        rootItem->child(rootItem->childCount() - 1)->\r
+                setItemType(ShoppingTreeItem::Item);\r
+        for(int column = 0; column < columnData.size(); column++)\r
+        {\r
+            rootItem->child(rootItem->childCount() - 1)->setData(column, columnData[column]);\r
+        }\r
+        m_domElementForItem.insert(rootItem->child(rootItem->childCount() - 1),\r
+                                   child);\r
+        child = child.nextSiblingElement("item");\r
+    }\r
+\r
+    QHashIterator<ShoppingTreeItem*,QDomElement> i(m_domElementForItem);\r
+    while(i.hasNext())\r
+    {\r
+        i.next();\r
+        connect(i.key(), SIGNAL(childInserted(ShoppingTreeItem*)), this,\r
+                SLOT(registerInsertedChild(ShoppingTreeItem*)));\r
+        connect(i.key(), SIGNAL(childRemoved(ShoppingTreeItem*)), this,\r
+                SLOT(deleteRemovedChild(ShoppingTreeItem*)));\r
+    }\r
+}\r
+\r
+ShoppingTreeModel::~ShoppingTreeModel()\r
+{\r
+    delete rootItem;\r
+}\r
+\r
+QVariant ShoppingTreeModel::data(const QModelIndex &index, int role) const\r
+{\r
+    if(!index.isValid())\r
+        return QVariant();\r
+\r
+    if(role != Qt::DisplayRole && role != Qt::EditRole)\r
+        return QVariant();\r
+\r
+    ShoppingTreeItem *item = getItem(index);\r
+    return item->data(index.column());\r
+}\r
+\r
+Qt::ItemFlags ShoppingTreeModel::flags(const QModelIndex &index) const\r
+{\r
+    if(!index.isValid())\r
+        return 0;\r
+\r
+    return Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;\r
+}\r
+\r
+ShoppingTreeItem *ShoppingTreeModel::getItem(const QModelIndex &index) const\r
+{\r
+    if(index.isValid()){\r
+        ShoppingTreeItem *item = static_cast<ShoppingTreeItem*>(index.internalPointer());\r
+        if(item) return item;\r
+    }\r
+\r
+    return rootItem;\r
+}\r
+\r
+QVariant ShoppingTreeModel::headerData(int section, Qt::Orientation orientation,\r
+                                       int role) const\r
+{\r
+    if(orientation == Qt::Horizontal && role == Qt::DisplayRole)\r
+        return rootItem->data(section);\r
+\r
+    return QVariant();\r
+}\r
+\r
+QModelIndex ShoppingTreeModel::index(int row, int column, const QModelIndex &parent) const\r
+{\r
+    if(parent.isValid() && parent.column() != 0)\r
+        return QModelIndex();\r
+\r
+    ShoppingTreeItem *parentItem = getItem(parent);\r
+\r
+    ShoppingTreeItem *childItem = parentItem->child(row);\r
+    if(childItem)\r
+        return createIndex(row, column, childItem);\r
+    else\r
+        return QModelIndex();\r
+}\r
+\r
+bool ShoppingTreeModel::insertColumns(int position, int columns, const QModelIndex &parent)\r
+{\r
+    bool success;\r
+\r
+    beginInsertColumns(parent, position, position + columns - 1);\r
+    success = rootItem->insertColumns(position, columns);\r
+    endInsertColumns();\r
+\r
+    return success;\r
+}\r
+\r
+bool ShoppingTreeModel::insertRows(int position, int rows, const QModelIndex &parent)\r
+{\r
+    ShoppingTreeItem *parentItem = getItem(parent);\r
+    bool success;\r
+\r
+    beginInsertRows(parent, position, position + rows - 1);\r
+    success = parentItem->insertChildren(position, rows, rootItem->columnCount());\r
+    endInsertRows();\r
+\r
+    return success;\r
+}\r
+\r
+QModelIndex ShoppingTreeModel::parent(const QModelIndex &index) const\r
+{\r
+    if(!index.isValid())\r
+        return QModelIndex();\r
+\r
+    ShoppingTreeItem *childItem = getItem(index);\r
+    ShoppingTreeItem *parentItem = childItem->parent();\r
+\r
+    if(parentItem == rootItem)\r
+        return QModelIndex();\r
+\r
+    return createIndex(parentItem->childNumber(), 0, parentItem);\r
+}\r
+\r
+bool ShoppingTreeModel::removeColumns(int position, int columns, const QModelIndex &parent)\r
+{\r
+    bool success;\r
+\r
+    beginRemoveColumns(parent, position, position + columns - 1);\r
+    success = rootItem->removeColumns(position, columns);\r
+    endRemoveColumns();\r
+\r
+    if (rootItem->columnCount() == 0)\r
+        removeRows(0, rowCount());\r
+\r
+    return success;\r
+}\r
+\r
+bool ShoppingTreeModel::removeRows(int position, int rows, const QModelIndex &parent)\r
+{\r
+    ShoppingTreeItem *parentItem = getItem(parent);\r
+    bool success;\r
+\r
+    beginRemoveRows(parent, position, position + rows - 1);\r
+    success = parentItem->removeChildren(position, rows);\r
+    endRemoveRows();\r
+\r
+    return success;\r
+}\r
+\r
+int ShoppingTreeModel::rowCount(const QModelIndex &parent) const\r
+{\r
+    ShoppingTreeItem *parentItem = getItem(parent);\r
+\r
+    return parentItem->childCount();\r
+}\r
+\r
+int ShoppingTreeModel::columnCount(const QModelIndex &parent) const\r
+{\r
+    return rootItem->columnCount();\r
+}\r
+bool ShoppingTreeModel::setData(const QModelIndex &index, const QVariant &value, int role)\r
+{\r
+    if(role != Qt::EditRole)\r
+        return false;\r
+\r
+    ShoppingTreeItem *item = getItem(index);\r
+\r
+    // only "items" have more than one editable column\r
+    if(index.column() != 0 && m_domElementForItem.value(item).tagName() != "item")\r
+        return false;\r
+\r
+    // edit item\r
+    bool result = (item->setData(index.column(),value) &&\r
+                   updateDomElement(item, index.column()));\r
+\r
+    if(result)\r
+        emit dataChanged(index, index);\r
+\r
+    return result;\r
+}\r
+\r
+bool ShoppingTreeModel::setHeaderData(int section, Qt::Orientation orientation,\r
+                                      const QVariant &value, int role)\r
+{\r
+    if(role != Qt::EditRole || orientation != Qt::Horizontal)\r
+        return false;\r
+\r
+    bool result = rootItem->setData(section, value);\r
+\r
+    if(result)\r
+        emit headerDataChanged(orientation, section, section);\r
+\r
+    return result;\r
+}\r
+\r
+void ShoppingTreeModel::registerInsertedChild(ShoppingTreeItem *item)\r
+{\r
+    // wait until item type is defined\r
+    item->waitItemTypeDefinition();\r
+\r
+    QDomElement parentElement = m_domElementForItem.value(item->parent());\r
+    QDomElement element;\r
+    if(item->getItemType() == ShoppingTreeItem::Category)\r
+        element = m_document.createElement("category");\r
+    else if(item->getItemType() == ShoppingTreeItem::Item)\r
+        element = m_document.createElement("item");\r
+    else\r
+        return;\r
+\r
+    parentElement.appendChild(element);\r
+    updateXmlFile();\r
+    m_domElementForItem.insert(item, element);\r
+    connect(item, SIGNAL(childInserted(ShoppingTreeItem*)), this,\r
+            SLOT(registerInsertedChild(ShoppingTreeItem*)));\r
+    connect(item, SIGNAL(childRemoved(ShoppingTreeItem*)), this,\r
+            SLOT(deleteRemovedChild(ShoppingTreeItem*)));\r
+}\r
+\r
+void ShoppingTreeModel::deleteRemovedChild(ShoppingTreeItem *item)\r
+{\r
+    QDomElement element = m_domElementForItem.value(item);\r
+    QDomNode parentNode = element.parentNode();\r
+    parentNode.removeChild(element);\r
+    updateXmlFile();\r
+    m_domElementForItem.remove(item);\r
+}\r
+\r
+void ShoppingTreeModel::parseCategoryElement(const QDomElement &element,\r
+                                             ShoppingTreeItem *parentItem)\r
+{\r
+    // if parent is null then add category to root\r
+    if(!parentItem)\r
+        parentItem = rootItem;\r
+\r
+    ShoppingTreeItem *item;\r
+    QString title = element.firstChildElement("title").text();\r
+    if(!title.isEmpty())\r
+    {\r
+        parentItem->insertChildren(parentItem->childCount(), 1,\r
+                                   rootItem->columnCount());\r
+\r
+        parentItem->child(parentItem->childCount() - 1)->\r
+                setItemType(ShoppingTreeItem::Category);\r
+        parentItem->child(parentItem->childCount() - 1)->setData(0, title);\r
+        m_domElementForItem.insert(parentItem->child(parentItem->childCount() - 1),\r
+                                   element);\r
+        item = parentItem->child(parentItem->childCount() - 1);\r
+    }\r
+    else\r
+    {\r
+        emit invalidDocument();\r
+        return;\r
+    }\r
+\r
+    // add each sub category and item to the tree\r
+    QDomElement child = element.firstChildElement();\r
+    while(!child.isNull())\r
+    {\r
+        if(child.tagName() == "category")\r
+        {\r
+            parseCategoryElement(child, parentItem);\r
+        }\r
+        else if(child.tagName() == "item")\r
+        {\r
+            item->insertChildren(\r
+                    item->childCount(), 1,\r
+                    rootItem->columnCount());\r
+            QVector<QVariant> columnData =\r
+                    getColumnsFromItemElement(child);\r
+            item->child(item->childCount() - 1)->setItemType(ShoppingTreeItem::Item);\r
+            for(int column = 0; column < columnData.size(); column++)\r
+            {\r
+                item->child(item->childCount() - 1)->setData(column, columnData[column]);\r
+            }\r
+            m_domElementForItem.insert(item->child(item->childCount() - 1),\r
+                                       child);\r
+        }\r
+        else\r
+        {\r
+            emit invalidDocument();\r
+            return;\r
+        }\r
+\r
+        child = child.nextSiblingElement();\r
+    }\r
+}\r
+\r
+QVector<QVariant> ShoppingTreeModel::getColumnsFromItemElement(const QDomElement &element)\r
+{\r
+    QVector<QVariant> data;\r
+    QString title = element.firstChildElement("title").text();\r
+    int quantity = element.firstChildElement("quantity").text().toInt();\r
+    QString store = element.firstChildElement("store").text();\r
+    if(title.isEmpty() || quantity < 0)\r
+    {\r
+        emit invalidDocument();\r
+        return data;\r
+    }\r
+\r
+    data << title << quantity << store;\r
+    return data;\r
+}\r
+\r
+bool ShoppingTreeModel::updateDomElement(ShoppingTreeItem *item, int column)\r
+{\r
+    QDomElement element = m_domElementForItem.value(item);\r
+\r
+    if(element.isNull())\r
+     return false;\r
+\r
+    bool success;\r
+    switch(column)\r
+    {\r
+        case 0:\r
+        {\r
+            QDomElement oldTitleElement = element.firstChildElement("title");\r
+            QDomElement newTitleElement = m_document.createElement("title");\r
+\r
+            QDomText newTitleText = m_document.createTextNode(item->data(0).toString());\r
+            newTitleElement.appendChild(newTitleText);\r
+\r
+            element.replaceChild(newTitleElement, oldTitleElement);\r
+            success = true;\r
+            break;\r
+        }\r
+        case 1:\r
+        {\r
+            QDomElement oldQuantityElement = element.firstChildElement("quantity");\r
+            QDomElement newQuantityElement = m_document.createElement("quantity");\r
+\r
+            QDomText newQuantityText = m_document.createTextNode(item->data(1).toString());\r
+            newQuantityElement.appendChild(newQuantityText);\r
+\r
+            element.replaceChild(newQuantityElement, oldQuantityElement);\r
+            success = true;\r
+            break;\r
+        }\r
+        case 2:\r
+        {\r
+            QDomElement oldStoreElement = element.firstChildElement("store");\r
+            QDomElement newStoreElement = m_document.createElement("store");\r
+\r
+            QDomText newStoreText = m_document.createTextNode(item->data(0).toString());\r
+            newStoreElement.appendChild(newStoreText);\r
+\r
+            element.replaceChild(newStoreElement, oldStoreElement);\r
+            success = true;\r
+            break;\r
+        }\r
+        default:\r
+            success = false;\r
+    }\r
+\r
+    updateXmlFile();\r
+\r
+    return success;\r
+}\r
+\r
+void ShoppingTreeModel::updateXmlFile() const\r
 {\r
+    QFile xmlFile(m_xmlFileName);\r
+    xmlFile.remove();\r
+    xmlFile.open(QIODevice::WriteOnly);\r
+    xmlFile.write(m_document.toByteArray(4));\r
+    xmlFile.close();\r
 }\r