git-svn-id: file:///svnroot/family-shop-mgr@10 26eb2498-383b-47a6-be48-5d6f36779e85
[family-shop-mgr] / code / family-shop-mgr / ShoppingTreeModel.cpp
1 /*\r
2  * This file is part of family-shop-mgr.\r
3  *\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
8  *\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
13  *\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
16  *\r
17  * Author: Unai IRIGOYEN\r
18  * Date: 12/07/2009\r
19  *\r
20  */\r
21 \r
22 #include "ShoppingTreeModel.h"\r
23 \r
24 #include "ShoppingTreeItem.h"\r
25 #include <QFile>\r
26 #include <QtGui>\r
27 \r
28 ShoppingTreeModel::ShoppingTreeModel(const QString &xmlFileName,\r
29                                      QObject *parent) :\r
30 QAbstractItemModel(parent), m_document("ShoppingList")\r
31 {\r
32     QString error;\r
33     int errLine;\r
34     int errColumn;\r
35 \r
36     m_xmlFileName = xmlFileName;\r
37     QFile file(m_xmlFileName);\r
38     if(!file.open(QIODevice::ReadOnly))\r
39         return;\r
40     // Parse xml file\r
41     if(!m_document.setContent(&file, true, &error, &errLine, &errColumn))\r
42     {\r
43         emit xmlParseError(error, errLine, errColumn);\r
44         file.close();\r
45         return;\r
46     }\r
47     file.close();\r
48 \r
49     QDomElement root = m_document.documentElement();\r
50     if(root.tagName() != "shoppingList" || !root.hasAttribute("version"))\r
51     {\r
52         emit invalidDocument();\r
53         return;\r
54     }\r
55     else if(root.attribute("version") == "1.0")\r
56     {\r
57         // set column titles\r
58         QVector<QVariant> rootData;\r
59         rootData << "Category/Item name"\r
60                 << "Quantity" << "Store";\r
61 \r
62         rootItem = new ShoppingTreeItem(rootData);\r
63     }\r
64     else\r
65     {\r
66         // upgrade document version if possible\r
67         ;\r
68     }\r
69 \r
70     QDomElement child = root.firstChildElement("category");\r
71     while(!child.isNull())\r
72     {\r
73         // Parse all categories\r
74         parseCategoryElement(child);\r
75         child = child.nextSiblingElement("category");\r
76     }\r
77 \r
78     child = root.firstChildElement("item");\r
79     while(!child.isNull())\r
80     {\r
81         // parse all items which don't have category\r
82         rootItem->insertChildren(\r
83                 rootItem->childCount(), 1,\r
84                 rootItem->columnCount());\r
85         QVector<QVariant> columnData =\r
86                 getColumnsFromItemElement(child);\r
87         for(int column = 0; column < columnData.size(); column++)\r
88         {\r
89             rootItem->child(rootItem->childCount() - 1)->setData(column, columnData[column]);\r
90         }\r
91         m_domElementForItem.insert(rootItem->child(rootItem->childCount() - 1),\r
92                                    child);\r
93     }\r
94 }\r
95 \r
96 ShoppingTreeModel::~ShoppingTreeModel()\r
97 {\r
98     delete rootItem;\r
99 }\r
100 \r
101 QVariant ShoppingTreeModel::data(const QModelIndex &index, int role) const\r
102 {\r
103     if(!index.isValid())\r
104         return QVariant();\r
105 \r
106     if(role != Qt::DisplayRole && role != Qt::EditRole)\r
107         return QVariant();\r
108 \r
109     ShoppingTreeItem *item = getItem(index);\r
110     return item->data(index.column());\r
111 }\r
112 \r
113 Qt::ItemFlags ShoppingTreeModel::flags(const QModelIndex &index) const\r
114 {\r
115     if(!index.isValid())\r
116         return 0;\r
117 \r
118     return Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;\r
119 }\r
120 \r
121 ShoppingTreeItem *ShoppingTreeModel::getItem(const QModelIndex &index) const\r
122 {\r
123     if(index.isValid()){\r
124         ShoppingTreeItem *item = static_cast<ShoppingTreeItem*>(index.internalPointer());\r
125         if(item) return item;\r
126     }\r
127 \r
128     return rootItem;\r
129 }\r
130 \r
131 QVariant ShoppingTreeModel::headerData(int section, Qt::Orientation orientation,\r
132                                        int role) const\r
133 {\r
134     if(orientation == Qt::Horizontal && role == Qt::DisplayRole)\r
135         return rootItem->data(section);\r
136 \r
137     return QVariant();\r
138 }\r
139 \r
140 QModelIndex ShoppingTreeModel::index(int row, int column, const QModelIndex &parent) const\r
141 {\r
142     if(parent.isValid() && parent.column() != 0)\r
143         return QModelIndex();\r
144 \r
145     ShoppingTreeItem *parentItem = getItem(parent);\r
146 \r
147     ShoppingTreeItem *childItem = parentItem->child(row);\r
148     if(childItem)\r
149         return createIndex(row, column, childItem);\r
150     else\r
151         return QModelIndex();\r
152 }\r
153 \r
154 bool ShoppingTreeModel::insertRows(int position, int rows, const QModelIndex &parent)\r
155 {\r
156     ShoppingTreeItem *parentItem = getItem(parent);\r
157     bool success;\r
158 \r
159     beginInsertRows(parent, position, position + rows - 1);\r
160     success = parentItem->insertChildren(position, rows, rootItem->columnCount());\r
161     endInsertRows();\r
162 \r
163     return success;\r
164 }\r
165 \r
166 QModelIndex ShoppingTreeModel::parent(const QModelIndex &index) const\r
167 {\r
168     if(!index.isValid())\r
169         return QModelIndex();\r
170 \r
171     ShoppingTreeItem *childItem = getItem(index);\r
172     ShoppingTreeItem *parentItem = childItem->parent();\r
173 \r
174     if(parentItem == rootItem)\r
175         return QModelIndex();\r
176 \r
177     return createIndex(parentItem->childNumber(), 0, parentItem);\r
178 }\r
179 \r
180 bool ShoppingTreeModel::removeRows(int position, int rows, const QModelIndex &parent)\r
181 {\r
182     ShoppingTreeItem *parentItem = getItem(parent);\r
183     bool success;\r
184 \r
185     beginRemoveRows(parent, position, position + rows - 1);\r
186     success = parentItem->removeChildren(position, rows);\r
187     endRemoveRows();\r
188 \r
189     return success;\r
190 }\r
191 \r
192 int ShoppingTreeModel::rowCount(const QModelIndex &parent) const\r
193 {\r
194     ShoppingTreeItem *parentItem = getItem(parent);\r
195 \r
196     return parentItem->childCount();\r
197 }\r
198 \r
199 int ShoppingTreeModel::columnCount(const QModelIndex &parent) const\r
200 {\r
201     return rootItem->columnCount();\r
202 }\r
203 bool ShoppingTreeModel::setData(const QModelIndex &index, const QVariant &value, int role)\r
204 {\r
205     if(role != Qt::EditRole)\r
206         return false;\r
207 \r
208     ShoppingTreeItem *item = getItem(index);\r
209 \r
210     // only "items" have more than one editable column\r
211     if(index.column() != 0 && m_domElementForItem.value(item).tagName() != "item")\r
212         return false;\r
213 \r
214     // edit item\r
215     bool result = (item->setData(index.column(),value) &&\r
216                    updateDomElement(item, index.column()));\r
217 \r
218     if(result)\r
219         emit dataChanged(index, index);\r
220 \r
221     return result;\r
222 }\r
223 \r
224 bool ShoppingTreeModel::setHeaderData(int section, Qt::Orientation orientation,\r
225                                       const QVariant &value, int role)\r
226 {\r
227     if(role != Qt::EditRole || orientation != Qt::Horizontal)\r
228         return false;\r
229 \r
230     bool result = rootItem->setData(section, value);\r
231 \r
232     if(result)\r
233         emit headerDataChanged(orientation, section, section);\r
234 \r
235     return result;\r
236 }\r
237 \r
238 void ShoppingTreeModel::parseCategoryElement(const QDomElement &element,\r
239                                              ShoppingTreeItem *parentItem)\r
240 {\r
241     // if parent is null then add category to root\r
242     if(!parentItem)\r
243         parentItem = rootItem;\r
244 \r
245     ShoppingTreeItem *item;\r
246     QString title = element.firstChildElement("title").text();\r
247     if(!title.isEmpty())\r
248     {\r
249         parentItem->insertChildren(parentItem->childCount(), 1,\r
250                                    rootItem->columnCount());\r
251 \r
252         parentItem->child(parentItem->childCount() - 1)->setData(0, title);\r
253         m_domElementForItem.insert(parentItem->child(parentItem->childCount() - 1),\r
254                                    element);\r
255         item = parentItem->child(parentItem->childCount() - 1);\r
256     }\r
257     else\r
258     {\r
259         emit invalidDocument();\r
260         return;\r
261     }\r
262 \r
263     // add each sub category and item to the tree\r
264     QDomElement child = element.firstChildElement();\r
265     while(!child.isNull())\r
266     {\r
267         if(child.tagName() == "category")\r
268         {\r
269             parseCategoryElement(child, parentItem);\r
270         }\r
271         else if(child.tagName() == "item")\r
272         {\r
273             item->insertChildren(\r
274                     item->childCount(), 1,\r
275                     rootItem->columnCount());\r
276             QVector<QVariant> columnData =\r
277                     getColumnsFromItemElement(child);\r
278             for(int column = 0; column < columnData.size(); column++)\r
279             {\r
280                 item->child(item->childCount() - 1)->setData(column, columnData[column]);\r
281             }\r
282             m_domElementForItem.insert(item->child(item->childCount() - 1),\r
283                                        child);\r
284         }\r
285         else\r
286         {\r
287             emit invalidDocument();\r
288             return;\r
289         }\r
290 \r
291         child = child.nextSiblingElement();\r
292     }\r
293 }\r
294 \r
295 QVector<QVariant> ShoppingTreeModel::getColumnsFromItemElement(const QDomElement &element)\r
296 {\r
297     QVector<QVariant> data;\r
298     QString title = element.firstChildElement("title").text();\r
299     int quantity = element.firstChildElement("quantity").text().toInt();\r
300     QString store = element.firstChildElement("store").text();\r
301     if(title.isEmpty() || quantity < 0)\r
302     {\r
303         emit invalidDocument();\r
304         return data;\r
305     }\r
306 \r
307     data << title << quantity << store;\r
308     return data;\r
309 }\r
310 \r
311 bool ShoppingTreeModel::updateDomElement(ShoppingTreeItem *item, int column)\r
312 {\r
313     QDomElement element = m_domElementForItem.value(item);\r
314 \r
315     if(element.isNull())\r
316      return false;\r
317 \r
318     bool success;\r
319     switch(column)\r
320     {\r
321         case 0:\r
322         {\r
323             QDomElement oldTitleElement = element.firstChildElement("title");\r
324             QDomElement newTitleElement = m_document.createElement("title");\r
325 \r
326             QDomText newTitleText = m_document.createTextNode(item->data(0).toString());\r
327             newTitleElement.appendChild(newTitleText);\r
328 \r
329             element.replaceChild(newTitleElement, oldTitleElement);\r
330             success = true;\r
331             break;\r
332         }\r
333         case 1:\r
334         {\r
335             QDomElement oldQuantityElement = element.firstChildElement("quantity");\r
336             QDomElement newQuantityElement = m_document.createElement("quantity");\r
337 \r
338             QDomText newQuantityText = m_document.createTextNode(item->data(1).toString());\r
339             newQuantityElement.appendChild(newQuantityText);\r
340 \r
341             element.replaceChild(newQuantityElement, oldQuantityElement);\r
342             success = true;\r
343             break;\r
344         }\r
345         case 2:\r
346         {\r
347             QDomElement oldStoreElement = element.firstChildElement("store");\r
348             QDomElement newStoreElement = m_document.createElement("store");\r
349 \r
350             QDomText newStoreText = m_document.createTextNode(item->data(0).toString());\r
351             newStoreElement.appendChild(newStoreText);\r
352 \r
353             element.replaceChild(newStoreElement, oldStoreElement);\r
354             success = true;\r
355             break;\r
356         }\r
357         default:\r
358             success = false;\r
359     }\r
360 \r
361     updateXmlFile();\r
362 \r
363     return success;\r
364 }\r
365 \r
366 void ShoppingTreeModel::updateXmlFile() const\r
367 {\r
368     QFile xmlFile(m_xmlFileName);\r
369     xmlFile.remove();\r
370     xmlFile.open(QIODevice::WriteOnly);\r
371     xmlFile.write(m_document.toByteArray(4));\r
372     xmlFile.close();\r
373 }\r