1 #include "todomodel.h"
2 #include <QStringList>
3 #include <QCheckBox>
4 #include <QMimeData>
5
6 #define NOTE_TODO_TASK_MIME "application/znotes.content.list"
7
Task(QDomDocument * document,QDomNode & node,int row,Task * parent)8 Task::Task(QDomDocument* document, QDomNode &node, int row, Task* parent)
9 : _document(document), _node(node), _row(row), _parent(parent), _done(false)
10 {
11 QDomElement element = _node.toElement();
12 if(!element.isNull())
13 {
14 if(element.attributes().contains("time_a")) _date_start = QDateTime::fromTime_t(element.attribute("time_a").toInt());
15 if(element.attributes().contains("time_l")) _date_limit = QDateTime::fromTime_t(element.attribute("time_l").toInt());
16 if(element.attributes().contains("time_d"))
17 {
18 _date_stop = QDateTime::fromTime_t(element.attribute("time_d").toInt());
19 _done = true;
20 }
21 if(element.attributes().contains("priority"))
22 {
23 const QString priority_text = element.attribute("priority");
24 if(priority_text=="low") _priority = low;
25 else if(priority_text=="medium") _priority = medium;
26 else _priority = high;
27 }
28 else _priority = medium;
29
30 for(int i=0; i<node.childNodes().count(); ++i)
31 {
32 QDomNode child_node = _node.childNodes().item(i);
33 QDomElement child_element = child_node.toElement();
34
35 if(child_element.tagName()=="title") _title = child_element.text();
36 else if(child_element.tagName()=="comment") _comment = child_element.text();
37
38 else if(child_element.tagName()=="task") _subtasks.append(new Task(_document, child_node, _subtasks.size(), this));
39 }
40 }
41 }
42
~Task()43 Task::~Task()
44 {
45 for(int i=0; i<_subtasks.size(); ++i)
46 {
47 delete _subtasks[i];
48 _subtasks[i] = 0;
49 }
50 }
51
insertSubTask(int pos)52 void Task::insertSubTask(int pos)
53 {
54 QDomElement new_task_elem = _document->createElement("task");
55 if(pos<0 || pos>_subtasks.size()) pos = _subtasks.size();
56 if(pos == _subtasks.size()) _node.appendChild(new_task_elem);
57 else _node.insertAfter(new_task_elem, _node.childNodes().at(pos-1));
58 Task* task = new Task(_document, new_task_elem, pos, this);
59 task->setDateStart(QDateTime::currentDateTime());
60 task->setTitle(QObject::tr("New task"));
61 _subtasks.insert(pos, task);
62 }
63
removeSubTask(int pos)64 void Task::removeSubTask(int pos)
65 {
66 Task* subtask = _subtasks.takeAt(pos);
67
68 const QDomNode& subtask_elem = subtask->node();
69 _node.removeChild(subtask_elem);
70
71 Task* subtask_new = subtask;
72 subtask = 0;
73 delete subtask_new;
74 }
75
updateChildNode(QDomDocument & document,QDomNode & node,const QString & tag_name,const QString & value)76 static void updateChildNode(QDomDocument& document, QDomNode& node, const QString& tag_name, const QString& value)
77 {
78 QDomElement old_element = node.firstChildElement(tag_name);
79 QDomElement new_element = document.createElement(tag_name);
80 QDomText new_element_text = document.createTextNode(value);
81 new_element.appendChild(new_element_text);
82
83 if(!old_element.isNull()) node.replaceChild(new_element, old_element);
84 else node.appendChild(new_element);
85 }
86
setTitle(const QString & v)87 void Task::setTitle(const QString& v)
88 {
89 _title = v;
90 updateChildNode(*_document, _node, "title", _title);
91 }
92
setComment(const QString & v)93 void Task::setComment(const QString& v)
94 {
95 _comment = v;
96 updateChildNode(*_document, _node, "comment", _comment);
97 }
98
setDateStart(const QDateTime & v)99 void Task::setDateStart(const QDateTime& v)
100 {
101 _date_start = v;
102 _node.toElement().setAttribute("time_a", _date_start.toTime_t());
103 }
104
setDateStop(const QDateTime & v)105 void Task::setDateStop(const QDateTime& v)
106 {
107 _date_stop = v;
108 _node.toElement().setAttribute("time_d", _date_stop.toTime_t());
109 }
110
setDateLimit(const QDateTime & v)111 void Task::setDateLimit(const QDateTime& v)
112 {
113 _date_limit = v;
114 _node.toElement().setAttribute("time_l", _date_limit.toTime_t());
115 }
116
setDone(bool v)117 void Task::setDone(bool v)
118 {
119 _done = v;
120 if(_done)
121 {
122 setDateStop(QDateTime::currentDateTime());
123 // for(int i=0; i<_subtasks.size(); ++i)
124 // {
125 // Task* subtask = _subtasks[i];
126 // if(!subtask->done()) subtask->setDone(true);
127 // }
128 }
129 else _node.toElement().removeAttribute("time_d");
130 }
131
setPriority(Priority v)132 void Task::setPriority(Priority v)
133 {
134 _priority = v;
135 switch(_priority)
136 {
137 case low: _node.toElement().setAttribute("priority", "low"); break;
138 case medium: _node.toElement().setAttribute("priority", "medium"); break;
139 case high: _node.toElement().setAttribute("priority", "high"); break;
140 }
141 }
142
143 //------------------------------------------------------------------------------
144
TodoModel(QObject * parent)145 TodoModel::TodoModel(QObject *parent)
146 : QAbstractItemModel(parent), _document(0), _root_task(0)
147 {
148 }
149
~TodoModel()150 TodoModel::~TodoModel()
151 {
152 delete _root_task;
153 }
154
load(QFile & file)155 QDomDocument* TodoModel::load(QFile& file)
156 {
157 delete _document;
158 _document = new QDomDocument("ztodo");
159
160 //Creating new TODO note
161 if(!_document->setContent(&file) || !_document->childNodes().count())
162 {
163 _document->appendChild(_document->createProcessingInstruction("xml", "version='1.0' encoding='UTF-8'"));
164
165 QDomElement elem = _document->createElement("ztodo");
166 elem.setAttribute("version", VERSION);
167 _document->appendChild(elem);
168 }
169
170 //Removing old tasks
171 delete _root_task;
172
173 //Inserting new tasks
174 QDomElement root_element = _document->documentElement();
175 #if QT_VERSION >= 0x050000
176 beginResetModel();
177 _root_task = new Task(_document, root_element, 0, NULL);
178 endResetModel();
179 #endif
180 #if QT_VERSION < 0x050000
181 _root_task = new Task(_document, root_element, 0, NULL);
182 reset();
183 #endif
184
185
186 return _document;
187 }
188
insertRows(int row,int count,const QModelIndex & parent)189 bool TodoModel::insertRows(int row, int count, const QModelIndex& parent)
190 {
191 Task* task = getTask(parent);
192 beginInsertRows(parent, row, row+count-1);
193 for(int i=0; i<count; ++i)
194 {
195 task->insertSubTask(row+i);
196 }
197 endInsertRows();
198 return true;
199 }
200
removeRows(int row,int count,const QModelIndex & parent)201 bool TodoModel::removeRows(int row, int count, const QModelIndex& parent)
202 {
203 Task* task_parent = getTask(parent);
204 beginRemoveRows(parent, row, row+count-1);
205 for(int i=0; i<count; ++i)
206 {
207 task_parent->removeSubTask(row+i);
208 }
209 endRemoveRows();
210 return true;
211 }
212
supportedDropActions() const213 Qt::DropActions TodoModel::supportedDropActions() const
214 {
215 return Qt::MoveAction;
216 }
217
mimeTypes() const218 QStringList TodoModel::mimeTypes() const
219 {
220 QStringList types;
221 types << NOTE_TODO_TASK_MIME;
222 return types;
223 }
224
mimeData(const QModelIndexList & indexes) const225 QMimeData* TodoModel::mimeData(const QModelIndexList& indexes) const
226 {
227 QMimeData* mimeData = new QMimeData();
228 QByteArray encodedData;
229 QDataStream stream(&encodedData, QIODevice::WriteOnly);
230 foreach(QModelIndex index, indexes)
231 {
232 Task* task = getTask(index);
233 stream << task->title();
234 stream << task->comment();
235 stream << task->dateStart().toTime_t();
236 stream << task->dateStop().toTime_t();
237 stream << task->dateLimit().toTime_t();
238 stream << task->done();
239 stream << int(task->priority());
240 // if (index.isValid())
241 // {
242 // QString text = data(index, Qt::DisplayRole).toString();
243 // stream << text;
244 // }
245 }
246 mimeData->setData(NOTE_TODO_TASK_MIME, encodedData);
247 return mimeData;
248 }
249
dropMimeData(const QMimeData * data,Qt::DropAction action,int row,int column,const QModelIndex & parent)250 bool TodoModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent)
251 {
252 Q_UNUSED(column);
253 if(action==Qt::IgnoreAction) return true;
254 if(!parent.isValid()) return false;
255 if(!data->hasFormat(NOTE_TODO_TASK_MIME)) return false;
256 if(action!=Qt::MoveAction) return true;
257 Task* task_parent = getTask(parent);
258 const QList<Task*>& task_list = task_parent->subtasks();
259 if(row==-1) row = task_list.size();
260 if(insertRow(row, parent))
261 {
262 Task* task = task_list.at(row);
263 //
264 QByteArray encodedData = data->data(NOTE_TODO_TASK_MIME);
265 QDataStream stream(&encodedData, QIODevice::ReadOnly);
266 QString title, comment;
267 uint date_start_ts, date_stop_ts, date_limit_ts;
268 bool done;
269 int priority;
270 //
271 stream >> title >> comment
272 >> date_start_ts >> date_stop_ts >> date_limit_ts
273 >> done >> priority;
274 task->setTitle(title);
275 task->setComment(comment);
276 task->setDateStart(QDateTime::fromTime_t(date_start_ts));
277 if(done) task->setDateStop(QDateTime::fromTime_t(date_stop_ts));
278 if(date_limit_ts) task->setDateLimit(QDateTime::fromTime_t(date_limit_ts));
279 task->setPriority(Task::Priority(priority));
280 return true;
281 }
282 return false;
283 }
284
rowCount(const QModelIndex & index) const285 int TodoModel::rowCount(const QModelIndex& index) const
286 {
287 //if(!parent.isValid()) return 0;
288 if(index.column() > 0) return 0;
289 Task* task = getTask(index);
290 if(!task) return 0;
291 return task->subtasks().size();
292 }
293
columnCount(const QModelIndex & parent) const294 int TodoModel::columnCount(const QModelIndex& parent) const
295 {
296 Q_UNUSED(parent)
297 return 7;
298 }
299
getTask(const QModelIndex & index) const300 Task* TodoModel::getTask(const QModelIndex &index) const
301 {
302 if(index.isValid())
303 {
304 Task* task = static_cast<Task*>(index.internalPointer());
305 if(task) return task;
306 }
307 return _root_task;
308 }
309
getDateGap(const QDateTime & dest_date)310 QString getDateGap(const QDateTime& dest_date)
311 {
312 if(dest_date.isNull()) return QString();
313
314 QDateTime current = QDateTime::currentDateTime();
315
316 int days_gap = current.daysTo(dest_date);
317 int secs_gap = current.secsTo(dest_date);
318
319 if(secs_gap<0) return QObject::tr("expired");
320
321 if(days_gap) return QObject::tr("%n day(s)", "", days_gap);
322 if(secs_gap>=3600) return QObject::tr("%n hour(s)", "", secs_gap/3600);
323 if(secs_gap>=60) return QObject::tr("%n minute(s)", "", secs_gap/60);
324 return QObject::tr("%n second(s)", "", secs_gap);
325 }
326
data(const QModelIndex & index,int role) const327 QVariant TodoModel::data(const QModelIndex& index, int role) const
328 {
329 if(!index.isValid()) return QVariant();
330
331 Task* task = static_cast<Task*>(index.internalPointer());
332
333 if(role == Qt::DisplayRole)
334 {
335 switch(index.column())
336 {
337 case 0:
338 {
339 int child_count = task->subtasks().count();
340 if(child_count==0) return task->title();
341 int child_done = 0;
342 for(int i=0; i<child_count; ++i)
343 if(task->subtasks().at(i)->done())
344 ++child_done;
345 return task->title()+QString(" (%1/%2)").arg(child_done).arg(child_count);
346 }
347 case 1: return (!task->done())?getDateGap(task->dateLimit()):"";
348 case 2: return task->dateStart();
349 case 3: return task->dateStop();
350 case 4: return task->dateLimit();
351 case 5: return task->priority();
352 case 6: return task->comment();
353 default: return QVariant();
354 }
355 }
356 else if(role == Qt::EditRole)
357 {
358 switch(index.column())
359 {
360 case 0: return task->title();
361 case 1: if(!task->done()) task->dateLimit();
362 case 2: return task->dateStart().toString();
363 case 3: return task->dateStop().toString();
364 case 4: return task->dateLimit();
365 case 6: return task->comment();
366 default: return QVariant();
367 }
368 }
369 else if(role == Qt::CheckStateRole)
370 {
371 switch(index.column())
372 {
373 case 0: return (task->done())?Qt::Checked:Qt::Unchecked;
374 default: return QVariant();
375 }
376 }
377 else if(role == Qt::FontRole)
378 {
379 if(task->done())
380 {
381 QFont font;
382 font.setStrikeOut(true);
383 return font;
384 }
385 return QFont();
386 }
387 else if(role == Qt::ForegroundRole)
388 {
389 QPalette::ColorGroup colorgroup = (task->done())?QPalette::Disabled:QPalette::Normal;
390 return QPalette().color(colorgroup, QPalette::Text);
391 }
392 return QVariant();
393 }
394
setData(const QModelIndex & index,const QVariant & data,int role)395 bool TodoModel::setData(const QModelIndex& index, const QVariant& data, int role)
396 {
397 Task* task = static_cast<Task*>(index.internalPointer());
398 switch(index.column())
399 {
400 case 0:
401 if(role == Qt::CheckStateRole)
402 {
403 task->setDone(data.toBool());
404 emit dataChanged(index, index);
405 return true;
406 }
407 else if(role == Qt::EditRole)
408 {
409 task->setTitle(data.toString());
410 emit dataChanged(index, index);
411 return true;
412 }
413 case 4:
414 if(role == Qt::EditRole)
415 {
416 QDateTime date = data.toDateTime();
417 task->setDateLimit(date);
418 emit dataChanged(index, index);
419 QModelIndex date_display_index = index.sibling(index.row(), 1);
420 emit dataChanged(date_display_index, date_display_index);
421 return true;
422 }
423 case 6:
424 if(role == Qt::EditRole)
425 {
426 task->setComment(data.toString());
427 emit dataChanged(index, index);
428 return true;
429 }
430 default:
431 return QAbstractItemModel::setData(index, data, role);
432 }
433 return QAbstractItemModel::setData(index, data, role);
434 }
435
flags(const QModelIndex & index) const436 Qt::ItemFlags TodoModel::flags(const QModelIndex& index) const
437 {
438 if(!index.isValid()) return Qt::ItemIsDropEnabled;
439 switch(index.column())
440 {
441 case 0: return Qt::ItemIsDragEnabled|Qt::ItemIsDropEnabled|Qt::ItemIsSelectable|Qt::ItemIsEnabled|Qt::ItemIsUserCheckable|Qt::ItemIsEditable;
442 case 1: return Qt::ItemIsDragEnabled|Qt::ItemIsDropEnabled|Qt::ItemIsSelectable|Qt::ItemIsEnabled;
443 default: return Qt::ItemIsDropEnabled|Qt::ItemIsDropEnabled|Qt::ItemIsSelectable|Qt::ItemIsEnabled;
444 }
445 }
446
headerData(int section,Qt::Orientation orientation,int role) const447 QVariant TodoModel::headerData(int section, Qt::Orientation orientation, int role) const
448 {
449 Q_UNUSED(section)
450 Q_UNUSED(orientation)
451 Q_UNUSED(role)
452 // if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
453 // {
454 // switch (section)
455 // {
456 // case 0: return tr("Name");
457 // case 1: return tr("Attributes");
458 // case 2: return tr("Value");
459 // default: return QVariant();
460 // }
461 // }
462 return QVariant();
463 }
464
index(int row,int column,const QModelIndex & parent) const465 QModelIndex TodoModel::index(int row, int column, const QModelIndex& parent) const
466 {
467 //if(!hasIndex(row, column, parent)) return QModelIndex();
468
469 Task* parent_item = getTask(parent);
470
471 Task* child_item = (parent_item->subtasks().size()>row)?
472 parent_item->subtasks().at(row):0;
473
474 if(child_item) return createIndex(row, column, child_item);
475 else return QModelIndex();
476 }
477
parent(const QModelIndex & child) const478 QModelIndex TodoModel::parent(const QModelIndex& child) const
479 {
480 if (!child.isValid()) return QModelIndex();
481
482 Task* child_item = getTask(child);
483 Task* parent_item = child_item->parent();
484
485 //if(parent_item==_root_task) return index(0,0);
486 if(parent_item==_root_task) return QModelIndex();
487
488 return createIndex(parent_item->row(), 0, parent_item);
489 }
490
TodoProxyModel()491 TodoProxyModel::TodoProxyModel()
492 : QSortFilterProxyModel(), hide_done_tasks(false)
493 {
494 }
495
496
hideDoneTasks(bool hide)497 void TodoProxyModel::hideDoneTasks(bool hide)
498 {
499 hide_done_tasks = hide;
500 emit filterChanged();
501 }
502
filterAcceptsRow(int source_row,const QModelIndex & source_parent) const503 bool TodoProxyModel::filterAcceptsRow (int source_row, const QModelIndex& source_parent) const
504 {
505 if(hide_done_tasks)
506 {
507 // qDebug() << __LINE__;
508 //тут херятся первые строки =(
509 QModelIndex source_index = source_parent.isValid() ?
510 source_parent.child(source_row, 0) :
511 sourceModel()->index(0,0).child(source_row, 0);
512 // qDebug() << __LINE__;
513 //
514 // qDebug() << source_index.isValid() << source_index.data(Qt::CheckStateRole).toBool() << source_index.data().toString();
515 //
516 if(source_index.isValid())
517 {
518 // qDebug() << __LINE__;
519 bool done = source_index.data(Qt::CheckStateRole).toBool();
520 // qDebug() << __LINE__;
521 if(done) return false;
522 }
523 }
524 return true;
525 }
526