1 /***************************************************************************
2 * Copyright (C) 2007 by Arrigo Zanette *
4 ***************************************************************************/
8 #include <QCryptographicHash>
10 #include <QGraphicsEllipseItem>
11 #include <QProgressDialog>
15 #include "sakwidget.h"
16 #include "saksubwidget.h"
17 #include "sakmessageitem.h"
18 #include "pixmapviewer.h"
20 #include "backupper.h"
23 #include "gmailstorage/gmailpyinterface.h"
25 #include "gmailstorage/gmailmyinterface.h"
28 //END Task <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
41 static Window CurrentFocusWindow
;
42 static int CurrentRevertToReturn
;
49 class GView
: public QGraphicsView
53 // if (QGLFormat::hasOpenGL()) {
54 // qDebug() << "Using OpenGL";
55 // QGLWidget* w = new QGLWidget;
56 // w->setAttribute(Qt::WA_TranslucentBackground, true);
61 // delete this->viewport();
63 void drawBackground(QPainter
* p
, const QRectF
& rect
) {
64 viewport()->setAttribute(Qt::WA_TranslucentBackground
, true);
65 setAttribute(Qt::WA_NoSystemBackground
, true);
66 viewport()->setAttribute(Qt::WA_NoSystemBackground
, true);
67 setAttribute(Qt::WA_TranslucentBackground
, true);
68 if (backgroundPixmap
.isNull()) {
69 QBrush
brush(QColor(100,0,0,200));
70 p
->setCompositionMode(QPainter::CompositionMode_Source
);
71 p
->fillRect(rect
, brush
);
73 p
->drawPixmap(rect
, backgroundPixmap
, rect
.translated(backgroundPixmap
.rect().center() - viewport()->rect().center()));
76 QPixmap backgroundPixmap
;
80 //BEGIN Sak basic >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
82 Sak::Sak(QObject
* parent
)
90 , m_changedTask(false)
91 , m_subtaskView(false)
92 , m_yesToBackup(false)
93 , m_fakeClicking(false)
95 workingOnDeclared
= 0;
96 workingOnTimer
= new QTimer
;
98 m_desktopRect
= qApp
->desktop()->screenGeometry(QPoint(0,0));
100 m_subtaskCompleter
= 0;
101 summaryList
= hitsList
= 0; trayIcon
=0;
104 if (QCoreApplication::arguments().contains("--clear")) {
105 QHash
<QString
, Task
>::iterator itr
= m_tasks
.begin();
106 while(itr
!= m_tasks
.end()) {
112 if (m_tasks
.count() <= 0)
115 m_previewing
= false;
116 m_changedHit
= false;
118 m_autoSaveTimer
= startTimer(1000 * 60 * 45); // every 45 minutes
121 // Need to go here, or after plasma reboot the icon will disappear
122 trayIconMenu
= new QMenu(m_settings
);
123 //trayIconMenu->addAction(minimizeAction);
124 //trayIconMenu->addAction(maximizeAction);
125 //trayIconMenu->addAction(restoreAction);
126 //trayIconMenu->addSeparator();
127 trayIconMenu
->addAction(startAction
);
128 trayIconMenu
->addAction(stopAction
);
129 trayIconMenu
->addAction(workingOnAction
);
130 trayIconMenu
->addAction(flushAction
);
131 trayIconMenu
->addSeparator();
132 trayIconMenu
->addAction(quitAction
);
133 trayIcon
= new QSystemTrayIcon(this);
134 trayIcon
->setContextMenu(trayIconMenu
);
135 trayIcon
->setIcon( QIcon(":/images/icon.png") );
136 trayIcon
->setToolTip( tr("Sistema Anti Kazzeggio") );
138 connect(trayIcon
, SIGNAL(activated(QSystemTrayIcon::ActivationReason
)), this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason
)));
139 trayIcon
->installEventFilter(this);
142 m_settings
->setWindowIcon( QIcon(":/images/icon.png") );
143 m_settings
->setWindowTitle("SaK - Sistema Anti Kazzeggio");
148 m_backupper
= new Backupper
;
149 m_incremental
= new Incremental
;
151 m_gmail
= new GmailPyInterface
;
156 // load the data model
157 QSettings
settings(QSettings::IniFormat
, QSettings::UserScope
, "ZanzaSoft", "SAK");
158 QByteArray tasksArray
= settings
.value("tasks").toByteArray();
159 QDataStream
stream(&tasksArray
, QIODevice::ReadWrite
);
160 stream
.setVersion(QDataStream::Qt_4_3
);
164 QDir
saveDir(QFileInfo(settings
.fileName()).dir());
165 saveDir
.mkdir("SakTasks");
166 saveDir
.cd("SakTasks");
167 QStringList nameFilters
;
168 nameFilters
<< "*.xml";
169 QStringList files
= saveDir
.entryList( nameFilters
, QDir::Files
);
170 foreach (QString taskXmlFileName
, files
) {
171 Task
t( loadTaskFromFile(saveDir
.filePath(taskXmlFileName
)) );
172 m_tasks
[t
.title
] = t
;
177 // add subtasks, if missing
179 QHash
<QString
, Task
>::iterator itr
= m_tasks
.begin();
180 while(itr
!= m_tasks
.end()) {
181 itr
->updateSubTasks();
187 Task
& awayTask
= m_tasks
["<away>"];
188 awayTask
.title
= "<away>";
189 awayTask
.fgColor
= Qt::gray
;
190 awayTask
.bgColor
= Qt::white
;
191 awayTask
.icon
= QPixmap(":/images/away.png");
193 m_editedTasks
= m_tasks
;
198 interactiveMergeHits();
200 m_editedTasks
= m_tasks
;
202 setupSettingsWidget();
204 QPixmap askingLadyPixmap
;
205 QPixmap viewBackgroundPixmap
;
206 QVariant askingLadyVariant
= settings
.value("Lady");
207 if (askingLadyVariant
.canConvert(QVariant::Pixmap
)) {
208 askingLadyPixmap
= askingLadyVariant
.value
<QPixmap
>();
210 QString fileName
= QFileInfo(settings
.fileName()).absoluteDir().filePath( askingLadyVariant
.toString() );
211 askingLadyPixmap
= QPixmap(fileName
);
213 if(!askingLadyPixmap
.isNull()) {
214 askingLady
->setPixmap(askingLadyPixmap
);
217 QVariant viewBackgroundVariant
= settings
.value("View background");
218 if (viewBackgroundVariant
.canConvert(QVariant::Pixmap
)) {
219 viewBackgroundPixmap
= viewBackgroundVariant
.value
<QPixmap
>();
221 QString fileName
= QFileInfo(settings
.fileName()).absoluteDir().filePath( viewBackgroundVariant
.toString() );
222 viewBackgroundPixmap
= QPixmap(fileName
);
225 if(!askingLadyPixmap
.isNull()) {
226 askingLady
->setPixmap(askingLadyPixmap
);\
228 if (!viewBackgroundPixmap
.isNull()) {
229 viewBackground
->setPixmap(viewBackgroundPixmap
);
233 m_settings
->installEventFilter(this);
234 hitsList
->installEventFilter(this);
235 tasksTree
->installEventFilter(this);
236 tasksTree
->setUniformRowHeights(false);
237 QTreeWidgetItem
* headerItem
= new QTreeWidgetItem
;
238 headerItem
->setSizeHint(0 , QSize(0,0));
239 headerItem
->setSizeHint(1 , QSize(0,0));
240 headerItem
->setSizeHint(2 , QSize(0,0));
241 tasksTree
->setHeaderItem(headerItem
);
243 connect(bgColorButton
, SIGNAL(clicked()), this, SLOT(selectColor()));
244 connect(fgColorButton
, SIGNAL(clicked()), this, SLOT(selectColor()));
245 connect(previewButton
, SIGNAL(clicked()), this, SLOT(popup()));
246 connect(tasksTree
, SIGNAL(itemSelectionChanged()), this, SLOT(selectedTask()));
247 connect(tasksTree
, SIGNAL(itemClicked(QTreeWidgetItem
*,int)), this, SLOT(selectedTask()));
250 connect(cal1
, SIGNAL(clicked(QDate
)), this, SLOT(selectedStartDate(QDate
)));
251 connect(cal2
, SIGNAL(clicked(QDate
)), this, SLOT(selectedEndDate(QDate
)));
252 connect(cal3
, SIGNAL(clicked(QDate
)), this, SLOT(selectedStartDate(QDate
)));
253 connect(cal4
, SIGNAL(clicked(QDate
)), this, SLOT(selectedEndDate(QDate
)));
255 connect(today1
, SIGNAL(clicked()), this, SLOT(selectTodayDate()));
256 connect(thisWeek1
, SIGNAL(clicked()), this, SLOT(selectThisWeeDate()));
257 connect(thisMonth1
, SIGNAL(clicked()), this, SLOT(selectThisMonthDate()));
258 connect(lastWeek1
, SIGNAL(clicked()), this, SLOT(selectLastWeekDate()));
259 connect(lastMonth1
, SIGNAL(clicked()), this, SLOT(selectLastMonthDate()));
261 connect(today2
, SIGNAL(clicked()), this, SLOT(selectTodayDate()));
262 connect(thisWeek2
, SIGNAL(clicked()), this, SLOT(selectThisWeeDate()));
263 connect(thisMonth2
, SIGNAL(clicked()), this, SLOT(selectThisMonthDate()));
264 connect(lastWeek2
, SIGNAL(clicked()), this, SLOT(selectLastWeekDate()));
265 connect(lastMonth2
, SIGNAL(clicked()), this, SLOT(selectLastMonthDate()));
267 connect(hitsList
, SIGNAL(itemChanged(QTreeWidgetItem
*,int)), this, SLOT(hitsListItemChanged(QTreeWidgetItem
*,int)));
271 m_view
->setScene(new QGraphicsScene
);
272 m_view
->scene()->setSceneRect(m_desktopRect
);
274 m_view
->installEventFilter(this);
275 m_view
->setFrameStyle(QFrame::NoFrame
);
276 m_view
->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
277 m_view
->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
278 m_view
->setWindowFlags(m_view
->windowFlags() | Qt::WindowStaysOnTopHint
| Qt::ToolTip
);
279 //m_view->setWindowModality(Qt::ApplicationModal);
280 m_view
->setAttribute(Qt::WA_QuitOnClose
, false);
281 // enable transparency with Qt4.5
282 m_view
->setAttribute(Qt::WA_TranslucentBackground
, true);
283 m_view
->setWindowIcon( QIcon(":/images/icon.png") );
284 m_view
->setWindowTitle("SaK - Sistema Anti Kazzeggio");
286 m_currentInterval
= durationSpinBox
->value();
287 m_currentInterval
= qMax((int)1, qMin((int)1440, m_currentInterval
));
288 qDebug() << "SAK: pinging interval " << m_currentInterval
<< Task::hours(m_currentInterval
) << " hours ";
290 hitsTimeline
->setPeriod(QDateTime(cal1
->selectedDate()), QDateTime(cal2
->selectedDate()));
291 populateHitsList(createHitsList(QDateTime(cal1
->selectedDate()), QDateTime(cal2
->selectedDate())));
293 connect(workingOnTimer
, SIGNAL(timeout()), startAction
, SLOT(trigger()));
298 // ensure the timer is killed
301 m_currentInterval
= qMax((int)1, m_currentInterval
);
302 int msecs
= (int)(Task::hours(m_currentInterval
)*3600.0*1000.0 / 2);
303 m_timerId
= startTimer( msecs
);
304 m_nextTimerEvent
= QDateTime::currentDateTime().addMSecs(msecs
);
305 startAction
->setEnabled(false);
306 stopAction
->setEnabled(true);
307 workingOnTimer
->stop();
314 killTimer(m_timerId
); m_timerId
=-1;
316 stopAction
->setEnabled(false);
317 startAction
->setEnabled(true);
321 void Sak::workingOn()
323 // 1. Select the duration
325 QLabel
* label
= new QLabel("Please select the duration:");
326 QDoubleSpinBox
* spinBox
= new QDoubleSpinBox
;
327 QPushButton
* okButton
= new QPushButton("Select task/subtask");
328 QPushButton
* cancelButton
= new QPushButton("Cancel");
330 connect(spinBox
, SIGNAL(valueChanged(double)), okButton
,SLOT(show()));
331 connect(cancelButton
, SIGNAL(clicked()), &d
, SLOT(reject()));
332 connect(okButton
, SIGNAL(clicked()), &d
, SLOT(accept()));
333 spinBox
->setSuffix(" hours");
334 spinBox
->setMinimum(0);
335 spinBox
->setMaximum(24);
336 spinBox
->setValue(0);
337 QVBoxLayout
* mainLayout
= new QVBoxLayout
;
338 QHBoxLayout
* buttons
= new QHBoxLayout
;
339 buttons
->addWidget(cancelButton
);
340 buttons
->addWidget(okButton
);
341 mainLayout
->addWidget(label
);
342 mainLayout
->addWidget(spinBox
);
343 mainLayout
->addLayout(buttons
);
344 d
.setLayout(mainLayout
);
347 workingOnDeclared
= spinBox
->value();
353 stopAction
->setEnabled(false);
354 startAction
->setEnabled(true);
357 // check xml is valid
358 static bool checkXml (QFile
& file
)
360 QByteArray data
= file
.readLine();
361 QXmlStreamReader
stream(data
);
362 QXmlStreamReader::TokenType token
= stream
.readNext(); // skip StartDocument
363 token
= stream
.readNext();
364 if ( token
!= QXmlStreamReader::Comment
) {
365 qDebug() << "checkXml: missing md5";
369 data
= file
.readAll();
370 QXmlStreamReader
reader(data
);
371 while(!reader
.atEnd() || !reader
.hasError()) {
372 if ( reader
.readNext() == QXmlStreamReader::EndDocument
)
375 qDebug() << "checkXml: ERROR: " << reader
.hasError() << reader
.errorString() << data
.size();
377 return !reader
.hasError();
380 Task
Sak::recoverTaskFromBackup(QFile
& taskXmlFile
, const QString
& error
)
383 if (!m_yesToBackup
&& (rc
= QMessageBox::warning(0, "Corrupted xml!",
384 QString("Reading xml file " + taskXmlFile
.fileName() + " failed\nDo you want to try latest valid backup?" )
385 ,QMessageBox::Yes
| QMessageBox::No
| QMessageBox::YesToAll
)) == QMessageBox::No
) {
386 qDebug() << "Error reading task data from file " << taskXmlFile
.fileName() << ":" << error
;
389 if (rc
== QMessageBox::YesToAll
)
390 m_yesToBackup
= true;
391 // read latest backup
392 QString filePath
= taskXmlFile
.fileName();
393 QFileInfo
fInfo(filePath
);
394 QString baseFileName
= fInfo
.fileName();
395 QDir dir
= fInfo
.dir();
396 dir
.cd("../sakbcks");
397 QStringList list
= dir
.entryList(QStringList() << baseFileName
+"*", QDir::Files
, QDir::Time
);
398 // start from last and validate files
399 qDebug() << filePath
<< list
;
400 for(int i
=0; i
<list
.size(); i
++) {
401 QFile
f(dir
.filePath(list
.at(i
)));
402 if (!f
.open(QIODevice::ReadOnly
)) {
403 qDebug() << "error opening file " << dir
.filePath(list
.at(i
));
405 if (checkXml( f
) ) {
406 qDebug() << list
.at(i
) << "IS OK!!";
408 taskXmlFile
.remove();
409 taskXmlFile
.open(QIODevice::ReadOnly
);
410 loadTaskFromFile(taskXmlFile
.fileName(), false);
419 Task
Sak::loadTaskFromFile(const QString
& filePath
, bool tryRecover
)
421 QFile
taskXmlFile(filePath
);
423 qDebug() << "Examine task file " << taskXmlFile
.fileName();
424 if (!taskXmlFile
.open(QIODevice::ReadOnly
)) {
425 qDebug() << "Failed opening xml file " << taskXmlFile
.fileName();
427 QByteArray data
= taskXmlFile
.readLine();
428 QXmlStreamReader
stream(data
);
429 QXmlStreamReader::TokenType token
= stream
.readNext(); // skip StartDocument
430 token
= stream
.readNext();
431 if ( token
!= QXmlStreamReader::Comment
) {
432 if (!m_yesToBackup
&& (!tryRecover
|| QMessageBox::No
== QMessageBox::warning(0, "Missing md5!",
433 QString("Check of file " + taskXmlFile
.fileName() + " failed (missing md5 sum)\nDo you want to load it anyway?" )
434 ,QMessageBox::Yes
| QMessageBox::No
)) ) {
435 qDebug() << "Skip file " << taskXmlFile
.fileName() << " (want a file starting with a comment representing MD5, got" << token
<< ")";
439 QString md5
= stream
.text().toString().trimmed();
440 qDebug() << "md5 = " << md5
;
443 data
= taskXmlFile
.readAll();
444 if ( md5
!= QCryptographicHash::hash(data
, QCryptographicHash::Md5
).toHex() ) {
445 if (!m_yesToBackup
&& (!tryRecover
|| QMessageBox::No
== QMessageBox::warning(0, "Checksum mismatch!",
446 QString("Checksum of file " + taskXmlFile
.fileName() + " mismatch (maybe it has been edited by hand).\nDo you want to load it anyway?" )
447 ,QMessageBox::Yes
| QMessageBox::No
)) ) {
448 qDebug() << "Skip file " << taskXmlFile
.fileName() << " (bad md5 sum)";
455 stream
.addData(data
);
457 if ( stream
.readNext() != QXmlStreamReader::StartDocument
) {
459 return recoverTaskFromBackup(taskXmlFile
, "Missing start document");
465 if (stream
.error() != QXmlStreamReader::NoError
) {
467 return recoverTaskFromBackup(taskXmlFile
,stream
.errorString());
476 void Sak::flushSettings()
478 QSettings
settings(QSettings::IniFormat
, QSettings::UserScope
, "ZanzaSoft", "SAK");
479 // QByteArray tasksArray;
480 // QDataStream stream(&tasksArray, QIODevice::ReadWrite);
481 // stream.setVersion(QDataStream::Qt_4_0);
482 // stream << m_tasks;
483 // settings.setValue("tasks", tasksArray);
484 settings
.setValue("Ping interval", durationSpinBox
->value());
485 settings
.setValue("Message", bodyEdit
->toPlainText());
486 QDir settingsDir
= QFileInfo(settings
.fileName()).absoluteDir();
487 QString fileName
= settingsDir
.filePath( "lady.png");
488 askingLady
->pixmap().save(fileName
);
489 settings
.setValue("Lady", "lady.png");
491 fileName
= settingsDir
.filePath( "viewbg.png");
492 QString fileNameTmp
= fileName
+".tmp";
493 viewBackground
->pixmap().save(fileNameTmp
, "png");
494 QFile::rename(fileName
, fileName
+ ".tmp.tmp");
495 QFile::rename(fileNameTmp
, fileName
);
496 QFile::remove( fileName
+ ".tmp.tmp");
497 settings
.setValue("View background", "viewbg.png");
508 if (!m_settings
) return;
509 m_backupper
->doCyclicBackup();
510 QSettings
settings(QSettings::IniFormat
, QSettings::UserScope
, "ZanzaSoft", "SAK");
513 QDir
saveDir(QFileInfo(settings
.fileName()).dir());
514 saveDir
.mkdir("SakTasks");
515 saveDir
.cd("SakTasks");
517 foreach(Task t
, m_tasks
) {
518 if (t
.title
.isEmpty()) continue;
519 QFile
xmlTaskSave(saveDir
.filePath(t
.title
+ ".xml"));
520 QByteArray taskArray
;
521 QXmlStreamWriter
stream(&taskArray
);
522 stream
.setAutoFormatting(true);
523 stream
.setAutoFormattingIndent(2);
524 stream
.writeStartDocument();
526 stream
.writeEndDocument();
527 xmlTaskSave
.open(QIODevice::ReadWrite
| QIODevice::Truncate
);
528 qDebug() << "Saving xml to file " << xmlTaskSave
.fileName();
530 hash
.append("<!-- ");
531 hash
.append( QCryptographicHash::hash(taskArray
, QCryptographicHash::Md5
).toHex() );
532 hash
.append(" -->\n");
533 xmlTaskSave
.write(hash
);
534 xmlTaskSave
.write(taskArray
);
537 // flush buffers to disk
542 // remove files not matching a task
543 QStringList nameFilters
;
544 nameFilters
<< "*.xml";
545 QStringList files
= saveDir
.entryList( nameFilters
, QDir::Files
);
546 foreach (QString taskXmlFileName
, files
) {
547 if (!m_tasks
.contains(QFileInfo(taskXmlFileName
).baseName())) {
548 qWarning()<< "Remove task " << QFileInfo(taskXmlFileName
).baseName() << " from disk";
549 QFile(saveDir
.filePath(taskXmlFileName
)).remove();
554 m_incremental
->clearAddedPieces();
557 //void Sak::saveAsDb()
559 // if (!m_settings) return;
560 // QString fileName = QFileDialog::getSaveFileName();
561 // QFile file(fileName);
564 // QSettings settingsQSettings::IniFormat, QSettings::UserScope, ("ZanzaSoft", "SAK");
565 // QFile file1(settings.fileName());
566 // if (!file1.copy(fileName)) {
567 // qWarning() << "Error copying " << settings.fileName() << " to " << fileName << file1.errorString();
571 void Sak::exportDbCsv()
573 if (!m_settings
) return;
574 QString fileName
= QFileDialog::getSaveFileName();
575 QFile
file(fileName
);
576 if (!file
.open(QIODevice::ReadWrite
|QIODevice::Truncate
)) {
577 QMessageBox::warning(0, "Error saving", QString("Error saving to file %1").arg(fileName
));
580 QTextStream
stream(&file
);
581 foreach(const Task
& t
, m_tasks
) {
582 QHash
< QString
, QList
< Task::Hit
> >::const_iterator itr
= t
.hits
.begin();
583 while(itr
!= t
.hits
.end()) {
584 QList
< Task::Hit
>::const_iterator hitr
= itr
.value().begin(), hend
= itr
.value().end();
585 while(hitr
!= hend
) {
586 stream
<< t
.title
<< ";" << itr
.key() << ";" << hitr
->timestamp
.toString(DATETIMEFORMAT
) << ";" << hitr
->duration
<< ";\n";
596 void Sak::logInGmail()
598 m_gmail
->forceLogin();
601 void Sak::saveToGmail()
603 if (!m_settings
) return;
605 QSettings
settings(QSettings::IniFormat
, QSettings::UserScope
, "ZanzaSoft", "SAK");
607 QDir
saveDir(QFileInfo(settings
.fileName()).dir());
608 saveDir
.mkdir("SakTasks");
609 saveDir
.cd("SakTasks");
610 QStringList nameFilters
;
611 nameFilters
<< "*.xml";
612 QStringList files
= saveDir
.entryList( nameFilters
, QDir::Files
);
613 QStringList filePaths
;
614 foreach (QString taskXmlFileName
, files
) {
615 filePaths
<< saveDir
.filePath(taskXmlFileName
);
617 m_gmail
->storeTaskFiles(filePaths
);
620 void Sak::importFromGmail()
622 QStringList filePaths
= m_gmail
->fetchLatestTasks();
625 void Sak::open(const QStringList
& _fileNames
)
627 QStringList fileNames
= _fileNames
.size()?_fileNames
:QFileDialog::getOpenFileNames(0, "Open a new task", QString(), "*.xml" );
628 foreach(QString fileName
, fileNames
) {
629 QFile
file(fileName
);
630 if (!file
.exists()) {
631 QMessageBox::warning(0, "Cannot find task", QString("Cannot find task file %1").arg(fileName
));
634 QSettings
settings(QSettings::IniFormat
, QSettings::UserScope
, "ZanzaSoft", "SAK");
635 QDir
saveDir(QFileInfo(settings
.fileName()).dir());
636 saveDir
.mkdir("SakTasks");
637 saveDir
.cd("SakTasks");
639 if ( QFile(saveDir
.filePath(QFileInfo(fileName
).completeBaseName())).exists() ) {
640 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?");
641 QPushButton
* overwriteButton
= mbox
.addButton("Overwrite", QMessageBox::YesRole
);
642 QPushButton
* mergeButton
= mbox
.addButton("Merge", QMessageBox::NoRole
);
643 QPushButton
* cancelButton
= mbox
.addButton("Cancel", QMessageBox::RejectRole
);
644 mbox
.setDefaultButton(cancelButton
);
646 QAbstractButton
* b
= mbox
.clickedButton();
647 if (b
== cancelButton
) { continue; }
649 m_backupper
->doCyclicBackup();
650 if (b
== mergeButton
) {
651 Task t
= loadTaskFromFile(file
.fileName());
652 QHash
< QString
, QList
< Task::Hit
> > ::const_iterator itr
= t
.hits
.begin(), end
= t
.hits
.end();
654 QString subtask
= itr
.key();
655 foreach(Task::Hit hit
, itr
.value())
656 m_incremental
->addPiece(t
.title
, subtask
, hit
.timestamp
, hit
.duration
);
659 interactiveMergeHits();
660 } else if (b
== overwriteButton
) {
661 file
.copy(saveDir
.filePath(QFileInfo(fileName
).completeBaseName()));
667 if (!fileNames
.isEmpty()) {
679 if (!m_settings
) return;
682 m_settings
->deleteLater();
683 m_view
->scene()->deleteLater();
684 m_view
->deleteLater();
686 delete m_incremental
;
688 m_previewing
= false;
689 m_changedHit
= false;
696 killTimer(m_autoSaveTimer
); m_autoSaveTimer
=-1;
701 void Sak::layoutSubTasks( const QMap
<int, SakSubWidget
*> sortedWidgets
, int currentRank
) {
702 QMap
<int, SakSubWidget
*>::const_iterator itr
= sortedWidgets
.begin(), end
= sortedWidgets
.end();
703 QRect r
= m_desktopRect
;
704 for(int i
=0; itr
!= end
; i
++, itr
++) {
705 int h
= (*itr
)->size().height();
706 int w
= (*itr
)->size().width();
707 (*itr
)->setPos(QPointF((r
.width() - w
)/2, (r
.height()-h
)/2 + (i
- currentRank
- 1) * (h
+2)));
712 int Sak::taskCounter
= 0;
714 bool Sak::eventFilter(QObject
* obj
, QEvent
* e
)
716 // if (obj == m_view) {
717 // qDebug() << "event : " << e->type();
719 if (obj
== tasksTree
) {
720 return taskTreeEventFilter(e
);
721 } else if (obj
== hitsList
|| obj
== summaryList
) {
722 return hitsListEventFilter(e
);
723 } else if (obj
== summaryView
) {
724 if (e
->type() == QEvent::Resize
|| e
->type() == QEvent::Show
) {
725 summaryView
->fitInView(summaryView
->sceneRect(), Qt::KeepAspectRatio
);
728 } else if (obj
== m_settings
&& e
->type() == QEvent::Close
) {
735 if (trayIcon
->isVisible()) {
740 } else if (obj
== m_view
&& e
->type() == QEvent::Wheel
) {
741 QWheelEvent
* we
= dynamic_cast<QWheelEvent
*>(e
);
743 scrollSubTasks(we
->delta() / 120);
744 } else scrollTasks(we
->delta() / 120);
745 } else if (obj
== m_view
&& e
->type() == QEvent::KeyPress
) {
746 QKeyEvent
* ke
= dynamic_cast<QKeyEvent
*>(e
);
747 if ((ke
->modifiers() & Qt::AltModifier
) && (ke
->modifiers() & Qt::ControlModifier
) ) {
750 } else if ( ((ke
->modifiers() & Qt::ControlModifier
) && (ke
->key() == Qt::Key_Backspace
) )
751 || ((ke
->modifiers() & Qt::ControlModifier
) && (ke
->key() == Qt::Key_Left
) )) {
756 } else if (m_subtaskView
&& ke
->key() == Qt::Key_Up
) {
759 } else if (m_subtaskView
&& ke
->key() == Qt::Key_Down
) {
762 } else if (!m_subtaskView
&& ke
->key() == Qt::Key_Left
) {
765 } else if (!m_subtaskView
&& ke
->key() == Qt::Key_Right
) {
768 } else if (!m_subtaskView
&& ke
->key() == Qt::Key_Escape
) {
771 } else { // forward events to current widget
772 if (!m_subtaskView
) {
773 if (m_widgetsIterator
== m_widgets
.end()) return false;
774 SakWidget
* currentShowing
= m_widgetsIterator
.value();
775 currentShowing
->keyPressEvent(ke
);
778 // autoscroll on text completion!!!
779 if (m_subwidgetsIterator
== m_subwidgets
.end()) return false;
780 SakSubWidget
* currentShowing
= m_subwidgetsIterator
.value();
781 currentShowing
->keyPressEvent(ke
);
783 if (m_subWidgetRank
!= 0 && m_subtaskCompleter
) {
784 QString
completion(m_subtaskCompleter
->completionPrefix());
785 if (ke
->text().size() == 1) {
786 if (ke
->key() == Qt::Key_Backslash
|| ke
->key() == Qt::Key_Backspace
)
788 else completion
+= ke
->text();
789 m_subtaskCompleter
->setCompletionPrefix(completion
);
792 QStringList
list( ((QStringListModel
*)m_subtaskCompleter
->model())->stringList() );
793 int newRank
= 1 + ((QStringListModel
*)m_subtaskCompleter
->model())->stringList().indexOf(m_subtaskCompleter
->currentIndex().row() >= 0 && completion
.size() ? m_subtaskCompleter
->currentCompletion() : completion
);
795 if (m_subWidgetRank
!= newRank
) {
796 scrollSubTasks(newRank
- m_subWidgetRank
);
798 QLineEdit
* editor
= dynamic_cast<QLineEdit
*>((*m_subwidgets
.begin())->widget());
800 editor
->setText(completion
);
805 } else if (m_subtaskCompleter
) {
806 QLineEdit
* editor
= dynamic_cast<QLineEdit
*>((*m_subwidgets
.begin())->widget());
808 m_subtaskCompleter
->setCompletionPrefix(editor
->text());
815 } else if (obj
== m_view
&& e
->type() == QEvent::Show
) {
817 QTimer::singleShot(100, this, SLOT(grabKeyboard()));
818 }else if (obj
== m_view
&& e
->type() == QEvent::Close
) {
819 if (trayIcon
->isVisible()) {
822 } else if (obj
&& obj
== trayIcon
&& e
->type() == QEvent::ToolTip
) {
823 QDateTime last
= m_incremental
->lastTimeStamp
;
824 int seconds
= QDateTime::currentDateTime().secsTo(m_nextTimerEvent
);
825 int hours
= seconds
/ 3600;
826 int minutes
= (seconds
/ 60) % 60;
828 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>")))));
834 //END basic >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
837 //BEGIN Tasks >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
838 void Sak::addDefaultTask()
840 QString tentativeName
;
842 tentativeName
= QString("Task %1").arg(taskCounter
++);
843 } while(m_editedTasks
.contains(tentativeName
));
845 Task
& t
= m_editedTasks
[tentativeName
];
846 t
.title
= tentativeName
;
847 QTreeWidgetItem
* item
= new QTreeWidgetItem(QStringList(tentativeName
));
848 item
->setData(0,Qt::UserRole
, QVariant(QMetaType::VoidStar
, &t
));
849 tasksTree
->addTopLevelItem(item
);
853 void Sak::populateTasks()
857 QHash
<QString
, Task
>::iterator itr
= m_editedTasks
.begin(), end
=m_editedTasks
.end();
858 for(; itr
!=end
; itr
++) {
859 Task
& t(itr
.value());
860 t
.checkConsistency();
862 if (t
.title
.isEmpty() || t
.title
== "<away>") continue; // skip away task
863 QTreeWidgetItem
* item
= new QTreeWidgetItem(QStringList(t
.title
));
864 item
->setData(0, Qt::UserRole
, QVariant(QMetaType::VoidStar
, &t
));
866 icon
.addPixmap(t
.icon
);
867 item
->setSizeHint(0, QSize(32,32));
868 item
->setIcon(0, icon
);
869 for(int i
=0; i
<3; i
++) {
870 item
->setForeground(i
,t
.fgColor
);
871 item
->setBackground(i
,t
.bgColor
);
873 //item->setCheckState(1, t.active ? Qt::Checked : Qt::Unchecked);
874 //item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
875 item
->setIcon(1,QIcon(t
.active
? ":/images/active.png" : ":/images/inactive.png"));
876 item
->setText(2,QString("%1 hours worked till now (overestimated %2)").arg(t
.totHours
, 4, 'f', 2, ' ').arg(t
.totOverestimation
));
877 foreach(Task::SubTask st
, t
.subTasks
) {
878 if (!st
.title
.isEmpty()) {
879 QTreeWidgetItem
* sitem
= new QTreeWidgetItem(item
, QStringList(st
.title
));
880 item
->setData(0, Qt::UserRole
, QVariant(QMetaType::VoidStar
, &st
));
881 sitem
->setSizeHint(0, QSize(32,32));
882 QColor fgColor
= st
.fgColor
.isValid() ? st
.fgColor
: t
.fgColor
;
883 QColor bgColor
= st
.bgColor
.isValid() ? st
.bgColor
: t
.bgColor
;
884 for(int i
=0; i
<3; i
++) {
885 sitem
->setForeground(i
,fgColor
);
886 sitem
->setBackground(i
,bgColor
);
888 sitem
->setIcon(1,QIcon(st
.active
? ":/images/active.png" : ":/images/inactive.png"));
889 sitem
->setText(2,QString("%1 hours worked till now").arg(st
.totHours
,4,'f',2,' '));
892 tasksTree
->addTopLevelItem(item
);
896 void Sak::saveTaskChanges()
900 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
) {
901 m_tasks
= m_editedTasks
;
902 } else m_editedTasks
= m_tasks
; //. undo changes
904 selectedStartDate(QDate());
909 void Sak::selectColor() {
910 if (tasksTree
->selectedItems().isEmpty()) return;
912 if (sender() == fgColorButton
) {
913 QColor c
= QColorDialog::getColor(fgColorButton
->palette().color(QPalette::ButtonText
));
914 if (!c
.isValid()) return;
915 QPalette p
= fgColorButton
->palette();
916 p
.setColor(QPalette::ButtonText
, c
);
917 fgColorButton
->setPalette(p
);
918 bgColorButton
->setPalette(p
);
919 } else if (sender() == bgColorButton
) {
920 QColor c
= QColorDialog::getColor(bgColorButton
->palette().color(QPalette::Button
));
921 if (!c
.isValid()) return;
922 QPalette p
= bgColorButton
->palette();
923 p
.setColor(QPalette::Button
, c
);
924 fgColorButton
->setPalette(p
);
925 bgColorButton
->setPalette(p
);
930 bool Sak::taskTreeEventFilter(QEvent
* e
)
932 if (e
->type() == QEvent::ContextMenu
) {
933 QContextMenuEvent
* me
= dynamic_cast<QContextMenuEvent
*>(e
);
934 if (!me
) return false;
935 m_addTaskMenu
->popup(me
->globalPos());
937 } else if (e
->type() == QEvent::KeyRelease
) {
938 QKeyEvent
* ke
= dynamic_cast<QKeyEvent
*>(e
);
939 if (!ke
) return false;
940 if ( (ke
->key() != Qt::Key_Delete
&& ke
->key() != Qt::Key_Backspace
) ) return false;
941 if (currentSubtask
!="") {
942 QMessageBox
whatToDo(QMessageBox::Warning
, "Deleting subtask", "Deleting subtask " + currentSubtask
+ " of task " + currentTask
);
943 QPushButton
* moveHitsToParentButton
= whatToDo
.addButton("Move hits to task " + currentTask
, QMessageBox::AcceptRole
);
944 QPushButton
* removeHitsButton
= whatToDo
.addButton("Remove hits", QMessageBox::AcceptRole
);
945 QPushButton
* cancelButton
= whatToDo
.addButton("Cancel", QMessageBox::RejectRole
);
946 whatToDo
.setDefaultButton(cancelButton
);
948 if ( whatToDo
.clickedButton() == cancelButton
) return true;
949 if (m_editedTasks
.find(currentTask
) == m_editedTasks
.end()) return true;
951 Task
& t(m_editedTasks
[currentTask
]);
952 t
.subTasks
.take(currentSubtask
);
953 if (whatToDo
.clickedButton() == removeHitsButton
) {
954 t
.hits
.take(currentSubtask
);
955 } else if (whatToDo
.clickedButton() == moveHitsToParentButton
) {
956 QList
<Task::Hit
> sorter(t
.hits
.take(""));
957 sorter
<< t
.hits
.take(currentSubtask
);
958 qStableSort(sorter
.begin(), sorter
.end());
962 // remove file from disk
964 m_editedTasks
.remove(currentTask
);
968 selectedStartDate(QDate());
970 } else if (e
->type() == QEvent::Hide
) {
977 void Sak::commitCurrentTask()
980 if (currentSubtask
.isEmpty()) {
981 QString currentTitle
= taskTitleEditor
->text();
982 if (!currentTitle
.isEmpty()) {
983 if (currentTitle
!= currentTask
) {
984 if (m_editedTasks
.contains(currentTitle
)) {
985 QMessageBox::warning(0, "Conflict in task names", "Conflict in task names: current task " + currentTask
+ ", edited title " + currentTitle
);
986 taskTitleEditor
->setText(currentTask
);
988 } else if (m_editedTasks
.contains(currentTask
)) {
989 m_editedTasks
[currentTitle
] = m_editedTasks
.take(currentTask
);
990 m_editedTasks
[currentTitle
].title
= currentTitle
;
994 Task
& t
= m_editedTasks
[currentTitle
];
995 t
.description
= taskTextEditor
->toPlainText();
996 t
.tags
= taskTagsEditor
->text().split(QRegExp("[,:]"));
997 t
.url
= taskUrlEditor
->text();
998 t
.bgColor
= bgColorButton
->palette().color(QPalette::Button
);
999 t
.fgColor
= fgColorButton
->palette().color(QPalette::ButtonText
);
1000 t
.icon
= taskPixmapViewer
->pixmap();
1001 QList
<QTreeWidgetItem
*> items
= tasksTree
->findItems(currentTask
,Qt::MatchExactly
,0);
1002 foreach(QTreeWidgetItem
* ii
, items
) {
1003 ii
->setText(0, currentTitle
);
1004 ii
->setIcon(0, taskPixmapViewer
->pixmap());
1005 for (int i
=0; i
<3; i
++) {
1006 ii
->setForeground(i
, QColor(t
.fgColor
));
1007 ii
->setBackground(i
, QColor(t
.bgColor
));
1010 if (dueEditor
->date() != dueEditor
->minimumDate())
1011 t
.dueDate
= dueEditor
->date();
1012 t
.estimatedHours
= estimatedHoursEditor
->value();
1013 currentTask
=currentTitle
;
1015 if (tasksTree
->selectedItems().size() != 1) return;
1016 QTreeWidgetItem
* item
= tasksTree
->selectedItems()[0];
1017 item
->setText(0, taskTitleEditor
->text());
1019 icon
.addPixmap(t
.icon
);
1020 item
->setSizeHint(0, QSize(32,32));
1021 item
->setIcon(0, icon
);
1022 for(int i
=0; i
<3; i
++) {
1023 item
->setForeground(i
,t
.fgColor
);
1024 item
->setBackground(i
,t
.bgColor
);
1028 } else { // subtask edited
1029 if (!m_editedTasks
.contains(currentTask
)) return;
1030 Task
& t(m_editedTasks
[currentTask
]);
1031 QString currentTitle
= taskTitleEditor
->text();
1033 if (!currentTitle
.isEmpty()) {
1034 if (currentTitle
!= currentSubtask
) {
1035 if (t
.subTasks
.contains(currentTitle
)) {
1036 QMessageBox::warning(0, "Conflict in subtask names", "Conflict in subtask names");
1037 taskTitleEditor
->setText(currentSubtask
);
1039 } else if (t
.subTasks
.contains(currentSubtask
)) {
1040 t
.subTasks
[currentTitle
] = t
.subTasks
.take(currentSubtask
);
1041 t
.subTasks
[currentTitle
].title
= currentTitle
;
1042 t
.hits
[currentTitle
] = t
.hits
.take(currentSubtask
);
1046 Task::SubTask
& st
= t
.subTasks
[currentTitle
];
1047 st
.description
= taskTextEditor
->toPlainText();
1048 st
.bgColor
= bgColorButton
->palette().color(QPalette::Button
);
1049 st
.fgColor
= fgColorButton
->palette().color(QPalette::ButtonText
);
1050 QList
<QTreeWidgetItem
*> items
= tasksTree
->findItems(currentTask
,Qt::MatchExactly
,0);
1051 foreach(QTreeWidgetItem
* jj
, items
) {
1052 for(int i
=0; i
<jj
->childCount(); i
++) {
1053 QTreeWidgetItem
* ii
= jj
->child(i
);
1054 if (ii
->text(0) != currentSubtask
) continue;
1055 ii
->setText(0, currentTitle
);
1056 for (int i
=0; i
<3; i
++) {
1057 ii
->setForeground(i
, QColor(st
.fgColor
));
1058 ii
->setBackground(i
, QColor(st
.bgColor
));
1062 currentSubtask
= currentTitle
;
1064 if (tasksTree
->selectedItems().size() != 1) return;
1065 QTreeWidgetItem
* item
= tasksTree
->selectedItems()[0];
1066 item
->setText(0, taskTitleEditor
->text());
1068 icon
.addPixmap(t
.icon
);
1069 item
->setSizeHint(0, QSize(32,32));
1070 item
->setIcon(0, icon
);
1071 for(int i
=0; i
<3; i
++) {
1072 item
->setForeground(i
,st
.fgColor
);
1073 item
->setBackground(i
,st
.bgColor
);
1078 void Sak::selectedTask()
1080 if (tasksTree
->selectedItems().isEmpty()) {
1081 taskPixmapViewer
->setEnabled(false);
1082 taskPixmapViewer
->setPixmap(QPixmap());
1083 taskTextEditor
->setEnabled(false);
1084 taskTitleEditor
->setEnabled(false);
1085 taskUrlEditor
->setEnabled(false);
1086 bgColorButton
->setEnabled(false);
1087 fgColorButton
->setEnabled(false);
1088 dueEditor
->setEnabled(false);
1089 estimatedHoursEditor
->setEnabled(false);
1094 // Disable notifications
1095 taskPixmapViewer
->blockSignals(true);
1096 taskTextEditor
->blockSignals(true);
1097 taskTitleEditor
->blockSignals(true);
1098 taskTagsEditor
->blockSignals(true);
1099 taskUrlEditor
->blockSignals(true);
1100 bgColorButton
->blockSignals(true);
1101 fgColorButton
->blockSignals(true);
1102 dueEditor
->blockSignals(true);
1103 estimatedHoursEditor
->blockSignals(true);
1106 QTreeWidgetItem
* selectedItem
= tasksTree
->selectedItems().first();
1107 QTreeWidgetItem
* parentItem
= selectedItem
->parent();
1108 QString tt
= selectedItem
->text(0);
1111 taskPixmapViewer
->setEnabled(true);
1112 taskTextEditor
->setEnabled(true);
1113 taskTagsEditor
->setEnabled(true);
1114 taskUrlEditor
->setEnabled(true);
1115 dueEditor
->setEnabled(true);
1116 estimatedHoursEditor
->setEnabled(true);
1117 bgColorButton
->setEnabled(true);
1118 fgColorButton
->setEnabled(true);
1119 taskPixmapViewer
->setEnabled(true);
1121 taskPixmapViewer
->setEnabled(false);
1122 taskTextEditor
->setEnabled(false);
1123 taskTagsEditor
->setEnabled(false);
1124 taskUrlEditor
->setEnabled(false);
1125 taskPixmapViewer
->setPixmap(QPixmap());
1126 dueEditor
->setEnabled(false);
1127 estimatedHoursEditor
->setEnabled(false);
1128 bgColorButton
->setEnabled(false);
1129 fgColorButton
->setEnabled(false);
1130 taskPixmapViewer
->setEnabled(false);
1132 taskTitleEditor
->setEnabled(true);
1135 if (!parentItem
) { // editing a task
1136 if (!m_editedTasks
.contains(tt
)) return;
1137 const Task
& t
= m_editedTasks
[tt
];
1138 taskPixmapViewer
->setPixmap(t
.icon
);
1139 taskTextEditor
->setPlainText(t
.description
);
1140 taskUrlEditor
->setText(t
.url
);
1141 taskTitleEditor
->setText(t
.title
);
1143 p
.setColor(QPalette::Button
, t
.bgColor
);
1144 p
.setColor(QPalette::ButtonText
, t
.fgColor
);
1145 bgColorButton
->setPalette(p
);
1146 fgColorButton
->setPalette(p
);
1147 estimatedHoursEditor
->setValue(t
.estimatedHours
);
1148 dueEditor
->setDate(t
.dueDate
.isValid() ? t
.dueDate
: dueEditor
->minimumDate());
1150 currentTask
= t
.title
;
1151 currentSubtask
= "";
1152 } else { // editing a subtask
1153 if (!m_editedTasks
.contains(parentItem
->text(0))) return;
1154 const Task
& t
= m_editedTasks
[parentItem
->text(0)];
1155 if (!t
.subTasks
.contains(tt
)) return;
1156 const Task::SubTask
& st
= t
.subTasks
[tt
];
1158 taskTextEditor
->setPlainText(st
.description
);
1159 taskTitleEditor
->setText(st
.title
);
1160 taskUrlEditor
->setText(t
.url
);
1162 p
.setColor(QPalette::Button
, st
.bgColor
.isValid() ? st
.bgColor
: t
.bgColor
);
1163 p
.setColor(QPalette::ButtonText
, st
.fgColor
.isValid() ? st
.fgColor
: t
.fgColor
);
1164 bgColorButton
->setPalette(p
);
1165 fgColorButton
->setPalette(p
);
1167 currentTask
= t
.title
;
1168 currentSubtask
= st
.title
;
1171 // Re-enable notifications
1172 taskPixmapViewer
->blockSignals(false);
1173 taskTextEditor
->blockSignals(false);
1174 taskTitleEditor
->blockSignals(false);
1175 taskTagsEditor
->blockSignals(false);
1176 taskUrlEditor
->blockSignals(false);
1177 bgColorButton
->blockSignals(false);
1178 fgColorButton
->blockSignals(false);
1179 dueEditor
->blockSignals(false);
1180 estimatedHoursEditor
->blockSignals(false);
1183 void Sak::doubleClickedTask(QTreeWidgetItem
* i
, int column
)
1187 if (i
->parent() == 0) {
1188 QHash
<QString
, Task
>::iterator itr
= m_editedTasks
.find(i
->text(0));
1189 Q_ASSERT(itr
!= m_editedTasks
.end());
1190 bool& active ( itr
.value().active
);
1192 i
->setIcon(column
, active
? QIcon(":/images/active.png") : QIcon(":/images/inactive.png"));
1194 QHash
<QString
, Task
>::iterator itr
= m_editedTasks
.find(i
->parent()->text(0));
1195 Q_ASSERT(itr
!= m_editedTasks
.end());
1196 bool& active ( itr
.value().subTasks
[i
->text(0)].active
);
1198 i
->setIcon(column
, active
? QIcon(":/images/active.png") : QIcon(":/images/inactive.png"));
1200 ((QTreeWidget
*)sender())->update();
1206 void Sak::timerEvent(QTimerEvent
* e
)
1208 if (e
->timerId() == m_timerId
) {
1209 if (!m_view
->isVisible() && !m_settings
->isVisible() && m_tasks
.count() > 0) {
1212 killTimer(m_timerId
); m_timerId
=-1;
1213 killTimer(m_timeoutPopup
); m_timeoutPopup
=-1;
1214 int msecs
= (int)(qMax( 30000.0, Task::hours(m_currentInterval
)*3600.0*1000.0/10.0));
1216 m_timeoutPopup
= startTimer(msecs
);
1219 m_nextTimerEvent
= QDateTime::currentDateTime().addMSecs(msecs
);
1221 if (m_settings
&& m_settings
->isVisible() && !m_settings
->isActiveWindow()) {
1222 trayIcon
->showMessage("Delayed check point", "Delayed check point due to open settings. Close the setting window!", QSystemTrayIcon::Warning
, -1);
1223 m_settings
->close();
1225 qDebug() << "SAK: wait 5 seconds";
1226 killTimer(m_timerId
); m_timerId
=-1;
1227 m_timerId
= startTimer(5000);
1228 m_nextTimerEvent
= QDateTime::currentDateTime().addMSecs(5000);
1230 } else if (e
->timerId() == m_timeoutPopup
) {
1231 // ensure the timer is resetted
1232 killTimer(m_timeoutPopup
); m_timeoutPopup
=-1;
1233 killTimer(m_timerId
); m_timerId
= -1;
1235 if (!m_subtaskView
) {
1236 // if not selecting subtasks clear everything and signal away
1237 workingOnTask("<away>","");
1238 trayIcon
->showMessage("New away events", "You have missed a check point. Fix it in the detailed hit list.", QSystemTrayIcon::Information
, 999999);
1241 } else { // wait 5 seconds
1242 m_nextTimerEvent
= QDateTime::currentDateTime().addMSecs(5000);
1243 m_timeoutPopup
= startTimer(5000);
1245 } else if (e
->timerId() == m_autoSaveTimer
) {
1247 } else if (e
->timerId() == m_getFocusTimer
) {
1250 qDebug() << "unknown timer event";
1254 void Sak::clearView()
1256 m_fakeClicking
= false;
1257 killTimer(m_timeoutPopup
); m_timeoutPopup
=-1;
1258 m_subtaskView
=false;
1260 delete m_subtaskCompleter
; m_subtaskCompleter
= 0;
1263 m_view
->releaseKeyboard();
1264 killTimer(m_getFocusTimer
); m_getFocusTimer
=-1;
1265 #if defined(Q_WS_X11)
1266 // restore focus to previous application
1268 X11::XSetInputFocus((X11::Display
*)QX11Info::display(), X11::CurrentFocusWindow
, X11::CurrentRevertToReturn
, CurrentTime
);
1269 X11::XFlush((X11::Display
*)QX11Info::display());
1272 // restart normal timer
1273 if (!m_previewing
) {
1274 killTimer(m_timerId
); m_timerId
=-1;
1275 int msecs
= (int)(Task::hours(m_currentInterval
)*3600.0*1000.0);
1276 m_timerId
= startTimer(msecs
);
1277 m_nextTimerEvent
= QDateTime::currentDateTime().addMSecs(msecs
);
1280 if (!m_view
) return;
1281 QGraphicsScene
* s
= m_view
->scene();
1282 QList
<QGraphicsItem
*> items
= m_view
->items();
1284 m_widgetsIterator
= m_widgets
.end();
1287 m_view
->setScene(new QGraphicsScene
);
1288 m_view
->scene()->setSceneRect(m_desktopRect
);
1289 m_previewing
= false;
1292 void Sak::workingOnTask(const QString
& taskName
, const QString
& subTask
)
1295 if (!m_previewing
) {
1297 qDebug() << "Working on " << taskName
;
1298 if (m_tasks
.contains(taskName
)) {
1299 Task
& t
= m_tasks
[taskName
];
1301 if (t
.title
!= "<away>" && !t
.title
.isEmpty()) {
1303 int historyIndex
= m_taskSelectionHistory
.indexOf(t
.title
);
1304 if (historyIndex
!= -1) {
1305 m_taskSelectionHistory
.takeAt(historyIndex
);
1307 m_taskSelectionHistory
.push_back(t
.title
);
1309 QList
<QString
> & subtaskSelectionHistory(m_subtaskSelectionHistory
[t
.title
]);
1310 historyIndex
= subtaskSelectionHistory
.indexOf(subTask
);
1311 if (historyIndex
!= -1) {
1312 subtaskSelectionHistory
.takeAt(historyIndex
);
1314 subtaskSelectionHistory
.push_back(subTask
);
1318 QDateTime now
= QDateTime::currentDateTime();
1319 QHash
<QString
, Task
>::iterator itr
= m_tasks
.begin();
1321 QHash
<QString
, QList
< Task::Hit
> >::iterator hitr
= t
.hits
.begin();
1322 // merge last two hits of every subtask if they are close enough
1323 while(hitr
!= t
.hits
.end()) {
1324 QList
<Task::Hit
>& otHits( *hitr
);
1325 if (otHits
.count() > 1) {
1326 Task::Hit
& lastHit(otHits
[otHits
.size() - 1]);
1327 Task::Hit
& beforeLastHit(otHits
[otHits
.size() - 2]);
1329 // the list might be not sorted (eg. after startup merge)
1330 // so give up merge of last two (unsorted) hits
1331 if (lastHit
.timestamp
< beforeLastHit
.timestamp
) { hitr
++; continue; }
1333 qDebug() << "Task: " << t
.title
<< " : " << hitr
.key();
1334 qDebug() << " Last hit: " << lastHit
;
1335 qDebug() << " Before last hit: " << beforeLastHit
;
1337 int diff
= (lastHit
.timestamp
.toTime_t() - 30*lastHit
.duration
) - (beforeLastHit
.timestamp
.toTime_t() + 30*beforeLastHit
.duration
);
1338 qDebug() << " diff: " << diff
;
1339 if (diff
< 120 && diff
> -60) { // at most 2 minutes apart and 1 overlapped
1340 beforeLastHit
.timestamp
= beforeLastHit
.timestamp
.addSecs(-30*beforeLastHit
.duration
);
1341 int secsToEnd
= beforeLastHit
.timestamp
.secsTo(lastHit
.timestamp
.addSecs(30*m_currentInterval
));
1342 if (secsToEnd
> 24 * 3600 * 3600) {
1343 qWarning() << "TRAPPED ERROR IN SECS COUNT!!!!!!!!";
1344 qWarning() << "BEFORE LAST HIST WAS " << beforeLastHit
.timestamp
<< beforeLastHit
.duration
;
1345 qWarning() << "LAST HIT WAS " << lastHit
.timestamp
<< lastHit
.duration
;
1346 assert(secsToEnd
< 24 * 3600 * 3600);
1348 beforeLastHit
.timestamp
= beforeLastHit
.timestamp
.addSecs( secsToEnd
/2.0 );
1349 beforeLastHit
.duration
= (int)( qRound( secsToEnd
/ 60.0 ) );
1350 // remove the current very last hit
1357 // add hit to hit list
1358 // NOTE: we do not try to merge the very last hit with previous ones because we want let
1359 // the user being able to easily recover from a selection error
1360 if (workingOnDeclared
) {
1361 t
.hits
[subTask
] << Task::Hit(now
.addSecs(3600.0*workingOnDeclared
/2), 60*workingOnDeclared
);
1362 m_incremental
->writePiece(t
.title
, subTask
, now
.addSecs(3600.0*workingOnDeclared
/2), 60*workingOnDeclared
);
1363 stopAction
->trigger();
1364 workingOnTimer
->setSingleShot(true);
1365 workingOnTimer
->setInterval(3600*1000*workingOnDeclared
);
1366 workingOnTimer
->start();
1367 workingOnDeclared
= 0;
1369 t
.hits
[subTask
] << Task::Hit(now
, m_currentInterval
);
1370 m_incremental
->writePiece(t
.title
, subTask
, now
, m_currentInterval
);
1372 t
.checkConsistency();
1373 QList
<QTreeWidgetItem
*> items
= tasksTree
->findItems (t
.title
, Qt::MatchExactly
, 0);
1374 if (!items
.isEmpty())
1375 items
.first()->setText(1, QString("%1 hours worked till now (overestimated %2)").arg(t
.totHours
).arg(t
.totOverestimation
));
1377 // update subtask if new added!
1380 // update statistics !!!!
1381 m_editedTasks
= m_tasks
;
1382 QMetaObject::invokeMethod(this, "selectedStartDate", Qt::QueuedConnection
, Q_ARG(QDate
, cal1
->selectedDate()));
1389 // attractor = 't' (top), 'b', 'l', 'r'
1390 void layoutInSquare( QList
<SakWidget
*> sortedWidgets
, QRect rect
, char attractor
)
1392 int w
= rect
.width();
1393 if (rect
.width() < 64) return;
1394 int maxw
= qMin(350, w
/2);
1395 QSize
size(maxw
, maxw
);
1396 if (sortedWidgets
.count() == 0) {
1398 } else if (sortedWidgets
.count() == 1) {
1399 sortedWidgets
[0]->setGeometry(rect
);
1400 } else if (sortedWidgets
.count() == 2) {
1402 if (attractor
== 'C') {
1403 off1
= QPoint(0,w
/4);
1404 off2
= QPoint(w
/2,w
/4);
1405 } else if (attractor
== 'T') {
1407 off2
= QPoint(w
/2,0);
1408 } else if (attractor
== 'B') {
1409 off1
= QPoint(0,w
/2);
1410 off2
= QPoint(w
/2,w
/2);
1411 } else if (attractor
== 'R') {
1412 off1
= QPoint(w
/2,0);
1413 off2
= QPoint(w
/2,w
/2);
1414 } else if (attractor
== 'L') {
1416 off2
= QPoint(0,w
/2);
1418 sortedWidgets
[0]->setGeometry(QRect(rect
.topLeft() + off1
, size
));
1419 sortedWidgets
[1]->setGeometry(QRect(rect
.topLeft() + off2
, size
));
1420 } else if (sortedWidgets
.count() == 3) {
1421 QPoint off1
, off2
, off3
;
1422 if (attractor
== 'T' || attractor
== 'C') {
1424 off2
= QPoint(w
/2,0);
1425 off3
= QPoint(w
/4,w
/2);
1426 } else if (attractor
== 'B') {
1427 off1
= QPoint(0,w
/2);
1428 off2
= QPoint(w
/2,w
/2);
1429 off3
= QPoint(w
/4,0);
1430 } else if (attractor
== 'R') {
1431 off1
= QPoint(w
/2,0);
1432 off2
= QPoint(w
/2,w
/2);
1433 off3
= QPoint(0,w
/4);
1434 } else if (attractor
== 'L') {
1436 off2
= QPoint(0,w
/2);
1437 off3
= QPoint(w
/2,w
/4);
1439 sortedWidgets
[0]->setGeometry(QRect(rect
.topLeft() + off1
, size
));
1440 sortedWidgets
[1]->setGeometry(QRect(rect
.topLeft() + off2
, size
));
1441 sortedWidgets
[2]->setGeometry(QRect(rect
.topLeft() + off3
, size
));
1442 } else if (sortedWidgets
.count() == 4) {
1446 QPoint
off4(w
/2,w
/2);
1447 sortedWidgets
[0]->setGeometry(QRect(rect
.topLeft() + off1
, size
));
1448 sortedWidgets
[1]->setGeometry(QRect(rect
.topLeft() + off2
, size
));
1449 sortedWidgets
[2]->setGeometry(QRect(rect
.topLeft() + off3
, size
));
1450 sortedWidgets
[3]->setGeometry(QRect(rect
.topLeft() + off4
, size
));
1452 Q_ASSERT(sortedWidgets
.count() <= 4);
1456 void layoutInRect( QList
<SakWidget
*> sortedWidgets
, QRect rect
, char attractor
)
1458 if (sortedWidgets
.count() == 0) return;
1459 int h
= rect
.height();
1460 int w
= rect
.width();
1461 int maxh
= qMin(350, h
);
1462 int maxw
= qMin(350, w
);
1463 if (sortedWidgets
.count() == 1) {
1465 sortedWidgets
[0]->setGeometry(QRect(rect
.topLeft() + QPoint((w
-maxh
)/2,(h
-maxh
)/2), QSize(maxh
,maxh
)));
1467 sortedWidgets
[0]->setGeometry(QRect(rect
.topLeft() + QPoint((w
-maxw
)/2,(h
-maxw
)/2), QSize(maxw
,maxw
)));
1470 } else if (sortedWidgets
.count() == 2) {
1472 sortedWidgets
[0]->setGeometry(QRect(rect
.topLeft() + QPoint(w
/2-maxh
,(h
-maxh
)/2), QSize(maxh
,maxh
)));
1473 sortedWidgets
[1]->setGeometry(QRect(rect
.topLeft() + QPoint(w
/2,(h
-maxh
)/2), QSize(maxh
,maxh
)));
1475 sortedWidgets
[0]->setGeometry(QRect(rect
.topLeft() + QPoint((h
-maxw
)/2,w
-maxw
), QSize(w
,w
)));
1476 sortedWidgets
[1]->setGeometry(QRect(rect
.topLeft() + QPoint((h
-maxw
)/2,w
/2), QSize(maxw
,maxw
)));
1480 if (h
< 64 || w
< 64) return;
1481 QList
<SakWidget
*> leftList
, rightList
;
1482 for (int i
=4; i
<sortedWidgets
.count(); i
++) {
1484 rightList
<< sortedWidgets
[i
];
1486 leftList
<< sortedWidgets
[i
];
1489 QRect
square(rect
.topLeft() + QPoint(h
/2, 0), QSize(h
,h
));
1490 layoutInSquare(sortedWidgets
.mid(0,4), square
, attractor
);
1492 QRect
leftRect(rect
.topLeft(), QSize(h
/2, h
));
1493 layoutInRect(leftList
, leftRect
, 'R');
1494 QRect
rightRect(rect
.topLeft() + QPoint((int)(0.75*w
),0), QSize(h
/2, h
));
1495 layoutInRect(rightList
, rightRect
, 'L');
1497 QRect
square(rect
.topLeft() + QPoint(0,w
/2), QSize(w
,w
));
1498 layoutInSquare(sortedWidgets
.mid(0,4), square
, attractor
);
1500 QRect
leftRect(rect
.topLeft(), QSize(w
, w
/2));
1501 layoutInRect(leftList
, leftRect
, 'B');
1502 QRect
rightRect(rect
.topLeft() + QPoint(0,(int)(0.75*h
)), QSize(w
, w
/2));
1503 layoutInRect(rightList
, rightRect
, 'T');
1507 QRect
Sak::Layouting( const QList
<SakWidget
*>& sortedWidgets
)
1509 QRect r
= m_desktopRect
;
1510 int height
= (int)(0.75 * r
.height());
1511 int width
= r
.width();
1513 int firstW
= width
/ 2 < height
? width
: height
* 2;
1514 int firstH
= firstW
/ 2;
1515 QRect
firstRect (r
.x() + (width
- firstW
) / 2, r
.y() + (height
- firstH
) / 2 + (int)(r
.height() * 0.25), firstW
, firstH
);
1517 layoutInRect(sortedWidgets
, firstRect
, 'C');
1518 return QRect(QPoint(r
.x(), r
.y()), QSize(r
.width(), (int)(0.25 * r
.height())));
1521 void Sak::grabKeyboard()
1523 #if defined(Q_WS_X11)
1525 // save current focused application
1526 XGetInputFocus((X11::Display
*)QX11Info::display(), &X11::CurrentFocusWindow
, &X11::CurrentRevertToReturn
);
1529 if (X11::CurrentFocusWindow
!= QX11Info::appRootWindow(QX11Info::appScreen()) ) {
1530 X11::XSetInputFocus((X11::Display
*)QX11Info::display(), QX11Info::appRootWindow(QX11Info::appScreen()), RevertToParent
, CurrentTime
);
1531 X11::XFlush((X11::Display
*)QX11Info::display());
1534 #if defined(Q_WS_WIN)
1535 SetForegroundWindow(m_view
->winId());
1537 if(m_fakeClicking
) {
1538 qDebug() << "fake clicking";
1543 input
.mi
.mouseData
=XBUTTON1
;
1544 input
.mi
.dwFlags
= MOUSEEVENTF_MOVE
|MOUSEEVENTF_XDOWN
|MOUSEEVENTF_XUP
;
1545 SendInput(1, &input
, sizeof(INPUT
));
1549 m_view
->grabKeyboard();
1550 qApp
->alert(m_view
, 50);
1554 // Reset to 0 working onn hours in case not called from "working on"
1555 workingOnDeclared
= 0;
1558 trayIcon
->showMessage("SAK popup disabled", "SAK triggered a new event but no popup will be shown", QSystemTrayIcon::Information
, -1);
1562 // save changes first
1568 if (m_subtaskView
) {
1570 foreach(SakSubWidget
* w
, m_subwidgets
.values()) {
1571 w
->scene()->removeItem(w
);
1574 m_subwidgets
.clear();
1576 m_marker
->scene()->removeItem(m_marker
);
1580 foreach(SakWidget
* w
, m_widgets
.values()) {
1583 m_subtaskView
=false;
1587 m_subtaskView
= false;
1589 if (sender() == previewButton
) {
1590 m_previewing
= true;
1592 QDateTime now
= QDateTime::currentDateTime();
1593 QDateTime fromWeek
= now
;
1594 fromWeek
.setDate(now
.date().addDays(-now
.date().dayOfWeek() + 1));
1595 fromWeek
.setTime(QTime(0,0,0));
1596 QDateTime fromMonth
= now
;
1597 fromMonth
.setDate(now
.date().addDays(-now
.date().day()));
1598 fromMonth
.setTime(QTime(0,0,0));
1599 QDateTime fromToday
= now
;
1600 fromToday
.setTime(QTime(0,0,0));
1602 double weekHits
= 0;
1604 double monthHits
= 0;
1605 QHash
<QString
, double> dayStats
;
1606 QHash
<QString
, double> weekStats
;
1607 QHash
<QString
, double> monthStats
;
1609 for(QHash
<QString
, Task
>::iterator itr
= m_tasks
.begin(); itr
!=m_tasks
.end(); itr
++) {
1610 Task
& t(itr
.value());
1611 t
.checkConsistency();
1613 double m
= t
.workedHours(fromMonth
, now
);
1614 monthStats
[t
.title
] = m
;
1616 double w
= t
.workedHours(fromWeek
, now
);
1617 weekStats
[t
.title
] = w
;
1619 double d
= t
.workedHours(fromToday
, now
);
1620 dayStats
[t
.title
] = d
;
1627 foreach(const Task
& t
, m_tasks
.values()) {
1628 if (!t
.active
|| t
.title
== QString() || t
.title
== "<away>") continue;
1629 SakWidget
* test
= new SakWidget(t
);
1630 test
->setVisible(false);
1631 double d
= dayStats
[t
.title
];
1632 double w
= weekStats
[t
.title
];
1633 double m
= monthStats
[t
.title
];
1634 test
->setStatistics(d
, w
, m
, d
/dayHits
* 100.0, w
/weekHits
* 100.0, m
/monthHits
* 100.0);
1635 test
->setObjectName(t
.title
);
1636 connect (test
, SIGNAL(clicked(const QString
&, const QString
&)), this, SLOT(workingOnTask(const QString
&, const QString
&)));
1637 connect (test
, SIGNAL(clicked(const QString
&)), this, SLOT(popupSubtasks(const QString
&)));
1638 int historyPosition
= 1 + m_taskSelectionHistory
.indexOf(t
.title
);
1639 int rank
= historyPosition
!= 0 ? -1000000 * historyPosition
: -d
;
1640 m_widgets
.insertMulti( rank
, test
);
1643 m_widgetsIterator
= m_widgets
.begin();
1644 if (m_widgetsIterator
!= m_widgets
.end()) {
1645 m_widgetsIterator
.value()->showDetails(true);
1649 const QList
<SakWidget
*>& values
= m_widgets
.values();
1650 QRect messageRect
= Layouting((const QList
<SakWidget
*>&)values
);
1651 foreach(SakWidget
* w
, values
) {
1652 m_view
->scene()->addItem(w
);
1657 // add the message item
1659 QPixmap
askingLadyPixmap(":/images/whip_vale.png");
1661 QPixmap
askingLadyPixmap(":/images/whip.png");
1663 if (!askingLady
->pixmap().isNull()) {
1664 askingLadyPixmap
= askingLady
->pixmap();
1667 if (!bodyEdit
->toPlainText().isEmpty()) {
1668 SakMessageItem
* sakMessage
= new SakMessageItem(bodyEdit
->toPlainText(), askingLadyPixmap
);
1669 sakMessage
->setGeometry(messageRect
);
1670 m_view
->scene()->addItem(sakMessage
);
1674 // add the exit item
1675 SakExitItem
* exitItem
= new SakExitItem(QPixmap(":/images/exit.png"));
1676 QRect r
= m_desktopRect
;
1677 connect(exitItem
, SIGNAL(exit()), this, SLOT(clearView()));
1678 exitItem
->setPos(r
.width() - exitItem
->boundingRect().width(), 0);
1679 m_view
->scene()->addItem(exitItem
);
1680 exitItem
->setZValue(1e8
);
1684 m_view
->setGeometry( QRect(m_desktopRect
)/*.adjusted(200,200,-200,-200 )*/ );
1685 m_view
->backgroundPixmap
= viewBackground
->pixmap().scaled(r
.size(), Qt::KeepAspectRatioByExpanding
);
1689 #if defined(Q_WS_WIN)
1690 SetForegroundWindow(m_view
->winId());
1692 m_getFocusTimer
= startTimer( 500 );
1693 qApp
->alert(m_view
, 100);
1694 m_fakeClicking
=true;
1698 void Sak::popupSubtasks(const QString
& _taskname
) {
1699 m_fakeClicking
=false;
1700 killTimer(m_timeoutPopup
); m_timeoutPopup
=-1;
1702 QString taskname
= _taskname
;
1703 if (taskname
.isEmpty()) {
1704 if ( m_taskSelectionHistory
.isEmpty() ) {
1707 taskname
= m_taskSelectionHistory
.back();
1709 QString subtaskName
;
1710 if (!m_subtaskSelectionHistory
[taskname
].isEmpty()) {
1711 subtaskName
= m_subtaskSelectionHistory
[taskname
].back();
1713 workingOnTask(taskname
, subtaskName
);
1718 // This structure will be used to create the keyboard
1721 // send cltr + alt to exit from vmware
1722 // Set up a generic keyboard event.
1723 ip
.type
= INPUT_KEYBOARD
;
1724 ip
.ki
.wScan
= 0; // hardware scan code for key
1726 ip
.ki
.dwExtraInfo
= 0;
1728 // Press the "CTRL+ALT" key
1729 ip
.ki
.dwFlags
= 0; // 0 for key press
1731 ip
.ki
.wVk
= VK_CONTROL
; // virtual-key code for the "CTRL" key
1733 qDebug() << SendInput(1, &ip
, sizeof(INPUT
));
1734 ip
.ki
.wVk
= VK_MENU
; // virtual-key code for the "ALT" key
1736 qDebug() << SendInput(1, &ip
, sizeof(INPUT
));
1738 // Release the "CTRL+ALT" key
1739 ip
.ki
.dwFlags
= KEYEVENTF_KEYUP
; // KEYEVENTF_KEYUP for key release
1741 ip
.ki
.wVk
= VK_CONTROL
; // virtual-key code for the "CTRL" key
1743 qDebug() << SendInput(1, &ip
, sizeof(INPUT
));
1744 ip
.ki
.wVk
= VK_MENU
; // virtual-key code for the "ALT" key
1746 qDebug() << SendInput(1, &ip
, sizeof(INPUT
));
1750 m_subtaskView
= true;
1752 QRect r
= m_desktopRect
;
1756 // hide tasks to show subtasks
1757 foreach(SakWidget
* w
, m_widgets
.values()) {
1761 QHash
<QString
, Task
>::const_iterator itr
= m_tasks
.find(taskname
);
1762 if (itr
== m_tasks
.end()) {
1763 workingOnTask(taskname
, "");
1767 const Task
& t(*itr
);
1768 QHash
< QString
, Task::SubTask
>::const_iterator titr
= t
.subTasks
.begin(), tend
= t
.subTasks
.end();
1769 m_subwidgets
.clear();
1771 QStringList subtaskSortedList
;
1772 QMultiMap
<int, Task::SubTask
> tmpSubWidgets
; // just for ranking -> delay actual creation of max 10 widgets for performance issues
1773 QDateTime
now(QDateTime::currentDateTime());
1774 for(; titr
!= tend
; titr
++) {
1775 if (!titr
->active
) continue;
1778 QHash
< QString
, QList
< QString
> >::const_iterator hhitr
= m_subtaskSelectionHistory
.find(t
.title
);
1779 if (hhitr
!= m_subtaskSelectionHistory
.end()) {
1780 int index
= hhitr
->indexOf(titr
->title
);
1781 if (index
!= 0) rank
= now
.addDays(index
+1);
1784 QHash
< QString
, QList
<Task::Hit
> >::const_iterator hitr
= t
.hits
.find(titr
.key());
1785 if (hitr
!= t
.hits
.end()) {
1786 if ( hitr
.value().count() && hitr
.value().last().timestamp
.isValid())
1787 rank
= hitr
.value().last().timestamp
;
1789 rank
= now
.addDays(-999);
1792 tmpSubWidgets
.insertMulti( - rank
.toTime_t(), *titr
);
1793 // create possible completion
1794 subtaskSortedList
.push_back((*titr
).title
);
1797 // Populate only first 10 widgets for performance reason on win32 (native widgets embedded in graphicsview are slow to create)
1799 for( QMultiMap
<int, Task::SubTask
>::const_iterator itr
= tmpSubWidgets
.begin(); itr
!=tmpSubWidgets
.end() && sCount
<10; itr
++, sCount
++)
1801 SakSubWidget
* sw
= new SakSubWidget(t
, itr
.value());
1802 sw
->setGeometry(QRectF(0,0,w
,h
));
1803 connect (sw
, SIGNAL(clicked(const QString
&, const QString
&)), this, SLOT(workingOnTask(const QString
&, const QString
&)));
1804 connect (sw
, SIGNAL(focused()), this, SLOT(focusedSubTask()));
1805 m_subwidgets
.insertMulti(itr
.key(), sw
);
1807 const QList
<SakSubWidget
*>& values
= m_subwidgets
.values();
1810 m_subtaskCompleter
= new QCompleter(subtaskSortedList
);
1811 m_subtaskCompleter
->setCaseSensitivity(Qt::CaseInsensitive
);
1813 // QRect messageRect = Layouting((const QList<SakWidget*>&)values);
1815 m_subwidgetsIterator
= m_subwidgets
.begin();
1816 m_subWidgetRank
= 0;
1818 // the one with text
1819 SakSubWidget
* tmpSw
= new SakSubWidget(t
, Task::SubTask(), true);
1820 QCompleter
* completer
= new QCompleter(subtaskSortedList
);
1821 completer
->setCaseSensitivity(Qt::CaseInsensitive
);
1822 completer
->setCompletionMode(QCompleter::InlineCompletion
);
1823 ((QLineEdit
*)tmpSw
->widget())->setCompleter(completer
);
1824 m_view
->scene()->addItem(tmpSw
);
1825 tmpSw
->setGeometry(QRectF(0,0,w
,h
));
1826 connect (tmpSw
, SIGNAL(clicked(const QString
&, const QString
&)), this, SLOT(workingOnTask(const QString
&, const QString
&)));
1827 connect (tmpSw
, SIGNAL(focused()), this, SLOT(focusedSubTask()));
1829 m_subwidgets
.insertMulti(-QDateTime::currentDateTime().addDays(999).toTime_t(), tmpSw
);
1832 m_subWidgetRank
+= values
.size() != 0;
1834 if (m_subwidgetsIterator
!= m_subwidgets
.end()) {
1835 m_subwidgetsIterator
.value()->showDetails(true);
1836 m_view
->scene()->setFocusItem(m_subwidgetsIterator
.value(), Qt::MouseFocusReason
);
1837 m_subwidgetsIterator
.value()->widget()->setFocus(Qt::MouseFocusReason
);
1839 m_view
->scene()->setFocusItem(tmpSw
, Qt::MouseFocusReason
);
1840 tmpSw
->widget()->setFocus(Qt::MouseFocusReason
);
1843 // add a marker to highligh current selection
1844 m_marker
= new QGraphicsEllipseItem(r
.width()/2 - 280, r
.height()/2 - 51, 20,20);
1845 m_marker
->setBrush(Qt::red
);
1846 m_view
->scene()->addItem(m_marker
);
1847 m_marker
->setVisible(true);
1849 layoutSubTasks(m_subwidgets
, m_subWidgetRank
);
1851 // Finally add subwidgets
1852 for(int i
=0; i
<values
.size(); i
++) {
1853 SakSubWidget
* sw
= values
[i
];
1854 m_view
->scene()->addItem(sw
);
1859 void Sak::scrollTasks(int npos
) {
1860 SakWidget
* currentShowing
= 0;
1862 for (int i
=npos
; i
<0; i
++) {
1863 if (m_widgetsIterator
== m_widgets
.end()) return;
1864 currentShowing
= m_widgetsIterator
!= m_widgets
.end() ? m_widgetsIterator
.value() : 0;
1865 if (m_widgetsIterator
== m_widgets
.begin()) m_widgetsIterator
= m_widgets
.end();
1866 m_widgetsIterator
--;
1869 for (int i
=0; i
<npos
; i
++) {
1870 if (m_widgetsIterator
== m_widgets
.end()) return;
1871 currentShowing
= m_widgetsIterator
.value();
1872 m_widgetsIterator
++;
1873 if (m_widgetsIterator
== m_widgets
.end()) m_widgetsIterator
= m_widgets
.begin();
1876 if (currentShowing
&& m_widgetsIterator
!= m_widgets
.end()) {
1877 currentShowing
->showDetails(false);
1878 m_widgetsIterator
.value()->showDetails(true);
1882 void Sak::scrollSubTasks(int npos
) {
1883 SakSubWidget
* currentShowing
= 0;
1885 for (int i
=npos
; i
<0; i
++) {
1886 if (m_subwidgetsIterator
== m_subwidgets
.end()) return;
1887 currentShowing
= m_subwidgetsIterator
!= m_subwidgets
.end() ? m_subwidgetsIterator
.value() : 0;
1888 if (m_subwidgetsIterator
== m_subwidgets
.begin()) {
1889 m_subwidgetsIterator
= m_subwidgets
.end();
1890 m_subWidgetRank
= m_subwidgets
.count();
1892 m_subwidgetsIterator
--;
1896 for (int i
=0; i
<npos
; i
++) {
1897 if (m_subwidgetsIterator
== m_subwidgets
.end()) return;
1898 currentShowing
= m_subwidgetsIterator
.value();
1899 m_subwidgetsIterator
++;
1901 if (m_subwidgetsIterator
== m_subwidgets
.end()) {
1902 m_subwidgetsIterator
= m_subwidgets
.begin();
1903 m_subWidgetRank
= 0;
1907 if (currentShowing
&& m_subwidgetsIterator
!= m_subwidgets
.end()) {
1908 currentShowing
->showDetails(false);
1909 m_subwidgetsIterator
.value()->showDetails(true);
1910 m_view
->scene()->setFocusItem(m_subwidgetsIterator
.value(), Qt::MouseFocusReason
);
1911 m_subwidgetsIterator
.value()->widget()->setFocus(Qt::MouseFocusReason
);
1913 layoutSubTasks(m_subwidgets
, m_subWidgetRank
);
1916 void Sak::focusedSubTask()
1918 SakSubWidget
* w
= dynamic_cast<SakSubWidget
*>(sender());
1920 QMap
<int, SakSubWidget
*>::iterator itr
= m_subwidgets
.begin(), end
=m_subwidgets
.end();
1921 for(int i
=0; itr
!= end
; i
++,itr
++) {
1922 if (itr
.value() == w
) {
1923 m_subwidgetsIterator
= itr
;
1924 m_subWidgetRank
= i
;
1930 //END Tasks >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
1933 //BEGIN settings ================================
1936 void Sak::setupSettingsWidget()
1938 m_settings
= new QMainWindow();
1939 m_settings
->setMinimumHeight(650);
1940 m_settings
->setMinimumWidth(700);
1941 QWidget
* centralWidget
= new QWidget
;
1942 m_settings
->setCentralWidget(centralWidget
);
1944 QVBoxLayout
* theMainLayout
= new QVBoxLayout
;
1945 centralWidget
->setLayout(theMainLayout
);
1947 tabs
= new QTabWidget
;
1948 theMainLayout
->addWidget(tabs
);
1949 previewButton
= new QPushButton("Preview");
1950 theMainLayout
->addWidget(previewButton
);
1956 tabs
->addTab(tab1
, "Tasks");
1957 tabs
->addTab(tab2
, "General");
1958 tabs
->addTab(tab4
, "Statistics");
1959 tabs
->addTab(tab3
, "Advanced");
1962 QMenuBar
* mainMenu
= new QMenuBar
;
1963 m_settings
->setMenuBar(mainMenu
);
1964 QMenu
* programMenu
= mainMenu
->addMenu("Program");
1965 programMenu
->addAction(quitAction
);
1966 programMenu
->addAction(minimizeAction
);
1967 QMenu
* dbMenu
= mainMenu
->addMenu("Db");
1968 dbMenu
->addAction(flushAction
);
1969 dbMenu
->addAction(openAction
);
1970 // dbMenu->addAction(saveAsDbAction);
1971 dbMenu
->addAction(exportDbCsvAction
);
1973 dbMenu
->addAction(gmailLoginAction
);
1974 dbMenu
->addAction(saveToGmailAction
);
1975 if (!m_gmail
->isValid()) {
1976 gmailLoginAction
->setEnabled(false);
1977 saveToGmailAction
->setEnabled(false);
1980 QMenu
* actionsMenu
= mainMenu
->addMenu("Actions");
1981 actionsMenu
->addAction(startAction
);
1982 actionsMenu
->addAction(stopAction
);
1983 actionsMenu
->addAction(workingOnAction
);
1985 QVBoxLayout
*mainLayout
= new QVBoxLayout
;
1986 QGridLayout
*messageLayout
= new QGridLayout
;
1987 durationLabel
= new QLabel(tr("Interval:"));
1988 durationLabel1
= new QLabel(tr("(effective only after restart)"));
1990 QSettings
settings(QSettings::IniFormat
, QSettings::UserScope
, "ZanzaSoft", "SAK");
1991 durationSpinBox
= new QSpinBox
;
1992 durationSpinBox
->setSingleStep(1);
1993 durationSpinBox
->setRange(1, 1440);
1994 durationSpinBox
->setSuffix(" minutes");
1995 durationSpinBox
->setValue(qMin(1440, qMax(1, settings
.value("Ping interval", 15).toInt())));
1996 durationSpinBox
->setCorrectionMode(QAbstractSpinBox::CorrectToNearestValue
);
1998 bodyLabel
= new QLabel(tr("Message:"));
1999 bodyEdit
= new QTextEdit
;
2000 bodyEdit
->setPlainText( settings
.value("Message", "<h1>Enter a message here!</h1>").toString() );
2001 askingLady
= new PixmapViewer(0,false);
2002 askingLady
->setPixmap(QPixmap(":/images/whip.png"));
2003 viewBackground
= new PixmapViewer(0, false);
2005 messageLayout
->addWidget(durationLabel
, 1, 0);
2006 messageLayout
->addWidget(durationSpinBox
, 1, 1);
2007 messageLayout
->addWidget(durationLabel1
, 1, 2);
2008 messageLayout
->addWidget(bodyLabel
, 3, 0);
2009 messageLayout
->addWidget(bodyEdit
, 3, 1, 2, 4);
2010 messageLayout
->addWidget(new QLabel("Character image:"), 7, 0);
2011 messageLayout
->addWidget(askingLady
, 7, 1, 2, 4);
2012 messageLayout
->addWidget(new QLabel("View background:"), 11, 0);
2013 messageLayout
->addWidget(viewBackground
, 11, 1, 2, 4);
2014 messageLayout
->setColumnStretch(3, 1);
2015 messageLayout
->setRowStretch(4, 1);
2016 mainLayout
->addLayout(messageLayout
);
2017 tab2
->setLayout(mainLayout
);
2019 mainLayout
= new QVBoxLayout
;
2020 tab1
->setLayout(mainLayout
);
2022 tasksTree
= new QTreeWidget
;
2023 tasksTree
->setSizePolicy(QSizePolicy::Expanding
, QSizePolicy::Expanding
);
2024 tasksTree
->setColumnCount(3);
2025 tasksTree
->setColumnWidth(0, 300);
2026 tasksTree
->setColumnWidth(1, 65);
2027 tasksTree
->setIconSize(QSize(32,32));
2028 connect(tasksTree
, SIGNAL(itemDoubleClicked(QTreeWidgetItem
*,int)), this, SLOT(doubleClickedTask(QTreeWidgetItem
*,int)));
2031 taskTextEditor
= new QTextEdit
;
2032 taskTextEditor
->setFixedHeight(100);
2033 taskTextEditor
->setSizePolicy(QSizePolicy::Expanding
, QSizePolicy::Preferred
);
2034 taskTextEditor
->setToolTip("Task description");
2035 connect(taskTextEditor
, SIGNAL(textChanged()), this, SLOT(commitCurrentTask()));
2038 taskTitleEditor
= new QLineEdit
;
2039 taskTitleEditor
->setFixedHeight(20);
2040 taskTitleEditor
->setSizePolicy(QSizePolicy::Expanding
, QSizePolicy::Preferred
);
2041 taskTitleEditor
->setValidator(new QRegExpValidator(QRegExp("[\\w]*"), this));
2042 taskTitleEditor
->setToolTip("Task title (alphanumeric)");
2043 connect(taskTitleEditor
, SIGNAL(editingFinished()), this, SLOT(commitCurrentTask()));
2045 taskTagsEditor
= new QLineEdit
;
2046 taskTagsEditor
->setFixedHeight(20);
2047 taskTagsEditor
->setSizePolicy(QSizePolicy::Expanding
, QSizePolicy::Preferred
);
2048 taskTagsEditor
->setValidator(new QRegExpValidator(QRegExp("[\\w,:]*"), this));
2049 taskTagsEditor
->setToolTip("Task tags (alphanumeric comma or colon separated)");
2050 connect(taskTagsEditor
, SIGNAL(editingFinished()), this, SLOT(commitCurrentTask()));
2052 taskUrlEditor
= new QLineEdit
;
2053 taskUrlEditor
->setFixedHeight(20);
2054 taskUrlEditor
->setSizePolicy(QSizePolicy::Expanding
, QSizePolicy::Preferred
);
2055 taskUrlEditor
->setPlaceholderText("http://");
2056 taskUrlEditor
->setToolTip("Task url");
2057 connect(taskUrlEditor
, SIGNAL(editingFinished()), this, SLOT(commitCurrentTask()));
2059 taskPixmapViewer
= new PixmapViewer
;
2060 mainLayout
->addWidget(tasksTree
, 2);
2061 QHBoxLayout
* detailsLayout
= new QHBoxLayout
;
2062 QVBoxLayout
* editsLayout
= new QVBoxLayout
;
2063 detailsLayout
->addWidget(taskPixmapViewer
);
2064 connect(taskPixmapViewer
, SIGNAL(changed()), this, SLOT(commitCurrentTask()));
2065 editsLayout
->addWidget(taskTitleEditor
);
2066 editsLayout
->addWidget(taskTextEditor
);
2067 editsLayout
->addWidget(taskTagsEditor
);
2068 editsLayout
->addWidget(taskUrlEditor
);
2070 QHBoxLayout
* datesLayout
= new QHBoxLayout
;
2071 datesLayout
->addWidget(new QLabel("Due: "));
2072 dueEditor
= new QDateEdit
;
2073 dueEditor
->setMinimumDate(QDate(2000,1,1));
2074 datesLayout
->addWidget(dueEditor
);
2075 datesLayout
->addWidget(new QLabel("Estimated: "));
2076 estimatedHoursEditor
= new QSpinBox
;
2077 estimatedHoursEditor
->setRange(0, 1e5
);
2078 estimatedHoursEditor
->setSuffix("hours");
2079 datesLayout
->addWidget(estimatedHoursEditor
);
2080 editsLayout
->addLayout(datesLayout
);
2081 detailsLayout
->addLayout(editsLayout
);
2082 connect(dueEditor
, SIGNAL(editingFinished()), this, SLOT(commitCurrentTask()));
2083 connect(estimatedHoursEditor
,SIGNAL(editingFinished()), this, SLOT(commitCurrentTask()));
2085 QVBoxLayout
* colorsLayout
= new QVBoxLayout
;
2086 bgColorButton
= new QPushButton("bg\ncolor");
2087 bgColorButton
->setToolTip("Background color");
2088 bgColorButton
->setSizePolicy(QSizePolicy(QSizePolicy::Preferred
, QSizePolicy::MinimumExpanding
));
2090 fgColorButton
= new QPushButton("fg\ncolor");
2091 fgColorButton
->setToolTip("Foreground color");
2092 fgColorButton
->setSizePolicy(QSizePolicy(QSizePolicy::Preferred
, QSizePolicy::MinimumExpanding
));
2095 // fix Windows XP "style"
2096 bgColorButton
->setStyle(new QWindowsStyle());
2097 fgColorButton
->setStyle(new QWindowsStyle());
2100 colorsLayout
->addWidget(bgColorButton
);
2101 colorsLayout
->addWidget(fgColorButton
);
2102 detailsLayout
->addLayout(colorsLayout
);
2104 mainLayout
->addLayout(detailsLayout
);
2106 QVBoxLayout
* tab4MainLayout
= new QVBoxLayout(tab4
);
2107 //taskSelector = new QComboBox;
2108 summaryList
= newTaskSummaryList();
2109 QTabWidget
* tabs
= new QTabWidget
;
2110 tabs
->setTabPosition(QTabWidget::East
);
2111 tabs
->addTab(summaryList
, "List");
2112 summaryView
= new QGraphicsView
;
2113 summaryView
->setScene(new QGraphicsScene
);
2114 summaryChart
= new TaskSummaryPieChart
;
2115 summaryView
->scene()->addItem(summaryChart
);
2116 summaryView
->scene()->setSceneRect(summaryChart
->boundingRect());
2117 summaryView
->fitInView(summaryView
->sceneRect());
2118 summaryView
->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
2119 summaryView
->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
2120 summaryView
->installEventFilter(this);
2121 tabs
->addTab(summaryView
, "Chart");
2122 tab4MainLayout
->addWidget(tabs
);
2124 today1
= new QPushButton("today");
2125 today2
= new QPushButton("today");
2126 thisWeek1
= new QPushButton("this week");
2127 thisWeek2
= new QPushButton("this week");
2128 thisMonth1
= new QPushButton("this month");
2129 thisMonth2
= new QPushButton("this month");
2130 lastWeek1
= new QPushButton("last 7 days");
2131 lastWeek2
= new QPushButton("last 7 days");
2132 lastMonth1
= new QPushButton("last 30 days");
2133 lastMonth2
= new QPushButton("last 30 days");
2135 cal3
= new QCalendarWidget
;
2136 cal3
->setMinimumSize(QSize(250,200));
2137 cal4
= new QCalendarWidget
;
2138 cal4
->setMinimumSize(QSize(250,200));
2139 cal3
->setSizePolicy(QSizePolicy::MinimumExpanding
, QSizePolicy::Fixed
);
2140 cal4
->setSizePolicy(QSizePolicy::MinimumExpanding
, QSizePolicy::Fixed
);
2141 QHBoxLayout
* calsLayout
= new QHBoxLayout
;
2142 calsLayout
->addWidget(cal3
);
2143 calsLayout
->addWidget(cal4
);
2144 QHBoxLayout
* calButtonsLayout
= new QHBoxLayout
;
2145 calButtonsLayout
->addWidget(today1
);
2146 calButtonsLayout
->addWidget(thisWeek1
);
2147 calButtonsLayout
->addWidget(thisMonth1
);
2148 calButtonsLayout
->addWidget(lastWeek1
);
2149 calButtonsLayout
->addWidget(lastMonth1
);
2150 cal4
->setSelectedDate(QDate::currentDate().addDays(1));
2151 tab4MainLayout
->addLayout(calsLayout
);
2152 tab4MainLayout
->addLayout(calButtonsLayout
);
2155 QVBoxLayout
* tab3MainLayout
= new QVBoxLayout(tab3
);
2156 //taskSelector = new QComboBox;
2157 hitsList
= newHitsList();
2158 connect(hitsList
, SIGNAL(currentItemChanged(QTreeWidgetItem
*,QTreeWidgetItem
*)), this, SLOT(hitsSelectedInList(QTreeWidgetItem
*,QTreeWidgetItem
*)));
2159 hitsTimeline
= new Timeline
;
2160 connect(hitsTimeline
, SIGNAL(hitSelected(HitItem
*)), this, SLOT(hitsSelectedInTimeline(HitItem
*)));
2161 cal1
= new QCalendarWidget
;
2162 cal1
->setMinimumSize(QSize(250,200));
2163 cal2
= new QCalendarWidget
;
2164 cal2
->setMinimumSize(QSize(250,200));
2165 cal1
->setSizePolicy(QSizePolicy::MinimumExpanding
, QSizePolicy::Fixed
);
2166 cal2
->setSizePolicy(QSizePolicy::MinimumExpanding
, QSizePolicy::Fixed
);
2167 //tab3MainLayout->addWidget(taskSelector);
2170 calsLayout
= new QHBoxLayout
;
2171 calsLayout
->addWidget(cal1
);
2172 calsLayout
->addWidget(cal2
);
2173 QHBoxLayout
* calButtonsLayout2
= new QHBoxLayout
;
2174 calButtonsLayout2
->addWidget(today2
);
2175 calButtonsLayout2
->addWidget(thisWeek2
);
2176 calButtonsLayout2
->addWidget(thisMonth2
);
2177 calButtonsLayout2
->addWidget(lastWeek2
);
2178 calButtonsLayout2
->addWidget(lastMonth2
);
2179 cal2
->setSelectedDate(QDate::currentDate().addDays(1));
2180 tab3MainLayout
->addWidget(hitsList
);
2181 tab3MainLayout
->addWidget(hitsTimeline
);
2182 tab3MainLayout
->addLayout(calsLayout
);
2183 tab3MainLayout
->addLayout(calButtonsLayout2
);
2187 m_settings
->setWindowTitle(tr("SaK"));
2188 m_settings
->resize(400, 300);
2192 QTreeWidget
* Sak::newHitsList()
2194 QTreeWidget
* hitsList
= new QTreeWidget
;
2195 hitsList
->setColumnCount(3);
2196 hitsList
->setColumnWidth(0, 200);
2197 hitsList
->setColumnWidth(1, 150);
2198 hitsList
->setColumnWidth(2, 150);
2199 hitsList
->setColumnWidth(3, 150);
2200 //hitsList->setColumnWidth(4, 150);
2201 hitsList
->setIconSize(QSize(24,24));
2202 hitsList
->setSortingEnabled(true);
2203 hitsList
->setItemDelegateForColumn(0, new MyDateItemDelegate
);
2204 hitsList
->setItemDelegateForColumn(1, new TaskItemDelegate(this));
2205 hitsList
->setItemDelegateForColumn(2, new SubTaskItemDelegate(this));
2206 QTreeWidgetItem
* header
= new QTreeWidgetItem
;
2207 header
->setText(0, "Date/Time");
2208 header
->setText(1, "Task");
2209 header
->setText(2, "Subtask");
2210 header
->setText(3, "Duration (min)");
2211 //header->setText(4, "Overestimation");
2212 hitsList
->setHeaderItem(header
);
2213 hitsList
->setSizePolicy(QSizePolicy::MinimumExpanding
, QSizePolicy::MinimumExpanding
);
2217 QTreeWidget
* Sak::newTaskSummaryList()
2219 QTreeWidget
* taskSummaryList
= new QTreeWidget
;
2220 taskSummaryList
->setColumnCount(4);
2221 taskSummaryList
->setColumnWidth(0, 250);
2222 taskSummaryList
->setColumnWidth(1, 150);
2223 taskSummaryList
->setColumnWidth(1, 150);
2224 taskSummaryList
->setIconSize(QSize(24,24));
2225 taskSummaryList
->setSortingEnabled(true);
2226 QTreeWidgetItem
* header
= new QTreeWidgetItem
;
2227 header
->setText(0, "Task");
2228 header
->setText(1, "Hours");
2229 taskSummaryList
->setHeaderItem(header
);
2230 taskSummaryList
->setSizePolicy(QSizePolicy::MinimumExpanding
, QSizePolicy::MinimumExpanding
);
2231 taskSummaryList
->setEnabled(true);
2232 return taskSummaryList
;
2235 void Sak::setVisible(bool visible
)
2237 minimizeAction
->setEnabled(visible
);
2238 maximizeAction
->setEnabled(!m_settings
->isMaximized());
2239 restoreAction
->setEnabled(m_settings
->isMaximized() || !visible
);
2240 m_settings
->setVisible(visible
);
2243 void Sak::createActions()
2245 minimizeAction
= new QAction(tr("Mi&nimize"), m_settings
);
2246 connect(minimizeAction
, SIGNAL(triggered()), m_settings
, SLOT(hide()));
2248 maximizeAction
= new QAction(tr("Ma&ximize"), m_settings
);
2249 connect(maximizeAction
, SIGNAL(triggered()), m_settings
, SLOT(showMaximized()));
2251 restoreAction
= new QAction(tr("&Restore"), m_settings
);
2252 connect(restoreAction
, SIGNAL(triggered()), m_settings
, SLOT(showNormal()));
2254 quitAction
= new QAction(tr("&Quit"), m_settings
);
2255 connect(quitAction
, SIGNAL(triggered()), qApp
, SLOT(quit()));
2257 startAction
= new QAction(tr("Start polling"), m_settings
);
2258 connect(startAction
, SIGNAL(triggered()), this, SLOT(start()));
2260 stopAction
= new QAction(tr("Pause polling"), m_settings
);
2261 connect(stopAction
, SIGNAL(triggered()), this, SLOT(pause()));
2263 workingOnAction
= new QAction(tr("Working on.."), m_settings
);
2264 connect(workingOnAction
, SIGNAL(triggered()), this, SLOT(workingOn()));
2266 flushAction
= new QAction(tr("&Flush data/settings to disk"), m_settings
);
2267 connect(flushAction
, SIGNAL(triggered()), this, SLOT(flush()));
2269 saveAsDbAction
= new QAction(tr("Backup as"), m_settings
);
2270 // connect(saveAsDbAction, SIGNAL(triggered()), this, SLOT(saveAsDb()));
2272 exportDbCsvAction
= new QAction(tr("Export hits in CSV format"), m_settings
);
2273 connect(exportDbCsvAction
, SIGNAL(triggered()), this, SLOT(exportDbCsv()));
2276 saveToGmailAction
= new QAction(tr("Store in your gmail account free space"), m_settings
);
2277 connect(saveToGmailAction
, SIGNAL(triggered()), this, SLOT(saveToGmail()));
2279 gmailLoginAction
= new QAction(tr("Log in gmail"), m_settings
);
2280 connect(gmailLoginAction
, SIGNAL(triggered()), this, SLOT(logInGmail()));
2282 saveToGmailAction
= NULL
;
2283 gmailLoginAction
= NULL
;
2286 openAction
= new QAction(tr("Import a task from file"), m_settings
);
2287 connect(openAction
, SIGNAL(triggered()), this, SLOT(open()));
2289 m_addHitAction
= new QAction("Add hit", m_settings
);
2290 m_addHitMenu
= new QMenu(m_settings
);
2291 m_addHitAction
->setText("Add hit");
2292 m_addHitMenu
->addAction(m_addHitAction
);
2293 connect(m_addHitAction
, SIGNAL(triggered()), this, SLOT(addDefaultHit()));
2295 m_exportDataAction
= new QAction("Export data", m_settings
);
2296 m_exportDataAction
->setText("Export data");
2297 m_addHitMenu
->addAction(m_exportDataAction
);
2298 connect(m_exportDataAction
, SIGNAL(triggered()), this, SLOT(exportHits()));
2300 m_addTaskAction
= new QAction("Add task", m_settings
);
2301 m_addTaskMenu
= new QMenu(m_settings
);
2302 m_addTaskAction
->setText("Add task");
2303 m_addTaskMenu
->addAction(m_addTaskAction
);
2304 connect(m_addTaskAction
, SIGNAL(triggered()), this, SLOT(addDefaultTask()));
2307 void Sak::trayIconActivated(QSystemTrayIcon::ActivationReason reason
)
2309 if (reason
== QSystemTrayIcon::DoubleClick
) {
2314 //END Settings <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<