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())));
238 // ensure the timer is killed
241 m_currentInterval
= qMax((int)1, m_currentInterval
);
242 int msecs
= (int)(Task::hours(m_currentInterval
)*3600.0*1000.0 / 2);
243 m_timerId
= startTimer( msecs
);
244 m_nextTimerEvent
= QDateTime::currentDateTime().addMSecs(msecs
);
245 startAction
->setEnabled(false);
246 stopAction
->setEnabled(true);
253 killTimer(m_timerId
);
256 stopAction
->setEnabled(false);
257 startAction
->setEnabled(true);
264 stopAction
->setEnabled(false);
265 startAction
->setEnabled(true);
268 Task
Sak::loadTaskFromFile(const QString
& filePath
)
270 QFile
taskXmlFile(filePath
);
272 qDebug() << "Examine task file " << taskXmlFile
.fileName();
273 if (!taskXmlFile
.open(QIODevice::ReadOnly
)) {
274 qDebug() << "Failed opening xml file " << taskXmlFile
.fileName();
276 QByteArray data
= taskXmlFile
.readLine();
277 QXmlStreamReader
stream(data
);
278 QXmlStreamReader::TokenType token
= stream
.readNext(); // skip StartDocument
279 token
= stream
.readNext();
280 if ( token
!= QXmlStreamReader::Comment
) {
281 qDebug() << "Skip file " << taskXmlFile
.fileName() << " (want a file starting with a comment representing MD5, got" << token
<< ")";
284 QString md5
= stream
.text().toString().trimmed();
285 qDebug() << "md5 = " << md5
;
288 data
= taskXmlFile
.readAll();
289 if ( md5
!= QCryptographicHash::hash(data
, QCryptographicHash::Md5
).toHex() ) {
290 if (QMessageBox::No
== QMessageBox::warning(0, "Corrupted file!",
291 QString("Check of file " + taskXmlFile
.fileName() + " failed (maybe it has been edited by hand).\nDo you want to load it anyway?" )
292 ,QMessageBox::Yes
| QMessageBox::No
) ) {
293 qDebug() << "Skip file " << taskXmlFile
.fileName() << " (bad md5 sum)";
300 stream
.addData(data
);
302 if ( stream
.readNext() != QXmlStreamReader::StartDocument
) {
303 qDebug() << "Skip file " << taskXmlFile
.fileName() << " (want start document)";
307 if (stream
.error() != QXmlStreamReader::NoError
) {
308 qDebug() << "Error reading task data from file " << taskXmlFile
.fileName() << ":" << stream
.errorString();
311 // QFile tmp("/tmp/" + t.title + ".xml");
312 // tmp.open(QIODevice::ReadWrite);
313 // QXmlStreamWriter ss(&tmp);
314 // ss.setAutoFormatting(true);
315 // ss.setAutoFormattingIndent(2);
316 // ss.writeStartDocument();
318 // ss.writeEndDocument();
331 if (!m_settings
) return;
332 m_backupper
->doCyclicBackup();
333 QSettings
settings(QSettings::IniFormat
, QSettings::UserScope
, "ZanzaSoft", "SAK");
334 // QByteArray tasksArray;
335 // QDataStream stream(&tasksArray, QIODevice::ReadWrite);
336 // stream.setVersion(QDataStream::Qt_4_0);
337 // stream << m_tasks;
338 // settings.setValue("tasks", tasksArray);
339 settings
.setValue("Ping interval", durationSpinBox
->value());
340 settings
.setValue("Message", bodyEdit
->toPlainText());
343 QDir
saveDir(QFileInfo(settings
.fileName()).dir());
344 saveDir
.mkdir("SakTasks");
345 saveDir
.cd("SakTasks");
347 foreach(Task t
, m_tasks
) {
348 if (t
.title
.isEmpty()) continue;
349 QFile
xmlTaskSave(saveDir
.filePath(t
.title
+ ".xml"));
350 QByteArray taskArray
;
351 QXmlStreamWriter
stream(&taskArray
);
352 stream
.setAutoFormatting(true);
353 stream
.setAutoFormattingIndent(2);
354 stream
.writeStartDocument();
356 stream
.writeEndDocument();
357 xmlTaskSave
.open(QIODevice::ReadWrite
| QIODevice::Truncate
);
358 qDebug() << "Saving xml to file " << xmlTaskSave
.fileName();
360 hash
.append("<!-- ");
361 hash
.append( QCryptographicHash::hash(taskArray
, QCryptographicHash::Md5
).toHex() );
362 hash
.append(" -->\n");
363 xmlTaskSave
.write(hash
);
364 xmlTaskSave
.write(taskArray
);
368 // remove files not matching a task
369 QStringList nameFilters
;
370 nameFilters
<< "*.xml";
371 QStringList files
= saveDir
.entryList( nameFilters
, QDir::Files
);
372 foreach (QString taskXmlFileName
, files
) {
373 if (!m_tasks
.contains(QFileInfo(taskXmlFileName
).baseName())) {
374 qWarning()<< "Remove task " << QFileInfo(taskXmlFileName
).baseName() << " from disk";
375 QFile(saveDir
.filePath(taskXmlFileName
)).remove();
380 m_incremental
->clearAddedPieces();
383 //void Sak::saveAsDb()
385 // if (!m_settings) return;
386 // QString fileName = QFileDialog::getSaveFileName();
387 // QFile file(fileName);
390 // QSettings settingsQSettings::IniFormat, QSettings::UserScope, ("ZanzaSoft", "SAK");
391 // QFile file1(settings.fileName());
392 // if (!file1.copy(fileName)) {
393 // qWarning() << "Error copying " << settings.fileName() << " to " << fileName << file1.errorString();
397 void Sak::exportDbCsv()
399 if (!m_settings
) return;
400 QString fileName
= QFileDialog::getSaveFileName();
401 QFile
file(fileName
);
402 if (!file
.open(QIODevice::ReadWrite
|QIODevice::Truncate
)) {
403 QMessageBox::warning(0, "Error saving", QString("Error saving to file %1").arg(fileName
));
406 QTextStream
stream(&file
);
407 foreach(const Task
& t
, m_tasks
) {
408 QHash
< QString
, QList
< Task::Hit
> >::const_iterator itr
= t
.hits
.begin();
409 while(itr
!= t
.hits
.end()) {
410 QList
< Task::Hit
>::const_iterator hitr
= itr
.value().begin(), hend
= itr
.value().end();
411 while(hitr
!= hend
) {
412 stream
<< t
.title
<< ";" << itr
.key() << ";" << hitr
->timestamp
.toString(DATETIMEFORMAT
) << ";" << hitr
->duration
<< ";\n";
422 void Sak::logInGmail()
424 m_gmail
->forceLogin();
427 void Sak::saveToGmail()
429 if (!m_settings
) return;
431 QSettings
settings(QSettings::IniFormat
, QSettings::UserScope
, "ZanzaSoft", "SAK");
433 QDir
saveDir(QFileInfo(settings
.fileName()).dir());
434 saveDir
.mkdir("SakTasks");
435 saveDir
.cd("SakTasks");
436 QStringList nameFilters
;
437 nameFilters
<< "*.xml";
438 QStringList files
= saveDir
.entryList( nameFilters
, QDir::Files
);
439 QStringList filePaths
;
440 foreach (QString taskXmlFileName
, files
) {
441 filePaths
<< saveDir
.filePath(taskXmlFileName
);
443 m_gmail
->storeTaskFiles(filePaths
);
446 void Sak::importFromGmail()
448 QStringList filePaths
= m_gmail
->fetchLatestTasks();
451 void Sak::open(const QStringList
& _fileNames
)
453 QStringList fileNames
= _fileNames
.size()?_fileNames
:QFileDialog::getOpenFileNames(0, "Open a new task", QString(), "*.xml" );
454 foreach(QString fileName
, fileNames
) {
455 QFile
file(fileName
);
456 if (!file
.exists()) {
457 QMessageBox::warning(0, "Cannot find task", QString("Cannot find task file %1").arg(fileName
));
460 QSettings
settings(QSettings::IniFormat
, QSettings::UserScope
, "ZanzaSoft", "SAK");
461 QDir
saveDir(QFileInfo(settings
.fileName()).dir());
462 saveDir
.mkdir("SakTasks");
463 saveDir
.cd("SakTasks");
465 if ( QFile(saveDir
.filePath(QFileInfo(fileName
).completeBaseName())).exists() ) {
466 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?");
467 QPushButton
* overwriteButton
= mbox
.addButton("Overwrite", QMessageBox::YesRole
);
468 QPushButton
* mergeButton
= mbox
.addButton("Merge", QMessageBox::NoRole
);
469 QPushButton
* cancelButton
= mbox
.addButton("Cancel", QMessageBox::RejectRole
);
470 mbox
.setDefaultButton(cancelButton
);
472 QAbstractButton
* b
= mbox
.clickedButton();
473 if (b
== cancelButton
) { continue; }
475 m_backupper
->doCyclicBackup();
476 if (b
== mergeButton
) {
477 Task t
= loadTaskFromFile(file
.fileName());
478 QHash
< QString
, QList
< Task::Hit
> > ::const_iterator itr
= t
.hits
.begin(), end
= t
.hits
.end();
480 QString subtask
= itr
.key();
481 foreach(Task::Hit hit
, itr
.value())
482 m_incremental
->addPiece(t
.title
, subtask
, hit
.timestamp
, hit
.duration
);
485 interactiveMergeHits();
486 } else if (b
== overwriteButton
) {
487 file
.copy(saveDir
.filePath(QFileInfo(fileName
).completeBaseName()));
493 if (!fileNames
.isEmpty()) {
505 if (!m_settings
) return;
507 m_settings
->deleteLater();
508 m_view
->scene()->deleteLater();
509 m_view
->deleteLater();
511 delete m_incremental
;
513 m_previewing
= false;
514 m_changedHit
= false;
521 killTimer(m_autoSaveTimer
);
526 void Sak::layoutSubTasks( const QMap
<int, SakSubWidget
*> sortedWidgets
, int currentRank
) {
527 QMap
<int, SakSubWidget
*>::const_iterator itr
= sortedWidgets
.begin(), end
= sortedWidgets
.end();
528 QRect r
= m_desktopRect
;
529 for(int i
=0; itr
!= end
; i
++, itr
++) {
530 int h
= (*itr
)->size().height();
531 int w
= (*itr
)->size().width();
532 (*itr
)->setPos(QPointF((r
.width() - w
)/2, (r
.height()-h
)/2 + (i
- currentRank
- 1) * (h
+2)));
537 int Sak::taskCounter
= 0;
539 bool Sak::eventFilter(QObject
* obj
, QEvent
* e
)
541 // if (obj == m_view) {
542 // qDebug() << "event : " << e->type();
544 if (obj
== tasksTree
) {
545 return taskTreeEventFilter(e
);
546 } else if (obj
== hitsList
|| obj
== summaryList
) {
547 return hitsListEventFilter(e
);
548 } else if (obj
== m_settings
&& e
->type() == QEvent::Close
) {
553 if (trayIcon
->isVisible()) {
558 } else if (obj
== m_view
&& e
->type() == QEvent::Wheel
) {
559 QWheelEvent
* we
= dynamic_cast<QWheelEvent
*>(e
);
561 scrollSubTasks(we
->delta() / 120);
562 } else scrollTasks(we
->delta() / 120);
563 } else if (obj
== m_view
&& e
->type() == QEvent::KeyPress
) {
564 QKeyEvent
* ke
= dynamic_cast<QKeyEvent
*>(e
);
565 if ((ke
->modifiers() & Qt::AltModifier
) && (ke
->modifiers() & Qt::ControlModifier
) ) {
568 } else if ( ((ke
->modifiers() & Qt::ControlModifier
) && (ke
->key() == Qt::Key_Backspace
) )
569 || ((ke
->modifiers() & Qt::ControlModifier
) && (ke
->key() == Qt::Key_Left
) )) {
574 } else if (m_subtaskView
&& ke
->key() == Qt::Key_Up
) {
577 } else if (m_subtaskView
&& ke
->key() == Qt::Key_Down
) {
580 } else if (!m_subtaskView
&& ke
->key() == Qt::Key_Left
) {
583 } else if (!m_subtaskView
&& ke
->key() == Qt::Key_Right
) {
586 } else if (!m_subtaskView
&& ke
->key() == Qt::Key_Escape
) {
589 } else { // forward events to current widget
590 if (!m_subtaskView
) {
591 if (m_widgetsIterator
== m_widgets
.end()) return false;
592 SakWidget
* currentShowing
= m_widgetsIterator
.value();
593 currentShowing
->keyPressEvent(ke
);
596 // autoscroll on text completion!!!
597 if (m_subwidgetsIterator
== m_subwidgets
.end()) return false;
598 SakSubWidget
* currentShowing
= m_subwidgetsIterator
.value();
599 currentShowing
->keyPressEvent(ke
);
601 if (m_subWidgetRank
!= 0 && m_subtaskCompleter
) {
602 QString
completion(m_subtaskCompleter
->completionPrefix());
603 if (ke
->text().size() == 1) {
604 if (ke
->key() == Qt::Key_Backslash
|| ke
->key() == Qt::Key_Backspace
)
606 else completion
+= ke
->text();
607 m_subtaskCompleter
->setCompletionPrefix(completion
);
610 QStringList
list( ((QStringListModel
*)m_subtaskCompleter
->model())->stringList() );
611 int newRank
= 1 + ((QStringListModel
*)m_subtaskCompleter
->model())->stringList().indexOf(m_subtaskCompleter
->currentIndex().row() >= 0 && completion
.size() ? m_subtaskCompleter
->currentCompletion() : completion
);
613 if (m_subWidgetRank
!= newRank
) {
614 scrollSubTasks(newRank
- m_subWidgetRank
);
616 QLineEdit
* editor
= dynamic_cast<QLineEdit
*>((*m_subwidgets
.begin())->widget());
618 editor
->setText(completion
);
623 } else if (m_subtaskCompleter
) {
624 QLineEdit
* editor
= dynamic_cast<QLineEdit
*>((*m_subwidgets
.begin())->widget());
626 m_subtaskCompleter
->setCompletionPrefix(editor
->text());
633 } else if (obj
== m_view
&& e
->type() == QEvent::Show
) {
635 QTimer::singleShot(500, this, SLOT(grabKeyboard()));
636 } else if (obj
== m_view
&& e
->type() == QEvent::Close
) {
637 if (trayIcon
->isVisible()) {
640 } else if (obj
&& obj
== trayIcon
&& e
->type() == QEvent::ToolTip
) {
641 QDateTime last
= m_incremental
->lastTimeStamp
;
642 int seconds
= QDateTime::currentDateTime().secsTo(m_nextTimerEvent
);
643 int hours
= seconds
/ 3600;
644 int minutes
= (seconds
/ 60) % 60;
646 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>")))));
652 //END basic >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
655 //BEGIN Tasks >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
656 void Sak::addDefaultTask()
658 QString tentativeName
;
660 tentativeName
= QString("Task %1").arg(taskCounter
++);
661 } while(m_editedTasks
.contains(tentativeName
));
663 Task
& t
= m_editedTasks
[tentativeName
];
664 t
.title
= tentativeName
;
665 QTreeWidgetItem
* item
= new QTreeWidgetItem(QStringList(tentativeName
));
666 item
->setData(0,Qt::UserRole
, QVariant(QMetaType::VoidStar
, &t
));
667 tasksTree
->addTopLevelItem(item
);
671 void Sak::populateTasks()
675 QHash
<QString
, Task
>::iterator itr
= m_editedTasks
.begin(), end
=m_editedTasks
.end();
676 for(; itr
!=end
; itr
++) {
677 Task
& t(itr
.value());
678 t
.checkConsistency();
680 if (t
.title
.isEmpty() || t
.title
== "<away>") continue; // skip away task
681 QTreeWidgetItem
* item
= new QTreeWidgetItem(QStringList(t
.title
));
682 item
->setData(0, Qt::UserRole
, QVariant(QMetaType::VoidStar
, &t
));
684 icon
.addPixmap(t
.icon
);
685 item
->setSizeHint(0, QSize(32,32));
686 item
->setIcon(0, icon
);
687 for(int i
=0; i
<3; i
++) {
688 item
->setForeground(i
,t
.fgColor
);
689 item
->setBackground(i
,t
.bgColor
);
691 //item->setCheckState(1, t.active ? Qt::Checked : Qt::Unchecked);
692 //item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
693 item
->setIcon(1,QIcon(t
.active
? ":/images/active.png" : ":/images/inactive.png"));
694 item
->setText(2,QString("%1 hours worked till now (overestimated %2)").arg(t
.totHours
, 4, 'f', 2, ' ').arg(t
.totOverestimation
));
695 foreach(Task::SubTask st
, t
.subTasks
) {
696 if (!st
.title
.isEmpty()) {
697 QTreeWidgetItem
* sitem
= new QTreeWidgetItem(item
, QStringList(st
.title
));
698 item
->setData(0, Qt::UserRole
, QVariant(QMetaType::VoidStar
, &st
));
699 sitem
->setSizeHint(0, QSize(32,32));
700 QColor fgColor
= st
.fgColor
.isValid() ? st
.fgColor
: t
.fgColor
;
701 QColor bgColor
= st
.bgColor
.isValid() ? st
.bgColor
: t
.bgColor
;
702 for(int i
=0; i
<3; i
++) {
703 sitem
->setForeground(i
,fgColor
);
704 sitem
->setBackground(i
,bgColor
);
706 sitem
->setIcon(1,QIcon(st
.active
? ":/images/active.png" : ":/images/inactive.png"));
707 sitem
->setText(2,QString("%1 hours worked till now").arg(st
.totHours
,4,'f',2,' '));
710 tasksTree
->addTopLevelItem(item
);
714 void Sak::saveTaskChanges()
718 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
) {
719 m_tasks
= m_editedTasks
;
720 } else m_editedTasks
= m_tasks
; //. undo changes
722 selectedStartDate(QDate());
727 void Sak::selectColor() {
728 if (tasksTree
->selectedItems().isEmpty()) return;
730 if (sender() == fgColorButton
) {
731 QColor c
= QColorDialog::getColor(fgColorButton
->palette().color(QPalette::ButtonText
));
732 if (!c
.isValid()) return;
733 QPalette p
= fgColorButton
->palette();
734 p
.setColor(QPalette::ButtonText
, c
);
735 fgColorButton
->setPalette(p
);
736 bgColorButton
->setPalette(p
);
737 } else if (sender() == bgColorButton
) {
738 QColor c
= QColorDialog::getColor(bgColorButton
->palette().color(QPalette::Button
));
739 if (!c
.isValid()) return;
740 QPalette p
= bgColorButton
->palette();
741 p
.setColor(QPalette::Button
, c
);
742 fgColorButton
->setPalette(p
);
743 bgColorButton
->setPalette(p
);
748 bool Sak::taskTreeEventFilter(QEvent
* e
)
750 if (e
->type() == QEvent::ContextMenu
) {
751 QContextMenuEvent
* me
= dynamic_cast<QContextMenuEvent
*>(e
);
752 if (!me
) return false;
753 m_addTaskMenu
->popup(me
->globalPos());
755 } else if (e
->type() == QEvent::KeyRelease
) {
756 QKeyEvent
* ke
= dynamic_cast<QKeyEvent
*>(e
);
757 if (!ke
) return false;
758 if ( (ke
->key() != Qt::Key_Delete
&& ke
->key() != Qt::Key_Backspace
) ) return false;
759 if (currentSubtask
!="") {
760 QMessageBox
whatToDo(QMessageBox::Warning
, "Deleting subtask", "Deleting subtask " + currentSubtask
+ " of task " + currentTask
);
761 QPushButton
* moveHitsToParentButton
= whatToDo
.addButton("Move hits to task " + currentTask
, QMessageBox::AcceptRole
);
762 QPushButton
* removeHitsButton
= whatToDo
.addButton("Remove hits", QMessageBox::AcceptRole
);
763 QPushButton
* cancelButton
= whatToDo
.addButton("Cancel", QMessageBox::RejectRole
);
764 whatToDo
.setDefaultButton(cancelButton
);
766 if ( whatToDo
.clickedButton() == cancelButton
) return true;
767 if (m_editedTasks
.find(currentTask
) == m_editedTasks
.end()) return true;
769 Task
& t(m_editedTasks
[currentTask
]);
770 t
.subTasks
.take(currentSubtask
);
771 if (whatToDo
.clickedButton() == removeHitsButton
) {
772 t
.hits
.take(currentSubtask
);
773 } else if (whatToDo
.clickedButton() == moveHitsToParentButton
) {
774 QList
<Task::Hit
> sorter(t
.hits
.take(""));
775 sorter
<< t
.hits
.take(currentSubtask
);
776 qStableSort(sorter
.begin(), sorter
.end());
780 // remove file from disk
782 m_editedTasks
.remove(currentTask
);
786 selectedStartDate(QDate());
788 } else if (e
->type() == QEvent::Hide
) {
795 void Sak::commitCurrentTask()
798 if (currentSubtask
.isEmpty()) {
799 QString currentTitle
= taskTitleEditor
->text();
800 if (!currentTitle
.isEmpty()) {
801 if (currentTitle
!= currentTask
) {
802 if (m_editedTasks
.contains(currentTitle
)) {
803 QMessageBox::warning(0, "Conflict in task names", "Conflict in task names: current task " + currentTask
+ ", edited title " + currentTitle
);
804 taskTitleEditor
->setText(currentTask
);
806 } else if (m_editedTasks
.contains(currentTask
)) {
807 m_editedTasks
[currentTitle
] = m_editedTasks
.take(currentTask
);
808 m_editedTasks
[currentTitle
].title
= currentTitle
;
812 Task
& t
= m_editedTasks
[currentTitle
];
813 t
.bgColor
= bgColorButton
->palette().color(QPalette::Button
);
814 t
.fgColor
= fgColorButton
->palette().color(QPalette::ButtonText
);
815 t
.icon
= taskPixmapViewer
->pixmap();
816 QList
<QTreeWidgetItem
*> items
= tasksTree
->findItems(currentTask
,Qt::MatchExactly
,0);
817 foreach(QTreeWidgetItem
* ii
, items
) {
818 ii
->setText(0, currentTitle
);
819 ii
->setIcon(0, taskPixmapViewer
->pixmap());
820 for (int i
=0; i
<3; i
++) {
821 ii
->setForeground(i
, QColor(t
.fgColor
));
822 ii
->setBackground(i
, QColor(t
.bgColor
));
825 if (dueEditor
->date() != dueEditor
->minimumDate())
826 t
.dueDate
= dueEditor
->date();
827 t
.estimatedHours
= estimatedHoursEditor
->value();
828 currentTask
=currentTitle
;
830 if (tasksTree
->selectedItems().size() != 1) return;
831 QTreeWidgetItem
* item
= tasksTree
->selectedItems()[0];
832 item
->setText(0, taskTitleEditor
->text());
834 icon
.addPixmap(t
.icon
);
835 item
->setSizeHint(0, QSize(32,32));
836 item
->setIcon(0, icon
);
837 for(int i
=0; i
<3; i
++) {
838 item
->setForeground(i
,t
.fgColor
);
839 item
->setBackground(i
,t
.bgColor
);
843 } else { // subtask edited
844 if (!m_editedTasks
.contains(currentTask
)) return;
845 Task
& t(m_editedTasks
[currentTask
]);
846 QString currentTitle
= taskTitleEditor
->text();
848 if (!currentTitle
.isEmpty()) {
849 if (currentTitle
!= currentSubtask
) {
850 if (t
.subTasks
.contains(currentTitle
)) {
851 QMessageBox::warning(0, "Conflict in subtask names", "Conflict in subtask names");
852 taskTitleEditor
->setText(currentSubtask
);
854 } else if (t
.subTasks
.contains(currentSubtask
)) {
855 t
.subTasks
[currentTitle
] = t
.subTasks
.take(currentSubtask
);
856 t
.subTasks
[currentTitle
].title
= currentTitle
;
857 t
.hits
[currentTitle
] = t
.hits
.take(currentSubtask
);
861 Task::SubTask
& st
= t
.subTasks
[currentTitle
];
862 st
.bgColor
= bgColorButton
->palette().color(QPalette::Button
);
863 st
.fgColor
= fgColorButton
->palette().color(QPalette::ButtonText
);
864 QList
<QTreeWidgetItem
*> items
= tasksTree
->findItems(currentTask
,Qt::MatchExactly
,0);
865 foreach(QTreeWidgetItem
* jj
, items
) {
866 for(int i
=0; i
<jj
->childCount(); i
++) {
867 QTreeWidgetItem
* ii
= jj
->child(i
);
868 if (ii
->text(0) != currentSubtask
) continue;
869 ii
->setText(0, currentTitle
);
870 for (int i
=0; i
<3; i
++) {
871 ii
->setForeground(i
, QColor(st
.fgColor
));
872 ii
->setBackground(i
, QColor(st
.bgColor
));
876 currentSubtask
= currentTitle
;
878 if (tasksTree
->selectedItems().size() != 1) return;
879 QTreeWidgetItem
* item
= tasksTree
->selectedItems()[0];
880 item
->setText(0, taskTitleEditor
->text());
882 icon
.addPixmap(t
.icon
);
883 item
->setSizeHint(0, QSize(32,32));
884 item
->setIcon(0, icon
);
885 for(int i
=0; i
<3; i
++) {
886 item
->setForeground(i
,st
.fgColor
);
887 item
->setBackground(i
,st
.bgColor
);
892 void Sak::selectedTask()
894 if (tasksTree
->selectedItems().isEmpty()) {
895 taskPixmapViewer
->setEnabled(false);
896 taskPixmapViewer
->setPixmap(QPixmap());
897 taskTextEditor
->setEnabled(false);
898 taskTitleEditor
->setEnabled(false);
899 bgColorButton
->setEnabled(false);
900 fgColorButton
->setEnabled(false);
901 dueEditor
->setEnabled(false);
902 estimatedHoursEditor
->setEnabled(false);
906 QTreeWidgetItem
* selectedItem
= tasksTree
->selectedItems().first();
907 QTreeWidgetItem
* parentItem
= selectedItem
->parent();
908 QString tt
= selectedItem
->text(0);
911 taskPixmapViewer
->setEnabled(true);
912 dueEditor
->setEnabled(true);
913 estimatedHoursEditor
->setEnabled(true);
915 taskPixmapViewer
->setEnabled(false);
916 taskPixmapViewer
->setPixmap(QPixmap());
917 dueEditor
->setEnabled(false);
918 estimatedHoursEditor
->setEnabled(false);
920 taskTextEditor
->setEnabled(true);
921 taskTitleEditor
->setEnabled(true);
922 bgColorButton
->setEnabled(true);
923 fgColorButton
->setEnabled(true);
926 if (!parentItem
) { // editing a task
927 if (!m_editedTasks
.contains(tt
)) return;
928 const Task
& t
= m_editedTasks
[tt
];
929 taskPixmapViewer
->setPixmap(t
.icon
);
930 taskTextEditor
->blockSignals(true);
931 taskTextEditor
->setPlainText(t
.description
);
932 taskTextEditor
->blockSignals(false);
933 taskTitleEditor
->setText(t
.title
);
935 p
.setColor(QPalette::Button
, t
.bgColor
);
936 p
.setColor(QPalette::ButtonText
, t
.fgColor
);
937 bgColorButton
->setPalette(p
);
938 fgColorButton
->setPalette(p
);
939 estimatedHoursEditor
->setValue(t
.estimatedHours
);
940 dueEditor
->setDate(t
.dueDate
.isValid() ? t
.dueDate
: dueEditor
->minimumDate());
942 currentTask
= t
.title
;
945 if (!m_editedTasks
.contains(parentItem
->text(0))) return;
946 const Task
& t
= m_editedTasks
[parentItem
->text(0)];
947 if (!t
.subTasks
.contains(tt
)) return;
948 const Task::SubTask
& st
= t
.subTasks
[tt
];
949 taskTextEditor
->setPlainText(st
.description
);
950 taskTitleEditor
->setText(st
.title
);
952 p
.setColor(QPalette::Button
, st
.bgColor
.isValid() ? st
.bgColor
: t
.bgColor
);
953 p
.setColor(QPalette::ButtonText
, st
.fgColor
.isValid() ? st
.fgColor
: t
.fgColor
);
954 bgColorButton
->setPalette(p
);
955 fgColorButton
->setPalette(p
);
957 currentTask
= t
.title
;
958 currentSubtask
= st
.title
;
962 void Sak::doubleClickedTask(QTreeWidgetItem
* i
, int column
)
966 if (i
->parent() == 0) {
967 QHash
<QString
, Task
>::iterator itr
= m_editedTasks
.find(i
->text(0));
968 Q_ASSERT(itr
!= m_editedTasks
.end());
969 bool& active ( itr
.value().active
);
971 i
->setIcon(column
, active
? QIcon(":/images/active.png") : QIcon(":/images/inactive.png"));
973 QHash
<QString
, Task
>::iterator itr
= m_editedTasks
.find(i
->parent()->text(0));
974 Q_ASSERT(itr
!= m_editedTasks
.end());
975 bool& active ( itr
.value().subTasks
[i
->text(0)].active
);
977 i
->setIcon(column
, active
? QIcon(":/images/active.png") : QIcon(":/images/inactive.png"));
979 ((QTreeWidget
*)sender())->update();
985 void Sak::timerEvent(QTimerEvent
* e
)
987 if (e
->timerId() == m_timerId
) {
988 if (!m_view
->isVisible() && !m_settings
->isVisible() && m_tasks
.count() > 0) {
991 killTimer(m_timeoutPopup
);
992 m_timeoutPopup
= startTimer((int)(qMax( 30000.0, Task::hours(m_currentInterval
)*3600.0*1000.0/10.0))); // 5 secmin
994 killTimer(m_timerId
);
995 int msecs
= (int)(Task::hours(m_currentInterval
)*3600.0*1000.0);
996 m_timerId
= startTimer(msecs
);
997 m_nextTimerEvent
= QDateTime::currentDateTime().addMSecs(msecs
);
999 if (m_settings
&& m_settings
->isVisible() && !m_settings
->isActiveWindow()) {
1000 trayIcon
->showMessage("Delayed check point", "Delayed check point due to open settings. Close the setting window!", QSystemTrayIcon::Warning
, -1);
1001 m_settings
->close();
1003 qDebug() << "SAK: wait 5 seconds";
1004 killTimer(m_timerId
);
1005 m_timerId
= startTimer(5000);
1006 m_nextTimerEvent
= QDateTime::currentDateTime().addMSecs(5000);
1008 } else if (e
->timerId() == m_timeoutPopup
) {
1009 // ensure the timer is resetted
1010 killTimer(m_timeoutPopup
);
1012 if (!m_subtaskView
) {
1013 // if not selecting subtasks clear everything and signal away
1014 workingOnTask("<away>","");
1015 trayIcon
->showMessage("New away events", "You have missed a check point. Fix it in the detailed hit list.", QSystemTrayIcon::Information
, 999999);
1018 } else if (e
->timerId() == m_autoSaveTimer
) {
1021 qDebug() << "unknown timer event";
1025 void Sak::clearView()
1027 killTimer(m_timeoutPopup
);
1028 m_subtaskView
=false;
1030 delete m_subtaskCompleter
; m_subtaskCompleter
= 0;
1032 if (!m_view
) return;
1033 QGraphicsScene
* s
= m_view
->scene();
1034 QList
<QGraphicsItem
*> items
= m_view
->items();
1037 m_view
->setScene(new QGraphicsScene
);
1038 m_view
->scene()->setSceneRect(m_desktopRect
);
1039 m_previewing
= false;
1040 m_view
->releaseKeyboard();
1042 #if defined(Q_WS_X11)
1043 // restore focus to previous application
1045 X11::XSetInputFocus((X11::Display
*)QX11Info::display(), X11::CurrentFocusWindow
, X11::CurrentRevertToReturn
, CurrentTime
);
1046 X11::XFlush((X11::Display
*)QX11Info::display());
1050 void Sak::workingOnTask(const QString
& taskName
, const QString
& subTask
)
1052 if (!m_previewing
) {
1054 qDebug() << "Working on " << taskName
;
1055 if (m_tasks
.contains(taskName
)) {
1056 Task
& t
= m_tasks
[taskName
];
1058 if (t
.title
!= "<away>" && !t
.title
.isEmpty()) {
1060 int historyIndex
= m_taskSelectionHistory
.indexOf(t
.title
);
1061 if (historyIndex
!= -1) {
1062 m_taskSelectionHistory
.takeAt(historyIndex
);
1064 m_taskSelectionHistory
.push_back(t
.title
);
1066 QList
<QString
> & subtaskSelectionHistory(m_subtaskSelectionHistory
[t
.title
]);
1067 historyIndex
= subtaskSelectionHistory
.indexOf(subTask
);
1068 if (historyIndex
!= -1) {
1069 subtaskSelectionHistory
.takeAt(historyIndex
);
1071 subtaskSelectionHistory
.push_back(subTask
);
1075 QDateTime now
= QDateTime::currentDateTime();
1076 QHash
<QString
, Task
>::iterator itr
= m_tasks
.begin();
1078 QHash
<QString
, QList
< Task::Hit
> >::iterator hitr
= t
.hits
.begin();
1079 // merge last two hits of every subtask if they are close enough
1080 while(hitr
!= t
.hits
.end()) {
1081 QList
<Task::Hit
>& otHits( *hitr
);
1082 if (otHits
.count() > 1) {
1083 Task::Hit
& lastHit(otHits
[otHits
.size() - 1]);
1084 Task::Hit
& beforeLastHit(otHits
[otHits
.size() - 2]);
1086 int diff
= (lastHit
.timestamp
.toTime_t() - 30*lastHit
.duration
) - (beforeLastHit
.timestamp
.toTime_t() + 30*beforeLastHit
.duration
);
1087 if (diff
< 120) { // at most 2 minutes apart
1088 beforeLastHit
.timestamp
= beforeLastHit
.timestamp
.addSecs(-30*beforeLastHit
.duration
);
1089 int secsToEnd
= beforeLastHit
.timestamp
.secsTo(lastHit
.timestamp
.addSecs(30*m_currentInterval
));
1090 if (secsToEnd
> 24 * 3600 * 3600) {
1091 qWarning() << "TRAPPED ERROR IN SECS COUNT!!!!!!!!";
1092 qWarning() << "BEFORE LAST HIST WAS " << beforeLastHit
.timestamp
<< beforeLastHit
.duration
;
1093 qWarning() << "LAST HIT WAS " << lastHit
.timestamp
<< lastHit
.duration
;
1094 assert(secsToEnd
< 24 * 3600 * 3600);
1096 beforeLastHit
.timestamp
= beforeLastHit
.timestamp
.addSecs( secsToEnd
/2.0 );
1097 beforeLastHit
.duration
= (int)( round( secsToEnd
/ 60.0 ) );
1098 // remove the current very last hit
1105 // add hit to hit list
1106 // NOTE: we do not try to merge the very last hit with previous ones because we want let
1107 // the user being able to easily recover from a selection error
1108 t
.hits
[subTask
] << Task::Hit(now
, m_currentInterval
);
1109 m_incremental
->writePiece(t
.title
, subTask
, now
, m_currentInterval
);
1110 t
.checkConsistency();
1111 QList
<QTreeWidgetItem
*> items
= tasksTree
->findItems (t
.title
, Qt::MatchExactly
, 0);
1112 if (!items
.isEmpty())
1113 items
.first()->setText(1, QString("%1 hours worked till now (overestimated %2)").arg(t
.totHours
).arg(t
.totOverestimation
));
1115 // update subtask if new added!
1118 // update statistics !!!!
1119 m_editedTasks
= m_tasks
;
1120 QMetaObject::invokeMethod(this, "selectedStartDate", Qt::QueuedConnection
, Q_ARG(QDate
, cal1
->selectedDate()));
1127 // attractor = 't' (top), 'b', 'l', 'r'
1128 void layoutInSquare( QList
<SakWidget
*> sortedWidgets
, QRect rect
, char attractor
)
1130 int w
= rect
.width();
1131 if (rect
.width() < 64) return;
1132 int maxw
= qMin(350, w
/2);
1133 QSize
size(maxw
, maxw
);
1134 if (sortedWidgets
.count() == 0) {
1136 } else if (sortedWidgets
.count() == 1) {
1137 sortedWidgets
[0]->setGeometry(rect
);
1138 } else if (sortedWidgets
.count() == 2) {
1140 if (attractor
== 'C') {
1141 off1
= QPoint(0,w
/4);
1142 off2
= QPoint(w
/2,w
/4);
1143 } else if (attractor
== 'T') {
1145 off2
= QPoint(w
/2,0);
1146 } else if (attractor
== 'B') {
1147 off1
= QPoint(0,w
/2);
1148 off2
= QPoint(w
/2,w
/2);
1149 } else if (attractor
== 'R') {
1150 off1
= QPoint(w
/2,0);
1151 off2
= QPoint(w
/2,w
/2);
1152 } else if (attractor
== 'L') {
1154 off2
= QPoint(0,w
/2);
1156 sortedWidgets
[0]->setGeometry(QRect(rect
.topLeft() + off1
, size
));
1157 sortedWidgets
[1]->setGeometry(QRect(rect
.topLeft() + off2
, size
));
1158 } else if (sortedWidgets
.count() == 3) {
1159 QPoint off1
, off2
, off3
;
1160 if (attractor
== 'T' || attractor
== 'C') {
1162 off2
= QPoint(w
/2,0);
1163 off3
= QPoint(w
/4,w
/2);
1164 } else if (attractor
== 'B') {
1165 off1
= QPoint(0,w
/2);
1166 off2
= QPoint(w
/2,w
/2);
1167 off3
= QPoint(w
/4,0);
1168 } else if (attractor
== 'R') {
1169 off1
= QPoint(w
/2,0);
1170 off2
= QPoint(w
/2,w
/2);
1171 off3
= QPoint(0,w
/4);
1172 } else if (attractor
== 'L') {
1174 off2
= QPoint(0,w
/2);
1175 off3
= QPoint(w
/2,w
/4);
1177 sortedWidgets
[0]->setGeometry(QRect(rect
.topLeft() + off1
, size
));
1178 sortedWidgets
[1]->setGeometry(QRect(rect
.topLeft() + off2
, size
));
1179 sortedWidgets
[2]->setGeometry(QRect(rect
.topLeft() + off3
, size
));
1180 } else if (sortedWidgets
.count() == 4) {
1184 QPoint
off4(w
/2,w
/2);
1185 sortedWidgets
[0]->setGeometry(QRect(rect
.topLeft() + off1
, size
));
1186 sortedWidgets
[1]->setGeometry(QRect(rect
.topLeft() + off2
, size
));
1187 sortedWidgets
[2]->setGeometry(QRect(rect
.topLeft() + off3
, size
));
1188 sortedWidgets
[3]->setGeometry(QRect(rect
.topLeft() + off4
, size
));
1190 Q_ASSERT(sortedWidgets
.count() <= 4);
1194 void layoutInRect( QList
<SakWidget
*> sortedWidgets
, QRect rect
, char attractor
)
1196 if (sortedWidgets
.count() == 0) return;
1197 int h
= rect
.height();
1198 int w
= rect
.width();
1199 int maxh
= qMin(350, h
);
1200 int maxw
= qMin(350, w
);
1201 if (sortedWidgets
.count() == 1) {
1203 sortedWidgets
[0]->setGeometry(QRect(rect
.topLeft() + QPoint((w
-maxh
)/2,(h
-maxh
)/2), QSize(maxh
,maxh
)));
1205 sortedWidgets
[0]->setGeometry(QRect(rect
.topLeft() + QPoint((w
-maxw
)/2,(h
-maxw
)/2), QSize(maxw
,maxw
)));
1208 } else if (sortedWidgets
.count() == 2) {
1210 sortedWidgets
[0]->setGeometry(QRect(rect
.topLeft() + QPoint(w
/2-maxh
,(h
-maxh
)/2), QSize(maxh
,maxh
)));
1211 sortedWidgets
[1]->setGeometry(QRect(rect
.topLeft() + QPoint(w
/2,(h
-maxh
)/2), QSize(maxh
,maxh
)));
1213 sortedWidgets
[0]->setGeometry(QRect(rect
.topLeft() + QPoint((h
-maxw
)/2,w
-maxw
), QSize(w
,w
)));
1214 sortedWidgets
[1]->setGeometry(QRect(rect
.topLeft() + QPoint((h
-maxw
)/2,w
/2), QSize(maxw
,maxw
)));
1218 if (h
< 64 || w
< 64) return;
1219 QList
<SakWidget
*> leftList
, rightList
;
1220 for (int i
=4; i
<sortedWidgets
.count(); i
++) {
1222 rightList
<< sortedWidgets
[i
];
1224 leftList
<< sortedWidgets
[i
];
1227 QRect
square(rect
.topLeft() + QPoint(h
/2, 0), QSize(h
,h
));
1228 layoutInSquare(sortedWidgets
.mid(0,4), square
, attractor
);
1230 QRect
leftRect(rect
.topLeft(), QSize(h
/2, h
));
1231 layoutInRect(leftList
, leftRect
, 'R');
1232 QRect
rightRect(rect
.topLeft() + QPoint((int)(0.75*w
),0), QSize(h
/2, h
));
1233 layoutInRect(rightList
, rightRect
, 'L');
1235 QRect
square(rect
.topLeft() + QPoint(0,w
/2), QSize(w
,w
));
1236 layoutInSquare(sortedWidgets
.mid(0,4), square
, attractor
);
1238 QRect
leftRect(rect
.topLeft(), QSize(w
, w
/2));
1239 layoutInRect(leftList
, leftRect
, 'B');
1240 QRect
rightRect(rect
.topLeft() + QPoint(0,(int)(0.75*h
)), QSize(w
, w
/2));
1241 layoutInRect(rightList
, rightRect
, 'T');
1245 QRect
Sak::Layouting( const QList
<SakWidget
*>& sortedWidgets
)
1247 QRect r
= m_desktopRect
;
1248 int height
= (int)(0.75 * r
.height());
1249 int width
= r
.width();
1251 int firstW
= width
/ 2 < height
? width
: height
* 2;
1252 int firstH
= firstW
/ 2;
1253 QRect
firstRect (r
.x() + (width
- firstW
) / 2, r
.y() + (height
- firstH
) / 2 + (int)(r
.height() * 0.25), firstW
, firstH
);
1255 layoutInRect(sortedWidgets
, firstRect
, 'C');
1256 return QRect(QPoint(r
.x(), r
.y()), QSize(r
.width(), (int)(0.25 * r
.height())));
1259 void Sak::grabKeyboard()
1261 #if defined(Q_WS_X11)
1263 // save current focused application
1264 XGetInputFocus((X11::Display
*)QX11Info::display(), &X11::CurrentFocusWindow
, &X11::CurrentRevertToReturn
);
1267 X11::XSetInputFocus((X11::Display
*)QX11Info::display(), QX11Info::appRootWindow(QX11Info::appScreen()), RevertToParent
, CurrentTime
);
1268 X11::XFlush((X11::Display
*)QX11Info::display());
1270 m_view
->grabKeyboard();
1276 trayIcon
->showMessage("SAK popup disabled", "SAK triggered a new event but no popup will be shown", QSystemTrayIcon::Information
, -1);
1280 // save changes first
1286 if (m_subtaskView
) {
1288 foreach(SakSubWidget
* w
, m_subwidgets
.values()) {
1289 w
->scene()->removeItem(w
);
1291 m_subwidgets
.clear();
1293 m_marker
->scene()->removeItem(m_marker
);
1297 foreach(SakWidget
* w
, m_widgets
.values()) {
1300 m_subtaskView
=false;
1304 m_subtaskView
= false;
1306 if (sender() == previewButton
) {
1307 m_previewing
= true;
1309 QDateTime now
= QDateTime::currentDateTime();
1310 QDateTime fromWeek
= now
;
1311 fromWeek
.setDate(now
.date().addDays(-now
.date().dayOfWeek() + 1));
1312 fromWeek
.setTime(QTime(0,0,0));
1313 QDateTime fromMonth
= now
;
1314 fromMonth
.setDate(now
.date().addDays(-now
.date().day()));
1315 fromMonth
.setTime(QTime(0,0,0));
1316 QDateTime fromToday
= now
;
1317 fromToday
.setTime(QTime(0,0,0));
1319 double weekHits
= 0;
1321 double monthHits
= 0;
1322 QHash
<QString
, double> dayStats
;
1323 QHash
<QString
, double> weekStats
;
1324 QHash
<QString
, double> monthStats
;
1326 foreach(const Task
& t
, m_tasks
.values()) {
1328 double m
= t
.workedHours(fromMonth
, now
);
1329 monthStats
[t
.title
] = m
;
1331 double w
= t
.workedHours(fromWeek
, now
);
1332 weekStats
[t
.title
] = w
;
1334 double d
= t
.workedHours(fromToday
, now
);
1335 dayStats
[t
.title
] = d
;
1342 foreach(const Task
& t
, m_tasks
.values()) {
1343 if (!t
.active
|| t
.title
== QString() || t
.title
== "<away>") continue;
1344 SakWidget
* test
= new SakWidget(t
);
1345 test
->setVisible(false);
1346 double d
= dayStats
[t
.title
];
1347 double w
= weekStats
[t
.title
];
1348 double m
= monthStats
[t
.title
];
1349 test
->setStatistics(d
, w
, m
, d
/dayHits
* 100.0, w
/weekHits
* 100.0, m
/monthHits
* 100.0);
1350 test
->setObjectName(t
.title
);
1351 connect (test
, SIGNAL(clicked(const QString
&, const QString
&)), this, SLOT(workingOnTask(const QString
&, const QString
&)));
1352 connect (test
, SIGNAL(clicked(const QString
&)), this, SLOT(popupSubtasks(const QString
&)));
1353 int historyPosition
= 1 + m_taskSelectionHistory
.indexOf(t
.title
);
1354 int rank
= historyPosition
!= 0 ? -1000000 * historyPosition
: -d
;
1355 m_widgets
.insertMulti( rank
, test
);
1358 m_widgetsIterator
= m_widgets
.begin();
1359 if (m_widgetsIterator
!= m_widgets
.end()) {
1360 m_widgetsIterator
.value()->showDetails(true);
1364 const QList
<SakWidget
*>& values
= m_widgets
.values();
1365 QRect messageRect
= Layouting((const QList
<SakWidget
*>&)values
);
1366 foreach(SakWidget
* w
, values
) {
1367 m_view
->scene()->addItem(w
);
1372 // add the message item
1373 SakMessageItem
* sakMessage
= new SakMessageItem(bodyEdit
->toPlainText());
1374 sakMessage
->setGeometry(messageRect
);
1375 m_view
->scene()->addItem(sakMessage
);
1378 // add the exit item
1379 SakExitItem
* exitItem
= new SakExitItem(QPixmap(":/images/exit.png"));
1380 QRect r
= m_desktopRect
;
1381 connect(exitItem
, SIGNAL(exit()), this, SLOT(clearView()));
1382 exitItem
->setPos(r
.width() - exitItem
->boundingRect().width(), 0);
1383 m_view
->scene()->addItem(exitItem
);
1384 exitItem
->setZValue(1e8
);
1388 m_view
->setGeometry( QRect(m_desktopRect
)/*.adjusted(200,200,-200,-200 )*/ );
1392 #if defined(Q_WS_WIN)
1393 SetForegroundWindow(m_view
->winId());
1395 qApp
->alert(m_view
, 5000);
1399 void Sak::popupSubtasks(const QString
& _taskname
) {
1401 killTimer(m_timeoutPopup
);
1403 QString taskname
= _taskname
;
1404 if (taskname
.isEmpty()) {
1405 if ( m_taskSelectionHistory
.isEmpty() ) {
1408 taskname
= m_taskSelectionHistory
.back();
1410 QString subtaskName
;
1411 if (!m_subtaskSelectionHistory
[taskname
].isEmpty()) {
1412 subtaskName
= m_subtaskSelectionHistory
[taskname
].back();
1414 workingOnTask(taskname
, subtaskName
);
1417 m_subtaskView
= true;
1419 QRect r
= m_desktopRect
;
1423 // hide tasks to show subtasks
1424 foreach(SakWidget
* w
, m_widgets
.values()) {
1428 QHash
<QString
, Task
>::const_iterator itr
= m_tasks
.find(taskname
);
1429 if (itr
== m_tasks
.end()) {
1430 workingOnTask(taskname
, "");
1433 const Task
& t(*itr
);
1434 QHash
< QString
, Task::SubTask
>::const_iterator titr
= t
.subTasks
.begin(), tend
= t
.subTasks
.end();
1435 m_subwidgets
.clear();
1436 QDateTime
now(QDateTime::currentDateTime());
1437 for(; titr
!= tend
; titr
++) {
1438 if (!titr
->active
) continue;
1439 SakSubWidget
* sw
= new SakSubWidget(t
, *titr
);
1440 sw
->setGeometry(QRectF(0,0,w
,h
));
1441 connect (sw
, SIGNAL(clicked(const QString
&, const QString
&)), this, SLOT(workingOnTask(const QString
&, const QString
&)));
1442 connect (sw
, SIGNAL(focused()), this, SLOT(focusedSubTask()));
1445 QHash
< QString
, QList
< QString
> >::const_iterator hhitr
= m_subtaskSelectionHistory
.find(t
.title
);
1446 if (hhitr
!= m_subtaskSelectionHistory
.end()) {
1447 int index
= hhitr
->indexOf(titr
->title
);
1448 if (index
!= 0) rank
= now
.addDays(index
+1);
1451 QHash
< QString
, QList
<Task::Hit
> >::const_iterator hitr
= t
.hits
.find(titr
.key());
1452 if (hitr
!= t
.hits
.end()) {
1453 if ( hitr
.value().count() && hitr
.value().last().timestamp
.isValid())
1454 rank
= hitr
.value().last().timestamp
;
1456 rank
= now
.addDays(-999);
1459 m_subwidgets
.insertMulti( - rank
.toTime_t(), sw
);
1462 const QList
<SakSubWidget
*>& values
= m_subwidgets
.values();
1464 // create possible completion
1465 QStringList subtaskSortedList
;
1466 foreach(SakSubWidget
* sub
, values
) {
1467 subtaskSortedList
.push_back(sub
->subtask().title
);
1469 m_subtaskCompleter
= new QCompleter(subtaskSortedList
);
1470 m_subtaskCompleter
->setCaseSensitivity(Qt::CaseInsensitive
);
1472 // QRect messageRect = Layouting((const QList<SakWidget*>&)values);
1474 m_subwidgetsIterator
= m_subwidgets
.begin();
1475 m_subWidgetRank
= 0;
1477 // the one with text
1478 SakSubWidget
* tmpSw
= new SakSubWidget(t
, Task::SubTask(), true);
1479 QCompleter
* completer
= new QCompleter(subtaskSortedList
);
1480 completer
->setCaseSensitivity(Qt::CaseInsensitive
);
1481 completer
->setCompletionMode(QCompleter::InlineCompletion
);
1482 ((QLineEdit
*)tmpSw
->widget())->setCompleter(completer
);
1483 m_view
->scene()->addItem(tmpSw
);
1484 tmpSw
->setGeometry(QRectF(0,0,w
,h
));
1485 connect (tmpSw
, SIGNAL(clicked(const QString
&, const QString
&)), this, SLOT(workingOnTask(const QString
&, const QString
&)));
1486 connect (tmpSw
, SIGNAL(focused()), this, SLOT(focusedSubTask()));
1488 m_subwidgets
.insertMulti(-QDateTime::currentDateTime().addDays(999).toTime_t(), tmpSw
);
1490 m_subWidgetRank
+= values
.size() != 0;
1492 if (m_subwidgetsIterator
!= m_subwidgets
.end()) {
1493 m_subwidgetsIterator
.value()->showDetails(true);
1494 m_view
->scene()->setFocusItem(m_subwidgetsIterator
.value(), Qt::MouseFocusReason
);
1495 m_subwidgetsIterator
.value()->widget()->setFocus(Qt::MouseFocusReason
);
1497 m_view
->scene()->setFocusItem(tmpSw
, Qt::MouseFocusReason
);
1498 tmpSw
->widget()->setFocus(Qt::MouseFocusReason
);
1501 // add a marker to highligh current selection
1502 m_marker
= new QGraphicsEllipseItem(r
.width()/2 - 280, r
.height()/2 - 51, 20,20);
1503 m_marker
->setBrush(Qt::red
);
1504 m_view
->scene()->addItem(m_marker
);
1505 m_marker
->setVisible(true);
1507 layoutSubTasks(m_subwidgets
, m_subWidgetRank
);
1509 // Finally add subwidgets
1510 for(int i
=0; i
<values
.size(); i
++) {
1511 SakSubWidget
* sw
= values
[i
];
1512 m_view
->scene()->addItem(sw
);
1517 void Sak::scrollTasks(int npos
) {
1518 SakWidget
* currentShowing
= 0;
1520 for (int i
=npos
; i
<0; i
++) {
1521 if (m_widgetsIterator
== m_widgets
.end()) return;
1522 currentShowing
= m_widgetsIterator
!= m_widgets
.end() ? m_widgetsIterator
.value() : 0;
1523 if (m_widgetsIterator
== m_widgets
.begin()) m_widgetsIterator
= m_widgets
.end();
1524 m_widgetsIterator
--;
1527 for (int i
=0; i
<npos
; i
++) {
1528 if (m_widgetsIterator
== m_widgets
.end()) return;
1529 currentShowing
= m_widgetsIterator
.value();
1530 m_widgetsIterator
++;
1531 if (m_widgetsIterator
== m_widgets
.end()) m_widgetsIterator
= m_widgets
.begin();
1534 if (currentShowing
&& m_widgetsIterator
!= m_widgets
.end()) {
1535 currentShowing
->showDetails(false);
1536 m_widgetsIterator
.value()->showDetails(true);
1540 void Sak::scrollSubTasks(int npos
) {
1541 SakSubWidget
* currentShowing
= 0;
1543 for (int i
=npos
; i
<0; i
++) {
1544 if (m_subwidgetsIterator
== m_subwidgets
.end()) return;
1545 currentShowing
= m_subwidgetsIterator
!= m_subwidgets
.end() ? m_subwidgetsIterator
.value() : 0;
1546 if (m_subwidgetsIterator
== m_subwidgets
.begin()) {
1547 m_subwidgetsIterator
= m_subwidgets
.end();
1548 m_subWidgetRank
= m_subwidgets
.count();
1550 m_subwidgetsIterator
--;
1554 for (int i
=0; i
<npos
; i
++) {
1555 if (m_subwidgetsIterator
== m_subwidgets
.end()) return;
1556 currentShowing
= m_subwidgetsIterator
.value();
1557 m_subwidgetsIterator
++;
1559 if (m_subwidgetsIterator
== m_subwidgets
.end()) {
1560 m_subwidgetsIterator
= m_subwidgets
.begin();
1561 m_subWidgetRank
= 0;
1565 if (currentShowing
&& m_subwidgetsIterator
!= m_subwidgets
.end()) {
1566 currentShowing
->showDetails(false);
1567 m_subwidgetsIterator
.value()->showDetails(true);
1568 m_view
->scene()->setFocusItem(m_subwidgetsIterator
.value(), Qt::MouseFocusReason
);
1569 m_subwidgetsIterator
.value()->widget()->setFocus(Qt::MouseFocusReason
);
1571 layoutSubTasks(m_subwidgets
, m_subWidgetRank
);
1574 void Sak::focusedSubTask()
1576 SakSubWidget
* w
= dynamic_cast<SakSubWidget
*>(sender());
1578 QMap
<int, SakSubWidget
*>::iterator itr
= m_subwidgets
.begin(), end
=m_subwidgets
.end();
1579 for(int i
=0; itr
!= end
; i
++,itr
++) {
1580 if (itr
.value() == w
) {
1581 m_subwidgetsIterator
= itr
;
1582 m_subWidgetRank
= i
;
1588 //END Tasks >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
1591 //BEGIN settings ================================
1594 void Sak::setupSettingsWidget()
1596 m_settings
= new QMainWindow();
1597 m_settings
->setMinimumHeight(650);
1598 m_settings
->setMinimumWidth(700);
1599 QWidget
* centralWidget
= new QWidget
;
1600 m_settings
->setCentralWidget(centralWidget
);
1602 QVBoxLayout
* theMainLayout
= new QVBoxLayout
;
1603 centralWidget
->setLayout(theMainLayout
);
1605 tabs
= new QTabWidget
;
1606 theMainLayout
->addWidget(tabs
);
1607 previewButton
= new QPushButton("Preview");
1608 theMainLayout
->addWidget(previewButton
);
1614 tabs
->addTab(tab1
, "Tasks");
1615 tabs
->addTab(tab2
, "General");
1616 tabs
->addTab(tab4
, "Statistics");
1617 tabs
->addTab(tab3
, "Advanced");
1620 QMenuBar
* mainMenu
= new QMenuBar
;
1621 m_settings
->setMenuBar(mainMenu
);
1622 QMenu
* programMenu
= mainMenu
->addMenu("Program");
1623 programMenu
->addAction(quitAction
);
1624 programMenu
->addAction(minimizeAction
);
1625 QMenu
* dbMenu
= mainMenu
->addMenu("Db");
1626 dbMenu
->addAction(flushAction
);
1627 dbMenu
->addAction(openAction
);
1628 // dbMenu->addAction(saveAsDbAction);
1629 dbMenu
->addAction(exportDbCsvAction
);
1631 dbMenu
->addAction(gmailLoginAction
);
1632 dbMenu
->addAction(saveToGmailAction
);
1633 if (!m_gmail
->isValid()) {
1634 gmailLoginAction
->setEnabled(false);
1635 saveToGmailAction
->setEnabled(false);
1638 QMenu
* actionsMenu
= mainMenu
->addMenu("Actions");
1639 actionsMenu
->addAction(startAction
);
1640 actionsMenu
->addAction(stopAction
);
1642 QVBoxLayout
*mainLayout
= new QVBoxLayout
;
1643 QGridLayout
*messageLayout
= new QGridLayout
;
1644 durationLabel
= new QLabel(tr("Interval:"));
1645 durationLabel1
= new QLabel(tr("(effective only after restart)"));
1647 QSettings
settings(QSettings::IniFormat
, QSettings::UserScope
, "ZanzaSoft", "SAK");
1648 durationSpinBox
= new QSpinBox
;
1649 durationSpinBox
->setSingleStep(1);
1650 durationSpinBox
->setRange(1, 1440);
1651 durationSpinBox
->setSuffix(" minutes");
1652 durationSpinBox
->setValue(qMin(1440, qMax(1, settings
.value("Ping interval", 15).toInt())));
1653 durationSpinBox
->setCorrectionMode(QAbstractSpinBox::CorrectToNearestValue
);
1655 bodyLabel
= new QLabel(tr("Message:"));
1656 bodyEdit
= new QTextEdit
;
1657 bodyEdit
->setPlainText( settings
.value("Message", "<h1>Enter a message here!</h1>").toString() );
1659 messageLayout
->addWidget(durationLabel
, 1, 0);
1660 messageLayout
->addWidget(durationSpinBox
, 1, 1);
1661 messageLayout
->addWidget(durationLabel1
, 1, 2);
1662 messageLayout
->addWidget(bodyLabel
, 3, 0);
1663 messageLayout
->addWidget(bodyEdit
, 3, 1, 2, 4);
1664 messageLayout
->setColumnStretch(3, 1);
1665 messageLayout
->setRowStretch(4, 1);
1666 mainLayout
->addLayout(messageLayout
);
1667 tab2
->setLayout(mainLayout
);
1669 mainLayout
= new QVBoxLayout
;
1670 tab1
->setLayout(mainLayout
);
1672 tasksTree
= new QTreeWidget
;
1673 tasksTree
->setSizePolicy(QSizePolicy::Expanding
, QSizePolicy::Expanding
);
1674 tasksTree
->setColumnCount(3);
1675 tasksTree
->setColumnWidth(0, 300);
1676 tasksTree
->setColumnWidth(1, 65);
1677 tasksTree
->setIconSize(QSize(32,32));
1678 connect(tasksTree
, SIGNAL(itemDoubleClicked(QTreeWidgetItem
*,int)), this, SLOT(doubleClickedTask(QTreeWidgetItem
*,int)));
1681 taskTextEditor
= new QTextEdit
;
1682 taskTextEditor
->setFixedHeight(100);
1683 taskTextEditor
->setSizePolicy(QSizePolicy::Expanding
, QSizePolicy::Preferred
);
1684 connect(taskTextEditor
, SIGNAL(textChanged()), this, SLOT(commitCurrentTask()));
1687 taskTitleEditor
= new QLineEdit
;
1688 taskTitleEditor
->setFixedHeight(20);
1689 taskTitleEditor
->setSizePolicy(QSizePolicy::Expanding
, QSizePolicy::Preferred
);
1690 connect(taskTitleEditor
, SIGNAL(editingFinished()), this, SLOT(commitCurrentTask()));
1692 taskPixmapViewer
= new PixmapViewer
;
1693 mainLayout
->addWidget(tasksTree
, 2);
1694 QHBoxLayout
* detailsLayout
= new QHBoxLayout
;
1695 QVBoxLayout
* editsLayout
= new QVBoxLayout
;
1696 detailsLayout
->addWidget(taskPixmapViewer
);
1697 connect(taskPixmapViewer
, SIGNAL(changed()), this, SLOT(commitCurrentTask()));
1698 editsLayout
->addWidget(taskTitleEditor
);
1699 editsLayout
->addWidget(taskTextEditor
);
1700 QHBoxLayout
* datesLayout
= new QHBoxLayout
;
1701 datesLayout
->addWidget(new QLabel("Due: "));
1702 dueEditor
= new QDateEdit
;
1703 dueEditor
->setMinimumDate(QDate(2000,1,1));
1704 datesLayout
->addWidget(dueEditor
);
1705 datesLayout
->addWidget(new QLabel("Estimated: "));
1706 estimatedHoursEditor
= new QSpinBox
;
1707 estimatedHoursEditor
->setRange(0, 1e5
);
1708 estimatedHoursEditor
->setSuffix("hours");
1709 datesLayout
->addWidget(estimatedHoursEditor
);
1710 editsLayout
->addLayout(datesLayout
);
1711 detailsLayout
->addLayout(editsLayout
);
1712 connect(dueEditor
, SIGNAL(editingFinished()), this, SLOT(commitCurrentTask()));
1713 connect(estimatedHoursEditor
,SIGNAL(editingFinished()), this, SLOT(commitCurrentTask()));
1715 QVBoxLayout
* colorsLayout
= new QVBoxLayout
;
1716 bgColorButton
= new QPushButton("bg\ncolor");
1717 bgColorButton
->setToolTip("Background color");
1718 bgColorButton
->setSizePolicy(QSizePolicy(QSizePolicy::Preferred
, QSizePolicy::MinimumExpanding
));
1720 fgColorButton
= new QPushButton("fg\ncolor");
1721 fgColorButton
->setToolTip("Foreground color");
1722 fgColorButton
->setSizePolicy(QSizePolicy(QSizePolicy::Preferred
, QSizePolicy::MinimumExpanding
));
1725 // fix Windows XP "style"
1726 bgColorButton
->setStyle(new QWindowsStyle());
1727 fgColorButton
->setStyle(new QWindowsStyle());
1730 colorsLayout
->addWidget(bgColorButton
);
1731 colorsLayout
->addWidget(fgColorButton
);
1732 detailsLayout
->addLayout(colorsLayout
);
1734 mainLayout
->addLayout(detailsLayout
);
1736 QVBoxLayout
* tab4MainLayout
= new QVBoxLayout(tab4
);
1737 //taskSelector = new QComboBox;
1738 summaryList
= newTaskSummaryList();
1739 QTabWidget
* tabs
= new QTabWidget
;
1740 tabs
->setTabPosition(QTabWidget::East
);
1741 tabs
->addTab(summaryList
, "List");
1742 QGraphicsView
* summaryView
= new QGraphicsView
;
1743 summaryView
->setScene(new QGraphicsScene
);
1744 TaskSummaryPieChart
* chart
= new TaskSummaryPieChart
;
1745 summaryView
->scene()->addItem(new TaskSummaryPieChart
);
1746 summaryView
->centerOn(chart
);
1747 tabs
->addTab(summaryView
, "Chart");
1748 tab4MainLayout
->addWidget(tabs
);
1749 cal3
= new QCalendarWidget
;
1750 cal3
->setMinimumSize(QSize(250,200));
1751 cal4
= new QCalendarWidget
;
1752 cal4
->setMinimumSize(QSize(250,200));
1753 cal3
->setSizePolicy(QSizePolicy::MinimumExpanding
, QSizePolicy::Fixed
);
1754 cal4
->setSizePolicy(QSizePolicy::MinimumExpanding
, QSizePolicy::Fixed
);
1755 QHBoxLayout
* calsLayout
= new QHBoxLayout
;
1756 calsLayout
->addWidget(cal3
);
1757 calsLayout
->addWidget(cal4
);
1758 cal4
->setSelectedDate(QDate::currentDate().addDays(1));
1759 tab4MainLayout
->addLayout(calsLayout
);
1762 QVBoxLayout
* tab3MainLayout
= new QVBoxLayout(tab3
);
1763 //taskSelector = new QComboBox;
1764 hitsList
= newHitsList();
1765 connect(hitsList
, SIGNAL(currentItemChanged(QTreeWidgetItem
*,QTreeWidgetItem
*)), this, SLOT(hitsSelectedInList(QTreeWidgetItem
*,QTreeWidgetItem
*)));
1766 hitsTimeline
= new Timeline
;
1767 connect(hitsTimeline
, SIGNAL(hitSelected(HitItem
*)), this, SLOT(hitsSelectedInTimeline(HitItem
*)));
1768 cal1
= new QCalendarWidget
;
1769 cal1
->setMinimumSize(QSize(250,200));
1770 cal2
= new QCalendarWidget
;
1771 cal2
->setMinimumSize(QSize(250,200));
1772 cal1
->setSizePolicy(QSizePolicy::MinimumExpanding
, QSizePolicy::Fixed
);
1773 cal2
->setSizePolicy(QSizePolicy::MinimumExpanding
, QSizePolicy::Fixed
);
1774 //tab3MainLayout->addWidget(taskSelector);
1776 calsLayout
= new QHBoxLayout
;
1777 calsLayout
->addWidget(cal1
);
1778 calsLayout
->addWidget(cal2
);
1779 cal2
->setSelectedDate(QDate::currentDate().addDays(1));
1780 tab3MainLayout
->addWidget(hitsList
);
1781 tab3MainLayout
->addWidget(hitsTimeline
);
1782 tab3MainLayout
->addLayout(calsLayout
);
1786 m_settings
->setWindowTitle(tr("SaK"));
1787 m_settings
->resize(400, 300);
1791 QTreeWidget
* Sak::newHitsList()
1793 QTreeWidget
* hitsList
= new QTreeWidget
;
1794 hitsList
->setColumnCount(4);
1795 hitsList
->setColumnWidth(0, 200);
1796 hitsList
->setColumnWidth(1, 150);
1797 hitsList
->setColumnWidth(2, 150);
1798 hitsList
->setColumnWidth(3, 150);
1799 hitsList
->setColumnWidth(4, 150);
1800 hitsList
->setIconSize(QSize(24,24));
1801 hitsList
->setSortingEnabled(true);
1802 hitsList
->setItemDelegateForColumn(0, new MyDateItemDelegate
);
1803 hitsList
->setItemDelegateForColumn(1, new TaskItemDelegate(this));
1804 hitsList
->setItemDelegateForColumn(2, new SubTaskItemDelegate(this));
1805 QTreeWidgetItem
* header
= new QTreeWidgetItem
;
1806 header
->setText(0, "Date/Time");
1807 header
->setText(1, "Task");
1808 header
->setText(2, "Subtask");
1809 header
->setText(3, "Duration (min)");
1810 header
->setText(4, "Overestimation");
1811 hitsList
->setHeaderItem(header
);
1812 hitsList
->setSizePolicy(QSizePolicy::MinimumExpanding
, QSizePolicy::MinimumExpanding
);
1816 QTreeWidget
* Sak::newTaskSummaryList()
1818 QTreeWidget
* taskSummaryList
= new QTreeWidget
;
1819 taskSummaryList
->setColumnCount(4);
1820 taskSummaryList
->setColumnWidth(0, 250);
1821 taskSummaryList
->setColumnWidth(1, 150);
1822 taskSummaryList
->setColumnWidth(1, 150);
1823 taskSummaryList
->setIconSize(QSize(24,24));
1824 taskSummaryList
->setSortingEnabled(true);
1825 QTreeWidgetItem
* header
= new QTreeWidgetItem
;
1826 header
->setText(0, "Task");
1827 header
->setText(1, "Hours");
1828 taskSummaryList
->setHeaderItem(header
);
1829 taskSummaryList
->setSizePolicy(QSizePolicy::MinimumExpanding
, QSizePolicy::MinimumExpanding
);
1830 taskSummaryList
->setEnabled(true);
1831 return taskSummaryList
;
1834 void Sak::setVisible(bool visible
)
1836 minimizeAction
->setEnabled(visible
);
1837 maximizeAction
->setEnabled(!m_settings
->isMaximized());
1838 restoreAction
->setEnabled(m_settings
->isMaximized() || !visible
);
1839 m_settings
->setVisible(visible
);
1842 void Sak::createActions()
1844 minimizeAction
= new QAction(tr("Mi&nimize"), m_settings
);
1845 connect(minimizeAction
, SIGNAL(triggered()), m_settings
, SLOT(hide()));
1847 maximizeAction
= new QAction(tr("Ma&ximize"), m_settings
);
1848 connect(maximizeAction
, SIGNAL(triggered()), m_settings
, SLOT(showMaximized()));
1850 restoreAction
= new QAction(tr("&Restore"), m_settings
);
1851 connect(restoreAction
, SIGNAL(triggered()), m_settings
, SLOT(showNormal()));
1853 quitAction
= new QAction(tr("&Quit"), m_settings
);
1854 connect(quitAction
, SIGNAL(triggered()), qApp
, SLOT(quit()));
1856 startAction
= new QAction(tr("Start polling"), m_settings
);
1857 connect(startAction
, SIGNAL(triggered()), this, SLOT(start()));
1859 stopAction
= new QAction(tr("Pause polling"), m_settings
);
1860 connect(stopAction
, SIGNAL(triggered()), this, SLOT(pause()));
1862 flushAction
= new QAction(tr("&Flush data/settings to disk"), m_settings
);
1863 connect(flushAction
, SIGNAL(triggered()), this, SLOT(flush()));
1865 saveAsDbAction
= new QAction(tr("Backup as"), m_settings
);
1866 // connect(saveAsDbAction, SIGNAL(triggered()), this, SLOT(saveAsDb()));
1868 exportDbCsvAction
= new QAction(tr("Export hits in CSV format"), m_settings
);
1869 connect(exportDbCsvAction
, SIGNAL(triggered()), this, SLOT(exportDbCsv()));
1872 saveToGmailAction
= new QAction(tr("Store in your gmail account free space"), m_settings
);
1873 connect(saveToGmailAction
, SIGNAL(triggered()), this, SLOT(saveToGmail()));
1875 gmailLoginAction
= new QAction(tr("Log in gmail"), m_settings
);
1876 connect(gmailLoginAction
, SIGNAL(triggered()), this, SLOT(logInGmail()));
1878 saveToGmailAction
= NULL
;
1879 gmailLoginAction
= NULL
;
1882 openAction
= new QAction(tr("Import a task from file"), m_settings
);
1883 connect(openAction
, SIGNAL(triggered()), this, SLOT(open()));
1885 m_addHitAction
= new QAction("Add hit", m_settings
);
1886 m_addHitMenu
= new QMenu(m_settings
);
1887 m_addHitAction
->setText("Add hit");
1888 m_addHitMenu
->addAction(m_addHitAction
);
1889 connect(m_addHitAction
, SIGNAL(triggered()), this, SLOT(addDefaultHit()));
1891 m_exportDataAction
= new QAction("Export data", m_settings
);
1892 m_exportDataAction
->setText("Export data");
1893 m_addHitMenu
->addAction(m_exportDataAction
);
1894 connect(m_exportDataAction
, SIGNAL(triggered()), this, SLOT(exportHits()));
1896 m_addTaskAction
= new QAction("Add task", m_settings
);
1897 m_addTaskMenu
= new QMenu(m_settings
);
1898 m_addTaskAction
->setText("Add task");
1899 m_addTaskMenu
->addAction(m_addTaskAction
);
1900 connect(m_addTaskAction
, SIGNAL(triggered()), this, SLOT(addDefaultTask()));
1903 void Sak::trayIconActivated(QSystemTrayIcon::ActivationReason reason
)
1905 if (reason
== QSystemTrayIcon::DoubleClick
) {
1910 //END Settings <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<