1 /*************************-**************************************************
2 * Copyright (C) 2007 by Arrigo Zanette *
4 ***************************************************************************/
10 #include "backupper.h"
11 #include "sakwidget.h"
12 #include "saksubwidget.h"
13 #include "sakmessageitem.h"
14 #include "pixmapviewer.h"
16 #include "backupper.h"
20 //BEGIN Hits >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
22 //BEGIN MyDateItemDelegate
25 MyDateItemDelegate::MyDateItemDelegate(QObject
*parent
)
26 : QItemDelegate(parent
)
30 QWidget
*MyDateItemDelegate::createEditor(QWidget
*parent
,
31 const QStyleOptionViewItem
&/* option */,
32 const QModelIndex
& index
) const
34 QDateTimeEdit
*editor
= new QDateTimeEdit(parent
);
35 editor
->setCalendarPopup(true);
36 editor
->setDisplayFormat(DATETIMEFORMAT
);
37 editor
->setDateTime(QDateTime::fromString(index
.data().toString(), DATETIMEFORMAT
));
41 void MyDateItemDelegate::setEditorData(QWidget
*editor
,
42 const QModelIndex
&index
) const
44 const QDateTime
& value
= index
.model()->data(index
, Qt::DisplayRole
).toDateTime();
46 QDateTimeEdit
*e
= static_cast<QDateTimeEdit
*>(editor
);
47 e
->setDateTime(value
);
50 void MyDateItemDelegate::setModelData(QWidget
*editor
, QAbstractItemModel
*model
,
51 const QModelIndex
&index
) const
53 QDateTimeEdit
*e
= static_cast<QDateTimeEdit
*>(editor
);
55 const QDateTime
& value
= e
->dateTime();
57 model
->setData(index
, value
.toString(DATETIMEFORMAT
), Qt::EditRole
);
60 void MyDateItemDelegate::updateEditorGeometry(QWidget
*editor
,
61 const QStyleOptionViewItem
&option
, const QModelIndex
&/* index */) const
63 editor
->setGeometry(option
.rect
);
66 //END MyDateItemDelegate
68 //BEGIN TaskItemDelegate
70 TaskItemDelegate::TaskItemDelegate(Sak
* sak
, QObject
*parent
)
71 : QItemDelegate(parent
), m_sak(sak
)
75 QWidget
*TaskItemDelegate::createEditor(QWidget
*parent
,
76 const QStyleOptionViewItem
&/* option */,
77 const QModelIndex
& index
) const
79 QComboBox
*editor
= new QComboBox(parent
);
82 QString current
= index
.data().toString();
83 foreach(const Task
& t
, *m_sak
->tasks()) {
84 editor
->addItem(t
.icon
, t
.title
);
85 if (t
.title
== current
)
89 editor
->setCurrentIndex(j
);
93 void TaskItemDelegate::setEditorData(QWidget
*editor
,
94 const QModelIndex
&index
) const
96 const QString
& value ( index
.data().toString() );
98 QComboBox
*e
= static_cast<QComboBox
*>(editor
);
99 for(int i
=0; i
<e
->count(); i
++) {
100 if (e
->itemText(i
) == value
) {
101 e
->setCurrentIndex(i
);
107 void TaskItemDelegate::setModelData(QWidget
*editor
, QAbstractItemModel
*model
,
108 const QModelIndex
&index
) const
110 QComboBox
*e
= static_cast<QComboBox
*>(editor
);
111 model
->setData(index
, e
->currentText(), Qt::EditRole
);
112 model
->setData(index
, e
->itemIcon(e
->currentIndex()), Qt::DecorationRole
);
115 void TaskItemDelegate::updateEditorGeometry(QWidget
*editor
,
116 const QStyleOptionViewItem
&option
, const QModelIndex
&/* index */) const
118 editor
->setGeometry(option
.rect
);
121 //END TaskItemDelegate
124 //BEGIN SubTaskItemDelegate
126 SubTaskItemDelegate::SubTaskItemDelegate(Sak
* s
, QObject
*parent
)
127 : QItemDelegate(parent
), m_sak(s
)
131 QWidget
*SubTaskItemDelegate::createEditor(QWidget
*parent
,
132 const QStyleOptionViewItem
&/* option */,
133 const QModelIndex
& index
) const
135 QComboBox
*editor
= new QComboBox(parent
);
136 editor
->setEditable(true);
139 QString taskTitle
= index
.model()->index(index
.row(), index
.column()-1, index
.parent()).data().toString();
141 QHash
<QString
, Task
>::iterator titr
= m_sak
->tasks()->find(taskTitle
);
142 if (titr
== m_sak
->tasks()->end()) {
145 const Task
& task(titr
.value());
147 QString current
= index
.data().toString();
148 QHash
< QString
, Task::SubTask
>::const_iterator itr
= task
.subTasks
.begin(), end
= task
.subTasks
.end();
149 for(int i
=0; itr
!= end
; itr
++, i
++) {
150 editor
->addItem(itr
->title
);
151 if (itr
->title
== current
)
155 editor
->setCurrentIndex(j
);
159 void SubTaskItemDelegate::setEditorData(QWidget
*editor
,
160 const QModelIndex
&index
) const
162 const QString
& value ( index
.data().toString() );
164 QComboBox
*e
= static_cast<QComboBox
*>(editor
);
165 for(int i
=0; i
<e
->count(); i
++) {
166 if (e
->itemText(i
) == value
) {
167 e
->setCurrentIndex(i
);
173 void SubTaskItemDelegate::setModelData(QWidget
*editor
, QAbstractItemModel
*model
,
174 const QModelIndex
&index
) const
176 QComboBox
*e
= static_cast<QComboBox
*>(editor
);
177 model
->setData(index
, e
->currentText(), Qt::EditRole
);
178 model
->setData(index
, e
->itemIcon(e
->currentIndex()), Qt::DecorationRole
);
181 void SubTaskItemDelegate::updateEditorGeometry(QWidget
*editor
,
182 const QStyleOptionViewItem
&option
, const QModelIndex
&/* index */) const
184 editor
->setGeometry(option
.rect
);
187 //END TaskItemDelegate
191 void Sak::addDefaultHit()
193 QTreeWidgetItem
* i
= new QTreeWidgetItem
;
194 Task::Hit
p(QDateTime::currentDateTime(), 0);
195 i
->setText(0, p
.timestamp
.toString(DATETIMEFORMAT
));
196 i
->setText(1, m_editedTasks
.begin().key());
197 i
->setIcon(1, m_editedTasks
.begin().value().icon
);
198 i
->setSizeHint(0, QSize(32, 32));
199 i
->setText(3, QString("%1").arg(p
.duration
));
200 i
->setFlags(i
->flags() | Qt::ItemIsEditable
);
201 hitsList
->addTopLevelItem( i
);
203 m_editedTasks
[i
->text(1)].hits
[i
->text(2)] << p
;
204 i
->setData(1, Qt::UserRole
, qVariantFromValue(HitElement(&m_editedTasks
[i
->text(1)], i
->text(2), p
.timestamp
, p
.duration
)));
207 void Sak::exportHits()
209 QString fileName
= QFileDialog::getSaveFileName();
210 QFile
file(fileName
);
211 if (!file
.open(QIODevice::Truncate
|QIODevice::ReadWrite
)) {
212 QMessageBox::warning(0, QString("Error saving"), QString("Error opening file %1").arg(fileName
));
215 QTextStream
stream(&file
);
216 for (int i
=0; i
<hitsList
->topLevelItemCount(); i
++) {
217 QTreeWidgetItem
* w
= hitsList
->topLevelItem ( i
);
218 QString
name(w
->text(1));
219 QDateTime
timestamp(QDateTime::fromString(w
->text(0), DATETIMEFORMAT
));
220 QHash
<QString
, Task
>::iterator titr
= m_tasks
.find(name
);
221 stream
<< w
->text(1) << ";" << w
->text(0) << ";" << w
->text(2) << ";" << w
->text(3) << ";\n";
228 QList
<HitElement
> Sak::createHitsList(const QDateTime
& from
, const QDateTime
& to
)
230 QMap
<QDateTime
, HitElement
> hits
;
231 foreach(const Task
& t
, m_editedTasks
) {
232 QHash
<QString
, QList
< Task::Hit
> > ::const_iterator hitr
= t
.hits
.begin(), hend
= t
.hits
.end();
233 while(hitr
!= hend
) {
234 const QList
< Task::Hit
> & l(hitr
.value());
235 for (int i
=0; i
<l
.count(); i
++) {
236 const QDateTime
& d
= l
[i
].timestamp
;
237 if ( (!from
.isValid() || d
>= from
) && ( !to
.isValid() || d
<= to
) ) {
238 hits
.insertMulti(d
, HitElement((Task
*)&t
, hitr
.key(), d
, l
[i
].duration
));
244 return hits
.values();
248 QMap
<double,QPair
<Task
*, QString
> > Sak::createSummaryList(const QList
<HitElement
>& hits
)
250 QHash
<QPair
<Task
*, QString
>, double> summaryMap
;
251 foreach(const HitElement
& hit
, hits
) {
252 if (!hit
.subtask
.isEmpty()) {
253 summaryMap
[QPair
<Task
*, QString
>(hit
.task
, hit
.subtask
)] += Task::hours(hit
.duration
);
255 summaryMap
[QPair
<Task
*, QString
>(hit
.task
, "")] += Task::hours(hit
.duration
);
257 QMap
<double, QPair
<Task
*, QString
> > summaryOrderedMap
;
258 QHash
<QPair
<Task
*, QString
>, double>::const_iterator itr
= summaryMap
.begin();
259 while(itr
!= summaryMap
.end()) {
260 summaryOrderedMap
.insertMulti(itr
.value(), itr
.key());
263 return summaryOrderedMap
;
268 void Sak::saveHitChanges()
271 if ( QMessageBox::question ( 0, "Hit list changed", "Hit list has changed: do you want to save changes?", QMessageBox::Save
| QMessageBox::Discard
, QMessageBox::Discard
) == QMessageBox::Save
) {
272 m_tasks
= m_editedTasks
;
273 } else m_editedTasks
= m_tasks
; // undo changes
275 selectedStartDate(QDate());
277 // add subtasks, if missing
279 QHash
<QString
, Task
>::iterator itr
= m_tasks
.begin();
280 while(itr
!= m_tasks
.end()) {
281 itr
->updateSubTasks();
288 bool Sak::hitsListEventFilter(QEvent
* e
)
290 if (e
->type() == QEvent::ContextMenu
) {
291 QContextMenuEvent
* me
= dynamic_cast<QContextMenuEvent
*>(e
);
292 if (!me
) return false;
293 m_addHitMenu
->popup(me
->globalPos());
295 } else if (e
->type() == QEvent::KeyRelease
) {
296 QKeyEvent
* ke
= dynamic_cast<QKeyEvent
*>(e
);
297 if (!ke
) return false;
298 if ( !hitsList
->hasFocus() || (ke
->key() != Qt::Key_Delete
&& ke
->key() != Qt::Key_Backspace
) ) return false;
299 QList
<QTreeWidgetItem
*> selected
= hitsList
->selectedItems();
300 if (selected
.isEmpty()) return false;
301 foreach(QTreeWidgetItem
* ii
, selected
) {
302 hitsListItemChanged(hitsList
->takeTopLevelItem(hitsList
->indexOfTopLevelItem (ii
)), -1);
305 } else if (e
->type() == QEvent::Hide
) {
311 void Sak::selectTodayDate()
313 QDateTime dt1
= QDateTime::currentDateTime();
314 dt1
.setTime(QTime(0,0,0));
315 QDateTime dt2
= dt1
.addDays(1);
316 selectedStartDate(dt1
.date());
317 selectedEndDate(dt2
.date());
320 void Sak::selectThisWeeDate()
322 QDateTime dt1
= QDateTime::currentDateTime();
323 dt1
.setTime(QTime(0,0,0));
324 dt1
= dt1
.addDays(-dt1
.date().dayOfWeek()+1);
325 QDateTime dt2
= dt1
.addDays(7);
326 selectedStartDate(dt1
.date());
327 selectedEndDate(dt2
.date());
330 void Sak::selectThisMonthDate()
332 QDateTime dt1
= QDateTime::currentDateTime();
333 dt1
.setTime(QTime(0,0,0));
334 dt1
= dt1
.addDays(-dt1
.date().day()+1);
335 QDateTime dt2
= dt1
.addMonths(1);
336 selectedStartDate(dt1
.date());
337 selectedEndDate(dt2
.date());
340 void Sak::selectLastWeekDate()
342 QDateTime dt2
= QDateTime::currentDateTime();
343 dt2
.setTime(QTime(23,59,59));
344 QDateTime dt1
= dt2
.addDays(-7);
345 selectedStartDate(dt1
.date());
346 selectedEndDate(dt2
.date());
349 void Sak::selectLastMonthDate()
351 QDateTime dt2
= QDateTime::currentDateTime();
352 dt2
.setTime(QTime(23,59,59));
353 QDateTime dt1
= dt2
.addDays(-30);
354 selectedStartDate(dt1
.date());
355 selectedEndDate(dt2
.date());
358 void Sak::selectedStartDate(const QDate
& _date
)
361 if (!date
.isValid()) {
362 date
= cal1
->selectedDate();
365 if (sender() != cal1
) {
366 cal1
->setSelectedDate(date
);
368 if (sender() != cal3
) {
369 cal3
->setSelectedDate(date
);
371 if (cal2
->selectedDate() < cal1
->selectedDate()) {
372 cal2
->setSelectedDate(date
.addDays(1));
373 cal4
->setSelectedDate(date
.addDays(1));
375 cal2
->setMinimumDate(date
);
376 cal4
->setMinimumDate(date
);
377 const QList
<HitElement
>& hits ( createHitsList(QDateTime(cal1
->selectedDate()), QDateTime(cal2
->selectedDate())) );
378 populateHitsList(hits
);
380 hitsTimeline
->setPeriod(QDateTime(cal1
->selectedDate()), QDateTime(cal2
->selectedDate()));
383 void Sak::selectedEndDate(const QDate
& _date
)
387 date
= cal1
->selectedDate();
389 if (sender() != cal2
) {
390 cal2
->setSelectedDate(date
);
392 if (sender() != cal4
) {
393 cal4
->setSelectedDate(date
);
395 if (cal1
->selectedDate() > date
) {
396 cal2
->setSelectedDate(cal1
->selectedDate().addDays(1));
397 cal4
->setSelectedDate(cal1
->selectedDate().addDays(1));
399 const QList
<HitElement
>& hits ( createHitsList(QDateTime(cal1
->selectedDate()), QDateTime(cal2
->selectedDate())) );
401 populateHitsList(hits
);
402 hitsTimeline
->setPeriod(QDateTime(cal1
->selectedDate()), QDateTime(cal2
->selectedDate()));
406 void Sak::hitsListItemChanged(QTreeWidgetItem
* i
, int column
)
408 hitsList
->blockSignals(true);
410 // save change in structure m_editedTasks
412 const HitElement
& origHit
= i
->data(1, Qt::UserRole
).value
<HitElement
>();
413 // find hit in m_editedTasks
414 Q_ASSERT(origHit
.task
);
415 Task
& t(*origHit
.task
);
416 Q_ASSERT(m_editedTasks
.contains(t
.title
));
417 Task
& et(m_editedTasks
[t
.title
]);
419 QList
< Task::Hit
>& origList(et
.hits
[origHit
.subtask
]);
421 int hitPosition
= origList
.indexOf(Task::Hit(origHit
.timestamp
, origHit
.duration
));
422 if (hitPosition
==-1) {
423 qWarning() << "CANNOT FIND IN TASK LIST "<< t
.title
<< origHit
.subtask
<< origHit
.timestamp
<< origHit
.duration
;
426 if (!m_editedTasks
.contains(i
->text(1))) {
427 i
->setText(1, t
.title
);
428 qDebug() << "Task " << i
->text(1) << " does not exists -> undo change";
429 } else if (column
== -1) {
430 qDebug() << "remove hit from task " << t
.title
;
431 origList
.takeAt(hitPosition
);
433 qDebug() << "remove hit from task " << t
.title
;
434 origList
.takeAt(hitPosition
);
435 Task
& nt(m_editedTasks
[i
->text(1)]);
436 Task::Hit
p(QDateTime::fromString(i
->text(0), DATETIMEFORMAT
), i
->text(3).toUInt());
437 QString subTask
= column
== 2 || nt
.subTasks
.contains(i
->text(2)) ? i
->text(2) : "";
438 i
->setData(1, Qt::UserRole
, qVariantFromValue(HitElement(&nt
, subTask
, p
.timestamp
, p
.duration
)));
439 qDebug() << "insert hit into task " << i
->text(1) << p
.timestamp
;
440 nt
.hits
[subTask
] << p
;
441 if (subTask
!= i
->text(2)) {
442 i
->setText(2, subTask
);
444 if (nt
.bgColor
!= i
->backgroundColor(0) || nt
.fgColor
!= i
->foreground(0).color()) {
445 for(int j
=0; j
<4; j
++) {
446 i
->setBackground(j
,nt
.bgColor
);
447 i
->setForeground(j
,nt
.fgColor
);
455 else if (m_editedTasks
.contains(i
->text(column
))) {
456 i
->setIcon(column
, m_editedTasks
[i
->text(column
)].icon
);
458 i
->setIcon(column
, QIcon());
461 hitsList
->blockSignals(false);
463 if ( !m_updatingFromTimeline
) {
465 const QList
<HitElement
>& hits ( createHitsList(QDateTime(cal1
->selectedDate()), QDateTime(cal2
->selectedDate())) );
466 populateHitsTimeline(hits
, hitsTimeline
);
470 void Sak::hitsSelectedInList(QTreeWidgetItem
* current
, QTreeWidgetItem
* /*prev*/)
473 // clear current selection
474 QList
<QGraphicsItem
*> sitems
= hitsTimeline
->scene()->selectedItems();
475 for(int j
=0; j
<sitems
.count(); j
++) {
476 HitItem
* tmp
= dynamic_cast<HitItem
*>(sitems
[j
]);
478 tmp
->setZValue(tmp
->timestamp().toTime_t());
479 tmp
->setSelected(false);
484 QPointF
center (QDateTime::fromString(current
->text(0), DATETIMEFORMAT
).toTime_t() / 60.0, 0);
485 int duration
= current
->text(3).toUInt();
486 QList
<QGraphicsItem
*> items
= hitsTimeline
->scene()->items();
487 for(int i
=0; i
<items
.count(); i
++) {
488 HitItem
* hitem
= dynamic_cast<HitItem
*>(items
[i
]);
489 if (hitem
&& hitem
->timestamp() == QDateTime::fromString(current
->text(0), DATETIMEFORMAT
) && hitem
->task()->title
== current
->text(1) && hitem
->subtask() == current
->text(2) && hitem
->duration() == duration
) {
490 hitem
->setZValue(1e20
);
491 hitem
->setSelected(true);
495 hitsTimeline
->centerOn(center
);
499 void Sak::hitsSelectedInTimeline(HitItem
* hitem
)
501 m_updatingFromTimeline
= true;
503 const Task
* t
= hitem
->task();
504 QList
<QTreeWidgetItem
*> items
= hitsList
->findItems(hitem
->timestamp().toString(DATETIMEFORMAT
), Qt::MatchExactly
, 0);
505 qDebug() << "look for " << hitem
->timestamp().toString(DATETIMEFORMAT
);
507 QString
tmp(QString ("%1").arg(hitem
->duration()));
508 qDebug() << "duration " << tmp
;
509 foreach(QTreeWidgetItem
* item
, items
) {
510 if (item
->text(1) == t
->title
&& item
->text(2) == hitem
->subtask() && item
->text(3) == tmp
) {
511 hitsList
->clearSelection();
512 item
->setSelected(true);
513 if (hitem
->timestamp() != hitem
->newTimestamp() || hitem
->duration() != hitem
->newDuration()) {
514 item
->setText(0, hitem
->newTimestamp().toString(DATETIMEFORMAT
));
515 item
->setText(3, QString("%1").arg(hitem
->newDuration()));
516 hitem
->commitChanges();
518 hitsList
->scrollToItem(item
);
522 m_updatingFromTimeline
= false;
527 void Sak::populateHitsList(const QList
<HitElement
>& hits
, QTreeWidget
* theHitsList
)
529 if (theHitsList
== 0)
530 theHitsList
= hitsList
;
531 Q_ASSERT(theHitsList
);
533 theHitsList
->clear();
536 double totOverestimation
;
537 HitElement::overestimations(hits
, o
, totOverestimation
);
538 QList
<QTreeWidgetItem
*> widgets
;
540 foreach(const HitElement
& hit
, hits
) {
541 QTreeWidgetItem
* w
= new QTreeWidgetItem
;
542 if (hit
.task
->title
== "<away>") {
543 hit
.task
->fgColor
= Qt::gray
;
545 w
->setText(0, hit
.timestamp
.toString(DATETIMEFORMAT
));
546 w
->setSizeHint(0, QSize(24, 24));
547 w
->setText(1, hit
.task
->title
);
548 w
->setData(1, Qt::UserRole
, qVariantFromValue(hit
));
549 w
->setText(2, hit
.subtask
);
550 w
->setText(3, QString("%1").arg(hit
.duration
));
551 for(int i
=0; i
<4; i
++) {
552 w
->setBackground(i
,hit
.task
->bgColor
);
553 w
->setForeground(i
,hit
.task
->fgColor
);
555 #if MARK_OVERESTIMATIONS
557 w
->setBackground(0,Qt::red
);
558 w
->setBackground(1,Qt::red
);
559 w
->setBackground(2,Qt::red
);
560 w
->setBackground(3,Qt::red
);
561 w
->setBackground(4,Qt::red
);
562 w
->setText(4,QString("%1").arg(o
[i
]));
564 w
->setText(4,QString("%1").arg(o
[i
]));
566 //w->setIcon(1, hit.task->icon);
568 w
->setFlags(w
->flags() | Qt::ItemIsEditable
);
569 else w
->setDisabled(true);
573 theHitsList
->addTopLevelItems(widgets
);
577 theHitsList
= summaryList
;
578 theHitsList
->clear();
579 const QMap
<double, QPair
<Task
*, QString
> >& map(createSummaryList(hits
));
580 QMap
<double, QPair
<Task
*, QString
> >::const_iterator itr
= map
.begin();
581 QHash
<Task
*, QTreeWidgetItem
*> topLevels
;
582 QList
<QTreeWidgetItem
*> widgets
;
583 QList
<QLabel
*> labels
;
584 for(; itr
!= map
.end(); itr
++) { // first be sure to insert top levels
585 QTreeWidgetItem
* topLevel
;
586 if (!topLevels
.contains(itr
.value().first
)) {
587 QTreeWidgetItem
* w
= new QTreeWidgetItem(QTreeWidgetItem::UserType
);
588 Task
* t
= itr
.value().first
;
589 w
->setData(0, Qt::UserRole
, t
->title
);
590 QLabel
* l
= t
->url
.size() ? new QLabel( QString("<a href=\"%2\" style=\"color:%3\" >%1</a>").arg( t
->title
).arg(t
->url
).arg(t
->fgColor
.name())) : new QLabel(QString("<span style=\"color:%2\">%1</span>").arg(t
->title
).arg(t
->fgColor
.name()));
591 l
->setOpenExternalLinks(true);
593 w
->setData(0, Qt::UserRole
, t
->title
);
594 // w->setIcon(0, t->icon);
595 w
->setBackgroundColor(0, t
->bgColor
);
596 w
->setForeground(0, t
->fgColor
);
597 w
->setBackgroundColor(1, t
->bgColor
);
598 w
->setForeground(1, t
->fgColor
);
599 topLevels
[itr
.value().first
] = w
;
603 topLevel
= topLevels
[itr
.value().first
];
604 if (itr
.value().second
== "")
605 topLevel
->setText(1, QString("%1").arg(topLevel
->text(1).toInt() + itr
.key(), 4, 'f', 2, ' '));
606 if (!itr
.value().second
.isEmpty()) {
607 QTreeWidgetItem
* w
= new QTreeWidgetItem(QTreeWidgetItem::UserType
);
608 Task
* t
= itr
.value().first
;
609 w
->setText(0, itr
.value().second
);
610 w
->setText(1, QString("%1").arg(itr
.key(), 4, 'f', 2, ' '));
611 w
->setBackgroundColor(0, t
->bgColor
);
612 w
->setForeground(0, t
->fgColor
);
613 w
->setBackgroundColor(1, t
->bgColor
);
614 w
->setForeground(1, t
->fgColor
);
615 topLevel
->addChild(w
);
619 theHitsList
->addTopLevelItems(topLevels
.values());
620 for( int i
=0; i
<widgets
.size(); i
++) {
621 theHitsList
->setItemWidget(widgets
.at(i
), 0, labels
.at(i
));
625 populateHitsTimeline(hits
, hitsTimeline
);
626 summaryChart
->setHits(hits
);
629 void Sak::populateHitsTimeline(const QList
<HitElement
>& hits
, Timeline
* timeline
)
631 if (!timeline
) return;
633 m_updatingFromTimeline
= true;
635 QGraphicsScene
* scene
= timeline
->scene();
637 QList
<QGraphicsItem
*> items
= scene
->items();
638 foreach(QGraphicsItem
* item
, items
) {
639 if (dynamic_cast<HitItem
*>(item
)) {
640 scene
->removeItem(item
);
644 foreach(HitElement e
, hits
) {
645 HitItem
* item
= new HitItem(e
.task
, e
.timestamp
, e
.duration
, e
.subtask
);
646 connect(item
, SIGNAL(changed()), timeline
, SLOT(selectionChanged()));
647 scene
->addItem(item
);
651 m_updatingFromTimeline
= false;
656 void Sak::interactiveMergeHits()
658 if (!m_incremental
->foundPieces
.count()) return;
660 mergeDialog
.setWindowTitle("Merge sparse hits");
661 mergeDialog
.setModal(true);
662 QTreeWidget
* theHitsList
= newHitsList();
663 QMap
<QDateTime
, HitElement
> hits
;
664 QMap
<QDateTime
, Incremental::Hit
>::iterator itr
= m_incremental
->foundPieces
.begin();
665 while(itr
!= m_incremental
->foundPieces
.end()) {
666 QHash
<QString
, Task
>::iterator titr
= m_tasks
.find(itr
.value().task
);
667 if (titr
== m_tasks
.end()) {
668 qDebug() << "Discard piece for unknown task " << itr
.value().task
;
670 hits
.insertMulti(itr
.key(), HitElement(&titr
.value(), itr
.value().subtask
, itr
.key(), itr
.value().duration
));
674 if (hits
.count() == 0) return;
676 qDebug() << "hits: " << hits
.count() << hits
.begin().key() << (--hits
.end()).key();
677 QList
<HitElement
> okHits (createHitsList(hits
.begin().key(), (--hits
.end()).key()));
678 QMap
<QDateTime
, HitElement
> tmpHits
;
679 for(int i
=0; i
<okHits
.count(); i
++) {
680 HitElement
& cmp(okHits
[i
]);
681 QMap
<QDateTime
, HitElement
>::iterator itr
= hits
.find(cmp
.timestamp
);
683 while(itr
!= hits
.end() && itr
.key() == cmp
.timestamp
) {
684 const HitElement
& cur ( itr
.value() );
685 if (cmp
.task
->title
== cur
.task
->title
&& cmp
.subtask
== cur
.subtask
&& cmp
.duration
== cur
.duration
) {
686 itr
= hits
.erase(itr
);
691 okHits
[i
].editable
=false;
692 tmpHits
.insertMulti(okHits
[i
].timestamp
, okHits
[i
]);
695 if (!hits
.count()) return;
696 else hits
.unite(tmpHits
);
698 populateHitsList(hits
.values(), theHitsList
);
699 mergeDialog
.setMinimumWidth(750);
700 mergeDialog
.setMinimumHeight(600);
701 QVBoxLayout
mainLayout(&mergeDialog
);
702 mainLayout
.addWidget(theHitsList
);
703 QPushButton
* ok
= new QPushButton("Do merge!");
704 QPushButton
* cancel
= new QPushButton("Cancel");
705 QHBoxLayout
* buttons
= new QHBoxLayout
;
706 buttons
->addWidget(ok
);
707 buttons
->addWidget(cancel
);
708 mainLayout
.addLayout(buttons
);
709 connect(ok
, SIGNAL(clicked()), &mergeDialog
, SLOT(accept()));
710 connect(cancel
, SIGNAL(clicked()), &mergeDialog
, SLOT(reject()));
712 if ( mergeDialog
.exec() ) {
713 for (int i
=0; i
<theHitsList
->topLevelItemCount(); i
++) {
714 QTreeWidgetItem
* w
= theHitsList
->topLevelItem ( i
);
715 if (!w
->isDisabled()) {
716 QString
name(w
->text(1));
717 QDateTime
timestamp(QDateTime::fromString(w
->text(0), DATETIMEFORMAT
));
718 int duration (w
->text(3).toUInt());
719 QHash
<QString
, Task
>::iterator titr
= m_tasks
.find(name
);
720 Q_ASSERT(titr
!= m_tasks
.end());
721 (*titr
).hits
[w
->text(2)] << Task::Hit(timestamp
, duration
);
725 m_incremental
->clearMergedPieces();