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
)
78 , m_changedTask(false)
79 , m_subtaskView(false)
81 m_desktopRect
= qApp
->desktop()->rect();
83 m_subtaskCompleter
= 0;
84 summaryList
= hitsList
= 0;
87 if (QCoreApplication::arguments().contains("--clear")) {
88 QHash
<QString
, Task
>::iterator itr
= m_tasks
.begin();
89 while(itr
!= m_tasks
.end()) {
95 if (m_tasks
.count() <= 0)
101 m_autoSaveTimer
= startTimer(1000 * 60 * 45); // every 45 minutes
104 // Need to go here, or after plasma reboot the icon will disappear
105 trayIconMenu
= new QMenu(m_settings
);
106 //trayIconMenu->addAction(minimizeAction);
107 //trayIconMenu->addAction(maximizeAction);
108 //trayIconMenu->addAction(restoreAction);
109 //trayIconMenu->addSeparator();
110 trayIconMenu
->addAction(startAction
);
111 trayIconMenu
->addAction(stopAction
);
112 trayIconMenu
->addAction(flushAction
);
113 trayIconMenu
->addSeparator();
114 trayIconMenu
->addAction(quitAction
);
115 trayIcon
= new QSystemTrayIcon(this);
116 trayIcon
->setContextMenu(trayIconMenu
);
117 trayIcon
->setIcon( QIcon(":/images/icon.png") );
118 trayIcon
->setToolTip( tr("Sistema Anti Kazzeggio") );
120 connect(trayIcon
, SIGNAL(activated(QSystemTrayIcon::ActivationReason
)), this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason
)));
121 trayIcon
->installEventFilter(this);
124 m_settings
->setWindowIcon( QIcon(":/images/icon.png") );
125 m_settings
->setWindowTitle("SaK - Sistema Anti Kazzeggio");
130 m_backupper
= new Backupper
;
131 m_incremental
= new Incremental
;
133 m_gmail
= new GmailPyInterface
;
138 // load the data model
139 QSettings
settings(QSettings::IniFormat
, QSettings::UserScope
, "ZanzaSoft", "SAK");
140 QByteArray tasksArray
= settings
.value("tasks").toByteArray();
141 QDataStream
stream(&tasksArray
, QIODevice::ReadWrite
);
142 stream
.setVersion(QDataStream::Qt_4_3
);
146 QDir
saveDir(QFileInfo(settings
.fileName()).dir());
147 saveDir
.mkdir("SakTasks");
148 saveDir
.cd("SakTasks");
149 QStringList nameFilters
;
150 nameFilters
<< "*.xml";
151 QStringList files
= saveDir
.entryList( nameFilters
, QDir::Files
);
152 foreach (QString taskXmlFileName
, files
) {
153 Task
t( loadTaskFromFile(saveDir
.filePath(taskXmlFileName
)) );
154 m_tasks
[t
.title
] = t
;
159 // add subtasks, if missing
161 QHash
<QString
, Task
>::iterator itr
= m_tasks
.begin();
162 while(itr
!= m_tasks
.end()) {
163 itr
->updateSubTasks();
169 Task
& awayTask
= m_tasks
["<away>"];
170 awayTask
.title
= "<away>";
171 awayTask
.fgColor
= Qt::gray
;
172 awayTask
.bgColor
= Qt::white
;
173 awayTask
.icon
= QPixmap(":/images/away.png");
175 m_editedTasks
= m_tasks
;
180 interactiveMergeHits();
182 m_editedTasks
= m_tasks
;
184 setupSettingsWidget();
187 m_settings
->installEventFilter(this);
188 hitsList
->installEventFilter(this);
189 tasksTree
->installEventFilter(this);
190 tasksTree
->setUniformRowHeights(false);
191 QTreeWidgetItem
* headerItem
= new QTreeWidgetItem
;
192 headerItem
->setSizeHint(0 , QSize(0,0));
193 headerItem
->setSizeHint(1 , QSize(0,0));
194 headerItem
->setSizeHint(2 , QSize(0,0));
195 tasksTree
->setHeaderItem(headerItem
);
197 connect(bgColorButton
, SIGNAL(clicked()), this, SLOT(selectColor()));
198 connect(fgColorButton
, SIGNAL(clicked()), this, SLOT(selectColor()));
199 connect(previewButton
, SIGNAL(clicked()), this, SLOT(popup()));
200 connect(tasksTree
, SIGNAL(itemSelectionChanged()), this, SLOT(selectedTask()));
201 connect(tasksTree
, SIGNAL(itemClicked(QTreeWidgetItem
*,int)), this, SLOT(selectedTask()));
204 connect(cal1
, SIGNAL(clicked(QDate
)), this, SLOT(selectedStartDate(QDate
)));
205 connect(cal2
, SIGNAL(clicked(QDate
)), this, SLOT(selectedEndDate(QDate
)));
206 connect(cal3
, SIGNAL(clicked(QDate
)), this, SLOT(selectedStartDate(QDate
)));
207 connect(cal4
, SIGNAL(clicked(QDate
)), this, SLOT(selectedEndDate(QDate
)));
208 connect(hitsList
, SIGNAL(itemChanged(QTreeWidgetItem
*,int)), this, SLOT(hitsListItemChanged(QTreeWidgetItem
*,int)));
212 m_view
->setScene(new QGraphicsScene
);
213 m_view
->scene()->setSceneRect(m_desktopRect
);
215 m_view
->installEventFilter(this);
216 m_view
->setFrameStyle(QFrame::NoFrame
);
217 m_view
->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
218 m_view
->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
219 m_view
->setWindowFlags(m_view
->windowFlags() | Qt::WindowStaysOnTopHint
| Qt::ToolTip
);
220 //m_view->setWindowModality(Qt::ApplicationModal);
221 m_view
->setAttribute(Qt::WA_QuitOnClose
, false);
222 // enable transparency with Qt4.5
223 m_view
->setAttribute(Qt::WA_TranslucentBackground
, true);
224 m_view
->setWindowIcon( QIcon(":/images/icon.png") );
225 m_view
->setWindowTitle("SaK - Sistema Anti Kazzeggio");
227 m_currentInterval
= durationSpinBox
->value();
228 m_currentInterval
= qMax((int)1, qMin((int)1440, m_currentInterval
));
229 qDebug() << "SAK: pinging interval " << m_currentInterval
<< Task::hours(m_currentInterval
) << " hours ";
231 hitsTimeline
->setPeriod(QDateTime(cal1
->selectedDate()), QDateTime(cal2
->selectedDate()));
232 populateHitsList(createHitsList(QDateTime(cal1
->selectedDate()), QDateTime(cal2
->selectedDate())));
237 m_currentInterval
= qMax((int)1, m_currentInterval
);
239 int msecs
= (int)(Task::hours(m_currentInterval
)*3600.0*1000.0 / 2);
240 m_timerId
= startTimer( msecs
);
241 m_nextTimerEvent
= QDateTime::currentDateTime().addMSecs(msecs
);
242 startAction
->setEnabled(false);
243 stopAction
->setEnabled(true);
245 startAction
->setEnabled(true);
246 stopAction
->setEnabled(false);
253 killTimer(m_timerId
);
255 stopAction
->setEnabled(false);
256 startAction
->setEnabled(true);
259 stopAction
->setEnabled(true);
260 startAction
->setEnabled(false);
264 Task
Sak::loadTaskFromFile(const QString
& filePath
)
266 QFile
taskXmlFile(filePath
);
268 qDebug() << "Examine task file " << taskXmlFile
.fileName();
269 if (!taskXmlFile
.open(QIODevice::ReadOnly
)) {
270 qDebug() << "Failed opening xml file " << taskXmlFile
.fileName();
272 QByteArray data
= taskXmlFile
.readLine();
273 QXmlStreamReader
stream(data
);
274 QXmlStreamReader::TokenType token
= stream
.readNext(); // skip StartDocument
275 token
= stream
.readNext();
276 if ( token
!= QXmlStreamReader::Comment
) {
277 qDebug() << "Skip file " << taskXmlFile
.fileName() << " (want a file starting with a comment representing MD5, got" << token
<< ")";
280 QString md5
= stream
.text().toString().trimmed();
281 qDebug() << "md5 = " << md5
;
284 data
= taskXmlFile
.readAll();
285 if ( md5
!= QCryptographicHash::hash(data
, QCryptographicHash::Md5
).toHex() ) {
286 if (QMessageBox::No
== QMessageBox::warning(0, "Corrupted file!",
287 QString("Check of file " + taskXmlFile
.fileName() + " failed (maybe it has been edited by hand).\nDo you want to load it anyway?" )
288 ,QMessageBox::Yes
| QMessageBox::No
) ) {
289 qDebug() << "Skip file " << taskXmlFile
.fileName() << " (bad md5 sum)";
296 stream
.addData(data
);
298 if ( stream
.readNext() != QXmlStreamReader::StartDocument
) {
299 qDebug() << "Skip file " << taskXmlFile
.fileName() << " (want start document)";
303 if (stream
.error() != QXmlStreamReader::NoError
) {
304 qDebug() << "Error reading task data from file " << taskXmlFile
.fileName() << ":" << stream
.errorString();
307 // QFile tmp("/tmp/" + t.title + ".xml");
308 // tmp.open(QIODevice::ReadWrite);
309 // QXmlStreamWriter ss(&tmp);
310 // ss.setAutoFormatting(true);
311 // ss.setAutoFormattingIndent(2);
312 // ss.writeStartDocument();
314 // ss.writeEndDocument();
327 if (!m_settings
) return;
328 m_backupper
->doCyclicBackup();
329 QSettings
settings(QSettings::IniFormat
, QSettings::UserScope
, "ZanzaSoft", "SAK");
330 // QByteArray tasksArray;
331 // QDataStream stream(&tasksArray, QIODevice::ReadWrite);
332 // stream.setVersion(QDataStream::Qt_4_0);
333 // stream << m_tasks;
334 // settings.setValue("tasks", tasksArray);
335 settings
.setValue("Ping interval", durationSpinBox
->value());
336 settings
.setValue("Message", bodyEdit
->toPlainText());
339 QDir
saveDir(QFileInfo(settings
.fileName()).dir());
340 saveDir
.mkdir("SakTasks");
341 saveDir
.cd("SakTasks");
343 foreach(Task t
, m_tasks
) {
344 if (t
.title
.isEmpty()) continue;
345 QFile
xmlTaskSave(saveDir
.filePath(t
.title
+ ".xml"));
346 QByteArray taskArray
;
347 QXmlStreamWriter
stream(&taskArray
);
348 stream
.setAutoFormatting(true);
349 stream
.setAutoFormattingIndent(2);
350 stream
.writeStartDocument();
352 stream
.writeEndDocument();
353 xmlTaskSave
.open(QIODevice::ReadWrite
| QIODevice::Truncate
);
354 qDebug() << "Saving xml to file " << xmlTaskSave
.fileName();
356 hash
.append("<!-- ");
357 hash
.append( QCryptographicHash::hash(taskArray
, QCryptographicHash::Md5
).toHex() );
358 hash
.append(" -->\n");
359 xmlTaskSave
.write(hash
);
360 xmlTaskSave
.write(taskArray
);
364 // remove files not matching a task
365 QStringList nameFilters
;
366 nameFilters
<< "*.xml";
367 QStringList files
= saveDir
.entryList( nameFilters
, QDir::Files
);
368 foreach (QString taskXmlFileName
, files
) {
369 if (!m_tasks
.contains(QFileInfo(taskXmlFileName
).baseName())) {
370 qWarning()<< "Remove task " << QFileInfo(taskXmlFileName
).baseName() << " from disk";
371 QFile(saveDir
.filePath(taskXmlFileName
)).remove();
376 m_incremental
->clearAddedPieces();
379 //void Sak::saveAsDb()
381 // if (!m_settings) return;
382 // QString fileName = QFileDialog::getSaveFileName();
383 // QFile file(fileName);
386 // QSettings settingsQSettings::IniFormat, QSettings::UserScope, ("ZanzaSoft", "SAK");
387 // QFile file1(settings.fileName());
388 // if (!file1.copy(fileName)) {
389 // qWarning() << "Error copying " << settings.fileName() << " to " << fileName << file1.errorString();
393 void Sak::exportDbCsv()
395 if (!m_settings
) return;
396 QString fileName
= QFileDialog::getSaveFileName();
397 QFile
file(fileName
);
398 if (!file
.open(QIODevice::ReadWrite
|QIODevice::Truncate
)) {
399 QMessageBox::warning(0, "Error saving", QString("Error saving to file %1").arg(fileName
));
402 QTextStream
stream(&file
);
403 foreach(const Task
& t
, m_tasks
) {
404 QHash
< QString
, QList
< Task::Hit
> >::const_iterator itr
= t
.hits
.begin();
405 while(itr
!= t
.hits
.end()) {
406 QList
< Task::Hit
>::const_iterator hitr
= itr
.value().begin(), hend
= itr
.value().end();
407 while(hitr
!= hend
) {
408 stream
<< t
.title
<< ";" << itr
.key() << ";" << hitr
->timestamp
.toString(DATETIMEFORMAT
) << ";" << hitr
->duration
<< ";\n";
418 void Sak::logInGmail()
420 m_gmail
->forceLogin();
423 void Sak::saveToGmail()
425 if (!m_settings
) return;
427 QSettings
settings(QSettings::IniFormat
, QSettings::UserScope
, "ZanzaSoft", "SAK");
429 QDir
saveDir(QFileInfo(settings
.fileName()).dir());
430 saveDir
.mkdir("SakTasks");
431 saveDir
.cd("SakTasks");
432 QStringList nameFilters
;
433 nameFilters
<< "*.xml";
434 QStringList files
= saveDir
.entryList( nameFilters
, QDir::Files
);
435 QStringList filePaths
;
436 foreach (QString taskXmlFileName
, files
) {
437 filePaths
<< saveDir
.filePath(taskXmlFileName
);
439 m_gmail
->storeTaskFiles(filePaths
);
442 void Sak::importFromGmail()
444 QStringList filePaths
= m_gmail
->fetchLatestTasks();
447 void Sak::open(const QStringList
& _fileNames
)
449 QStringList fileNames
= _fileNames
.size()?_fileNames
:QFileDialog::getOpenFileNames(0, "Open a new task", QString(), "*.xml" );
450 foreach(QString fileName
, fileNames
) {
451 QFile
file(fileName
);
452 if (!file
.exists()) {
453 QMessageBox::warning(0, "Cannot find task", QString("Cannot find task file %1").arg(fileName
));
456 QSettings
settings(QSettings::IniFormat
, QSettings::UserScope
, "ZanzaSoft", "SAK");
457 QDir
saveDir(QFileInfo(settings
.fileName()).dir());
458 saveDir
.mkdir("SakTasks");
459 saveDir
.cd("SakTasks");
461 if ( QFile(saveDir
.filePath(QFileInfo(fileName
).completeBaseName())).exists() ) {
462 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?");
463 QPushButton
* overwriteButton
= mbox
.addButton("Overwrite", QMessageBox::YesRole
);
464 QPushButton
* mergeButton
= mbox
.addButton("Merge", QMessageBox::NoRole
);
465 QPushButton
* cancelButton
= mbox
.addButton("Cancel", QMessageBox::RejectRole
);
466 mbox
.setDefaultButton(cancelButton
);
468 QAbstractButton
* b
= mbox
.clickedButton();
469 if (b
== cancelButton
) { continue; }
471 m_backupper
->doCyclicBackup();
472 if (b
== mergeButton
) {
473 Task t
= loadTaskFromFile(file
.fileName());
474 QHash
< QString
, QList
< Task::Hit
> > ::const_iterator itr
= t
.hits
.begin(), end
= t
.hits
.end();
476 QString subtask
= itr
.key();
477 foreach(Task::Hit hit
, itr
.value())
478 m_incremental
->addPiece(t
.title
, subtask
, hit
.timestamp
, hit
.duration
);
481 interactiveMergeHits();
482 } else if (b
== overwriteButton
) {
483 file
.copy(saveDir
.filePath(QFileInfo(fileName
).completeBaseName()));
489 if (!fileNames
.isEmpty()) {
501 if (!m_settings
) return;
503 m_settings
->deleteLater();
504 m_view
->scene()->deleteLater();
505 m_view
->deleteLater();
507 delete m_incremental
;
509 m_previewing
= false;
510 m_changedHit
= false;
517 killTimer(m_autoSaveTimer
);
522 void Sak::layoutSubTasks( const QMap
<int, SakSubWidget
*> sortedWidgets
, int currentRank
) {
523 QMap
<int, SakSubWidget
*>::const_iterator itr
= sortedWidgets
.begin(), end
= sortedWidgets
.end();
524 QRect r
= m_desktopRect
;
525 for(int i
=0; itr
!= end
; i
++, itr
++) {
526 int h
= (*itr
)->size().height();
527 int w
= (*itr
)->size().width();
528 (*itr
)->setPos(QPointF((r
.width() - w
)/2, (r
.height()-h
)/2 + (i
- currentRank
- 1) * (h
+2)));
533 int Sak::taskCounter
= 0;
535 bool Sak::eventFilter(QObject
* obj
, QEvent
* e
)
537 // if (obj == m_view) {
538 // qDebug() << "event : " << e->type();
540 if (obj
== tasksTree
) {
541 return taskTreeEventFilter(e
);
542 } else if (obj
== hitsList
|| obj
== summaryList
) {
543 return hitsListEventFilter(e
);
544 } else if (obj
== m_settings
&& e
->type() == QEvent::Close
) {
549 if (trayIcon
->isVisible()) {
554 } else if (obj
== m_view
&& e
->type() == QEvent::Wheel
) {
555 QWheelEvent
* we
= dynamic_cast<QWheelEvent
*>(e
);
557 scrollSubTasks(we
->delta() / 120);
558 } else scrollTasks(we
->delta() / 120);
559 } else if (obj
== m_view
&& e
->type() == QEvent::KeyPress
) {
560 QKeyEvent
* ke
= dynamic_cast<QKeyEvent
*>(e
);
561 if ((ke
->modifiers() & Qt::AltModifier
) && (ke
->modifiers() & Qt::ControlModifier
) ) {
564 } else if ( ((ke
->modifiers() & Qt::ControlModifier
) && (ke
->key() == Qt::Key_Backspace
) )
565 || ((ke
->modifiers() & Qt::ControlModifier
) && (ke
->key() == Qt::Key_Left
) )) {
570 } else if (m_subtaskView
&& ke
->key() == Qt::Key_Up
) {
573 } else if (m_subtaskView
&& ke
->key() == Qt::Key_Down
) {
576 } else if (!m_subtaskView
&& ke
->key() == Qt::Key_Left
) {
579 } else if (!m_subtaskView
&& ke
->key() == Qt::Key_Right
) {
582 } else if (!m_subtaskView
&& ke
->key() == Qt::Key_Escape
) {
585 } else { // forward events to current widget
586 if (!m_subtaskView
) {
587 if (m_widgetsIterator
== m_widgets
.end()) return false;
588 SakWidget
* currentShowing
= m_widgetsIterator
.value();
589 currentShowing
->keyPressEvent(ke
);
592 // autoscroll on text completion!!!
593 if (m_subwidgetsIterator
== m_subwidgets
.end()) return false;
594 SakSubWidget
* currentShowing
= m_subwidgetsIterator
.value();
595 currentShowing
->keyPressEvent(ke
);
597 if (m_subWidgetRank
!= 0 && m_subtaskCompleter
) {
598 QString
completion(m_subtaskCompleter
->completionPrefix());
599 if (ke
->text().size() == 1) {
600 if (ke
->key() == Qt::Key_Backslash
|| ke
->key() == Qt::Key_Backspace
)
602 else completion
+= ke
->text();
603 m_subtaskCompleter
->setCompletionPrefix(completion
);
606 QStringList
list( ((QStringListModel
*)m_subtaskCompleter
->model())->stringList() );
607 int newRank
= 1 + ((QStringListModel
*)m_subtaskCompleter
->model())->stringList().indexOf(m_subtaskCompleter
->currentIndex().row() >= 0 && completion
.size() ? m_subtaskCompleter
->currentCompletion() : completion
);
609 if (m_subWidgetRank
!= newRank
) {
610 scrollSubTasks(newRank
- m_subWidgetRank
);
612 QLineEdit
* editor
= dynamic_cast<QLineEdit
*>((*m_subwidgets
.begin())->widget());
614 editor
->setText(completion
);
619 } else if (m_subtaskCompleter
) {
620 QLineEdit
* editor
= dynamic_cast<QLineEdit
*>((*m_subwidgets
.begin())->widget());
622 m_subtaskCompleter
->setCompletionPrefix(editor
->text());
629 } else if (obj
== m_view
&& e
->type() == QEvent::Show
) {
631 QTimer::singleShot(500, this, SLOT(grabKeyboard()));
632 } else if (obj
== m_view
&& e
->type() == QEvent::Close
) {
633 if (trayIcon
->isVisible()) {
636 } else if (obj
&& obj
== trayIcon
&& e
->type() == QEvent::ToolTip
) {
637 QDateTime last
= m_incremental
->lastTimeStamp
;
638 int seconds
= QDateTime::currentDateTime().secsTo(m_nextTimerEvent
);
639 int hours
= seconds
/ 3600;
640 int minutes
= (seconds
/ 60) % 60;
642 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>")))));
648 //END basic >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
651 //BEGIN Tasks >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
652 void Sak::addDefaultTask()
654 QString tentativeName
;
656 tentativeName
= QString("Task %1").arg(taskCounter
++);
657 } while(m_editedTasks
.contains(tentativeName
));
659 Task
& t
= m_editedTasks
[tentativeName
];
660 t
.title
= tentativeName
;
661 QTreeWidgetItem
* item
= new QTreeWidgetItem(QStringList(tentativeName
));
662 item
->setData(0,Qt::UserRole
, QVariant(QMetaType::VoidStar
, &t
));
663 tasksTree
->addTopLevelItem(item
);
667 void Sak::populateTasks()
671 QHash
<QString
, Task
>::iterator itr
= m_editedTasks
.begin(), end
=m_editedTasks
.end();
672 for(; itr
!=end
; itr
++) {
673 Task
& t(itr
.value());
674 t
.checkConsistency();
676 if (t
.title
.isEmpty() || t
.title
== "<away>") continue; // skip away task
677 QTreeWidgetItem
* item
= new QTreeWidgetItem(QStringList(t
.title
));
678 item
->setData(0, Qt::UserRole
, QVariant(QMetaType::VoidStar
, &t
));
680 icon
.addPixmap(t
.icon
);
681 item
->setSizeHint(0, QSize(32,32));
682 item
->setIcon(0, icon
);
683 for(int i
=0; i
<3; i
++) {
684 item
->setForeground(i
,t
.fgColor
);
685 item
->setBackground(i
,t
.bgColor
);
687 //item->setCheckState(1, t.active ? Qt::Checked : Qt::Unchecked);
688 //item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
689 item
->setIcon(1,QIcon(t
.active
? ":/images/active.png" : ":/images/inactive.png"));
690 item
->setText(2,QString("%1 hours worked till now (overestimated %2)").arg(t
.totHours
, 4, 'f', 2, ' ').arg(t
.totOverestimation
));
691 foreach(Task::SubTask st
, t
.subTasks
) {
692 if (!st
.title
.isEmpty()) {
693 QTreeWidgetItem
* sitem
= new QTreeWidgetItem(item
, QStringList(st
.title
));
694 item
->setData(0, Qt::UserRole
, QVariant(QMetaType::VoidStar
, &st
));
695 sitem
->setSizeHint(0, QSize(32,32));
696 QColor fgColor
= st
.fgColor
.isValid() ? st
.fgColor
: t
.fgColor
;
697 QColor bgColor
= st
.bgColor
.isValid() ? st
.bgColor
: t
.bgColor
;
698 for(int i
=0; i
<3; i
++) {
699 sitem
->setForeground(i
,fgColor
);
700 sitem
->setBackground(i
,bgColor
);
702 sitem
->setIcon(1,QIcon(st
.active
? ":/images/active.png" : ":/images/inactive.png"));
703 sitem
->setText(2,QString("%1 hours worked till now").arg(st
.totHours
,4,'f',2,' '));
706 tasksTree
->addTopLevelItem(item
);
710 void Sak::saveTaskChanges()
714 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
) {
715 m_tasks
= m_editedTasks
;
716 } else m_editedTasks
= m_tasks
; //. undo changes
718 selectedStartDate(QDate());
723 void Sak::selectColor() {
724 if (tasksTree
->selectedItems().isEmpty()) return;
726 if (sender() == fgColorButton
) {
727 QColor c
= QColorDialog::getColor(fgColorButton
->palette().color(QPalette::ButtonText
));
728 if (!c
.isValid()) return;
729 QPalette p
= fgColorButton
->palette();
730 p
.setColor(QPalette::ButtonText
, c
);
731 fgColorButton
->setPalette(p
);
732 bgColorButton
->setPalette(p
);
733 } else if (sender() == bgColorButton
) {
734 QColor c
= QColorDialog::getColor(bgColorButton
->palette().color(QPalette::Button
));
735 if (!c
.isValid()) return;
736 QPalette p
= bgColorButton
->palette();
737 p
.setColor(QPalette::Button
, c
);
738 fgColorButton
->setPalette(p
);
739 bgColorButton
->setPalette(p
);
744 bool Sak::taskTreeEventFilter(QEvent
* e
)
746 if (e
->type() == QEvent::ContextMenu
) {
747 QContextMenuEvent
* me
= dynamic_cast<QContextMenuEvent
*>(e
);
748 if (!me
) return false;
749 m_addTaskMenu
->popup(me
->globalPos());
751 } else if (e
->type() == QEvent::KeyRelease
) {
752 QKeyEvent
* ke
= dynamic_cast<QKeyEvent
*>(e
);
753 if (!ke
) return false;
754 if ( (ke
->key() != Qt::Key_Delete
&& ke
->key() != Qt::Key_Backspace
) ) return false;
755 if (currentSubtask
!="") {
756 QMessageBox
whatToDo(QMessageBox::Warning
, "Deleting subtask", "Deleting subtask " + currentSubtask
+ " of task " + currentTask
);
757 QPushButton
* moveHitsToParentButton
= whatToDo
.addButton("Move hits to task " + currentTask
, QMessageBox::AcceptRole
);
758 QPushButton
* removeHitsButton
= whatToDo
.addButton("Remove hits", QMessageBox::AcceptRole
);
759 QPushButton
* cancelButton
= whatToDo
.addButton("Cancel", QMessageBox::RejectRole
);
760 whatToDo
.setDefaultButton(cancelButton
);
762 if ( whatToDo
.clickedButton() == cancelButton
) return true;
763 if (m_editedTasks
.find(currentTask
) == m_editedTasks
.end()) return true;
765 Task
& t(m_editedTasks
[currentTask
]);
766 t
.subTasks
.take(currentSubtask
);
767 if (whatToDo
.clickedButton() == removeHitsButton
) {
768 t
.hits
.take(currentSubtask
);
769 } else if (whatToDo
.clickedButton() == moveHitsToParentButton
) {
770 QList
<Task::Hit
> sorter(t
.hits
.take(""));
771 sorter
<< t
.hits
.take(currentSubtask
);
772 qStableSort(sorter
.begin(), sorter
.end());
776 // remove file from disk
778 m_editedTasks
.remove(currentTask
);
782 selectedStartDate(QDate());
784 } else if (e
->type() == QEvent::Hide
) {
791 void Sak::commitCurrentTask()
794 if (currentSubtask
.isEmpty()) {
795 QString currentTitle
= taskTitleEditor
->text();
796 if (!currentTitle
.isEmpty()) {
797 if (currentTitle
!= currentTask
) {
798 if (m_editedTasks
.contains(currentTitle
)) {
799 QMessageBox::warning(0, "Conflict in task names", "Conflict in task names: current task " + currentTask
+ ", edited title " + currentTitle
);
800 taskTitleEditor
->setText(currentTask
);
802 } else if (m_editedTasks
.contains(currentTask
)) {
803 m_editedTasks
[currentTitle
] = m_editedTasks
.take(currentTask
);
804 m_editedTasks
[currentTitle
].title
= currentTitle
;
808 Task
& t
= m_editedTasks
[currentTitle
];
809 t
.bgColor
= bgColorButton
->palette().color(QPalette::Button
);
810 t
.fgColor
= fgColorButton
->palette().color(QPalette::ButtonText
);
811 t
.icon
= taskPixmapViewer
->pixmap();
812 QList
<QTreeWidgetItem
*> items
= tasksTree
->findItems(currentTask
,Qt::MatchExactly
,0);
813 foreach(QTreeWidgetItem
* ii
, items
) {
814 ii
->setText(0, currentTitle
);
815 ii
->setIcon(0, taskPixmapViewer
->pixmap());
816 for (int i
=0; i
<3; i
++) {
817 ii
->setForeground(i
, QColor(t
.fgColor
));
818 ii
->setBackground(i
, QColor(t
.bgColor
));
821 if (dueEditor
->date() != dueEditor
->minimumDate())
822 t
.dueDate
= dueEditor
->date();
823 t
.estimatedHours
= estimatedHoursEditor
->value();
824 currentTask
=currentTitle
;
826 if (tasksTree
->selectedItems().size() != 1) return;
827 QTreeWidgetItem
* item
= tasksTree
->selectedItems()[0];
828 item
->setText(0, taskTitleEditor
->text());
830 icon
.addPixmap(t
.icon
);
831 item
->setSizeHint(0, QSize(32,32));
832 item
->setIcon(0, icon
);
833 for(int i
=0; i
<3; i
++) {
834 item
->setForeground(i
,t
.fgColor
);
835 item
->setBackground(i
,t
.bgColor
);
839 } else { // subtask edited
840 if (!m_editedTasks
.contains(currentTask
)) return;
841 Task
& t(m_editedTasks
[currentTask
]);
842 QString currentTitle
= taskTitleEditor
->text();
844 if (!currentTitle
.isEmpty()) {
845 if (currentTitle
!= currentSubtask
) {
846 if (t
.subTasks
.contains(currentTitle
)) {
847 QMessageBox::warning(0, "Conflict in subtask names", "Conflict in subtask names");
848 taskTitleEditor
->setText(currentSubtask
);
850 } else if (t
.subTasks
.contains(currentSubtask
)) {
851 t
.subTasks
[currentTitle
] = t
.subTasks
.take(currentSubtask
);
852 t
.subTasks
[currentTitle
].title
= currentTitle
;
853 t
.hits
[currentTitle
] = t
.hits
.take(currentSubtask
);
857 Task::SubTask
& st
= t
.subTasks
[currentTitle
];
858 st
.bgColor
= bgColorButton
->palette().color(QPalette::Button
);
859 st
.fgColor
= fgColorButton
->palette().color(QPalette::ButtonText
);
860 QList
<QTreeWidgetItem
*> items
= tasksTree
->findItems(currentTask
,Qt::MatchExactly
,0);
861 foreach(QTreeWidgetItem
* jj
, items
) {
862 for(int i
=0; i
<jj
->childCount(); i
++) {
863 QTreeWidgetItem
* ii
= jj
->child(i
);
864 if (ii
->text(0) != currentSubtask
) continue;
865 ii
->setText(0, currentTitle
);
866 for (int i
=0; i
<3; i
++) {
867 ii
->setForeground(i
, QColor(st
.fgColor
));
868 ii
->setBackground(i
, QColor(st
.bgColor
));
872 currentSubtask
= currentTitle
;
874 if (tasksTree
->selectedItems().size() != 1) return;
875 QTreeWidgetItem
* item
= tasksTree
->selectedItems()[0];
876 item
->setText(0, taskTitleEditor
->text());
878 icon
.addPixmap(t
.icon
);
879 item
->setSizeHint(0, QSize(32,32));
880 item
->setIcon(0, icon
);
881 for(int i
=0; i
<3; i
++) {
882 item
->setForeground(i
,st
.fgColor
);
883 item
->setBackground(i
,st
.bgColor
);
888 void Sak::selectedTask()
890 if (tasksTree
->selectedItems().isEmpty()) {
891 taskPixmapViewer
->setEnabled(false);
892 taskPixmapViewer
->setPixmap(QPixmap());
893 taskTextEditor
->setEnabled(false);
894 taskTitleEditor
->setEnabled(false);
895 bgColorButton
->setEnabled(false);
896 fgColorButton
->setEnabled(false);
897 dueEditor
->setEnabled(false);
898 estimatedHoursEditor
->setEnabled(false);
902 QTreeWidgetItem
* selectedItem
= tasksTree
->selectedItems().first();
903 QTreeWidgetItem
* parentItem
= selectedItem
->parent();
904 QString tt
= selectedItem
->text(0);
907 taskPixmapViewer
->setEnabled(true);
908 dueEditor
->setEnabled(true);
909 estimatedHoursEditor
->setEnabled(true);
911 taskPixmapViewer
->setEnabled(false);
912 taskPixmapViewer
->setPixmap(QPixmap());
913 dueEditor
->setEnabled(false);
914 estimatedHoursEditor
->setEnabled(false);
916 taskTextEditor
->setEnabled(true);
917 taskTitleEditor
->setEnabled(true);
918 bgColorButton
->setEnabled(true);
919 fgColorButton
->setEnabled(true);
922 if (!parentItem
) { // editing a task
923 if (!m_editedTasks
.contains(tt
)) return;
924 const Task
& t
= m_editedTasks
[tt
];
925 taskPixmapViewer
->setPixmap(t
.icon
);
926 taskTextEditor
->blockSignals(true);
927 taskTextEditor
->setPlainText(t
.description
);
928 taskTextEditor
->blockSignals(false);
929 taskTitleEditor
->setText(t
.title
);
931 p
.setColor(QPalette::Button
, t
.bgColor
);
932 p
.setColor(QPalette::ButtonText
, t
.fgColor
);
933 bgColorButton
->setPalette(p
);
934 fgColorButton
->setPalette(p
);
935 estimatedHoursEditor
->setValue(t
.estimatedHours
);
936 dueEditor
->setDate(t
.dueDate
.isValid() ? t
.dueDate
: dueEditor
->minimumDate());
938 currentTask
= t
.title
;
941 if (!m_editedTasks
.contains(parentItem
->text(0))) return;
942 const Task
& t
= m_editedTasks
[parentItem
->text(0)];
943 if (!t
.subTasks
.contains(tt
)) return;
944 const Task::SubTask
& st
= t
.subTasks
[tt
];
945 taskTextEditor
->setPlainText(st
.description
);
946 taskTitleEditor
->setText(st
.title
);
948 p
.setColor(QPalette::Button
, st
.bgColor
.isValid() ? st
.bgColor
: t
.bgColor
);
949 p
.setColor(QPalette::ButtonText
, st
.fgColor
.isValid() ? st
.fgColor
: t
.fgColor
);
950 bgColorButton
->setPalette(p
);
951 fgColorButton
->setPalette(p
);
953 currentTask
= t
.title
;
954 currentSubtask
= st
.title
;
958 void Sak::doubleClickedTask(QTreeWidgetItem
* i
, int column
)
962 if (i
->parent() == 0) {
963 QHash
<QString
, Task
>::iterator itr
= m_editedTasks
.find(i
->text(0));
964 Q_ASSERT(itr
!= m_editedTasks
.end());
965 bool& active ( itr
.value().active
);
967 i
->setIcon(column
, active
? QIcon(":/images/active.png") : QIcon(":/images/inactive.png"));
969 QHash
<QString
, Task
>::iterator itr
= m_editedTasks
.find(i
->parent()->text(0));
970 Q_ASSERT(itr
!= m_editedTasks
.end());
971 bool& active ( itr
.value().subTasks
[i
->text(0)].active
);
973 i
->setIcon(column
, active
? QIcon(":/images/active.png") : QIcon(":/images/inactive.png"));
975 ((QTreeWidget
*)sender())->update();
981 void Sak::timerEvent(QTimerEvent
* e
)
983 if (e
->timerId() == m_timerId
) {
984 if (!m_view
->isVisible() && !m_settings
->isVisible() && m_tasks
.count() > 0) {
987 killTimer(m_timeoutPopup
);
988 m_timeoutPopup
= startTimer((int)(qMax( 30000.0, Task::hours(m_currentInterval
)*3600.0*1000.0/10.0))); // 5 secmin
990 killTimer(m_timerId
);
991 int msecs
= (int)(Task::hours(m_currentInterval
)*3600.0*1000.0);
992 m_timerId
= startTimer(msecs
);
993 m_nextTimerEvent
= QDateTime::currentDateTime().addMSecs(msecs
);
995 if (m_settings
&& m_settings
->isVisible() && !m_settings
->isActiveWindow()) {
996 trayIcon
->showMessage("Delayed check point", "Delayed check point due to open settings. Close the setting window!", QSystemTrayIcon::Warning
, -1);
999 qDebug() << "SAK: wait 5 seconds";
1000 killTimer(m_timerId
);
1001 m_timerId
= startTimer(5000);
1002 m_nextTimerEvent
= QDateTime::currentDateTime().addMSecs(5000);
1004 } else if (e
->timerId() == m_timeoutPopup
) {
1005 // ensure the timer is resetted
1006 killTimer(m_timeoutPopup
);
1008 if (!m_subtaskView
) {
1009 // if not selecting subtasks clear everything and signal away
1010 workingOnTask("<away>","");
1011 trayIcon
->showMessage("New away events", "You have missed a check point. Fix it in the detailed hit list.", QSystemTrayIcon::Information
, 999999);
1014 } else if (e
->timerId() == m_autoSaveTimer
) {
1017 qDebug() << "unknown timer event";
1021 void Sak::clearView()
1023 killTimer(m_timeoutPopup
);
1024 m_subtaskView
=false;
1026 delete m_subtaskCompleter
; m_subtaskCompleter
= 0;
1028 if (!m_view
) return;
1029 QGraphicsScene
* s
= m_view
->scene();
1030 QList
<QGraphicsItem
*> items
= m_view
->items();
1033 m_view
->setScene(new QGraphicsScene
);
1034 m_view
->scene()->setSceneRect(m_desktopRect
);
1035 m_previewing
= false;
1036 m_view
->releaseKeyboard();
1038 #if defined(Q_WS_X11)
1039 // restore focus to previous application
1041 X11::XSetInputFocus((X11::Display
*)QX11Info::display(), X11::CurrentFocusWindow
, X11::CurrentRevertToReturn
, CurrentTime
);
1042 X11::XFlush((X11::Display
*)QX11Info::display());
1046 void Sak::workingOnTask(const QString
& taskName
, const QString
& subTask
)
1048 if (!m_previewing
) {
1050 qDebug() << "Working on " << taskName
;
1051 if (m_tasks
.contains(taskName
)) {
1052 Task
& t
= m_tasks
[taskName
];
1054 if (t
.title
!= "<away>" && !t
.title
.isEmpty()) {
1056 int historyIndex
= m_taskSelectionHistory
.indexOf(t
.title
);
1057 if (historyIndex
!= -1) {
1058 m_taskSelectionHistory
.takeAt(historyIndex
);
1060 m_taskSelectionHistory
.push_back(t
.title
);
1062 QList
<QString
> & subtaskSelectionHistory(m_subtaskSelectionHistory
[t
.title
]);
1063 historyIndex
= subtaskSelectionHistory
.indexOf(subTask
);
1064 if (historyIndex
!= -1) {
1065 subtaskSelectionHistory
.takeAt(historyIndex
);
1067 subtaskSelectionHistory
.push_back(subTask
);
1071 QDateTime now
= QDateTime::currentDateTime();
1072 QHash
<QString
, Task
>::iterator itr
= m_tasks
.begin();
1074 QHash
<QString
, QList
< Task::Hit
> >::iterator hitr
= t
.hits
.begin();
1075 // merge last two hits of every subtask if they are close enough
1076 while(hitr
!= t
.hits
.end()) {
1077 QList
<Task::Hit
>& otHits( *hitr
);
1078 if (otHits
.count() > 1) {
1079 Task::Hit
& lastHit(otHits
[otHits
.size() - 1]);
1080 Task::Hit
& beforeLastHit(otHits
[otHits
.size() - 2]);
1082 int diff
= (lastHit
.timestamp
.toTime_t() - 30*lastHit
.duration
) - (beforeLastHit
.timestamp
.toTime_t() + 30*beforeLastHit
.duration
);
1083 if (diff
< 120) { // at most 2 minutes apart
1084 beforeLastHit
.timestamp
= beforeLastHit
.timestamp
.addSecs(-30*beforeLastHit
.duration
);
1085 int secsToEnd
= beforeLastHit
.timestamp
.secsTo(lastHit
.timestamp
.addSecs(30*m_currentInterval
));
1086 if (secsToEnd
> 24 * 3600 * 3600) {
1087 qWarning() << "TRAPPED ERROR IN SECS COUNT!!!!!!!!";
1088 qWarning() << "BEFORE LAST HIST WAS " << beforeLastHit
.timestamp
<< beforeLastHit
.duration
;
1089 qWarning() << "LAST HIT WAS " << lastHit
.timestamp
<< lastHit
.duration
;
1090 assert(secsToEnd
< 24 * 3600 * 3600);
1092 beforeLastHit
.timestamp
= beforeLastHit
.timestamp
.addSecs( secsToEnd
/2.0 );
1093 beforeLastHit
.duration
= (int)( round( secsToEnd
/ 60.0 ) );
1094 // remove the current very last hit
1101 // add hit to hit list
1102 // NOTE: we do not try to merge the very last hit with previous ones because we want let
1103 // the user being able to easily recover from a selection error
1104 t
.hits
[subTask
] << Task::Hit(now
, m_currentInterval
);
1105 m_incremental
->writePiece(t
.title
, subTask
, now
, m_currentInterval
);
1106 t
.checkConsistency();
1107 QList
<QTreeWidgetItem
*> items
= tasksTree
->findItems (t
.title
, Qt::MatchExactly
, 0);
1108 if (!items
.isEmpty())
1109 items
.first()->setText(1, QString("%1 hours worked till now (overestimated %2)").arg(t
.totHours
).arg(t
.totOverestimation
));
1111 // update subtask if new added!
1114 // update statistics !!!!
1115 m_editedTasks
= m_tasks
;
1116 QMetaObject::invokeMethod(this, "selectedStartDate", Qt::QueuedConnection
, Q_ARG(QDate
, cal1
->selectedDate()));
1123 // attractor = 't' (top), 'b', 'l', 'r'
1124 void layoutInSquare( QList
<SakWidget
*> sortedWidgets
, QRect rect
, char attractor
)
1126 int w
= rect
.width();
1127 if (rect
.width() < 64) return;
1128 int maxw
= qMin(350, w
/2);
1129 QSize
size(maxw
, maxw
);
1130 if (sortedWidgets
.count() == 0) {
1132 } else if (sortedWidgets
.count() == 1) {
1133 sortedWidgets
[0]->setGeometry(rect
);
1134 } else if (sortedWidgets
.count() == 2) {
1136 if (attractor
== 'C') {
1137 off1
= QPoint(0,w
/4);
1138 off2
= QPoint(w
/2,w
/4);
1139 } else if (attractor
== 'T') {
1141 off2
= QPoint(w
/2,0);
1142 } else if (attractor
== 'B') {
1143 off1
= QPoint(0,w
/2);
1144 off2
= QPoint(w
/2,w
/2);
1145 } else if (attractor
== 'R') {
1146 off1
= QPoint(w
/2,0);
1147 off2
= QPoint(w
/2,w
/2);
1148 } else if (attractor
== 'L') {
1150 off2
= QPoint(0,w
/2);
1152 sortedWidgets
[0]->setGeometry(QRect(rect
.topLeft() + off1
, size
));
1153 sortedWidgets
[1]->setGeometry(QRect(rect
.topLeft() + off2
, size
));
1154 } else if (sortedWidgets
.count() == 3) {
1155 QPoint off1
, off2
, off3
;
1156 if (attractor
== 'T' || attractor
== 'C') {
1158 off2
= QPoint(w
/2,0);
1159 off3
= QPoint(w
/4,w
/2);
1160 } else if (attractor
== 'B') {
1161 off1
= QPoint(0,w
/2);
1162 off2
= QPoint(w
/2,w
/2);
1163 off3
= QPoint(w
/4,0);
1164 } else if (attractor
== 'R') {
1165 off1
= QPoint(w
/2,0);
1166 off2
= QPoint(w
/2,w
/2);
1167 off3
= QPoint(0,w
/4);
1168 } else if (attractor
== 'L') {
1170 off2
= QPoint(0,w
/2);
1171 off3
= QPoint(w
/2,w
/4);
1173 sortedWidgets
[0]->setGeometry(QRect(rect
.topLeft() + off1
, size
));
1174 sortedWidgets
[1]->setGeometry(QRect(rect
.topLeft() + off2
, size
));
1175 sortedWidgets
[2]->setGeometry(QRect(rect
.topLeft() + off3
, size
));
1176 } else if (sortedWidgets
.count() == 4) {
1180 QPoint
off4(w
/2,w
/2);
1181 sortedWidgets
[0]->setGeometry(QRect(rect
.topLeft() + off1
, size
));
1182 sortedWidgets
[1]->setGeometry(QRect(rect
.topLeft() + off2
, size
));
1183 sortedWidgets
[2]->setGeometry(QRect(rect
.topLeft() + off3
, size
));
1184 sortedWidgets
[3]->setGeometry(QRect(rect
.topLeft() + off4
, size
));
1186 Q_ASSERT(sortedWidgets
.count() <= 4);
1190 void layoutInRect( QList
<SakWidget
*> sortedWidgets
, QRect rect
, char attractor
)
1192 if (sortedWidgets
.count() == 0) return;
1193 int h
= rect
.height();
1194 int w
= rect
.width();
1195 int maxh
= qMin(350, h
);
1196 int maxw
= qMin(350, w
);
1197 if (sortedWidgets
.count() == 1) {
1199 sortedWidgets
[0]->setGeometry(QRect(rect
.topLeft() + QPoint((w
-maxh
)/2,(h
-maxh
)/2), QSize(maxh
,maxh
)));
1201 sortedWidgets
[0]->setGeometry(QRect(rect
.topLeft() + QPoint((w
-maxw
)/2,(h
-maxw
)/2), QSize(maxw
,maxw
)));
1204 } else if (sortedWidgets
.count() == 2) {
1206 sortedWidgets
[0]->setGeometry(QRect(rect
.topLeft() + QPoint(w
/2-maxh
,(h
-maxh
)/2), QSize(maxh
,maxh
)));
1207 sortedWidgets
[1]->setGeometry(QRect(rect
.topLeft() + QPoint(w
/2,(h
-maxh
)/2), QSize(maxh
,maxh
)));
1209 sortedWidgets
[0]->setGeometry(QRect(rect
.topLeft() + QPoint((h
-maxw
)/2,w
-maxw
), QSize(w
,w
)));
1210 sortedWidgets
[1]->setGeometry(QRect(rect
.topLeft() + QPoint((h
-maxw
)/2,w
/2), QSize(maxw
,maxw
)));
1214 if (h
< 64 || w
< 64) return;
1215 QList
<SakWidget
*> leftList
, rightList
;
1216 for (int i
=4; i
<sortedWidgets
.count(); i
++) {
1218 rightList
<< sortedWidgets
[i
];
1220 leftList
<< sortedWidgets
[i
];
1223 QRect
square(rect
.topLeft() + QPoint(h
/2, 0), QSize(h
,h
));
1224 layoutInSquare(sortedWidgets
.mid(0,4), square
, attractor
);
1226 QRect
leftRect(rect
.topLeft(), QSize(h
/2, h
));
1227 layoutInRect(leftList
, leftRect
, 'R');
1228 QRect
rightRect(rect
.topLeft() + QPoint((int)(0.75*w
),0), QSize(h
/2, h
));
1229 layoutInRect(rightList
, rightRect
, 'L');
1231 QRect
square(rect
.topLeft() + QPoint(0,w
/2), QSize(w
,w
));
1232 layoutInSquare(sortedWidgets
.mid(0,4), square
, attractor
);
1234 QRect
leftRect(rect
.topLeft(), QSize(w
, w
/2));
1235 layoutInRect(leftList
, leftRect
, 'B');
1236 QRect
rightRect(rect
.topLeft() + QPoint(0,(int)(0.75*h
)), QSize(w
, w
/2));
1237 layoutInRect(rightList
, rightRect
, 'T');
1241 QRect
Sak::Layouting( const QList
<SakWidget
*>& sortedWidgets
)
1243 QRect r
= m_desktopRect
;
1244 int height
= (int)(0.75 * r
.height());
1245 int width
= r
.width();
1247 int firstW
= width
/ 2 < height
? width
: height
* 2;
1248 int firstH
= firstW
/ 2;
1249 QRect
firstRect (r
.x() + (width
- firstW
) / 2, r
.y() + (height
- firstH
) / 2 + (int)(r
.height() * 0.25), firstW
, firstH
);
1251 layoutInRect(sortedWidgets
, firstRect
, 'C');
1252 return QRect(QPoint(r
.x(), r
.y()), QSize(r
.width(), (int)(0.25 * r
.height())));
1255 void Sak::grabKeyboard()
1257 #if defined(Q_WS_X11)
1259 // save current focused application
1260 XGetInputFocus((X11::Display
*)QX11Info::display(), &X11::CurrentFocusWindow
, &X11::CurrentRevertToReturn
);
1263 X11::XSetInputFocus((X11::Display
*)QX11Info::display(), QX11Info::appRootWindow(QX11Info::appScreen()), RevertToParent
, CurrentTime
);
1264 X11::XFlush((X11::Display
*)QX11Info::display());
1266 m_view
->grabKeyboard();
1271 // save changes first
1277 if (m_subtaskView
) {
1279 foreach(SakSubWidget
* w
, m_subwidgets
.values()) {
1280 w
->scene()->removeItem(w
);
1282 m_subwidgets
.clear();
1284 m_marker
->scene()->removeItem(m_marker
);
1288 foreach(SakWidget
* w
, m_widgets
.values()) {
1291 m_subtaskView
=false;
1295 m_subtaskView
= false;
1297 if (sender() == previewButton
) {
1298 m_previewing
= true;
1300 QDateTime now
= QDateTime::currentDateTime();
1301 QDateTime fromWeek
= now
;
1302 fromWeek
.setDate(now
.date().addDays(-now
.date().dayOfWeek() + 1));
1303 fromWeek
.setTime(QTime(0,0,0));
1304 QDateTime fromMonth
= now
;
1305 fromMonth
.setDate(now
.date().addDays(-now
.date().day()));
1306 fromMonth
.setTime(QTime(0,0,0));
1307 QDateTime fromToday
= now
;
1308 fromToday
.setTime(QTime(0,0,0));
1310 double weekHits
= 0;
1312 double monthHits
= 0;
1313 QHash
<QString
, double> dayStats
;
1314 QHash
<QString
, double> weekStats
;
1315 QHash
<QString
, double> monthStats
;
1317 foreach(const Task
& t
, m_tasks
.values()) {
1319 double m
= t
.workedHours(fromMonth
, now
);
1320 monthStats
[t
.title
] = m
;
1322 double w
= t
.workedHours(fromWeek
, now
);
1323 weekStats
[t
.title
] = w
;
1325 double d
= t
.workedHours(fromToday
, now
);
1326 dayStats
[t
.title
] = d
;
1333 foreach(const Task
& t
, m_tasks
.values()) {
1334 if (!t
.active
|| t
.title
== QString() || t
.title
== "<away>") continue;
1335 SakWidget
* test
= new SakWidget(t
);
1336 test
->setVisible(false);
1337 double d
= dayStats
[t
.title
];
1338 double w
= weekStats
[t
.title
];
1339 double m
= monthStats
[t
.title
];
1340 test
->setStatistics(d
, w
, m
, d
/dayHits
* 100.0, w
/weekHits
* 100.0, m
/monthHits
* 100.0);
1341 test
->setObjectName(t
.title
);
1342 connect (test
, SIGNAL(clicked(const QString
&, const QString
&)), this, SLOT(workingOnTask(const QString
&, const QString
&)));
1343 connect (test
, SIGNAL(clicked(const QString
&)), this, SLOT(popupSubtasks(const QString
&)));
1344 int historyPosition
= 1 + m_taskSelectionHistory
.indexOf(t
.title
);
1345 int rank
= historyPosition
!= 0 ? -1000000 * historyPosition
: -d
;
1346 m_widgets
.insertMulti( rank
, test
);
1349 m_widgetsIterator
= m_widgets
.begin();
1350 if (m_widgetsIterator
!= m_widgets
.end()) {
1351 m_widgetsIterator
.value()->showDetails(true);
1355 const QList
<SakWidget
*>& values
= m_widgets
.values();
1356 QRect messageRect
= Layouting((const QList
<SakWidget
*>&)values
);
1357 foreach(SakWidget
* w
, values
) {
1358 m_view
->scene()->addItem(w
);
1363 // add the message item
1364 SakMessageItem
* sakMessage
= new SakMessageItem(bodyEdit
->toPlainText());
1365 sakMessage
->setGeometry(messageRect
);
1366 m_view
->scene()->addItem(sakMessage
);
1369 // add the exit item
1370 SakExitItem
* exitItem
= new SakExitItem(QPixmap(":/images/exit.png"));
1371 QRect r
= m_desktopRect
;
1372 connect(exitItem
, SIGNAL(exit()), this, SLOT(clearView()));
1373 exitItem
->setPos(r
.width() - exitItem
->boundingRect().width(), 0);
1374 m_view
->scene()->addItem(exitItem
);
1375 exitItem
->setZValue(1e8
);
1379 m_view
->setGeometry( QRect(m_desktopRect
)/*.adjusted(200,200,-200,-200 )*/ );
1383 #if defined(Q_WS_WIN)
1384 SetForegroundWindow(m_view
->winId());
1386 qApp
->alert(m_view
, 5000);
1390 void Sak::popupSubtasks(const QString
& _taskname
) {
1392 killTimer(m_timeoutPopup
);
1394 QString taskname
= _taskname
;
1395 if (taskname
.isEmpty()) {
1396 if ( m_taskSelectionHistory
.isEmpty() ) {
1399 taskname
= m_taskSelectionHistory
.back();
1401 QString subtaskName
;
1402 if (!m_subtaskSelectionHistory
[taskname
].isEmpty()) {
1403 subtaskName
= m_subtaskSelectionHistory
[taskname
].back();
1405 workingOnTask(taskname
, subtaskName
);
1408 m_subtaskView
= true;
1410 QRect r
= m_desktopRect
;
1414 // hide tasks to show subtasks
1415 foreach(SakWidget
* w
, m_widgets
.values()) {
1419 QHash
<QString
, Task
>::const_iterator itr
= m_tasks
.find(taskname
);
1420 if (itr
== m_tasks
.end()) {
1421 workingOnTask(taskname
, "");
1424 const Task
& t(*itr
);
1425 QHash
< QString
, Task::SubTask
>::const_iterator titr
= t
.subTasks
.begin(), tend
= t
.subTasks
.end();
1426 m_subwidgets
.clear();
1427 QDateTime
now(QDateTime::currentDateTime());
1428 for(; titr
!= tend
; titr
++) {
1429 if (!titr
->active
) continue;
1430 SakSubWidget
* sw
= new SakSubWidget(t
, *titr
);
1431 sw
->setGeometry(QRectF(0,0,w
,h
));
1432 connect (sw
, SIGNAL(clicked(const QString
&, const QString
&)), this, SLOT(workingOnTask(const QString
&, const QString
&)));
1433 connect (sw
, SIGNAL(focused()), this, SLOT(focusedSubTask()));
1436 QHash
< QString
, QList
< QString
> >::const_iterator hhitr
= m_subtaskSelectionHistory
.find(t
.title
);
1437 if (hhitr
!= m_subtaskSelectionHistory
.end()) {
1438 int index
= hhitr
->indexOf(titr
->title
);
1439 if (index
!= 0) rank
= now
.addDays(index
+1);
1442 QHash
< QString
, QList
<Task::Hit
> >::const_iterator hitr
= t
.hits
.find(titr
.key());
1443 if (hitr
!= t
.hits
.end()) {
1444 if ( hitr
.value().count() && hitr
.value().last().timestamp
.isValid())
1445 rank
= hitr
.value().last().timestamp
;
1447 rank
= now
.addDays(-999);
1450 m_subwidgets
.insertMulti( - rank
.toTime_t(), sw
);
1453 const QList
<SakSubWidget
*>& values
= m_subwidgets
.values();
1455 // create possible completion
1456 QStringList subtaskSortedList
;
1457 foreach(SakSubWidget
* sub
, values
) {
1458 subtaskSortedList
.push_back(sub
->subtask().title
);
1460 m_subtaskCompleter
= new QCompleter(subtaskSortedList
);
1461 m_subtaskCompleter
->setCaseSensitivity(Qt::CaseInsensitive
);
1463 // QRect messageRect = Layouting((const QList<SakWidget*>&)values);
1465 m_subwidgetsIterator
= m_subwidgets
.begin();
1466 m_subWidgetRank
= 0;
1468 // the one with text
1469 SakSubWidget
* tmpSw
= new SakSubWidget(t
, Task::SubTask(), true);
1470 QCompleter
* completer
= new QCompleter(subtaskSortedList
);
1471 completer
->setCaseSensitivity(Qt::CaseInsensitive
);
1472 completer
->setCompletionMode(QCompleter::InlineCompletion
);
1473 ((QLineEdit
*)tmpSw
->widget())->setCompleter(completer
);
1474 m_view
->scene()->addItem(tmpSw
);
1475 tmpSw
->setGeometry(QRectF(0,0,w
,h
));
1476 connect (tmpSw
, SIGNAL(clicked(const QString
&, const QString
&)), this, SLOT(workingOnTask(const QString
&, const QString
&)));
1477 connect (tmpSw
, SIGNAL(focused()), this, SLOT(focusedSubTask()));
1479 m_subwidgets
.insertMulti(-QDateTime::currentDateTime().addDays(999).toTime_t(), tmpSw
);
1481 m_subWidgetRank
+= values
.size() != 0;
1483 if (m_subwidgetsIterator
!= m_subwidgets
.end()) {
1484 m_subwidgetsIterator
.value()->showDetails(true);
1485 m_view
->scene()->setFocusItem(m_subwidgetsIterator
.value(), Qt::MouseFocusReason
);
1486 m_subwidgetsIterator
.value()->widget()->setFocus(Qt::MouseFocusReason
);
1488 m_view
->scene()->setFocusItem(tmpSw
, Qt::MouseFocusReason
);
1489 tmpSw
->widget()->setFocus(Qt::MouseFocusReason
);
1492 // add a marker to highligh current selection
1493 m_marker
= new QGraphicsEllipseItem(r
.width()/2 - 280, r
.height()/2 - 51, 20,20);
1494 m_marker
->setBrush(Qt::red
);
1495 m_view
->scene()->addItem(m_marker
);
1496 m_marker
->setVisible(true);
1498 layoutSubTasks(m_subwidgets
, m_subWidgetRank
);
1500 // Finally add subwidgets
1501 for(int i
=0; i
<values
.size(); i
++) {
1502 SakSubWidget
* sw
= values
[i
];
1503 m_view
->scene()->addItem(sw
);
1508 void Sak::scrollTasks(int npos
) {
1509 SakWidget
* currentShowing
= 0;
1511 for (int i
=npos
; i
<0; i
++) {
1512 if (m_widgetsIterator
== m_widgets
.end()) return;
1513 currentShowing
= m_widgetsIterator
!= m_widgets
.end() ? m_widgetsIterator
.value() : 0;
1514 if (m_widgetsIterator
== m_widgets
.begin()) m_widgetsIterator
= m_widgets
.end();
1515 m_widgetsIterator
--;
1518 for (int i
=0; i
<npos
; i
++) {
1519 if (m_widgetsIterator
== m_widgets
.end()) return;
1520 currentShowing
= m_widgetsIterator
.value();
1521 m_widgetsIterator
++;
1522 if (m_widgetsIterator
== m_widgets
.end()) m_widgetsIterator
= m_widgets
.begin();
1525 if (currentShowing
&& m_widgetsIterator
!= m_widgets
.end()) {
1526 currentShowing
->showDetails(false);
1527 m_widgetsIterator
.value()->showDetails(true);
1531 void Sak::scrollSubTasks(int npos
) {
1532 SakSubWidget
* currentShowing
= 0;
1534 for (int i
=npos
; i
<0; i
++) {
1535 if (m_subwidgetsIterator
== m_subwidgets
.end()) return;
1536 currentShowing
= m_subwidgetsIterator
!= m_subwidgets
.end() ? m_subwidgetsIterator
.value() : 0;
1537 if (m_subwidgetsIterator
== m_subwidgets
.begin()) {
1538 m_subwidgetsIterator
= m_subwidgets
.end();
1539 m_subWidgetRank
= m_subwidgets
.count();
1541 m_subwidgetsIterator
--;
1545 for (int i
=0; i
<npos
; i
++) {
1546 if (m_subwidgetsIterator
== m_subwidgets
.end()) return;
1547 currentShowing
= m_subwidgetsIterator
.value();
1548 m_subwidgetsIterator
++;
1550 if (m_subwidgetsIterator
== m_subwidgets
.end()) {
1551 m_subwidgetsIterator
= m_subwidgets
.begin();
1552 m_subWidgetRank
= 0;
1556 if (currentShowing
&& m_subwidgetsIterator
!= m_subwidgets
.end()) {
1557 currentShowing
->showDetails(false);
1558 m_subwidgetsIterator
.value()->showDetails(true);
1559 m_view
->scene()->setFocusItem(m_subwidgetsIterator
.value(), Qt::MouseFocusReason
);
1560 m_subwidgetsIterator
.value()->widget()->setFocus(Qt::MouseFocusReason
);
1562 layoutSubTasks(m_subwidgets
, m_subWidgetRank
);
1565 void Sak::focusedSubTask()
1567 SakSubWidget
* w
= dynamic_cast<SakSubWidget
*>(sender());
1569 QMap
<int, SakSubWidget
*>::iterator itr
= m_subwidgets
.begin(), end
=m_subwidgets
.end();
1570 for(int i
=0; itr
!= end
; i
++,itr
++) {
1571 if (itr
.value() == w
) {
1572 m_subwidgetsIterator
= itr
;
1573 m_subWidgetRank
= i
;
1579 //END Tasks >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
1582 //BEGIN settings ================================
1585 void Sak::setupSettingsWidget()
1587 m_settings
= new QMainWindow();
1588 m_settings
->setMinimumHeight(650);
1589 m_settings
->setMinimumWidth(700);
1590 QWidget
* centralWidget
= new QWidget
;
1591 m_settings
->setCentralWidget(centralWidget
);
1593 QVBoxLayout
* theMainLayout
= new QVBoxLayout
;
1594 centralWidget
->setLayout(theMainLayout
);
1596 tabs
= new QTabWidget
;
1597 theMainLayout
->addWidget(tabs
);
1598 previewButton
= new QPushButton("Preview");
1599 theMainLayout
->addWidget(previewButton
);
1605 tabs
->addTab(tab1
, "Tasks");
1606 tabs
->addTab(tab2
, "General");
1607 tabs
->addTab(tab4
, "Statistics");
1608 tabs
->addTab(tab3
, "Advanced");
1611 QMenuBar
* mainMenu
= new QMenuBar
;
1612 m_settings
->setMenuBar(mainMenu
);
1613 QMenu
* programMenu
= mainMenu
->addMenu("Program");
1614 programMenu
->addAction(quitAction
);
1615 programMenu
->addAction(minimizeAction
);
1616 QMenu
* dbMenu
= mainMenu
->addMenu("Db");
1617 dbMenu
->addAction(flushAction
);
1618 dbMenu
->addAction(openAction
);
1619 // dbMenu->addAction(saveAsDbAction);
1620 dbMenu
->addAction(exportDbCsvAction
);
1622 dbMenu
->addAction(gmailLoginAction
);
1623 dbMenu
->addAction(saveToGmailAction
);
1624 if (!m_gmail
->isValid()) {
1625 gmailLoginAction
->setEnabled(false);
1626 saveToGmailAction
->setEnabled(false);
1629 QMenu
* actionsMenu
= mainMenu
->addMenu("Actions");
1630 actionsMenu
->addAction(startAction
);
1631 actionsMenu
->addAction(stopAction
);
1633 QVBoxLayout
*mainLayout
= new QVBoxLayout
;
1634 QGridLayout
*messageLayout
= new QGridLayout
;
1635 durationLabel
= new QLabel(tr("Interval:"));
1636 durationLabel1
= new QLabel(tr("(effective only after restart)"));
1638 QSettings
settings(QSettings::IniFormat
, QSettings::UserScope
, "ZanzaSoft", "SAK");
1639 durationSpinBox
= new QSpinBox
;
1640 durationSpinBox
->setSingleStep(1);
1641 durationSpinBox
->setRange(1, 1440);
1642 durationSpinBox
->setSuffix(" minutes");
1643 durationSpinBox
->setValue(qMin(1440, qMax(1, settings
.value("Ping interval", 15).toInt())));
1644 durationSpinBox
->setCorrectionMode(QAbstractSpinBox::CorrectToNearestValue
);
1646 bodyLabel
= new QLabel(tr("Message:"));
1647 bodyEdit
= new QTextEdit
;
1648 bodyEdit
->setPlainText( settings
.value("Message", "<h1>Enter a message here!</h1>").toString() );
1650 messageLayout
->addWidget(durationLabel
, 1, 0);
1651 messageLayout
->addWidget(durationSpinBox
, 1, 1);
1652 messageLayout
->addWidget(durationLabel1
, 1, 2);
1653 messageLayout
->addWidget(bodyLabel
, 3, 0);
1654 messageLayout
->addWidget(bodyEdit
, 3, 1, 2, 4);
1655 messageLayout
->setColumnStretch(3, 1);
1656 messageLayout
->setRowStretch(4, 1);
1657 mainLayout
->addLayout(messageLayout
);
1658 tab2
->setLayout(mainLayout
);
1660 mainLayout
= new QVBoxLayout
;
1661 tab1
->setLayout(mainLayout
);
1663 tasksTree
= new QTreeWidget
;
1664 tasksTree
->setSizePolicy(QSizePolicy::Expanding
, QSizePolicy::Expanding
);
1665 tasksTree
->setColumnCount(3);
1666 tasksTree
->setColumnWidth(0, 300);
1667 tasksTree
->setColumnWidth(1, 65);
1668 tasksTree
->setIconSize(QSize(32,32));
1669 connect(tasksTree
, SIGNAL(itemDoubleClicked(QTreeWidgetItem
*,int)), this, SLOT(doubleClickedTask(QTreeWidgetItem
*,int)));
1672 taskTextEditor
= new QTextEdit
;
1673 taskTextEditor
->setFixedHeight(100);
1674 taskTextEditor
->setSizePolicy(QSizePolicy::Expanding
, QSizePolicy::Preferred
);
1675 connect(taskTextEditor
, SIGNAL(textChanged()), this, SLOT(commitCurrentTask()));
1678 taskTitleEditor
= new QLineEdit
;
1679 taskTitleEditor
->setFixedHeight(20);
1680 taskTitleEditor
->setSizePolicy(QSizePolicy::Expanding
, QSizePolicy::Preferred
);
1681 connect(taskTitleEditor
, SIGNAL(editingFinished()), this, SLOT(commitCurrentTask()));
1683 taskPixmapViewer
= new PixmapViewer
;
1684 mainLayout
->addWidget(tasksTree
, 2);
1685 QHBoxLayout
* detailsLayout
= new QHBoxLayout
;
1686 QVBoxLayout
* editsLayout
= new QVBoxLayout
;
1687 detailsLayout
->addWidget(taskPixmapViewer
);
1688 connect(taskPixmapViewer
, SIGNAL(changed()), this, SLOT(commitCurrentTask()));
1689 editsLayout
->addWidget(taskTitleEditor
);
1690 editsLayout
->addWidget(taskTextEditor
);
1691 QHBoxLayout
* datesLayout
= new QHBoxLayout
;
1692 datesLayout
->addWidget(new QLabel("Due: "));
1693 dueEditor
= new QDateEdit
;
1694 dueEditor
->setMinimumDate(QDate(2000,1,1));
1695 datesLayout
->addWidget(dueEditor
);
1696 datesLayout
->addWidget(new QLabel("Estimated: "));
1697 estimatedHoursEditor
= new QSpinBox
;
1698 estimatedHoursEditor
->setRange(0, 1e5
);
1699 estimatedHoursEditor
->setSuffix("hours");
1700 datesLayout
->addWidget(estimatedHoursEditor
);
1701 editsLayout
->addLayout(datesLayout
);
1702 detailsLayout
->addLayout(editsLayout
);
1703 connect(dueEditor
, SIGNAL(editingFinished()), this, SLOT(commitCurrentTask()));
1704 connect(estimatedHoursEditor
,SIGNAL(editingFinished()), this, SLOT(commitCurrentTask()));
1706 QVBoxLayout
* colorsLayout
= new QVBoxLayout
;
1707 bgColorButton
= new QPushButton("bg\ncolor");
1708 bgColorButton
->setToolTip("Background color");
1709 bgColorButton
->setSizePolicy(QSizePolicy(QSizePolicy::Preferred
, QSizePolicy::MinimumExpanding
));
1711 fgColorButton
= new QPushButton("fg\ncolor");
1712 fgColorButton
->setToolTip("Foreground color");
1713 fgColorButton
->setSizePolicy(QSizePolicy(QSizePolicy::Preferred
, QSizePolicy::MinimumExpanding
));
1716 // fix Windows XP "style"
1717 bgColorButton
->setStyle(new QWindowsStyle());
1718 fgColorButton
->setStyle(new QWindowsStyle());
1721 colorsLayout
->addWidget(bgColorButton
);
1722 colorsLayout
->addWidget(fgColorButton
);
1723 detailsLayout
->addLayout(colorsLayout
);
1725 mainLayout
->addLayout(detailsLayout
);
1727 QVBoxLayout
* tab4MainLayout
= new QVBoxLayout(tab4
);
1728 //taskSelector = new QComboBox;
1729 summaryList
= newTaskSummaryList();
1730 QTabWidget
* tabs
= new QTabWidget
;
1731 tabs
->setTabPosition(QTabWidget::East
);
1732 tabs
->addTab(summaryList
, "List");
1733 QGraphicsView
* summaryView
= new QGraphicsView
;
1734 summaryView
->setScene(new QGraphicsScene
);
1735 TaskSummaryPieChart
* chart
= new TaskSummaryPieChart
;
1736 summaryView
->scene()->addItem(new TaskSummaryPieChart
);
1737 summaryView
->centerOn(chart
);
1738 tabs
->addTab(summaryView
, "Chart");
1739 tab4MainLayout
->addWidget(tabs
);
1740 cal3
= new QCalendarWidget
;
1741 cal3
->setMinimumSize(QSize(250,200));
1742 cal4
= new QCalendarWidget
;
1743 cal4
->setMinimumSize(QSize(250,200));
1744 cal3
->setSizePolicy(QSizePolicy::MinimumExpanding
, QSizePolicy::Fixed
);
1745 cal4
->setSizePolicy(QSizePolicy::MinimumExpanding
, QSizePolicy::Fixed
);
1746 QHBoxLayout
* calsLayout
= new QHBoxLayout
;
1747 calsLayout
->addWidget(cal3
);
1748 calsLayout
->addWidget(cal4
);
1749 cal4
->setSelectedDate(QDate::currentDate().addDays(1));
1750 tab4MainLayout
->addLayout(calsLayout
);
1753 QVBoxLayout
* tab3MainLayout
= new QVBoxLayout(tab3
);
1754 //taskSelector = new QComboBox;
1755 hitsList
= newHitsList();
1756 connect(hitsList
, SIGNAL(currentItemChanged(QTreeWidgetItem
*,QTreeWidgetItem
*)), this, SLOT(hitsSelectedInList(QTreeWidgetItem
*,QTreeWidgetItem
*)));
1757 hitsTimeline
= new Timeline
;
1758 connect(hitsTimeline
, SIGNAL(hitSelected(HitItem
*)), this, SLOT(hitsSelectedInTimeline(HitItem
*)));
1759 cal1
= new QCalendarWidget
;
1760 cal1
->setMinimumSize(QSize(250,200));
1761 cal2
= new QCalendarWidget
;
1762 cal2
->setMinimumSize(QSize(250,200));
1763 cal1
->setSizePolicy(QSizePolicy::MinimumExpanding
, QSizePolicy::Fixed
);
1764 cal2
->setSizePolicy(QSizePolicy::MinimumExpanding
, QSizePolicy::Fixed
);
1765 //tab3MainLayout->addWidget(taskSelector);
1767 calsLayout
= new QHBoxLayout
;
1768 calsLayout
->addWidget(cal1
);
1769 calsLayout
->addWidget(cal2
);
1770 cal2
->setSelectedDate(QDate::currentDate().addDays(1));
1771 tab3MainLayout
->addWidget(hitsList
);
1772 tab3MainLayout
->addWidget(hitsTimeline
);
1773 tab3MainLayout
->addLayout(calsLayout
);
1777 m_settings
->setWindowTitle(tr("SaK"));
1778 m_settings
->resize(400, 300);
1782 QTreeWidget
* Sak::newHitsList()
1784 QTreeWidget
* hitsList
= new QTreeWidget
;
1785 hitsList
->setColumnCount(4);
1786 hitsList
->setColumnWidth(0, 200);
1787 hitsList
->setColumnWidth(1, 150);
1788 hitsList
->setColumnWidth(2, 150);
1789 hitsList
->setColumnWidth(3, 150);
1790 hitsList
->setColumnWidth(4, 150);
1791 hitsList
->setIconSize(QSize(24,24));
1792 hitsList
->setSortingEnabled(true);
1793 hitsList
->setItemDelegateForColumn(0, new MyDateItemDelegate
);
1794 hitsList
->setItemDelegateForColumn(1, new TaskItemDelegate(this));
1795 hitsList
->setItemDelegateForColumn(2, new SubTaskItemDelegate(this));
1796 QTreeWidgetItem
* header
= new QTreeWidgetItem
;
1797 header
->setText(0, "Date/Time");
1798 header
->setText(1, "Task");
1799 header
->setText(2, "Subtask");
1800 header
->setText(3, "Duration (min)");
1801 header
->setText(4, "Overestimation");
1802 hitsList
->setHeaderItem(header
);
1803 hitsList
->setSizePolicy(QSizePolicy::MinimumExpanding
, QSizePolicy::MinimumExpanding
);
1807 QTreeWidget
* Sak::newTaskSummaryList()
1809 QTreeWidget
* taskSummaryList
= new QTreeWidget
;
1810 taskSummaryList
->setColumnCount(4);
1811 taskSummaryList
->setColumnWidth(0, 250);
1812 taskSummaryList
->setColumnWidth(1, 150);
1813 taskSummaryList
->setColumnWidth(1, 150);
1814 taskSummaryList
->setIconSize(QSize(24,24));
1815 taskSummaryList
->setSortingEnabled(true);
1816 QTreeWidgetItem
* header
= new QTreeWidgetItem
;
1817 header
->setText(0, "Task");
1818 header
->setText(1, "Hours");
1819 taskSummaryList
->setHeaderItem(header
);
1820 taskSummaryList
->setSizePolicy(QSizePolicy::MinimumExpanding
, QSizePolicy::MinimumExpanding
);
1821 taskSummaryList
->setEnabled(true);
1822 return taskSummaryList
;
1825 void Sak::setVisible(bool visible
)
1827 minimizeAction
->setEnabled(visible
);
1828 maximizeAction
->setEnabled(!m_settings
->isMaximized());
1829 restoreAction
->setEnabled(m_settings
->isMaximized() || !visible
);
1830 m_settings
->setVisible(visible
);
1833 void Sak::createActions()
1835 minimizeAction
= new QAction(tr("Mi&nimize"), m_settings
);
1836 connect(minimizeAction
, SIGNAL(triggered()), m_settings
, SLOT(hide()));
1838 maximizeAction
= new QAction(tr("Ma&ximize"), m_settings
);
1839 connect(maximizeAction
, SIGNAL(triggered()), m_settings
, SLOT(showMaximized()));
1841 restoreAction
= new QAction(tr("&Restore"), m_settings
);
1842 connect(restoreAction
, SIGNAL(triggered()), m_settings
, SLOT(showNormal()));
1844 quitAction
= new QAction(tr("&Quit"), m_settings
);
1845 connect(quitAction
, SIGNAL(triggered()), qApp
, SLOT(quit()));
1847 startAction
= new QAction(tr("Start polling"), m_settings
);
1848 connect(startAction
, SIGNAL(triggered()), this, SLOT(start()));
1850 stopAction
= new QAction(tr("Stop polling"), m_settings
);
1851 connect(stopAction
, SIGNAL(triggered()), this, SLOT(stop()));
1853 flushAction
= new QAction(tr("&Flush data/settings to disk"), m_settings
);
1854 connect(flushAction
, SIGNAL(triggered()), this, SLOT(flush()));
1856 saveAsDbAction
= new QAction(tr("Backup as"), m_settings
);
1857 // connect(saveAsDbAction, SIGNAL(triggered()), this, SLOT(saveAsDb()));
1859 exportDbCsvAction
= new QAction(tr("Export hits in CSV format"), m_settings
);
1860 connect(exportDbCsvAction
, SIGNAL(triggered()), this, SLOT(exportDbCsv()));
1863 saveToGmailAction
= new QAction(tr("Store in your gmail account free space"), m_settings
);
1864 connect(saveToGmailAction
, SIGNAL(triggered()), this, SLOT(saveToGmail()));
1866 gmailLoginAction
= new QAction(tr("Log in gmail"), m_settings
);
1867 connect(gmailLoginAction
, SIGNAL(triggered()), this, SLOT(logInGmail()));
1869 saveToGmailAction
= NULL
;
1870 gmailLoginAction
= NULL
;
1873 openAction
= new QAction(tr("Import a task from file"), m_settings
);
1874 connect(openAction
, SIGNAL(triggered()), this, SLOT(open()));
1876 m_addHitAction
= new QAction("Add hit", m_settings
);
1877 m_addHitMenu
= new QMenu(m_settings
);
1878 m_addHitAction
->setText("Add hit");
1879 m_addHitMenu
->addAction(m_addHitAction
);
1880 connect(m_addHitAction
, SIGNAL(triggered()), this, SLOT(addDefaultHit()));
1882 m_exportDataAction
= new QAction("Export data", m_settings
);
1883 m_exportDataAction
->setText("Export data");
1884 m_addHitMenu
->addAction(m_exportDataAction
);
1885 connect(m_exportDataAction
, SIGNAL(triggered()), this, SLOT(exportHits()));
1887 m_addTaskAction
= new QAction("Add task", m_settings
);
1888 m_addTaskMenu
= new QMenu(m_settings
);
1889 m_addTaskAction
->setText("Add task");
1890 m_addTaskMenu
->addAction(m_addTaskAction
);
1891 connect(m_addTaskAction
, SIGNAL(triggered()), this, SLOT(addDefaultTask()));
1894 void Sak::trayIconActivated(QSystemTrayIcon::ActivationReason reason
)
1896 if (reason
== QSystemTrayIcon::DoubleClick
) {
1901 //END Settings <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<