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