2 * This file is part of family-shop-mgr.
\r
4 * family-shop-mgr is free software: you can redistribute it and/or modify
\r
5 * it under the terms of the GNU General Public License as published by
\r
6 * the Free Software Foundation, either version 3 of the License, or
\r
7 * (at your option) any later version.
\r
9 * family-shop-mgr is distributed in the hope that it will be useful,
\r
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
12 * GNU General Public License for more details.
\r
14 * You should have received a copy of the GNU General Public License
\r
15 * along with family-shop-mgr. If not, see <http://www.gnu.org/licenses/>.
\r
17 * Author: Unai IRIGOYEN
\r
22 #include "ShoppingTreeModel.h"
\r
24 #include "ShoppingTreeItem.h"
\r
26 #include <QApplication>
\r
29 /*******************************************************************/
\r
30 ShoppingTreeModel::ShoppingTreeModel(const QString &xmlFileName,
\r
32 QAbstractItemModel(parent), m_document("ShoppingList")
\r
38 m_xmlFileName = QApplication::applicationDirPath() + "/" + xmlFileName;
\r
39 QFile file(m_xmlFileName);
\r
40 if(!file.open(QIODevice::ReadOnly))
\r
43 if(!m_document.setContent(&file, true, &error, &errLine, &errColumn))
\r
45 emit xmlParseError(error, errLine, errColumn);
\r
51 QDomElement root = m_document.documentElement();
\r
52 if(root.tagName() != "shoppingList" || !root.hasAttribute("version"))
\r
54 emit invalidDocument();
\r
57 else if(root.attribute("version") == "1.0")
\r
59 // set column titles
\r
60 QVector<QVariant> rootData;
\r
61 rootData << "Category/Item name"
\r
62 << "Quantity" << "Store";
\r
64 rootItem = new ShoppingTreeItem(rootData);
\r
65 m_domElementForItem.insert(rootItem, root);
\r
69 // upgrade document version if possible
\r
73 QDomElement child = root.firstChildElement("category");
\r
74 while(!child.isNull())
\r
76 // Parse all categories
\r
77 parseCategoryElement(child);
\r
78 child = child.nextSiblingElement("category");
\r
81 child = root.firstChildElement("item");
\r
82 while(!child.isNull())
\r
84 // parse all items which don't have category
\r
85 rootItem->insertChildren(
\r
86 rootItem->childCount(), 1,
\r
87 rootItem->columnCount());
\r
88 QVector<QVariant> columnData =
\r
89 getColumnsFromItemElement(child);
\r
90 rootItem->child(rootItem->childCount() - 1)->
\r
91 setItemType(ShoppingTreeItem::Item);
\r
92 for(int column = 0; column < columnData.size(); column++)
\r
94 rootItem->child(rootItem->childCount() - 1)->
\r
95 setData(column, columnData[column]);
\r
97 m_domElementForItem.insert(rootItem->child(rootItem->childCount() - 1),
\r
99 child = child.nextSiblingElement("item");
\r
103 connect(rootItem, SIGNAL(childItemSet(ShoppingTreeItem*)), this,
\r
104 SLOT(registerInsertedChild(ShoppingTreeItem*)));
\r
105 connect(rootItem, SIGNAL(childRemoved(ShoppingTreeItem*)), this,
\r
106 SLOT(deleteRemovedChild(ShoppingTreeItem*)));
\r
108 QHashIterator<ShoppingTreeItem*,QDomElement> i(m_domElementForItem);
\r
112 connect(i.key(), SIGNAL(childItemSet(ShoppingTreeItem*)), this,
\r
113 SLOT(registerInsertedChild(ShoppingTreeItem*)));
\r
114 connect(i.key(), SIGNAL(childRemoved(ShoppingTreeItem*)), this,
\r
115 SLOT(deleteRemovedChild(ShoppingTreeItem*)));
\r
119 /*******************************************************************/
\r
120 ShoppingTreeModel::~ShoppingTreeModel()
\r
125 /*******************************************************************/
\r
126 bool ShoppingTreeModel::addCategory(QString name)
\r
128 QModelIndex parent = QModelIndex();
\r
130 if (!this->insertRow(parent.row()+1, parent))
\r
134 QModelIndex child = this->index(parent.row()+1, column, parent);
\r
135 ShoppingTreeItem* item = this->getItem(child);
\r
136 item->setItemType(ShoppingTreeItem::Category);
\r
137 this->setData(child, QVariant(name), Qt::EditRole);
\r
141 /*******************************************************************/
\r
142 bool ShoppingTreeModel::addSubCategory(QString name, int position,
\r
143 const QModelIndex &parent)
\r
145 if (!this->insertRow(position, parent))
\r
148 for(int column = 0; column < this->columnCount(parent); ++column)
\r
150 QModelIndex child = this->index(parent.row()+1, column, parent);
\r
151 ShoppingTreeItem* item = this->getItem(child);
\r
152 item->setItemType(ShoppingTreeItem::Category);
\r
153 this->setData(child, QVariant(name), Qt::EditRole);
\r
158 /*******************************************************************/
\r
159 bool ShoppingTreeModel::addItem(QString name, int position,
\r
160 const QModelIndex &parent)
\r
162 if (!this->insertRow(position, parent))
\r
166 QModelIndex child = this->index(parent.row()+1, column, parent);
\r
167 ShoppingTreeItem* item = this->getItem(child);
\r
168 item->setItemType(ShoppingTreeItem::Item);
\r
169 this->setData(child, QVariant(name), Qt::EditRole);
\r
174 /*******************************************************************/
\r
175 bool ShoppingTreeModel::removeCategoryOrItem(const QModelIndex &index)
\r
177 return this->removeRow(index.row(), index.parent());
\r
180 /*******************************************************************/
\r
181 QVariant ShoppingTreeModel::data(const QModelIndex &index, int role) const
\r
183 if(!index.isValid())
\r
186 if(role != Qt::DisplayRole && role != Qt::EditRole)
\r
189 ShoppingTreeItem *item = getItem(index);
\r
190 return item->data(index.column());
\r
193 /*******************************************************************/
\r
194 Qt::ItemFlags ShoppingTreeModel::flags(const QModelIndex &index) const
\r
196 if(!index.isValid())
\r
199 return Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
\r
202 /*******************************************************************/
\r
203 ShoppingTreeItem *ShoppingTreeModel::getItem(const QModelIndex &index) const
\r
205 if(index.isValid()){
\r
206 ShoppingTreeItem *item =
\r
207 static_cast<ShoppingTreeItem*>(index.internalPointer());
\r
208 if(item) return item;
\r
214 /*******************************************************************/
\r
215 QVariant ShoppingTreeModel::headerData(int section,
\r
216 Qt::Orientation orientation,
\r
219 if(orientation == Qt::Horizontal && role == Qt::DisplayRole)
\r
220 return rootItem->data(section);
\r
225 /*******************************************************************/
\r
226 QModelIndex ShoppingTreeModel::index(int row, int column,
\r
227 const QModelIndex &parent) const
\r
229 if(parent.isValid() && parent.column() != 0)
\r
230 return QModelIndex();
\r
232 ShoppingTreeItem *parentItem = getItem(parent);
\r
234 ShoppingTreeItem *childItem = parentItem->child(row);
\r
236 return createIndex(row, column, childItem);
\r
238 return QModelIndex();
\r
241 /*******************************************************************/
\r
242 bool ShoppingTreeModel::insertColumns(int position, int columns,
\r
243 const QModelIndex &parent)
\r
247 beginInsertColumns(parent, position, position + columns - 1);
\r
248 success = rootItem->insertColumns(position, columns);
\r
249 endInsertColumns();
\r
254 /*******************************************************************/
\r
255 bool ShoppingTreeModel::insertRows(int position, int rows,
\r
256 const QModelIndex &parent)
\r
258 ShoppingTreeItem *parentItem = getItem(parent);
\r
259 bool success = false;
\r
261 beginInsertRows(parent, position, position + rows - 1);
\r
262 success = parentItem->insertChildren(position, rows,
\r
263 rootItem->columnCount(), this);
\r
269 /*******************************************************************/
\r
270 QModelIndex ShoppingTreeModel::parent(const QModelIndex &index) const
\r
272 if(!index.isValid())
\r
273 return QModelIndex();
\r
275 ShoppingTreeItem *childItem = getItem(index);
\r
276 ShoppingTreeItem *parentItem = childItem->parent();
\r
278 if(parentItem == rootItem)
\r
279 return QModelIndex();
\r
281 return createIndex(parentItem->childNumber(), 0, parentItem);
\r
284 /*******************************************************************/
\r
285 bool ShoppingTreeModel::removeColumns(int position, int columns, const QModelIndex &parent)
\r
289 beginRemoveColumns(parent, position, position + columns - 1);
\r
290 success = rootItem->removeColumns(position, columns);
\r
291 endRemoveColumns();
\r
293 if (rootItem->columnCount() == 0)
\r
294 removeRows(0, rowCount());
\r
299 /*******************************************************************/
\r
300 bool ShoppingTreeModel::removeRows(int position, int rows, const QModelIndex &parent)
\r
302 ShoppingTreeItem *parentItem = getItem(parent);
\r
305 beginRemoveRows(parent, position, position + rows - 1);
\r
306 success = parentItem->removeChildren(position, rows);
\r
312 /*******************************************************************/
\r
313 int ShoppingTreeModel::rowCount(const QModelIndex &parent) const
\r
315 ShoppingTreeItem *parentItem = getItem(parent);
\r
317 return parentItem->childCount();
\r
320 /*******************************************************************/
\r
321 int ShoppingTreeModel::columnCount(const QModelIndex &parent) const
\r
323 if(parent.isValid())
\r
324 return getItem(parent)->columnCount();
\r
326 return rootItem->columnCount();
\r
329 /*******************************************************************/
\r
330 bool ShoppingTreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
\r
332 if(role != Qt::EditRole)
\r
335 ShoppingTreeItem *item = getItem(index);
\r
337 // only "items" have more than one editable column
\r
338 if(index.column() != 0 && m_domElementForItem.value(item).tagName() != "item")
\r
342 bool result = (item->setData(index.column(),value) &&
\r
343 updateDomElement(item, index.column()));
\r
346 emit dataChanged(index, index);
\r
351 /*******************************************************************/
\r
352 bool ShoppingTreeModel::setHeaderData(int section, Qt::Orientation orientation,
\r
353 const QVariant &value, int role)
\r
355 if(role != Qt::EditRole || orientation != Qt::Horizontal)
\r
358 bool result = rootItem->setData(section, value);
\r
361 emit headerDataChanged(orientation, section, section);
\r
366 /*******************************************************************/
\r
367 void ShoppingTreeModel::registerInsertedChild(ShoppingTreeItem *item)
\r
369 QDomElement parentElement = m_domElementForItem.value(item->parent());
\r
370 QDomElement childElement;
\r
371 if(item->getItemType() == ShoppingTreeItem::Category)
\r
373 childElement = m_document.createElement("category");
\r
374 QDomElement title = m_document.createElement("title");
\r
375 QDomText newTitleText = m_document.createTextNode("new category");
\r
376 title.appendChild(newTitleText);
\r
377 childElement.appendChild(title);
\r
379 else if(item->getItemType() == ShoppingTreeItem::Item)
\r
381 childElement = m_document.createElement("category");
\r
382 QDomElement title = m_document.createElement("title");
\r
383 QDomText newTitleText = m_document.createTextNode("new category");
\r
384 title.appendChild(newTitleText);
\r
385 childElement.appendChild(title);
\r
386 QDomElement quantity = m_document.createElement("quantity");
\r
387 QDomText newQuantityText = m_document.createTextNode("0");
\r
388 quantity.appendChild(newQuantityText);
\r
389 childElement.appendChild(quantity);
\r
390 QDomElement store = m_document.createElement("store");
\r
391 QDomText newStoreText = m_document.createTextNode("");
\r
392 store.appendChild(newStoreText);
\r
393 childElement.appendChild(store);
\r
394 QDomElement lastModified = m_document.createElement("lastModified");
\r
395 QDomText newDateText = m_document.createTextNode(
\r
396 QDateTime::currentDateTime().toString("dd/MM/yyyy-hh:mm:ss"));
\r
397 lastModified.appendChild(newDateText);
\r
398 childElement.appendChild(lastModified);
\r
403 parentElement.appendChild(childElement);
\r
404 m_domElementForItem.insert(item, childElement);
\r
408 /*******************************************************************/
\r
409 void ShoppingTreeModel::deleteRemovedChild(ShoppingTreeItem *item)
\r
411 QDomElement element = m_domElementForItem.value(item);
\r
412 QDomNode parentNode = element.parentNode();
\r
413 parentNode.removeChild(element);
\r
415 m_domElementForItem.remove(item);
\r
418 /*******************************************************************/
\r
419 void ShoppingTreeModel::parseCategoryElement(const QDomElement &element,
\r
420 ShoppingTreeItem *parentItem)
\r
422 // if parent is null then add category to root
\r
424 parentItem = rootItem;
\r
426 ShoppingTreeItem *item;
\r
427 QDomElement child = element.firstChildElement("title");
\r
428 QString title = child.text();
\r
429 if(!title.isEmpty())
\r
431 parentItem->insertChildren(parentItem->childCount(), 1,
\r
432 rootItem->columnCount());
\r
434 parentItem->child(parentItem->childCount() - 1)->
\r
435 setItemType(ShoppingTreeItem::Category);
\r
436 parentItem->child(parentItem->childCount() - 1)->setData(0, title);
\r
437 m_domElementForItem.insert(parentItem->child(parentItem->childCount() - 1),
\r
439 item = parentItem->child(parentItem->childCount() - 1);
\r
443 emit invalidDocument();
\r
447 // add each sub category and item to the tree
\r
448 child = child.nextSiblingElement();
\r
449 while(!child.isNull())
\r
451 if(child.tagName() == "category")
\r
453 parseCategoryElement(child, parentItem);
\r
455 else if(child.tagName() == "item")
\r
457 item->insertChildren(
\r
458 item->childCount(), 1,
\r
459 rootItem->columnCount());
\r
460 QVector<QVariant> columnData =
\r
461 getColumnsFromItemElement(child);
\r
462 item->child(item->childCount() - 1)->setItemType(ShoppingTreeItem::Item);
\r
463 for(int column = 0; column < columnData.size(); column++)
\r
465 item->child(item->childCount() - 1)->setData(column, columnData[column]);
\r
467 m_domElementForItem.insert(item->child(item->childCount() - 1),
\r
472 emit invalidDocument();
\r
476 child = child.nextSiblingElement();
\r
480 /*******************************************************************/
\r
481 QVector<QVariant> ShoppingTreeModel::getColumnsFromItemElement(const QDomElement &element)
\r
483 QVector<QVariant> data;
\r
484 QString title = element.firstChildElement("title").text();
\r
485 int quantity = element.firstChildElement("quantity").text().toInt();
\r
486 QString store = element.firstChildElement("store").text();
\r
487 if(title.isEmpty() || quantity < 0)
\r
489 emit invalidDocument();
\r
493 data << title << quantity << store;
\r
497 /*******************************************************************/
\r
498 bool ShoppingTreeModel::updateDomElement(ShoppingTreeItem *item, int column)
\r
500 QDomElement element = m_domElementForItem.value(item);
\r
502 if(element.isNull())
\r
505 bool success = false;
\r
510 QDomElement oldTitleElement = element.firstChildElement("title");
\r
511 QDomElement newTitleElement = m_document.createElement("title");
\r
513 QDomText newTitleText = m_document.createTextNode(item->data(0).toString());
\r
514 newTitleElement.appendChild(newTitleText);
\r
516 element.replaceChild(newTitleElement, oldTitleElement);
\r
522 QDomElement oldQuantityElement = element.firstChildElement("quantity");
\r
523 QDomElement newQuantityElement = m_document.createElement("quantity");
\r
525 QDomText newQuantityText = m_document.createTextNode(item->data(1).toString());
\r
526 newQuantityElement.appendChild(newQuantityText);
\r
528 element.replaceChild(newQuantityElement, oldQuantityElement);
\r
534 QDomElement oldStoreElement = element.firstChildElement("store");
\r
535 QDomElement newStoreElement = m_document.createElement("store");
\r
537 QDomText newStoreText = m_document.createTextNode(item->data(0).toString());
\r
538 newStoreElement.appendChild(newStoreText);
\r
540 element.replaceChild(newStoreElement, oldStoreElement);
\r
548 QDomElement oldDateElement = element.firstChildElement("lastModified");
\r
549 if(!oldDateElement.isNull())
\r
551 QDomElement newDateElement = m_document.createElement("lastModified");
\r
553 QDomText newDateText = m_document.createTextNode(
\r
554 QDateTime::currentDateTime().toString("dd/MM/yyyy-hh:mm:ss"));
\r
555 newDateElement.appendChild(newDateText);
\r
557 element.replaceChild(newDateElement, oldDateElement);
\r
565 /*******************************************************************/
\r
566 void ShoppingTreeModel::updateXmlFile() const
\r
568 QFile xmlFile(m_xmlFileName);
\r
570 xmlFile.open(QIODevice::WriteOnly);
\r
571 xmlFile.write(m_document.toByteArray(4));
\r
575 /*******************************************************************/
\r
576 void ShoppingTreeModel::sort(int column, Qt::SortOrder order)
\r
578 // TODO: implement sorting
\r