1 /***************************************************************************
2 * Copyright (C) 2007 by Arrigo Zanette *
4 ***************************************************************************/
8 #include <QCryptographicHash>
10 #include <QGraphicsEllipseItem>
14 #include "sakwidget.h"
15 #include "saksubwidget.h"
16 #include "sakmessageitem.h"
17 #include "pixmapviewer.h"
19 #include "backupper.h"
22 #include "gmailstorage/gmailpyinterface.h"
24 #include "gmailstorage/gmailmyinterface.h"
27 //END Task <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
40 static Window CurrentFocusWindow
;
41 static int CurrentRevertToReturn
;
48 class GView
: public QGraphicsView
52 // if (QGLFormat::hasOpenGL()) {
53 // qDebug() << "Using OpenGL";
54 // QGLWidget* w = new QGLWidget;
55 // w->setAttribute(Qt::WA_TranslucentBackground, true);
60 // delete this->viewport();
62 void drawBackground(QPainter
* p
, const QRectF
& rect
) {
63 QBrush
brush(QColor(0,0,0,200));
64 p
->setCompositionMode(QPainter::CompositionMode_Source
);
65 p
->fillRect(rect
, brush
);
70 //BEGIN Sak basic >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
72 Sak::Sak(QObject
* parent
)
79 , m_changedTask(false)
80 , m_subtaskView(false)
82 m_desktopRect
= qApp
->desktop()->rect();
84 m_subtaskCompleter
= 0;
85 summaryList
= hitsList
= 0;
88 if (QCoreApplication::arguments().contains("--clear")) {
89 QHash
<QString
, Task
>::iterator itr
= m_tasks
.begin();
90 while(itr
!= m_tasks
.end()) {
96 if (m_tasks
.count() <= 0)
100 m_changedHit
= false;
102 m_autoSaveTimer
= startTimer(1000 * 60 * 45); // every 45 minutes
105 // Need to go here, or after plasma reboot the icon will disappear
106 trayIconMenu
= new QMenu(m_settings
);
107 //trayIconMenu->addAction(minimizeAction);
108 //trayIconMenu->addAction(maximizeAction);
109 //trayIconMenu->addAction(restoreAction);
110 //trayIconMenu->addSeparator();
111 trayIconMenu
->addAction(startAction
);
112 trayIconMenu
->addAction(stopAction
);
113 trayIconMenu
->addAction(flushAction
);
114 trayIconMenu
->addSeparator();
115 trayIconMenu
->addAction(quitAction
);
116 trayIcon
= new QSystemTrayIcon(this);
117 trayIcon
->setContextMenu(trayIconMenu
);
118 trayIcon
->setIcon( QIcon(":/images/icon.png") );
119 trayIcon
->setToolTip( tr("Sistema Anti Kazzeggio") );
121 connect(trayIcon
, SIGNAL(activated(QSystemTrayIcon::ActivationReason
)), this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason
)));
122 trayIcon
->installEventFilter(this);
125 m_settings
->setWindowIcon( QIcon(":/images/icon.png") );
126 m_settings
->setWindowTitle("SaK - Sistema Anti Kazzeggio");
131 m_backupper
= new Backupper
;
132 m_incremental
= new Incremental
;
134 m_gmail
= new GmailPyInterface
;
139 // load the data model
140 QSettings
settings(QSettings::IniFormat
, QSettings::UserScope
, "ZanzaSoft", "SAK");
141 QByteArray tasksArray
= settings
.value("tasks").toByteArray();
142 QDataStream
stream(&tasksArray
, QIODevice::ReadWrite
);
143 stream
.setVersion(QDataStream::Qt_4_3
);
147 QDir
saveDir(QFileInfo(settings
.fileName()).dir());
148 saveDir
.mkdir("SakTasks");
149 saveDir
.cd("SakTasks");
150 QStringList nameFilters
;
151 nameFilters
<< "*.xml";
152 QStringList files
= saveDir
.entryList( nameFilters
, QDir::Files
);
153 foreach (QString taskXmlFileName
, files
) {
154 Task
t( loadTaskFromFile(saveDir
.filePath(taskXmlFileName
)) );
155 m_tasks
[t
.title
] = t
;
160 // add subtasks, if missing
162 QHash
<QString
, Task
>::iterator itr
= m_tasks
.begin();
163 while(itr
!= m_tasks
.end()) {
164 itr
->updateSubTasks();
170 Task
& awayTask
= m_tasks
["<away>"];
171 awayTask
.title
= "<away>";
172 awayTask
.fgColor
= Qt::gray
;
173 awayTask
.bgColor
= Qt::white
;
174 awayTask
.icon
= QPixmap(":/images/away.png");
176 m_editedTasks
= m_tasks
;
181 interactiveMergeHits();
183 m_editedTasks
= m_tasks
;
185 setupSettingsWidget();
188 m_settings
->installEventFilter(this);
189 hitsList
->installEventFilter(this);
190 tasksTree
->installEventFilter(this);
191 tasksTree
->setUniformRowHeights(false);
192 QTreeWidgetItem
* headerItem
= new QTreeWidgetItem
;
193 headerItem
->setSizeHint(0 , QSize(0,0));
194 headerItem
->setSizeHint(1 , QSize(0,0));
195 headerItem
->setSizeHint(2 , QSize(0,0));
196 tasksTree
->setHeaderItem(headerItem
);
198 connect(bgColorButton
, SIGNAL(clicked()), this, SLOT(selectColor()));
199 connect(fgColorButton
, SIGNAL(clicked()), this, SLOT(selectColor()));
200 connect(previewButton
, SIGNAL(clicked()), this, SLOT(popup()));
201 connect(tasksTree
, SIGNAL(itemSelectionChanged()), this, SLOT(selectedTask()));
202 connect(tasksTree
, SIGNAL(itemClicked(QTreeWidgetItem
*,int)), this, SLOT(selectedTask()));
205 connect(cal1
, SIGNAL(clicked(QDate
)), this, SLOT(selectedStartDate(QDate
)));
206 connect(cal2
, SIGNAL(clicked(QDate
)), this, SLOT(selectedEndDate(QDate
)));
207 connect(cal3
, SIGNAL(clicked(QDate
)), this, SLOT(selectedStartDate(QDate
)));
208 connect(cal4
, SIGNAL(clicked(QDate
)), this, SLOT(selectedEndDate(QDate
)));
209 connect(hitsList
, SIGNAL(itemChanged(QTreeWidgetItem
*,int)), this, SLOT(hitsListItemChanged(QTreeWidgetItem
*,int)));
213 m_view
->setScene(new QGraphicsScene
);
214 m_view
->scene()->setSceneRect(m_desktopRect
);
216 m_view
->installEventFilter(this);
217 m_view
->setFrameStyle(QFrame::NoFrame
);
218 m_view
->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
219 m_view
->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
220 m_view
->setWindowFlags(m_view
->windowFlags() | Qt::WindowStaysOnTopHint
| Qt::ToolTip
);
221 //m_view->setWindowModality(Qt::ApplicationModal);
222 m_view
->setAttribute(Qt::WA_QuitOnClose
, false);
223 // enable transparency with Qt4.5
224 m_view
->setAttribute(Qt::WA_TranslucentBackground
, true);
225 m_view
->setWindowIcon( QIcon(":/images/icon.png") );
226 m_view
->setWindowTitle("SaK - Sistema Anti Kazzeggio");
228 m_currentInterval
= durationSpinBox
->value();
229 m_currentInterval
= qMax((int)1, qMin((int)1440, m_currentInterval
));
230 qDebug() << "SAK: pinging interval " << m_currentInterval
<< Task::hours(m_currentInterval
) << " hours ";
232 hitsTimeline
->setPeriod(QDateTime(cal1
->selectedDate()), QDateTime(cal2
->selectedDate()));
233 populateHitsList(createHitsList(QDateTime(cal1
->selectedDate()), QDateTime(cal2
->selectedDate())));
239 m_currentInterval
= qMax((int)1, m_currentInterval
);
241 int msecs
= (int)(Task::hours(m_currentInterval
)*3600.0*1000.0 / 2);
242 m_timerId
= startTimer( msecs
);
243 m_nextTimerEvent
= QDateTime::currentDateTime().addMSecs(msecs
);
244 startAction
->setEnabled(false);
245 stopAction
->setEnabled(true);
247 startAction
->setEnabled(true);
248 stopAction
->setEnabled(false);
256 //killTimer(m_timerId);
258 stopAction
->setEnabled(false);
259 startAction
->setEnabled(true);
261 stopAction
->setEnabled(true);
262 startAction
->setEnabled(false);
266 Task
Sak::loadTaskFromFile(const QString
& filePath
)
268 QFile
taskXmlFile(filePath
);
270 qDebug() << "Examine task file " << taskXmlFile
.fileName();
271 if (!taskXmlFile
.open(QIODevice::ReadOnly
)) {
272 qDebug() << "Failed opening xml file " << taskXmlFile
.fileName();
274 QByteArray data
= taskXmlFile
.readLine();
275 QXmlStreamReader
stream(data
);
276 QXmlStreamReader::TokenType token
= stream
.readNext(); // skip StartDocument
277 token
= stream
.readNext();
278 if ( token
!= QXmlStreamReader::Comment
) {
279 qDebug() << "Skip file " << taskXmlFile
.fileName() << " (want a file starting with a comment representing MD5, got" << token
<< ")";
282 QString md5
= stream
.text().toString().trimmed();
283 qDebug() << "md5 = " << md5
;
286 data
= taskXmlFile
.readAll();
287 if ( md5
!= QCryptographicHash::hash(data
, QCryptographicHash::Md5
).toHex() ) {
288 if (QMessageBox::No
== QMessageBox::warning(0, "Corrupted file!",
289 QString("Check of file " + taskXmlFile
.fileName() + " failed (maybe it has been edited by hand).\nDo you want to load it anyway?" )
290 ,QMessageBox::Yes
| QMessageBox::No
) ) {
291 qDebug() << "Skip file " << taskXmlFile
.fileName() << " (bad md5 sum)";
298 stream
.addData(data
);
300 if ( stream
.readNext() != QXmlStreamReader::StartDocument
) {
301 qDebug() << "Skip file " << taskXmlFile
.fileName() << " (want start document)";
305 if (stream
.error() != QXmlStreamReader::NoError
) {
306 qDebug() << "Error reading task data from file " << taskXmlFile
.fileName() << ":" << stream
.errorString();
309 // QFile tmp("/tmp/" + t.title + ".xml");
310 // tmp.open(QIODevice::ReadWrite);
311 // QXmlStreamWriter ss(&tmp);
312 // ss.setAutoFormatting(true);
313 // ss.setAutoFormattingIndent(2);
314 // ss.writeStartDocument();
316 // ss.writeEndDocument();
329 if (!m_settings
) return;
330 m_backupper
->doCyclicBackup();
331 QSettings
settings(QSettings::IniFormat
, QSettings::UserScope
, "ZanzaSoft", "SAK");
332 // QByteArray tasksArray;
333 // QDataStream stream(&tasksArray, QIODevice::ReadWrite);
334 // stream.setVersion(QDataStream::Qt_4_0);
335 // stream << m_tasks;
336 // settings.setValue("tasks", tasksArray);
337 settings
.setValue("Ping interval", durationSpinBox
->value());
338 settings
.setValue("Message", bodyEdit
->toPlainText());
341 QDir
saveDir(QFileInfo(settings
.fileName()).dir());
342 saveDir
.mkdir("SakTasks");
343 saveDir
.cd("SakTasks");
345 foreach(Task t
, m_tasks
) {
346 if (t
.title
.isEmpty()) continue;
347 QFile
xmlTaskSave(saveDir
.filePath(t
.title
+ ".xml"));
348 QByteArray taskArray
;
349 QXmlStreamWriter
stream(&taskArray
);
350 stream
.setAutoFormatting(true);
351 stream
.setAutoFormattingIndent(2);
352 stream
.writeStartDocument();
354 stream
.writeEndDocument();
355 xmlTaskSave
.open(QIODevice::ReadWrite
| QIODevice::Truncate
);
356 qDebug() << "Saving xml to file " << xmlTaskSave
.fileName();
358 hash
.append("<!-- ");
359 hash
.append( QCryptographicHash::hash(taskArray
, QCryptographicHash::Md5
).toHex() );
360 hash
.append(" -->\n");
361 xmlTaskSave
.write(hash
);
362 xmlTaskSave
.write(taskArray
);
366 // remove files not matching a task
367 QStringList nameFilters
;
368 nameFilters
<< "*.xml";
369 QStringList files
= saveDir
.entryList( nameFilters
, QDir::Files
);
370 foreach (QString taskXmlFileName
, files
) {
371 if (!m_tasks
.contains(QFileInfo(taskXmlFileName
).baseName())) {
372 qWarning()<< "Remove task " << QFileInfo(taskXmlFileName
).baseName() << " from disk";
373 QFile(saveDir
.filePath(taskXmlFileName
)).remove();
378 m_incremental
->clearAddedPieces();
381 //void Sak::saveAsDb()
383 // if (!m_settings) return;
384 // QString fileName = QFileDialog::getSaveFileName();
385 // QFile file(fileName);
388 // QSettings settingsQSettings::IniFormat, QSettings::UserScope, ("ZanzaSoft", "SAK");
389 // QFile file1(settings.fileName());
390 // if (!file1.copy(fileName)) {
391 // qWarning() << "Error copying " << settings.fileName() << " to " << fileName << file1.errorString();
395 void Sak::exportDbCsv()
397 if (!m_settings
) return;
398 QString fileName
= QFileDialog::getSaveFileName();
399 QFile
file(fileName
);
400 if (!file
.open(QIODevice::ReadWrite
|QIODevice::Truncate
)) {
401 QMessageBox::warning(0, "Error saving", QString("Error saving to file %1").arg(fileName
));
404 QTextStream
stream(&file
);
405 foreach(const Task
& t
, m_tasks
) {
406 QHash
< QString
, QList
< Task::Hit
> >::const_iterator itr
= t
.hits
.begin();
407 while(itr
!= t
.hits
.end()) {
408 QList
< Task::Hit
>::const_iterator hitr
= itr
.value().begin(), hend
= itr
.value().end();
409 while(hitr
!= hend
) {
410 stream
<< t
.title
<< ";" << itr
.key() << ";" << hitr
->timestamp
.toString(DATETIMEFORMAT
) << ";" << hitr
->duration
<< ";\n";
420 void Sak::logInGmail()
422 m_gmail
->forceLogin();
425 void Sak::saveToGmail()
427 if (!m_settings
) return;
429 QSettings
settings(QSettings::IniFormat
, QSettings::UserScope
, "ZanzaSoft", "SAK");
431 QDir
saveDir(QFileInfo(settings
.fileName()).dir());
432 saveDir
.mkdir("SakTasks");
433 saveDir
.cd("SakTasks");
434 QStringList nameFilters
;
435 nameFilters
<< "*.xml";
436 QStringList files
= saveDir
.entryList( nameFilters
, QDir::Files
);
437 QStringList filePaths
;
438 foreach (QString taskXmlFileName
, files
) {
439 filePaths
<< saveDir
.filePath(taskXmlFileName
);
441 m_gmail
->storeTaskFiles(filePaths
);
444 void Sak::importFromGmail()
446 QStringList filePaths
= m_gmail
->fetchLatestTasks();
449 void Sak::open(const QStringList
& _fileNames
)
451 QStringList fileNames
= _fileNames
.size()?_fileNames
:QFileDialog::getOpenFileNames(0, "Open a new task", QString(), "*.xml" );
452 foreach(QString fileName
, fileNames
) {
453 QFile
file(fileName
);
454 if (!file
.exists()) {
455 QMessageBox::warning(0, "Cannot find task", QString("Cannot find task file %1").arg(fileName
));
458 QSettings
settings(QSettings::IniFormat
, QSettings::UserScope
, "ZanzaSoft", "SAK");
459 QDir
saveDir(QFileInfo(settings
.fileName()).dir());
460 saveDir
.mkdir("SakTasks");
461 saveDir
.cd("SakTasks");
463 if ( QFile(saveDir
.filePath(QFileInfo(fileName
).completeBaseName())).exists() ) {
464 QMessageBox
mbox(QMessageBox::Warning
, "Save current task", "Current task will be overwritten by the new task: do you want to backup it to file before?");
465 QPushButton
* overwriteButton
= mbox
.addButton("Overwrite", QMessageBox::YesRole
);
466 QPushButton
* mergeButton
= mbox
.addButton("Merge", QMessageBox::NoRole
);
467 QPushButton
* cancelButton
= mbox
.addButton("Cancel", QMessageBox::RejectRole
);
468 mbox
.setDefaultButton(cancelButton
);
470 QAbstractButton
* b
= mbox
.clickedButton();
471 if (b
== cancelButton
) { continue; }
473 m_backupper
->doCyclicBackup();
474 if (b
== mergeButton
) {
475 Task t
= loadTaskFromFile(file
.fileName());
476 QHash
< QString
, QList
< Task::Hit
> > ::const_iterator itr
= t
.hits
.begin(), end
= t
.hits
.end();
478 QString subtask
= itr
.key();
479 foreach(Task::Hit hit
, itr
.value())
480 m_incremental
->addPiece(t
.title
, subtask
, hit
.timestamp
, hit
.duration
);
483 interactiveMergeHits();
484 } else if (b
== overwriteButton
) {
485 file
.copy(saveDir
.filePath(QFileInfo(fileName
).completeBaseName()));
491 if (!fileNames
.isEmpty()) {
503 if (!m_settings
) return;
505 m_settings
->deleteLater();
506 m_view
->scene()->deleteLater();
507 m_view
->deleteLater();
509 delete m_incremental
;
511 m_previewing
= false;
512 m_changedHit
= false;
519 killTimer(m_autoSaveTimer
);
524 void Sak::layoutSubTasks( const QMap
<int, SakSubWidget
*> sortedWidgets
, int currentRank
) {
525 QMap
<int, SakSubWidget
*>::const_iterator itr
= sortedWidgets
.begin(), end
= sortedWidgets
.end();
526 QRect r
= m_desktopRect
;
527 for(int i
=0; itr
!= end
; i
++, itr
++) {
528 int h
= (*itr
)->size().height();
529 int w
= (*itr
)->size().width();
530 (*itr
)->setPos(QPointF((r
.width() - w
)/2, (r
.height()-h
)/2 + (i
- currentRank
- 1) * (h
+2)));
535 int Sak::taskCounter
= 0;
537 bool Sak::eventFilter(QObject
* obj
, QEvent
* e
)
539 // if (obj == m_view) {
540 // qDebug() << "event : " << e->type();
542 if (obj
== tasksTree
) {
543 return taskTreeEventFilter(e
);
544 } else if (obj
== hitsList
|| obj
== summaryList
) {
545 return hitsListEventFilter(e
);
546 } else if (obj
== m_settings
&& e
->type() == QEvent::Close
) {
551 if (trayIcon
->isVisible()) {
556 } else if (obj
== m_view
&& e
->type() == QEvent::Wheel
) {
557 QWheelEvent
* we
= dynamic_cast<QWheelEvent
*>(e
);
559 scrollSubTasks(we
->delta() / 120);
560 } else scrollTasks(we
->delta() / 120);
561 } else if (obj
== m_view
&& e
->type() == QEvent::KeyPress
) {
562 QKeyEvent
* ke
= dynamic_cast<QKeyEvent
*>(e
);
563 if ((ke
->modifiers() & Qt::AltModifier
) && (ke
->modifiers() & Qt::ControlModifier
) ) {
566 } else if ( ((ke
->modifiers() & Qt::ControlModifier
) && (ke
->key() == Qt::Key_Backspace
) )
567 || ((ke
->modifiers() & Qt::ControlModifier
) && (ke
->key() == Qt::Key_Left
) )) {
572 } else if (m_subtaskView
&& ke
->key() == Qt::Key_Up
) {
575 } else if (m_subtaskView
&& ke
->key() == Qt::Key_Down
) {
578 } else if (!m_subtaskView
&& ke
->key() == Qt::Key_Left
) {
581 } else if (!m_subtaskView
&& ke
->key() == Qt::Key_Right
) {
584 } else if (!m_subtaskView
&& ke
->key() == Qt::Key_Escape
) {
587 } else { // forward events to current widget
588 if (!m_subtaskView
) {
589 if (m_widgetsIterator
== m_widgets
.end()) return false;
590 SakWidget
* currentShowing
= m_widgetsIterator
.value();
591 currentShowing
->keyPressEvent(ke
);
594 // autoscroll on text completion!!!
595 if (m_subwidgetsIterator
== m_subwidgets
.end()) return false;
596 SakSubWidget
* currentShowing
= m_subwidgetsIterator
.value();
597 currentShowing
->keyPressEvent(ke
);
599 if (m_subWidgetRank
!= 0 && m_subtaskCompleter
) {
600 QString
completion(m_subtaskCompleter
->completionPrefix());
601 if (ke
->text().size() == 1) {
602 if (ke
->key() == Qt::Key_Backslash
|| ke
->key() == Qt::Key_Backspace
)
604 else completion
+= ke
->text();
605 m_subtaskCompleter
->setCompletionPrefix(completion
);
608 QStringList
list( ((QStringListModel
*)m_subtaskCompleter
->model())->stringList() );
609 int newRank
= 1 + ((QStringListModel
*)m_subtaskCompleter
->model())->stringList().indexOf(m_subtaskCompleter
->currentIndex().row() >= 0 && completion
.size() ? m_subtaskCompleter
->currentCompletion() : completion
);
611 if (m_subWidgetRank
!= newRank
) {
612 scrollSubTasks(newRank
- m_subWidgetRank
);
614 QLineEdit
* editor
= dynamic_cast<QLineEdit
*>((*m_subwidgets
.begin())->widget());
616 editor
->setText(completion
);
621 } else if (m_subtaskCompleter
) {
622 QLineEdit
* editor
= dynamic_cast<QLineEdit
*>((*m_subwidgets
.begin())->widget());
624 m_subtaskCompleter
->setCompletionPrefix(editor
->text());
631 } else if (obj
== m_view
&& e
->type() == QEvent::Show
) {
633 QTimer::singleShot(500, this, SLOT(grabKeyboard()));
634 } else if (obj
== m_view
&& e
->type() == QEvent::Close
) {
635 if (trayIcon
->isVisible()) {
638 } else if (obj
&& obj
== trayIcon
&& e
->type() == QEvent::ToolTip
) {
639 QDateTime last
= m_incremental
->lastTimeStamp
;
640 int seconds
= QDateTime::currentDateTime().secsTo(m_nextTimerEvent
);
641 int hours
= seconds
/ 3600;
642 int minutes
= (seconds
/ 60) % 60;
644 trayIcon
->setToolTip(tr(qPrintable(QString("<h2>Sistema Anti Kazzeggio</h2>Last registered hit at <b>%1</b>.<br />%2").arg(last
.toString()).arg(m_timerId
> 0 ? QString("Next hit in <b>%2:%3:%4</b>").arg(hours
).arg(minutes
).arg(seconds
) : QString("<b>Paused</b>")))));
650 //END basic >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
653 //BEGIN Tasks >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
654 void Sak::addDefaultTask()
656 QString tentativeName
;
658 tentativeName
= QString("Task %1").arg(taskCounter
++);
659 } while(m_editedTasks
.contains(tentativeName
));
661 Task
& t
= m_editedTasks
[tentativeName
];
662 t
.title
= tentativeName
;
663 QTreeWidgetItem
* item
= new QTreeWidgetItem(QStringList(tentativeName
));
664 item
->setData(0,Qt::UserRole
, QVariant(QMetaType::VoidStar
, &t
));
665 tasksTree
->addTopLevelItem(item
);
669 void Sak::populateTasks()
673 QHash
<QString
, Task
>::iterator itr
= m_editedTasks
.begin(), end
=m_editedTasks
.end();
674 for(; itr
!=end
; itr
++) {
675 Task
& t(itr
.value());
676 t
.checkConsistency();
678 if (t
.title
.isEmpty() || t
.title
== "<away>") continue; // skip away task
679 QTreeWidgetItem
* item
= new QTreeWidgetItem(QStringList(t
.title
));
680 item
->setData(0, Qt::UserRole
, QVariant(QMetaType::VoidStar
, &t
));
682 icon
.addPixmap(t
.icon
);
683 item
->setSizeHint(0, QSize(32,32));
684 item
->setIcon(0, icon
);
685 for(int i
=0; i
<3; i
++) {
686 item
->setForeground(i
,t
.fgColor
);
687 item
->setBackground(i
,t
.bgColor
);
689 //item->setCheckState(1, t.active ? Qt::Checked : Qt::Unchecked);
690 //item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
691 item
->setIcon(1,QIcon(t
.active
? ":/images/active.png" : ":/images/inactive.png"));
692 item
->setText(2,QString("%1 hours worked till now (overestimated %2)").arg(t
.totHours
, 4, 'f', 2, ' ').arg(t
.totOverestimation
));
693 foreach(Task::SubTask st
, t
.subTasks
) {
694 if (!st
.title
.isEmpty()) {
695 QTreeWidgetItem
* sitem
= new QTreeWidgetItem(item
, QStringList(st
.title
));
696 item
->setData(0, Qt::UserRole
, QVariant(QMetaType::VoidStar
, &st
));
697 sitem
->setSizeHint(0, QSize(32,32));
698 QColor fgColor
= st
.fgColor
.isValid() ? st
.fgColor
: t
.fgColor
;
699 QColor bgColor
= st
.bgColor
.isValid() ? st
.bgColor
: t
.bgColor
;
700 for(int i
=0; i
<3; i
++) {
701 sitem
->setForeground(i
,fgColor
);
702 sitem
->setBackground(i
,bgColor
);
704 sitem
->setIcon(1,QIcon(st
.active
? ":/images/active.png" : ":/images/inactive.png"));
705 sitem
->setText(2,QString("%1 hours worked till now").arg(st
.totHours
,4,'f',2,' '));
708 tasksTree
->addTopLevelItem(item
);
712 void Sak::saveTaskChanges()
716 if ( QMessageBox::question ( 0, "Task list changed", "Task list has changed: do you want to save changes?", QMessageBox::Save
| QMessageBox::Discard
, QMessageBox::Discard
) == QMessageBox::Save
) {
717 m_tasks
= m_editedTasks
;
718 } else m_editedTasks
= m_tasks
; //. undo changes
720 selectedStartDate(QDate());
725 void Sak::selectColor() {
726 if (tasksTree
->selectedItems().isEmpty()) return;
728 if (sender() == fgColorButton
) {
729 QColor c
= QColorDialog::getColor(fgColorButton
->palette().color(QPalette::ButtonText
));
730 if (!c
.isValid()) return;
731 QPalette p
= fgColorButton
->palette();
732 p
.setColor(QPalette::ButtonText
, c
);
733 fgColorButton
->setPalette(p
);
734 bgColorButton
->setPalette(p
);
735 } else if (sender() == bgColorButton
) {
736 QColor c
= QColorDialog::getColor(bgColorButton
->palette().color(QPalette::Button
));
737 if (!c
.isValid()) return;
738 QPalette p
= bgColorButton
->palette();
739 p
.setColor(QPalette::Button
, c
);
740 fgColorButton
->setPalette(p
);
741 bgColorButton
->setPalette(p
);
746 bool Sak::taskTreeEventFilter(QEvent
* e
)
748 if (e
->type() == QEvent::ContextMenu
) {
749 QContextMenuEvent
* me
= dynamic_cast<QContextMenuEvent
*>(e
);
750 if (!me
) return false;
751 m_addTaskMenu
->popup(me
->globalPos());
753 } else if (e
->type() == QEvent::KeyRelease
) {
754 QKeyEvent
* ke
= dynamic_cast<QKeyEvent
*>(e
);
755 if (!ke
) return false;
756 if ( (ke
->key() != Qt::Key_Delete
&& ke
->key() != Qt::Key_Backspace
) ) return false;
757 if (currentSubtask
!="") {
758 QMessageBox
whatToDo(QMessageBox::Warning
, "Deleting subtask", "Deleting subtask " + currentSubtask
+ " of task " + currentTask
);
759 QPushButton
* moveHitsToParentButton
= whatToDo
.addButton("Move hits to task " + currentTask
, QMessageBox::AcceptRole
);
760 QPushButton
* removeHitsButton
= whatToDo
.addButton("Remove hits", QMessageBox::AcceptRole
);
761 QPushButton
* cancelButton
= whatToDo
.addButton("Cancel", QMessageBox::RejectRole
);
762 whatToDo
.setDefaultButton(cancelButton
);
764 if ( whatToDo
.clickedButton() == cancelButton
) return true;
765 if (m_editedTasks
.find(currentTask
) == m_editedTasks
.end()) return true;
767 Task
& t(m_editedTasks
[currentTask
]);
768 t
.subTasks
.take(currentSubtask
);
769 if (whatToDo
.clickedButton() == removeHitsButton
) {
770 t
.hits
.take(currentSubtask
);
771 } else if (whatToDo
.clickedButton() == moveHitsToParentButton
) {
772 QList
<Task::Hit
> sorter(t
.hits
.take(""));
773 sorter
<< t
.hits
.take(currentSubtask
);
774 qStableSort(sorter
.begin(), sorter
.end());
778 // remove file from disk
780 m_editedTasks
.remove(currentTask
);
784 selectedStartDate(QDate());
786 } else if (e
->type() == QEvent::Hide
) {
793 void Sak::commitCurrentTask()
796 if (currentSubtask
.isEmpty()) {
797 QString currentTitle
= taskTitleEditor
->text();
798 if (!currentTitle
.isEmpty()) {
799 if (currentTitle
!= currentTask
) {
800 if (m_editedTasks
.contains(currentTitle
)) {
801 QMessageBox::warning(0, "Conflict in task names", "Conflict in task names: current task " + currentTask
+ ", edited title " + currentTitle
);
802 taskTitleEditor
->setText(currentTask
);
804 } else if (m_editedTasks
.contains(currentTask
)) {
805 m_editedTasks
[currentTitle
] = m_editedTasks
.take(currentTask
);
806 m_editedTasks
[currentTitle
].title
= currentTitle
;
810 Task
& t
= m_editedTasks
[currentTitle
];
811 t
.bgColor
= bgColorButton
->palette().color(QPalette::Button
);
812 t
.fgColor
= fgColorButton
->palette().color(QPalette::ButtonText
);
813 t
.icon
= taskPixmapViewer
->pixmap();
814 QList
<QTreeWidgetItem
*> items
= tasksTree
->findItems(currentTask
,Qt::MatchExactly
,0);
815 foreach(QTreeWidgetItem
* ii
, items
) {
816 ii
->setText(0, currentTitle
);
817 ii
->setIcon(0, taskPixmapViewer
->pixmap());
818 for (int i
=0; i
<3; i
++) {
819 ii
->setForeground(i
, QColor(t
.fgColor
));
820 ii
->setBackground(i
, QColor(t
.bgColor
));
823 if (dueEditor
->date() != dueEditor
->minimumDate())
824 t
.dueDate
= dueEditor
->date();
825 t
.estimatedHours
= estimatedHoursEditor
->value();
826 currentTask
=currentTitle
;
828 if (tasksTree
->selectedItems().size() != 1) return;
829 QTreeWidgetItem
* item
= tasksTree
->selectedItems()[0];
830 item
->setText(0, taskTitleEditor
->text());
832 icon
.addPixmap(t
.icon
);
833 item
->setSizeHint(0, QSize(32,32));
834 item
->setIcon(0, icon
);
835 for(int i
=0; i
<3; i
++) {
836 item
->setForeground(i
,t
.fgColor
);
837 item
->setBackground(i
,t
.bgColor
);
841 } else { // subtask edited
842 if (!m_editedTasks
.contains(currentTask
)) return;
843 Task
& t(m_editedTasks
[currentTask
]);
844 QString currentTitle
= taskTitleEditor
->text();
846 if (!currentTitle
.isEmpty()) {
847 if (currentTitle
!= currentSubtask
) {
848 if (t
.subTasks
.contains(currentTitle
)) {
849 QMessageBox::warning(0, "Conflict in subtask names", "Conflict in subtask names");
850 taskTitleEditor
->setText(currentSubtask
);
852 } else if (t
.subTasks
.contains(currentSubtask
)) {
853 t
.subTasks
[currentTitle
] = t
.subTasks
.take(currentSubtask
);
854 t
.subTasks
[currentTitle
].title
= currentTitle
;
855 t
.hits
[currentTitle
] = t
.hits
.take(currentSubtask
);
859 Task::SubTask
& st
= t
.subTasks
[currentTitle
];
860 st
.bgColor
= bgColorButton
->palette().color(QPalette::Button
);
861 st
.fgColor
= fgColorButton
->palette().color(QPalette::ButtonText
);
862 QList
<QTreeWidgetItem
*> items
= tasksTree
->findItems(currentTask
,Qt::MatchExactly
,0);
863 foreach(QTreeWidgetItem
* jj
, items
) {
864 for(int i
=0; i
<jj
->childCount(); i
++) {
865 QTreeWidgetItem
* ii
= jj
->child(i
);
866 if (ii
->text(0) != currentSubtask
) continue;
867 ii
->setText(0, currentTitle
);
868 for (int i
=0; i
<3; i
++) {
869 ii
->setForeground(i
, QColor(st
.fgColor
));
870 ii
->setBackground(i
, QColor(st
.bgColor
));
874 currentSubtask
= currentTitle
;
876 if (tasksTree
->selectedItems().size() != 1) return;
877 QTreeWidgetItem
* item
= tasksTree
->selectedItems()[0];
878 item
->setText(0, taskTitleEditor
->text());
880 icon
.addPixmap(t
.icon
);
881 item
->setSizeHint(0, QSize(32,32));
882 item
->setIcon(0, icon
);
883 for(int i
=0; i
<3; i
++) {
884 item
->setForeground(i
,st
.fgColor
);
885 item
->setBackground(i
,st
.bgColor
);
890 void Sak::selectedTask()
892 if (tasksTree
->selectedItems().isEmpty()) {
893 taskPixmapViewer
->setEnabled(false);
894 taskPixmapViewer
->setPixmap(QPixmap());
895 taskTextEditor
->setEnabled(false);
896 taskTitleEditor
->setEnabled(false);
897 bgColorButton
->setEnabled(false);
898 fgColorButton
->setEnabled(false);
899 dueEditor
->setEnabled(false);
900 estimatedHoursEditor
->setEnabled(false);
904 QTreeWidgetItem
* selectedItem
= tasksTree
->selectedItems().first();
905 QTreeWidgetItem
* parentItem
= selectedItem
->parent();
906 QString tt
= selectedItem
->text(0);
909 taskPixmapViewer
->setEnabled(true);
910 dueEditor
->setEnabled(true);
911 estimatedHoursEditor
->setEnabled(true);
913 taskPixmapViewer
->setEnabled(false);
914 taskPixmapViewer
->setPixmap(QPixmap());
915 dueEditor
->setEnabled(false);
916 estimatedHoursEditor
->setEnabled(false);
918 taskTextEditor
->setEnabled(true);
919 taskTitleEditor
->setEnabled(true);
920 bgColorButton
->setEnabled(true);
921 fgColorButton
->setEnabled(true);
924 if (!parentItem
) { // editing a task
925 if (!m_editedTasks
.contains(tt
)) return;
926 const Task
& t
= m_editedTasks
[tt
];
927 taskPixmapViewer
->setPixmap(t
.icon
);
928 taskTextEditor
->blockSignals(true);
929 taskTextEditor
->setPlainText(t
.description
);
930 taskTextEditor
->blockSignals(false);
931 taskTitleEditor
->setText(t
.title
);
933 p
.setColor(QPalette::Button
, t
.bgColor
);
934 p
.setColor(QPalette::ButtonText
, t
.fgColor
);
935 bgColorButton
->setPalette(p
);
936 fgColorButton
->setPalette(p
);
937 estimatedHoursEditor
->setValue(t
.estimatedHours
);
938 dueEditor
->setDate(t
.dueDate
.isValid() ? t
.dueDate
: dueEditor
->minimumDate());
940 currentTask
= t
.title
;
943 if (!m_editedTasks
.contains(parentItem
->text(0))) return;
944 const Task
& t
= m_editedTasks
[parentItem
->text(0)];
945 if (!t
.subTasks
.contains(tt
)) return;
946 const Task::SubTask
& st
= t
.subTasks
[tt
];
947 taskTextEditor
->setPlainText(st
.description
);
948 taskTitleEditor
->setText(st
.title
);
950 p
.setColor(QPalette::Button
, st
.bgColor
.isValid() ? st
.bgColor
: t
.bgColor
);
951 p
.setColor(QPalette::ButtonText
, st
.fgColor
.isValid() ? st
.fgColor
: t
.fgColor
);
952 bgColorButton
->setPalette(p
);
953 fgColorButton
->setPalette(p
);
955 currentTask
= t
.title
;
956 currentSubtask
= st
.title
;
960 void Sak::doubleClickedTask(QTreeWidgetItem
* i
, int column
)
964 if (i
->parent() == 0) {
965 QHash
<QString
, Task
>::iterator itr
= m_editedTasks
.find(i
->text(0));
966 Q_ASSERT(itr
!= m_editedTasks
.end());
967 bool& active ( itr
.value().active
);
969 i
->setIcon(column
, active
? QIcon(":/images/active.png") : QIcon(":/images/inactive.png"));
971 QHash
<QString
, Task
>::iterator itr
= m_editedTasks
.find(i
->parent()->text(0));
972 Q_ASSERT(itr
!= m_editedTasks
.end());
973 bool& active ( itr
.value().subTasks
[i
->text(0)].active
);
975 i
->setIcon(column
, active
? QIcon(":/images/active.png") : QIcon(":/images/inactive.png"));
977 ((QTreeWidget
*)sender())->update();
983 void Sak::timerEvent(QTimerEvent
* e
)
985 if (e
->timerId() == m_timerId
) {
986 if (!m_view
->isVisible() && !m_settings
->isVisible() && m_tasks
.count() > 0) {
989 killTimer(m_timeoutPopup
);
990 m_timeoutPopup
= startTimer((int)(qMax( 30000.0, Task::hours(m_currentInterval
)*3600.0*1000.0/10.0))); // 5 secmin
992 killTimer(m_timerId
);
993 int msecs
= (int)(Task::hours(m_currentInterval
)*3600.0*1000.0);
994 m_timerId
= startTimer(msecs
);
995 m_nextTimerEvent
= QDateTime::currentDateTime().addMSecs(msecs
);
997 if (m_settings
&& m_settings
->isVisible() && !m_settings
->isActiveWindow()) {
998 trayIcon
->showMessage("Delayed check point", "Delayed check point due to open settings. Close the setting window!", QSystemTrayIcon::Warning
, -1);
1001 qDebug() << "SAK: wait 5 seconds";
1002 killTimer(m_timerId
);
1003 m_timerId
= startTimer(5000);
1004 m_nextTimerEvent
= QDateTime::currentDateTime().addMSecs(5000);
1006 } else if (e
->timerId() == m_timeoutPopup
) {
1007 // ensure the timer is resetted
1008 killTimer(m_timeoutPopup
);
1010 if (!m_subtaskView
) {
1011 // if not selecting subtasks clear everything and signal away
1012 workingOnTask("<away>","");
1013 trayIcon
->showMessage("New away events", "You have missed a check point. Fix it in the detailed hit list.", QSystemTrayIcon::Information
, 999999);
1016 } else if (e
->timerId() == m_autoSaveTimer
) {
1019 qDebug() << "unknown timer event";
1023 void Sak::clearView()
1025 killTimer(m_timeoutPopup
);
1026 m_subtaskView
=false;
1028 delete m_subtaskCompleter
; m_subtaskCompleter
= 0;
1030 if (!m_view
) return;
1031 QGraphicsScene
* s
= m_view
->scene();
1032 QList
<QGraphicsItem
*> items
= m_view
->items();
1035 m_view
->setScene(new QGraphicsScene
);
1036 m_view
->scene()->setSceneRect(m_desktopRect
);
1037 m_previewing
= false;
1038 m_view
->releaseKeyboard();
1040 #if defined(Q_WS_X11)
1041 // restore focus to previous application
1043 X11::XSetInputFocus((X11::Display
*)QX11Info::display(), X11::CurrentFocusWindow
, X11::CurrentRevertToReturn
, CurrentTime
);
1044 X11::XFlush((X11::Display
*)QX11Info::display());
1048 void Sak::workingOnTask(const QString
& taskName
, const QString
& subTask
)
1050 if (!m_previewing
) {
1052 qDebug() << "Working on " << taskName
;
1053 if (m_tasks
.contains(taskName
)) {
1054 Task
& t
= m_tasks
[taskName
];
1056 if (t
.title
!= "<away>" && !t
.title
.isEmpty()) {
1058 int historyIndex
= m_taskSelectionHistory
.indexOf(t
.title
);
1059 if (historyIndex
!= -1) {
1060 m_taskSelectionHistory
.takeAt(historyIndex
);
1062 m_taskSelectionHistory
.push_back(t
.title
);
1064 QList
<QString
> & subtaskSelectionHistory(m_subtaskSelectionHistory
[t
.title
]);
1065 historyIndex
= subtaskSelectionHistory
.indexOf(subTask
);
1066 if (historyIndex
!= -1) {
1067 subtaskSelectionHistory
.takeAt(historyIndex
);
1069 subtaskSelectionHistory
.push_back(subTask
);
1073 QDateTime now
= QDateTime::currentDateTime();
1074 QHash
<QString
, Task
>::iterator itr
= m_tasks
.begin();
1076 QHash
<QString
, QList
< Task::Hit
> >::iterator hitr
= t
.hits
.begin();
1077 // merge last two hits of every subtask if they are close enough
1078 while(hitr
!= t
.hits
.end()) {
1079 QList
<Task::Hit
>& otHits( *hitr
);
1080 if (otHits
.count() > 1) {
1081 Task::Hit
& lastHit(otHits
[otHits
.size() - 1]);
1082 Task::Hit
& beforeLastHit(otHits
[otHits
.size() - 2]);
1084 int diff
= (lastHit
.timestamp
.toTime_t() - 30*lastHit
.duration
) - (beforeLastHit
.timestamp
.toTime_t() + 30*beforeLastHit
.duration
);
1085 if (diff
< 120) { // at most 2 minutes apart
1086 beforeLastHit
.timestamp
= beforeLastHit
.timestamp
.addSecs(-30*beforeLastHit
.duration
);
1087 int secsToEnd
= beforeLastHit
.timestamp
.secsTo(lastHit
.timestamp
.addSecs(30*m_currentInterval
));
1088 if (secsToEnd
> 24 * 3600 * 3600) {
1089 qWarning() << "TRAPPED ERROR IN SECS COUNT!!!!!!!!";
1090 qWarning() << "BEFORE LAST HIST WAS " << beforeLastHit
.timestamp
<< beforeLastHit
.duration
;
1091 qWarning() << "LAST HIT WAS " << lastHit
.timestamp
<< lastHit
.duration
;
1092 assert(secsToEnd
< 24 * 3600 * 3600);
1094 beforeLastHit
.timestamp
= beforeLastHit
.timestamp
.addSecs( secsToEnd
/2.0 );
1095 beforeLastHit
.duration
= (int)( round( secsToEnd
/ 60.0 ) );
1096 // remove the current very last hit
1103 // add hit to hit list
1104 // NOTE: we do not try to merge the very last hit with previous ones because we want let
1105 // the user being able to easily recover from a selection error
1106 t
.hits
[subTask
] << Task::Hit(now
, m_currentInterval
);
1107 m_incremental
->writePiece(t
.title
, subTask
, now
, m_currentInterval
);
1108 t
.checkConsistency();
1109 QList
<QTreeWidgetItem
*> items
= tasksTree
->findItems (t
.title
, Qt::MatchExactly
, 0);
1110 if (!items
.isEmpty())
1111 items
.first()->setText(1, QString("%1 hours worked till now (overestimated %2)").arg(t
.totHours
).arg(t
.totOverestimation
));
1113 // update subtask if new added!
1116 // update statistics !!!!
1117 m_editedTasks
= m_tasks
;
1118 QMetaObject::invokeMethod(this, "selectedStartDate", Qt::QueuedConnection
, Q_ARG(QDate
, cal1
->selectedDate()));
1125 // attractor = 't' (top), 'b', 'l', 'r'
1126 void layoutInSquare( QList
<SakWidget
*> sortedWidgets
, QRect rect
, char attractor
)
1128 int w
= rect
.width();
1129 if (rect
.width() < 64) return;
1130 int maxw
= qMin(350, w
/2);
1131 QSize
size(maxw
, maxw
);
1132 if (sortedWidgets
.count() == 0) {
1134 } else if (sortedWidgets
.count() == 1) {
1135 sortedWidgets
[0]->setGeometry(rect
);
1136 } else if (sortedWidgets
.count() == 2) {
1138 if (attractor
== 'C') {
1139 off1
= QPoint(0,w
/4);
1140 off2
= QPoint(w
/2,w
/4);
1141 } else if (attractor
== 'T') {
1143 off2
= QPoint(w
/2,0);
1144 } else if (attractor
== 'B') {
1145 off1
= QPoint(0,w
/2);
1146 off2
= QPoint(w
/2,w
/2);
1147 } else if (attractor
== 'R') {
1148 off1
= QPoint(w
/2,0);
1149 off2
= QPoint(w
/2,w
/2);
1150 } else if (attractor
== 'L') {
1152 off2
= QPoint(0,w
/2);
1154 sortedWidgets
[0]->setGeometry(QRect(rect
.topLeft() + off1
, size
));
1155 sortedWidgets
[1]->setGeometry(QRect(rect
.topLeft() + off2
, size
));
1156 } else if (sortedWidgets
.count() == 3) {
1157 QPoint off1
, off2
, off3
;
1158 if (attractor
== 'T' || attractor
== 'C') {
1160 off2
= QPoint(w
/2,0);
1161 off3
= QPoint(w
/4,w
/2);
1162 } else if (attractor
== 'B') {
1163 off1
= QPoint(0,w
/2);
1164 off2
= QPoint(w
/2,w
/2);
1165 off3
= QPoint(w
/4,0);
1166 } else if (attractor
== 'R') {
1167 off1
= QPoint(w
/2,0);
1168 off2
= QPoint(w
/2,w
/2);
1169 off3
= QPoint(0,w
/4);
1170 } else if (attractor
== 'L') {
1172 off2
= QPoint(0,w
/2);
1173 off3
= QPoint(w
/2,w
/4);
1175 sortedWidgets
[0]->setGeometry(QRect(rect
.topLeft() + off1
, size
));
1176 sortedWidgets
[1]->setGeometry(QRect(rect
.topLeft() + off2
, size
));
1177 sortedWidgets
[2]->setGeometry(QRect(rect
.topLeft() + off3
, size
));
1178 } else if (sortedWidgets
.count() == 4) {
1182 QPoint
off4(w
/2,w
/2);
1183 sortedWidgets
[0]->setGeometry(QRect(rect
.topLeft() + off1
, size
));
1184 sortedWidgets
[1]->setGeometry(QRect(rect
.topLeft() + off2
, size
));
1185 sortedWidgets
[2]->setGeometry(QRect(rect
.topLeft() + off3
, size
));
1186 sortedWidgets
[3]->setGeometry(QRect(rect
.topLeft() + off4
, size
));
1188 Q_ASSERT(sortedWidgets
.count() <= 4);
1192 void layoutInRect( QList
<SakWidget
*> sortedWidgets
, QRect rect
, char attractor
)
1194 if (sortedWidgets
.count() == 0) return;
1195 int h
= rect
.height();
1196 int w
= rect
.width();
1197 int maxh
= qMin(350, h
);
1198 int maxw
= qMin(350, w
);
1199 if (sortedWidgets
.count() == 1) {
1201 sortedWidgets
[0]->setGeometry(QRect(rect
.topLeft() + QPoint((w
-maxh
)/2,(h
-maxh
)/2), QSize(maxh
,maxh
)));
1203 sortedWidgets
[0]->setGeometry(QRect(rect
.topLeft() + QPoint((w
-maxw
)/2,(h
-maxw
)/2), QSize(maxw
,maxw
)));
1206 } else if (sortedWidgets
.count() == 2) {
1208 sortedWidgets
[0]->setGeometry(QRect(rect
.topLeft() + QPoint(w
/2-maxh
,(h
-maxh
)/2), QSize(maxh
,maxh
)));
1209 sortedWidgets
[1]->setGeometry(QRect(rect
.topLeft() + QPoint(w
/2,(h
-maxh
)/2), QSize(maxh
,maxh
)));
1211 sortedWidgets
[0]->setGeometry(QRect(rect
.topLeft() + QPoint((h
-maxw
)/2,w
-maxw
), QSize(w
,w
)));
1212 sortedWidgets
[1]->setGeometry(QRect(rect
.topLeft() + QPoint((h
-maxw
)/2,w
/2), QSize(maxw
,maxw
)));
1216 if (h
< 64 || w
< 64) return;
1217 QList
<SakWidget
*> leftList
, rightList
;
1218 for (int i
=4; i
<sortedWidgets
.count(); i
++) {
1220 rightList
<< sortedWidgets
[i
];
1222 leftList
<< sortedWidgets
[i
];
1225 QRect
square(rect
.topLeft() + QPoint(h
/2, 0), QSize(h
,h
));
1226 layoutInSquare(sortedWidgets
.mid(0,4), square
, attractor
);
1228 QRect
leftRect(rect
.topLeft(), QSize(h
/2, h
));
1229 layoutInRect(leftList
, leftRect
, 'R');
1230 QRect
rightRect(rect
.topLeft() + QPoint((int)(0.75*w
),0), QSize(h
/2, h
));
1231 layoutInRect(rightList
, rightRect
, 'L');
1233 QRect
square(rect
.topLeft() + QPoint(0,w
/2), QSize(w
,w
));
1234 layoutInSquare(sortedWidgets
.mid(0,4), square
, attractor
);
1236 QRect
leftRect(rect
.topLeft(), QSize(w
, w
/2));
1237 layoutInRect(leftList
, leftRect
, 'B');
1238 QRect
rightRect(rect
.topLeft() + QPoint(0,(int)(0.75*h
)), QSize(w
, w
/2));
1239 layoutInRect(rightList
, rightRect
, 'T');
1243 QRect
Sak::Layouting( const QList
<SakWidget
*>& sortedWidgets
)
1245 QRect r
= m_desktopRect
;
1246 int height
= (int)(0.75 * r
.height());
1247 int width
= r
.width();
1249 int firstW
= width
/ 2 < height
? width
: height
* 2;
1250 int firstH
= firstW
/ 2;
1251 QRect
firstRect (r
.x() + (width
- firstW
) / 2, r
.y() + (height
- firstH
) / 2 + (int)(r
.height() * 0.25), firstW
, firstH
);
1253 layoutInRect(sortedWidgets
, firstRect
, 'C');
1254 return QRect(QPoint(r
.x(), r
.y()), QSize(r
.width(), (int)(0.25 * r
.height())));
1257 void Sak::grabKeyboard()
1259 #if defined(Q_WS_X11)
1261 // save current focused application
1262 XGetInputFocus((X11::Display
*)QX11Info::display(), &X11::CurrentFocusWindow
, &X11::CurrentRevertToReturn
);
1265 X11::XSetInputFocus((X11::Display
*)QX11Info::display(), QX11Info::appRootWindow(QX11Info::appScreen()), RevertToParent
, CurrentTime
);
1266 X11::XFlush((X11::Display
*)QX11Info::display());
1268 m_view
->grabKeyboard();
1274 trayIcon
->showMessage("SAK popup disabled", "SAK triggered a new event but no popup will be shown", QSystemTrayIcon::Info
, -1);
1277 // save changes first
1283 if (m_subtaskView
) {
1285 foreach(SakSubWidget
* w
, m_subwidgets
.values()) {
1286 w
->scene()->removeItem(w
);
1288 m_subwidgets
.clear();
1290 m_marker
->scene()->removeItem(m_marker
);
1294 foreach(SakWidget
* w
, m_widgets
.values()) {
1297 m_subtaskView
=false;
1301 m_subtaskView
= false;
1303 if (sender() == previewButton
) {
1304 m_previewing
= true;
1306 QDateTime now
= QDateTime::currentDateTime();
1307 QDateTime fromWeek
= now
;
1308 fromWeek
.setDate(now
.date().addDays(-now
.date().dayOfWeek() + 1));
1309 fromWeek
.setTime(QTime(0,0,0));
1310 QDateTime fromMonth
= now
;
1311 fromMonth
.setDate(now
.date().addDays(-now
.date().day()));
1312 fromMonth
.setTime(QTime(0,0,0));
1313 QDateTime fromToday
= now
;
1314 fromToday
.setTime(QTime(0,0,0));
1316 double weekHits
= 0;
1318 double monthHits
= 0;
1319 QHash
<QString
, double> dayStats
;
1320 QHash
<QString
, double> weekStats
;
1321 QHash
<QString
, double> monthStats
;
1323 foreach(const Task
& t
, m_tasks
.values()) {
1325 double m
= t
.workedHours(fromMonth
, now
);
1326 monthStats
[t
.title
] = m
;
1328 double w
= t
.workedHours(fromWeek
, now
);
1329 weekStats
[t
.title
] = w
;
1331 double d
= t
.workedHours(fromToday
, now
);
1332 dayStats
[t
.title
] = d
;
1339 foreach(const Task
& t
, m_tasks
.values()) {
1340 if (!t
.active
|| t
.title
== QString() || t
.title
== "<away>") continue;
1341 SakWidget
* test
= new SakWidget(t
);
1342 test
->setVisible(false);
1343 double d
= dayStats
[t
.title
];
1344 double w
= weekStats
[t
.title
];
1345 double m
= monthStats
[t
.title
];
1346 test
->setStatistics(d
, w
, m
, d
/dayHits
* 100.0, w
/weekHits
* 100.0, m
/monthHits
* 100.0);
1347 test
->setObjectName(t
.title
);
1348 connect (test
, SIGNAL(clicked(const QString
&, const QString
&)), this, SLOT(workingOnTask(const QString
&, const QString
&)));
1349 connect (test
, SIGNAL(clicked(const QString
&)), this, SLOT(popupSubtasks(const QString
&)));
1350 int historyPosition
= 1 + m_taskSelectionHistory
.indexOf(t
.title
);
1351 int rank
= historyPosition
!= 0 ? -1000000 * historyPosition
: -d
;
1352 m_widgets
.insertMulti( rank
, test
);
1355 m_widgetsIterator
= m_widgets
.begin();
1356 if (m_widgetsIterator
!= m_widgets
.end()) {
1357 m_widgetsIterator
.value()->showDetails(true);
1361 const QList
<SakWidget
*>& values
= m_widgets
.values();
1362 QRect messageRect
= Layouting((const QList
<SakWidget
*>&)values
);
1363 foreach(SakWidget
* w
, values
) {
1364 m_view
->scene()->addItem(w
);
1369 // add the message item
1370 SakMessageItem
* sakMessage
= new SakMessageItem(bodyEdit
->toPlainText());
1371 sakMessage
->setGeometry(messageRect
);
1372 m_view
->scene()->addItem(sakMessage
);
1375 // add the exit item
1376 SakExitItem
* exitItem
= new SakExitItem(QPixmap(":/images/exit.png"));
1377 QRect r
= m_desktopRect
;
1378 connect(exitItem
, SIGNAL(exit()), this, SLOT(clearView()));
1379 exitItem
->setPos(r
.width() - exitItem
->boundingRect().width(), 0);
1380 m_view
->scene()->addItem(exitItem
);
1381 exitItem
->setZValue(1e8
);
1385 m_view
->setGeometry( QRect(m_desktopRect
)/*.adjusted(200,200,-200,-200 )*/ );
1389 #if defined(Q_WS_WIN)
1390 SetForegroundWindow(m_view
->winId());
1392 qApp
->alert(m_view
, 5000);
1396 void Sak::popupSubtasks(const QString
& _taskname
) {
1398 killTimer(m_timeoutPopup
);
1400 QString taskname
= _taskname
;
1401 if (taskname
.isEmpty()) {
1402 if ( m_taskSelectionHistory
.isEmpty() ) {
1405 taskname
= m_taskSelectionHistory
.back();
1407 QString subtaskName
;
1408 if (!m_subtaskSelectionHistory
[taskname
].isEmpty()) {
1409 subtaskName
= m_subtaskSelectionHistory
[taskname
].back();
1411 workingOnTask(taskname
, subtaskName
);
1414 m_subtaskView
= true;
1416 QRect r
= m_desktopRect
;
1420 // hide tasks to show subtasks
1421 foreach(SakWidget
* w
, m_widgets
.values()) {
1425 QHash
<QString
, Task
>::const_iterator itr
= m_tasks
.find(taskname
);
1426 if (itr
== m_tasks
.end()) {
1427 workingOnTask(taskname
, "");
1430 const Task
& t(*itr
);
1431 QHash
< QString
, Task::SubTask
>::const_iterator titr
= t
.subTasks
.begin(), tend
= t
.subTasks
.end();
1432 m_subwidgets
.clear();
1433 QDateTime
now(QDateTime::currentDateTime());
1434 for(; titr
!= tend
; titr
++) {
1435 if (!titr
->active
) continue;
1436 SakSubWidget
* sw
= new SakSubWidget(t
, *titr
);
1437 sw
->setGeometry(QRectF(0,0,w
,h
));
1438 connect (sw
, SIGNAL(clicked(const QString
&, const QString
&)), this, SLOT(workingOnTask(const QString
&, const QString
&)));
1439 connect (sw
, SIGNAL(focused()), this, SLOT(focusedSubTask()));
1442 QHash
< QString
, QList
< QString
> >::const_iterator hhitr
= m_subtaskSelectionHistory
.find(t
.title
);
1443 if (hhitr
!= m_subtaskSelectionHistory
.end()) {
1444 int index
= hhitr
->indexOf(titr
->title
);
1445 if (index
!= 0) rank
= now
.addDays(index
+1);
1448 QHash
< QString
, QList
<Task::Hit
> >::const_iterator hitr
= t
.hits
.find(titr
.key());
1449 if (hitr
!= t
.hits
.end()) {
1450 if ( hitr
.value().count() && hitr
.value().last().timestamp
.isValid())
1451 rank
= hitr
.value().last().timestamp
;
1453 rank
= now
.addDays(-999);
1456 m_subwidgets
.insertMulti( - rank
.toTime_t(), sw
);
1459 const QList
<SakSubWidget
*>& values
= m_subwidgets
.values();
1461 // create possible completion
1462 QStringList subtaskSortedList
;
1463 foreach(SakSubWidget
* sub
, values
) {
1464 subtaskSortedList
.push_back(sub
->subtask().title
);
1466 m_subtaskCompleter
= new QCompleter(subtaskSortedList
);
1467 m_subtaskCompleter
->setCaseSensitivity(Qt::CaseInsensitive
);
1469 // QRect messageRect = Layouting((const QList<SakWidget*>&)values);
1471 m_subwidgetsIterator
= m_subwidgets
.begin();
1472 m_subWidgetRank
= 0;
1474 // the one with text
1475 SakSubWidget
* tmpSw
= new SakSubWidget(t
, Task::SubTask(), true);
1476 QCompleter
* completer
= new QCompleter(subtaskSortedList
);
1477 completer
->setCaseSensitivity(Qt::CaseInsensitive
);
1478 completer
->setCompletionMode(QCompleter::InlineCompletion
);
1479 ((QLineEdit
*)tmpSw
->widget())->setCompleter(completer
);
1480 m_view
->scene()->addItem(tmpSw
);
1481 tmpSw
->setGeometry(QRectF(0,0,w
,h
));
1482 connect (tmpSw
, SIGNAL(clicked(const QString
&, const QString
&)), this, SLOT(workingOnTask(const QString
&, const QString
&)));
1483 connect (tmpSw
, SIGNAL(focused()), this, SLOT(focusedSubTask()));
1485 m_subwidgets
.insertMulti(-QDateTime::currentDateTime().addDays(999).toTime_t(), tmpSw
);
1487 m_subWidgetRank
+= values
.size() != 0;
1489 if (m_subwidgetsIterator
!= m_subwidgets
.end()) {
1490 m_subwidgetsIterator
.value()->showDetails(true);
1491 m_view
->scene()->setFocusItem(m_subwidgetsIterator
.value(), Qt::MouseFocusReason
);
1492 m_subwidgetsIterator
.value()->widget()->setFocus(Qt::MouseFocusReason
);
1494 m_view
->scene()->setFocusItem(tmpSw
, Qt::MouseFocusReason
);
1495 tmpSw
->widget()->setFocus(Qt::MouseFocusReason
);
1498 // add a marker to highligh current selection
1499 m_marker
= new QGraphicsEllipseItem(r
.width()/2 - 280, r
.height()/2 - 51, 20,20);
1500 m_marker
->setBrush(Qt::red
);
1501 m_view
->scene()->addItem(m_marker
);
1502 m_marker
->setVisible(true);
1504 layoutSubTasks(m_subwidgets
, m_subWidgetRank
);
1506 // Finally add subwidgets
1507 for(int i
=0; i
<values
.size(); i
++) {
1508 SakSubWidget
* sw
= values
[i
];
1509 m_view
->scene()->addItem(sw
);
1514 void Sak::scrollTasks(int npos
) {
1515 SakWidget
* currentShowing
= 0;
1517 for (int i
=npos
; i
<0; i
++) {
1518 if (m_widgetsIterator
== m_widgets
.end()) return;
1519 currentShowing
= m_widgetsIterator
!= m_widgets
.end() ? m_widgetsIterator
.value() : 0;
1520 if (m_widgetsIterator
== m_widgets
.begin()) m_widgetsIterator
= m_widgets
.end();
1521 m_widgetsIterator
--;
1524 for (int i
=0; i
<npos
; i
++) {
1525 if (m_widgetsIterator
== m_widgets
.end()) return;
1526 currentShowing
= m_widgetsIterator
.value();
1527 m_widgetsIterator
++;
1528 if (m_widgetsIterator
== m_widgets
.end()) m_widgetsIterator
= m_widgets
.begin();
1531 if (currentShowing
&& m_widgetsIterator
!= m_widgets
.end()) {
1532 currentShowing
->showDetails(false);
1533 m_widgetsIterator
.value()->showDetails(true);
1537 void Sak::scrollSubTasks(int npos
) {
1538 SakSubWidget
* currentShowing
= 0;
1540 for (int i
=npos
; i
<0; i
++) {
1541 if (m_subwidgetsIterator
== m_subwidgets
.end()) return;
1542 currentShowing
= m_subwidgetsIterator
!= m_subwidgets
.end() ? m_subwidgetsIterator
.value() : 0;
1543 if (m_subwidgetsIterator
== m_subwidgets
.begin()) {
1544 m_subwidgetsIterator
= m_subwidgets
.end();
1545 m_subWidgetRank
= m_subwidgets
.count();
1547 m_subwidgetsIterator
--;
1551 for (int i
=0; i
<npos
; i
++) {
1552 if (m_subwidgetsIterator
== m_subwidgets
.end()) return;
1553 currentShowing
= m_subwidgetsIterator
.value();
1554 m_subwidgetsIterator
++;
1556 if (m_subwidgetsIterator
== m_subwidgets
.end()) {
1557 m_subwidgetsIterator
= m_subwidgets
.begin();
1558 m_subWidgetRank
= 0;
1562 if (currentShowing
&& m_subwidgetsIterator
!= m_subwidgets
.end()) {
1563 currentShowing
->showDetails(false);
1564 m_subwidgetsIterator
.value()->showDetails(true);
1565 m_view
->scene()->setFocusItem(m_subwidgetsIterator
.value(), Qt::MouseFocusReason
);
1566 m_subwidgetsIterator
.value()->widget()->setFocus(Qt::MouseFocusReason
);
1568 layoutSubTasks(m_subwidgets
, m_subWidgetRank
);
1571 void Sak::focusedSubTask()
1573 SakSubWidget
* w
= dynamic_cast<SakSubWidget
*>(sender());
1575 QMap
<int, SakSubWidget
*>::iterator itr
= m_subwidgets
.begin(), end
=m_subwidgets
.end();
1576 for(int i
=0; itr
!= end
; i
++,itr
++) {
1577 if (itr
.value() == w
) {
1578 m_subwidgetsIterator
= itr
;
1579 m_subWidgetRank
= i
;
1585 //END Tasks >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
1588 //BEGIN settings ================================
1591 void Sak::setupSettingsWidget()
1593 m_settings
= new QMainWindow();
1594 m_settings
->setMinimumHeight(650);
1595 m_settings
->setMinimumWidth(700);
1596 QWidget
* centralWidget
= new QWidget
;
1597 m_settings
->setCentralWidget(centralWidget
);
1599 QVBoxLayout
* theMainLayout
= new QVBoxLayout
;
1600 centralWidget
->setLayout(theMainLayout
);
1602 tabs
= new QTabWidget
;
1603 theMainLayout
->addWidget(tabs
);
1604 previewButton
= new QPushButton("Preview");
1605 theMainLayout
->addWidget(previewButton
);
1611 tabs
->addTab(tab1
, "Tasks");
1612 tabs
->addTab(tab2
, "General");
1613 tabs
->addTab(tab4
, "Statistics");
1614 tabs
->addTab(tab3
, "Advanced");
1617 QMenuBar
* mainMenu
= new QMenuBar
;
1618 m_settings
->setMenuBar(mainMenu
);
1619 QMenu
* programMenu
= mainMenu
->addMenu("Program");
1620 programMenu
->addAction(quitAction
);
1621 programMenu
->addAction(minimizeAction
);
1622 QMenu
* dbMenu
= mainMenu
->addMenu("Db");
1623 dbMenu
->addAction(flushAction
);
1624 dbMenu
->addAction(openAction
);
1625 // dbMenu->addAction(saveAsDbAction);
1626 dbMenu
->addAction(exportDbCsvAction
);
1628 dbMenu
->addAction(gmailLoginAction
);
1629 dbMenu
->addAction(saveToGmailAction
);
1630 if (!m_gmail
->isValid()) {
1631 gmailLoginAction
->setEnabled(false);
1632 saveToGmailAction
->setEnabled(false);
1635 QMenu
* actionsMenu
= mainMenu
->addMenu("Actions");
1636 actionsMenu
->addAction(startAction
);
1637 actionsMenu
->addAction(stopAction
);
1639 QVBoxLayout
*mainLayout
= new QVBoxLayout
;
1640 QGridLayout
*messageLayout
= new QGridLayout
;
1641 durationLabel
= new QLabel(tr("Interval:"));
1642 durationLabel1
= new QLabel(tr("(effective only after restart)"));
1644 QSettings
settings(QSettings::IniFormat
, QSettings::UserScope
, "ZanzaSoft", "SAK");
1645 durationSpinBox
= new QSpinBox
;
1646 durationSpinBox
->setSingleStep(1);
1647 durationSpinBox
->setRange(1, 1440);
1648 durationSpinBox
->setSuffix(" minutes");
1649 durationSpinBox
->setValue(qMin(1440, qMax(1, settings
.value("Ping interval", 15).toInt())));
1650 durationSpinBox
->setCorrectionMode(QAbstractSpinBox::CorrectToNearestValue
);
1652 bodyLabel
= new QLabel(tr("Message:"));
1653 bodyEdit
= new QTextEdit
;
1654 bodyEdit
->setPlainText( settings
.value("Message", "<h1>Enter a message here!</h1>").toString() );
1656 messageLayout
->addWidget(durationLabel
, 1, 0);
1657 messageLayout
->addWidget(durationSpinBox
, 1, 1);
1658 messageLayout
->addWidget(durationLabel1
, 1, 2);
1659 messageLayout
->addWidget(bodyLabel
, 3, 0);
1660 messageLayout
->addWidget(bodyEdit
, 3, 1, 2, 4);
1661 messageLayout
->setColumnStretch(3, 1);
1662 messageLayout
->setRowStretch(4, 1);
1663 mainLayout
->addLayout(messageLayout
);
1664 tab2
->setLayout(mainLayout
);
1666 mainLayout
= new QVBoxLayout
;
1667 tab1
->setLayout(mainLayout
);
1669 tasksTree
= new QTreeWidget
;
1670 tasksTree
->setSizePolicy(QSizePolicy::Expanding
, QSizePolicy::Expanding
);
1671 tasksTree
->setColumnCount(3);
1672 tasksTree
->setColumnWidth(0, 300);
1673 tasksTree
->setColumnWidth(1, 65);
1674 tasksTree
->setIconSize(QSize(32,32));
1675 connect(tasksTree
, SIGNAL(itemDoubleClicked(QTreeWidgetItem
*,int)), this, SLOT(doubleClickedTask(QTreeWidgetItem
*,int)));
1678 taskTextEditor
= new QTextEdit
;
1679 taskTextEditor
->setFixedHeight(100);
1680 taskTextEditor
->setSizePolicy(QSizePolicy::Expanding
, QSizePolicy::Preferred
);
1681 connect(taskTextEditor
, SIGNAL(textChanged()), this, SLOT(commitCurrentTask()));
1684 taskTitleEditor
= new QLineEdit
;
1685 taskTitleEditor
->setFixedHeight(20);
1686 taskTitleEditor
->setSizePolicy(QSizePolicy::Expanding
, QSizePolicy::Preferred
);
1687 connect(taskTitleEditor
, SIGNAL(editingFinished()), this, SLOT(commitCurrentTask()));
1689 taskPixmapViewer
= new PixmapViewer
;
1690 mainLayout
->addWidget(tasksTree
, 2);
1691 QHBoxLayout
* detailsLayout
= new QHBoxLayout
;
1692 QVBoxLayout
* editsLayout
= new QVBoxLayout
;
1693 detailsLayout
->addWidget(taskPixmapViewer
);
1694 connect(taskPixmapViewer
, SIGNAL(changed()), this, SLOT(commitCurrentTask()));
1695 editsLayout
->addWidget(taskTitleEditor
);
1696 editsLayout
->addWidget(taskTextEditor
);
1697 QHBoxLayout
* datesLayout
= new QHBoxLayout
;
1698 datesLayout
->addWidget(new QLabel("Due: "));
1699 dueEditor
= new QDateEdit
;
1700 dueEditor
->setMinimumDate(QDate(2000,1,1));
1701 datesLayout
->addWidget(dueEditor
);
1702 datesLayout
->addWidget(new QLabel("Estimated: "));
1703 estimatedHoursEditor
= new QSpinBox
;
1704 estimatedHoursEditor
->setRange(0, 1e5
);
1705 estimatedHoursEditor
->setSuffix("hours");
1706 datesLayout
->addWidget(estimatedHoursEditor
);
1707 editsLayout
->addLayout(datesLayout
);
1708 detailsLayout
->addLayout(editsLayout
);
1709 connect(dueEditor
, SIGNAL(editingFinished()), this, SLOT(commitCurrentTask()));
1710 connect(estimatedHoursEditor
,SIGNAL(editingFinished()), this, SLOT(commitCurrentTask()));
1712 QVBoxLayout
* colorsLayout
= new QVBoxLayout
;
1713 bgColorButton
= new QPushButton("bg\ncolor");
1714 bgColorButton
->setToolTip("Background color");
1715 bgColorButton
->setSizePolicy(QSizePolicy(QSizePolicy::Preferred
, QSizePolicy::MinimumExpanding
));
1717 fgColorButton
= new QPushButton("fg\ncolor");
1718 fgColorButton
->setToolTip("Foreground color");
1719 fgColorButton
->setSizePolicy(QSizePolicy(QSizePolicy::Preferred
, QSizePolicy::MinimumExpanding
));
1722 // fix Windows XP "style"
1723 bgColorButton
->setStyle(new QWindowsStyle());
1724 fgColorButton
->setStyle(new QWindowsStyle());
1727 colorsLayout
->addWidget(bgColorButton
);
1728 colorsLayout
->addWidget(fgColorButton
);
1729 detailsLayout
->addLayout(colorsLayout
);
1731 mainLayout
->addLayout(detailsLayout
);
1733 QVBoxLayout
* tab4MainLayout
= new QVBoxLayout(tab4
);
1734 //taskSelector = new QComboBox;
1735 summaryList
= newTaskSummaryList();
1736 QTabWidget
* tabs
= new QTabWidget
;
1737 tabs
->setTabPosition(QTabWidget::East
);
1738 tabs
->addTab(summaryList
, "List");
1739 QGraphicsView
* summaryView
= new QGraphicsView
;
1740 summaryView
->setScene(new QGraphicsScene
);
1741 TaskSummaryPieChart
* chart
= new TaskSummaryPieChart
;
1742 summaryView
->scene()->addItem(new TaskSummaryPieChart
);
1743 summaryView
->centerOn(chart
);
1744 tabs
->addTab(summaryView
, "Chart");
1745 tab4MainLayout
->addWidget(tabs
);
1746 cal3
= new QCalendarWidget
;
1747 cal3
->setMinimumSize(QSize(250,200));
1748 cal4
= new QCalendarWidget
;
1749 cal4
->setMinimumSize(QSize(250,200));
1750 cal3
->setSizePolicy(QSizePolicy::MinimumExpanding
, QSizePolicy::Fixed
);
1751 cal4
->setSizePolicy(QSizePolicy::MinimumExpanding
, QSizePolicy::Fixed
);
1752 QHBoxLayout
* calsLayout
= new QHBoxLayout
;
1753 calsLayout
->addWidget(cal3
);
1754 calsLayout
->addWidget(cal4
);
1755 cal4
->setSelectedDate(QDate::currentDate().addDays(1));
1756 tab4MainLayout
->addLayout(calsLayout
);
1759 QVBoxLayout
* tab3MainLayout
= new QVBoxLayout(tab3
);
1760 //taskSelector = new QComboBox;
1761 hitsList
= newHitsList();
1762 connect(hitsList
, SIGNAL(currentItemChanged(QTreeWidgetItem
*,QTreeWidgetItem
*)), this, SLOT(hitsSelectedInList(QTreeWidgetItem
*,QTreeWidgetItem
*)));
1763 hitsTimeline
= new Timeline
;
1764 connect(hitsTimeline
, SIGNAL(hitSelected(HitItem
*)), this, SLOT(hitsSelectedInTimeline(HitItem
*)));
1765 cal1
= new QCalendarWidget
;
1766 cal1
->setMinimumSize(QSize(250,200));
1767 cal2
= new QCalendarWidget
;
1768 cal2
->setMinimumSize(QSize(250,200));
1769 cal1
->setSizePolicy(QSizePolicy::MinimumExpanding
, QSizePolicy::Fixed
);
1770 cal2
->setSizePolicy(QSizePolicy::MinimumExpanding
, QSizePolicy::Fixed
);
1771 //tab3MainLayout->addWidget(taskSelector);
1773 calsLayout
= new QHBoxLayout
;
1774 calsLayout
->addWidget(cal1
);
1775 calsLayout
->addWidget(cal2
);
1776 cal2
->setSelectedDate(QDate::currentDate().addDays(1));
1777 tab3MainLayout
->addWidget(hitsList
);
1778 tab3MainLayout
->addWidget(hitsTimeline
);
1779 tab3MainLayout
->addLayout(calsLayout
);
1783 m_settings
->setWindowTitle(tr("SaK"));
1784 m_settings
->resize(400, 300);
1788 QTreeWidget
* Sak::newHitsList()
1790 QTreeWidget
* hitsList
= new QTreeWidget
;
1791 hitsList
->setColumnCount(4);
1792 hitsList
->setColumnWidth(0, 200);
1793 hitsList
->setColumnWidth(1, 150);
1794 hitsList
->setColumnWidth(2, 150);
1795 hitsList
->setColumnWidth(3, 150);
1796 hitsList
->setColumnWidth(4, 150);
1797 hitsList
->setIconSize(QSize(24,24));
1798 hitsList
->setSortingEnabled(true);
1799 hitsList
->setItemDelegateForColumn(0, new MyDateItemDelegate
);
1800 hitsList
->setItemDelegateForColumn(1, new TaskItemDelegate(this));
1801 hitsList
->setItemDelegateForColumn(2, new SubTaskItemDelegate(this));
1802 QTreeWidgetItem
* header
= new QTreeWidgetItem
;
1803 header
->setText(0, "Date/Time");
1804 header
->setText(1, "Task");
1805 header
->setText(2, "Subtask");
1806 header
->setText(3, "Duration (min)");
1807 header
->setText(4, "Overestimation");
1808 hitsList
->setHeaderItem(header
);
1809 hitsList
->setSizePolicy(QSizePolicy::MinimumExpanding
, QSizePolicy::MinimumExpanding
);
1813 QTreeWidget
* Sak::newTaskSummaryList()
1815 QTreeWidget
* taskSummaryList
= new QTreeWidget
;
1816 taskSummaryList
->setColumnCount(4);
1817 taskSummaryList
->setColumnWidth(0, 250);
1818 taskSummaryList
->setColumnWidth(1, 150);
1819 taskSummaryList
->setColumnWidth(1, 150);
1820 taskSummaryList
->setIconSize(QSize(24,24));
1821 taskSummaryList
->setSortingEnabled(true);
1822 QTreeWidgetItem
* header
= new QTreeWidgetItem
;
1823 header
->setText(0, "Task");
1824 header
->setText(1, "Hours");
1825 taskSummaryList
->setHeaderItem(header
);
1826 taskSummaryList
->setSizePolicy(QSizePolicy::MinimumExpanding
, QSizePolicy::MinimumExpanding
);
1827 taskSummaryList
->setEnabled(true);
1828 return taskSummaryList
;
1831 void Sak::setVisible(bool visible
)
1833 minimizeAction
->setEnabled(visible
);
1834 maximizeAction
->setEnabled(!m_settings
->isMaximized());
1835 restoreAction
->setEnabled(m_settings
->isMaximized() || !visible
);
1836 m_settings
->setVisible(visible
);
1839 void Sak::createActions()
1841 minimizeAction
= new QAction(tr("Mi&nimize"), m_settings
);
1842 connect(minimizeAction
, SIGNAL(triggered()), m_settings
, SLOT(hide()));
1844 maximizeAction
= new QAction(tr("Ma&ximize"), m_settings
);
1845 connect(maximizeAction
, SIGNAL(triggered()), m_settings
, SLOT(showMaximized()));
1847 restoreAction
= new QAction(tr("&Restore"), m_settings
);
1848 connect(restoreAction
, SIGNAL(triggered()), m_settings
, SLOT(showNormal()));
1850 quitAction
= new QAction(tr("&Quit"), m_settings
);
1851 connect(quitAction
, SIGNAL(triggered()), qApp
, SLOT(quit()));
1853 startAction
= new QAction(tr("Start polling"), m_settings
);
1854 connect(startAction
, SIGNAL(triggered()), this, SLOT(start()));
1856 stopAction
= new QAction(tr("Stop polling"), m_settings
);
1857 connect(stopAction
, SIGNAL(triggered()), this, SLOT(stop()));
1859 flushAction
= new QAction(tr("&Flush data/settings to disk"), m_settings
);
1860 connect(flushAction
, SIGNAL(triggered()), this, SLOT(flush()));
1862 saveAsDbAction
= new QAction(tr("Backup as"), m_settings
);
1863 // connect(saveAsDbAction, SIGNAL(triggered()), this, SLOT(saveAsDb()));
1865 exportDbCsvAction
= new QAction(tr("Export hits in CSV format"), m_settings
);
1866 connect(exportDbCsvAction
, SIGNAL(triggered()), this, SLOT(exportDbCsv()));
1869 saveToGmailAction
= new QAction(tr("Store in your gmail account free space"), m_settings
);
1870 connect(saveToGmailAction
, SIGNAL(triggered()), this, SLOT(saveToGmail()));
1872 gmailLoginAction
= new QAction(tr("Log in gmail"), m_settings
);
1873 connect(gmailLoginAction
, SIGNAL(triggered()), this, SLOT(logInGmail()));
1875 saveToGmailAction
= NULL
;
1876 gmailLoginAction
= NULL
;
1879 openAction
= new QAction(tr("Import a task from file"), m_settings
);
1880 connect(openAction
, SIGNAL(triggered()), this, SLOT(open()));
1882 m_addHitAction
= new QAction("Add hit", m_settings
);
1883 m_addHitMenu
= new QMenu(m_settings
);
1884 m_addHitAction
->setText("Add hit");
1885 m_addHitMenu
->addAction(m_addHitAction
);
1886 connect(m_addHitAction
, SIGNAL(triggered()), this, SLOT(addDefaultHit()));
1888 m_exportDataAction
= new QAction("Export data", m_settings
);
1889 m_exportDataAction
->setText("Export data");
1890 m_addHitMenu
->addAction(m_exportDataAction
);
1891 connect(m_exportDataAction
, SIGNAL(triggered()), this, SLOT(exportHits()));
1893 m_addTaskAction
= new QAction("Add task", m_settings
);
1894 m_addTaskMenu
= new QMenu(m_settings
);
1895 m_addTaskAction
->setText("Add task");
1896 m_addTaskMenu
->addAction(m_addTaskAction
);
1897 connect(m_addTaskAction
, SIGNAL(triggered()), this, SLOT(addDefaultTask()));
1900 void Sak::trayIconActivated(QSystemTrayIcon::ActivationReason reason
)
1902 if (reason
== QSystemTrayIcon::DoubleClick
) {
1907 //END Settings <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<