2 * Copyright (C) 2003 by Scott Monachello <smonach@cox.net>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the
16 * Free Software Foundation, Inc.
17 * 51 Franklin Street, Fifth Floor
18 * Boston, MA 02110-1301 USA.
27 #include <QStyledItemDelegate>
32 #include <QMouseEvent>
36 #include <KApplication> // kapp
38 #include <KFileDialog>
39 #include <KLocale> // i18n
40 #include <KMessageBox>
41 #include <KProgressDialog>
42 #include <KUrlRequester>
44 #include "csvexportdialog.h"
45 #include "desktoptracker.h"
46 #include "edittaskdialog.h"
47 #include "idletimedetector.h"
48 #include "plannerparser.h"
49 #include "preferences.h"
50 #include "ktimetracker.h"
53 #include "treeviewheadercontextmenu.h"
54 #include "focusdetector.h"
55 #include "focusdetectornotifier.h"
56 #include "storageadaptor.h"
58 #define T_LINESIZE 1023
62 //BEGIN TaskViewDelegate (custom painting of the progress column)
63 class TaskViewDelegate
: public QStyledItemDelegate
{
65 TaskViewDelegate( QObject
*parent
= 0 ) : QStyledItemDelegate( parent
) {}
67 void paint( QPainter
*painter
, const QStyleOptionViewItem
&option
, const QModelIndex
&index
) const
69 if (index
.column () == 6)
71 QApplication::style()->drawControl( QStyle::CE_ItemViewItem
, &option
, painter
);
72 int rX
= option
.rect
.x() + 2;
73 int rY
= option
.rect
.y() + 2;
74 int rWidth
= option
.rect
.width() - 4;
75 int rHeight
= option
.rect
.height() - 4;
76 int value
= index
.model()->data( index
).toInt();
77 int newWidth
= (int)(rWidth
* (value
/ 100.));
79 if(QApplication::isLeftToRight())
81 int mid
= rY
+ rHeight
/ 2;
82 int width
= rWidth
/ 2;
83 QLinearGradient
gradient1( rX
, mid
, rX
+ width
, mid
);
84 gradient1
.setColorAt( 0, Qt::red
);
85 gradient1
.setColorAt( 1, Qt::yellow
);
86 painter
->fillRect( rX
, rY
, (newWidth
< width
) ? newWidth
: width
, rHeight
, gradient1
);
90 QLinearGradient
gradient2( rX
+ width
, mid
, rX
+ 2 * width
, mid
);
91 gradient2
.setColorAt( 0, Qt::yellow
);
92 gradient2
.setColorAt( 1, Qt::green
);
93 painter
->fillRect( rX
+ width
, rY
, newWidth
- width
, rHeight
, gradient2
);
96 painter
->setPen( option
.state
& QStyle::State_Selected
? option
.palette
.highlight().color() : option
.palette
.background().color() );
97 for (int x
= rHeight
; x
< newWidth
; x
+= rHeight
)
99 painter
->drawLine( rX
+ x
, rY
, rX
+ x
, rY
+ rHeight
- 1 );
104 int mid
= option
.rect
.height() - rHeight
/ 2;
105 int width
= rWidth
/ 2;
106 QLinearGradient
gradient1( rX
, mid
, rX
+ width
, mid
);
107 gradient1
.setColorAt( 0, Qt::red
);
108 gradient1
.setColorAt( 1, Qt::yellow
);
109 painter
->fillRect( option
.rect
.height(), rY
, (newWidth
< width
) ? newWidth
: width
, rHeight
, gradient1
);
111 if (newWidth
> width
)
113 QLinearGradient
gradient2( rX
+ width
, mid
, rX
+ 2 * width
, mid
);
114 gradient2
.setColorAt( 0, Qt::yellow
);
115 gradient2
.setColorAt( 1, Qt::green
);
116 painter
->fillRect( rX
+ width
, rY
, newWidth
- width
, rHeight
, gradient2
);
119 painter
->setPen( option
.state
& QStyle::State_Selected
? option
.palette
.highlight().color() : option
.palette
.background().color() );
120 for (int x
= rWidth
- rHeight
; x
> newWidth
; x
-= rHeight
)
122 painter
->drawLine( rWidth
- x
, rY
, rWidth
- x
, rY
+ rHeight
- 1 );
126 painter
->setPen( Qt::black
);
127 painter
->drawText( option
.rect
, Qt::AlignCenter
| Qt::AlignVCenter
, QString::number(value
) + " %" );
131 QStyledItemDelegate::paint( painter
, option
, index
);
139 class TaskView::Private
143 mStorage( new timetrackerstorage() ),
144 mFocusTrackingActive( false ) {}
150 timetrackerstorage
*mStorage
;
151 bool mFocusTrackingActive
;
152 Task
* mLastTaskWithFocus
;
153 QList
<Task
*> mActiveTasks
;
155 QMenu
*mPopupPercentageMenu
;
156 QMap
<QAction
*, int> mPercentage
;
157 QMenu
*mPopupPriorityMenu
;
158 QMap
<QAction
*, int> mPriority
;
163 TaskView::TaskView( QWidget
*parent
) : QTreeWidget(parent
), d( new Private() )
165 _preferences
= Preferences::instance();
166 new StorageAdaptor( this );
167 QDBusConnection::sessionBus().registerObject( "/ktimetrackerstorage", this );
169 connect( this, SIGNAL(itemExpanded(QTreeWidgetItem
*)),
170 this, SLOT(itemStateChanged(QTreeWidgetItem
*)) );
171 connect( this, SIGNAL(itemCollapsed(QTreeWidgetItem
*)),
172 this, SLOT(itemStateChanged(QTreeWidgetItem
*)) );
173 connect( this, SIGNAL(itemDoubleClicked(QTreeWidgetItem
*,int)),
174 this, SLOT(slotItemDoubleClicked(QTreeWidgetItem
*,int)) );
175 connect( FocusDetectorNotifier::instance()->focusDetector(),
176 SIGNAL(newFocus(QString
)),
177 this, SLOT(newFocusWindowDetected(QString
)) );
180 setWindowFlags( windowFlags() | Qt::WindowContextHelpButtonHint
);
181 labels
<< i18n( "Task Name" ) << i18n( "Session Time" ) << i18n( "Time" )
182 << i18n( "Total Session Time" ) << i18n( "Total Time" )
183 << i18n( "Priority" ) << i18n( "Percent Complete" );
184 setHeaderLabels( labels
);
185 headerItem()->setWhatsThis(0,i18n("The task name is what you call the task, it can be chosen freely."));
186 headerItem()->setWhatsThis(1,i18n("The session time is the time since you last chose \"start new session.\""));
187 headerItem()->setWhatsThis(3,i18n("The total session time is the session time of this task and all its subtasks."));
188 headerItem()->setWhatsThis(4,i18n("The total time is the time of this task and all its subtasks."));
189 setAllColumnsShowFocus( true );
190 setSortingEnabled( true );
191 setAlternatingRowColors( true );
192 setDragDropMode( QAbstractItemView::InternalMove
);
193 setItemDelegateForColumn( 6, new TaskViewDelegate(this) );
195 // set up the minuteTimer
196 _minuteTimer
= new QTimer(this);
197 connect( _minuteTimer
, SIGNAL(timeout()), this, SLOT(minuteUpdate()));
198 _minuteTimer
->start(1000 * secsPerMinute
);
200 // Set up the idle detection.
201 _idleTimeDetector
= new IdleTimeDetector( KTimeTrackerSettings::period() );
202 connect( _idleTimeDetector
, SIGNAL(subtractTime(int)),
203 this, SLOT(subtractTime(int)));
204 connect( _idleTimeDetector
, SIGNAL(stopAllTimers(QDateTime
)),
205 this, SLOT(stopAllTimers(QDateTime
)));
206 if (!_idleTimeDetector
->isIdleDetectionPossible())
207 KTimeTrackerSettings::setEnabled( false );
209 // Setup auto save timer
210 _autoSaveTimer
= new QTimer(this);
211 connect( _autoSaveTimer
, SIGNAL(timeout()), this, SLOT(save()));
213 // Setup manual save timer (to save changes a little while after they happen)
214 _manualSaveTimer
= new QTimer(this);
215 _manualSaveTimer
->setSingleShot( true );
216 connect( _manualSaveTimer
, SIGNAL(timeout()), this, SLOT(save()));
218 // Connect desktop tracker events to task starting/stopping
219 _desktopTracker
= new DesktopTracker();
220 connect( _desktopTracker
, SIGNAL(reachedActiveDesktop(Task
*)),
221 this, SLOT(startTimerFor(Task
*)));
222 connect( _desktopTracker
, SIGNAL(leftActiveDesktop(Task
*)),
223 this, SLOT(stopTimerFor(Task
*)));
225 // Header context menu
226 TreeViewHeaderContextMenu
*headerContextMenu
= new TreeViewHeaderContextMenu( this, this, TreeViewHeaderContextMenu::AlwaysCheckBox
, QVector
<int>() << 0 );
227 connect( headerContextMenu
, SIGNAL(columnToggled(int)), this, SLOT(slotColumnToggled(int)) );
230 d
->mPopupPercentageMenu
= new QMenu( this );
231 for ( int i
= 0; i
<= 100; i
+= 10 )
233 QString label
= i18n( "%1 %" , i
);
234 d
->mPercentage
[ d
->mPopupPercentageMenu
->addAction( label
) ] = i
;
236 connect( d
->mPopupPercentageMenu
, SIGNAL(triggered(QAction
*)),
237 this, SLOT(slotSetPercentage(QAction
*)) );
239 d
->mPopupPriorityMenu
= new QMenu( this );
240 for ( int i
= 0; i
<= 9; ++i
)
246 label
= i18n( "unspecified" );
249 label
= i18nc( "combox entry for highest priority", "1 (highest)" );
252 label
= i18nc( "combox entry for medium priority", "5 (medium)" );
255 label
= i18nc( "combox entry for lowest priority", "9 (lowest)" );
258 label
= QString( "%1" ).arg( i
);
261 d
->mPriority
[ d
->mPopupPriorityMenu
->addAction( label
) ] = i
;
263 connect( d
->mPopupPriorityMenu
, SIGNAL(triggered(QAction
*)),
264 this, SLOT(slotSetPriority(QAction
*)) );
266 setContextMenuPolicy( Qt::CustomContextMenu
);
267 connect( this, SIGNAL(customContextMenuRequested(QPoint
)),
268 this, SLOT(slotCustomContextMenuRequested(QPoint
)) );
271 sortByColumn( 0, Qt::AscendingOrder
);
272 for (int i
=0; i
<=columnCount(); ++i
) resizeColumnToContents(i
);
275 void TaskView::newFocusWindowDetected( const QString
&taskName
)
277 QString newTaskName
= taskName
;
278 newTaskName
.remove( '\n' );
280 if ( d
->mFocusTrackingActive
)
282 bool found
= false; // has taskName been found in our tasks
283 stopTimerFor( d
->mLastTaskWithFocus
);
285 for ( Task
* task
= itemAt( i
); task
; task
= itemAt( ++i
) )
287 if ( task
->name() == newTaskName
)
290 startTimerFor( task
);
291 d
->mLastTaskWithFocus
= task
;
296 QString taskuid
= addTask( newTaskName
);
297 if ( taskuid
.isNull() )
299 KMessageBox::error( 0, i18n(
300 "Error storing new task. Your changes were not saved. Make sure you can edit your iCalendar file. Also quit all applications using this file and remove any lock file related to its name from ~/.kde/share/apps/kabc/lock/ " ) );
303 for ( Task
* task
= itemAt( i
); task
; task
= itemAt( ++i
) )
305 if (task
->name() == newTaskName
)
307 startTimerFor( task
);
308 d
->mLastTaskWithFocus
= task
;
312 emit
updateButtons();
313 } // focustrackingactive
316 void TaskView::mouseMoveEvent( QMouseEvent
*event
)
318 QModelIndex index
= indexAt( event
->pos() );
320 if (index
.isValid() && index
.column() == 6)
322 int newValue
= (int)((event
->pos().x() - visualRect(index
).x()) / (double)(visualRect(index
).width()) * 100);
323 if ( event
->modifiers() & Qt::ShiftModifier
)
325 int delta
= newValue
% 10;
328 newValue
+= (10 - delta
);
334 QTreeWidgetItem
*item
= itemFromIndex( index
);
335 if (item
&& item
->isSelected())
337 Task
*task
= static_cast<Task
*>(item
);
340 task
->setPercentComplete( newValue
, d
->mStorage
);
341 emit
updateButtons();
347 QTreeWidget::mouseMoveEvent( event
);
351 void TaskView::mousePressEvent( QMouseEvent
*event
)
353 kDebug(5970) << "Entering function, event->button()=" << event
->button();
354 QModelIndex index
= indexAt( event
->pos() );
356 // if the user toggles a task as complete/incomplete
357 if ( index
.isValid() && index
.column() == 0 && visualRect( index
).x() <= event
->pos().x()
358 && event
->pos().x() < visualRect( index
).x() + 19)
360 QTreeWidgetItem
*item
= itemFromIndex( index
);
363 Task
*task
= static_cast<Task
*>(item
);
366 if (task
->isComplete()) task
->setPercentComplete( 0, d
->mStorage
);
369 task
->setPercentComplete( 100, d
->mStorage
);
371 emit
updateButtons();
375 else // the user did not mark a task as complete/incomplete
377 if ( KTimeTrackerSettings::configPDA() )
378 // if you have a touchscreen, you cannot right-click. So, display context menu on any click.
380 QPoint newPos
= viewport()->mapToGlobal( event
->pos() );
381 emit
contextMenuRequested( newPos
);
383 QTreeWidget::mousePressEvent( event
);
387 timetrackerstorage
* TaskView::storage()
392 TaskView::~TaskView()
394 FocusDetectorNotifier::instance()->detach( this );
396 KTimeTrackerSettings::self()->writeConfig();
399 Task
* TaskView::currentItem() const
401 kDebug(5970) << "Entering function";
402 return static_cast< Task
* >( QTreeWidget::currentItem() );
405 Task
* TaskView::itemAt(int i
)
406 /* This procedure delivers the item at the position i in the KTreeWidget.
407 Every item is a task. The items are counted linearily. The uppermost item
408 has the number i=0. */
410 if ( topLevelItemCount() == 0 ) return 0;
412 QTreeWidgetItemIterator
item( this );
413 while( *item
&& i
-- ) ++item
;
415 kDebug( 5970 ) << "Leaving TaskView::itemAt" << "returning " << (*item
==0);
422 void TaskView::load( const QString
&fileName
)
424 assert( !( fileName
.isEmpty() ) );
426 // if the program is used as an embedded plugin for konqueror, there may be a need
427 // to load from a file without touching the preferences.
428 kDebug(5970) << "Entering function";
430 QString err
= d
->mStorage
->load(this, fileName
);
434 KMessageBox::error(this, err
);
436 kDebug(5970) << "Leaving TaskView::load";
440 // Register tasks with desktop tracker
442 for ( Task
* t
= itemAt(i
); t
; t
= itemAt(++i
) )
443 _desktopTracker
->registerForDesktops( t
, t
->desktops() );
445 // Start all tasks that have an event without endtime
447 for ( Task
* t
= itemAt(i
); t
; t
= itemAt(++i
) )
449 if ( !d
->mStorage
->allEventsHaveEndTiMe( t
) )
452 d
->mActiveTasks
.append(t
);
453 emit
updateButtons();
454 if ( d
->mActiveTasks
.count() == 1 )
456 emit
tasksChanged( d
->mActiveTasks
);
461 if ( topLevelItemCount() > 0 )
464 setCurrentItem(topLevelItem( 0 ));
465 if ( !_desktopTracker
->startTracking().isEmpty() )
466 KMessageBox::error( 0, i18n( "Your virtual desktop number is too high, desktop tracking will not work" ) );
470 for (int i
=0; i
<=columnCount(); ++i
) resizeColumnToContents(i
);
471 kDebug(5970) << "Leaving function";
474 void TaskView::restoreItemState()
475 /* Restores the item state of every item. An item is a task in the list.
476 Its state is whether it is expanded or not. If a task shall be expanded
477 is stored in the _preferences object. */
479 kDebug(5970) << "Entering function";
481 if ( topLevelItemCount() > 0 )
483 QTreeWidgetItemIterator
item( this );
486 Task
*t
= (Task
*) *item
;
487 t
->setExpanded( _preferences
->readBoolEntry( t
->uid() ) );
491 kDebug(5970) << "Leaving function";
494 void TaskView::itemStateChanged( QTreeWidgetItem
*item
)
496 kDebug() << "Entering function";
497 if ( !item
|| _isloading
) return;
498 Task
*t
= (Task
*)item
;
499 kDebug(5970) <<"TaskView::itemStateChanged()" <<" uid=" << t
->uid() <<" state=" << t
->isExpanded();
500 if( _preferences
) _preferences
->writeEntry( t
->uid(), t
->isExpanded() );
503 void TaskView::closeStorage()
505 d
->mStorage
->closeStorage();
508 bool TaskView::allEventsHaveEndTiMe()
510 return d
->mStorage
->allEventsHaveEndTiMe();
513 void TaskView::iCalFileModified()
515 KTimeTracker::KTTCalendar
*calendar
= qobject_cast
<KTimeTracker::KTTCalendar
*>( sender() );
516 if ( !calendar
|| !calendar
->weakPointer() ) {
517 kWarning() << "TaskView::iCalFileModified(): calendar or weakPointer is null: " << calendar
;
519 kDebug(5970) << "entering function";
521 d
->mStorage
->buildTaskView( calendar
->weakPointer().toStrongRef(), this );
522 kDebug(5970) << "exiting iCalFileModified";
526 void TaskView::refresh()
528 kDebug(5970) << "entering function";
530 for ( Task
* t
= itemAt(i
); t
; t
= itemAt(++i
) )
532 t
->setPixmapProgress();
533 t
->update(); // maybe there was a change in the times's format
536 // remove root decoration if there is no more child.
538 while ( itemAt( ++i
) && ( itemAt( i
)->depth() == 0 ) ){};
539 //setRootIsDecorated( itemAt( i ) && ( itemAt( i )->depth() != 0 ) );
540 // FIXME workaround? seems that the QItemDelegate for the procent column only
541 // works properly if rootIsDecorated == true.
542 setRootIsDecorated( true );
544 emit
updateButtons();
545 kDebug(5970) << "exiting TaskView::refresh()";
548 QString
TaskView::reFreshTimes()
549 /** Refresh the times of the tasks, e.g. when the history has been changed by the user */
551 kDebug(5970) << "Entering function";
553 // re-calculate the time for every task based on events in the history
554 KCalCore::Event::List eventList
= storage()->rawevents(); // get all events (!= tasks)
556 resetDisplayTimeForAllTasks();
558 while (itemAt(++n
)) // loop over all tasks
560 for( KCalCore::Event::List::iterator i
= eventList
.begin(); i
!= eventList
.end(); ++i
) // loop over all events
562 if ( (*i
)->relatedTo() == itemAt(n
)->uid() ) // if event i belongs to task n
564 KDateTime kdatetimestart
= (*i
)->dtStart();
565 KDateTime kdatetimeend
= (*i
)->dtEnd();
566 KDateTime eventstart
= KDateTime::fromString(kdatetimestart
.toString().remove("Z"));
567 KDateTime eventend
= KDateTime::fromString(kdatetimeend
.toString().remove("Z"));
568 int duration
=eventstart
.secsTo( eventend
)/60;
569 itemAt(n
)->addTime( duration
);
570 emit
totalTimesChanged( 0, duration
);
571 kDebug(5970) << "duration is " << duration
;
573 if ( itemAt(n
)->sessionStartTiMe().isValid() )
575 // if there is a session
576 if ((itemAt(n
)->sessionStartTiMe().secsTo( eventstart
)>0) &&
577 (itemAt(n
)->sessionStartTiMe().secsTo( eventend
)>0))
578 // if the event is after the session start
580 int sessionTime
=eventstart
.secsTo( eventend
)/60;
581 itemAt(n
)->setSessionTime( itemAt(n
)->sessionTime()+sessionTime
);
585 // so there is no session at all
587 itemAt(n
)->addSessionTime( duration
);
588 emit
totalTimesChanged( duration
, 0 );
594 for (int i
=0; i
<count(); ++i
) itemAt(i
)->recalculatetotaltime();
595 for (int i
=0; i
<count(); ++i
) itemAt(i
)->recalculatetotalsessiontime();
598 kDebug(5970) << "Leaving TaskView::reFreshTimes()";
602 void TaskView::importPlanner( const QString
&fileName
)
604 kDebug( 5970 ) << "entering importPlanner";
605 PlannerParser
*handler
= new PlannerParser( this );
606 QString lFileName
= fileName
;
607 if ( lFileName
.isEmpty() )
608 lFileName
= KFileDialog::getOpenFileName( QString(), QString(), 0 );
609 QFile
xmlFile( lFileName
);
610 QXmlInputSource
source( &xmlFile
);
611 QXmlSimpleReader reader
;
612 reader
.setContentHandler( handler
);
613 reader
.parse( source
);
617 QString
TaskView::report( const ReportCriteria
& rc
)
619 return d
->mStorage
->report( this, rc
);
622 void TaskView::exportcsvFile()
624 kDebug(5970) << "TaskView::exportcsvFile()";
626 CSVExportDialog
dialog( ReportCriteria::CSVTotalsExport
, this );
627 if ( currentItem() && currentItem()->isRoot() )
628 dialog
.enableTasksToExportQuestion();
629 dialog
.urlExportTo
->KUrlRequester::setMode(KFile::File
);
632 QString err
= d
->mStorage
->report( this, dialog
.reportCriteria() );
633 if ( !err
.isEmpty() ) KMessageBox::error( this, i18n(err
.toLatin1()) );
637 QString
TaskView::exportcsvHistory()
639 kDebug(5970) << "TaskView::exportcsvHistory()";
642 CSVExportDialog
dialog( ReportCriteria::CSVHistoryExport
, this );
643 if ( currentItem() && currentItem()->isRoot() )
644 dialog
.enableTasksToExportQuestion();
645 dialog
.urlExportTo
->KUrlRequester::setMode(KFile::File
);
648 err
= d
->mStorage
->report( this, dialog
.reportCriteria() );
653 long TaskView::count()
656 QTreeWidgetItemIterator
item( this );
665 QStringList
TaskView::tasks()
671 result
<< itemAt(i
)->name();
677 Task
* TaskView::task( const QString
& taskId
)
681 while ( itemAt(++i
) )
683 if ( itemAt( i
)->uid() == taskId
) result
=itemAt( i
);
687 void TaskView::dropEvent ( QDropEvent
* event
)
689 QTreeWidget::dropEvent(event
);
693 void TaskView::scheduleSave()
695 _manualSaveTimer
->start( 10 );
698 void TaskView::save()
700 kDebug(5970) <<"Entering TaskView::save()";
701 QString err
=d
->mStorage
->save(this);
705 QString errMsg
= d
->mStorage
->icalfile() + ":\n";
707 if (err
==QString("Could not save. Could not lock file."))
708 errMsg
+= i18n("Could not save. Disk full?");
710 errMsg
+= i18n("Could not save.");
712 KMessageBox::error(this, errMsg
);
716 void TaskView::startCurrentTimer()
718 startTimerFor( currentItem() );
721 void TaskView::startTimerFor( Task
* task
, const QDateTime
&startTime
)
723 kDebug(5970) << "Entering function";
724 if (task
!= 0 && d
->mActiveTasks
.indexOf(task
) == -1)
726 if (!task
->isComplete())
728 if ( KTimeTrackerSettings::uniTasking() ) stopAllTimers();
729 _idleTimeDetector
->startIdleDetection();
730 task
->setRunning(true, d
->mStorage
, startTime
);
731 d
->mActiveTasks
.append(task
);
732 emit
updateButtons();
733 if ( d
->mActiveTasks
.count() == 1 )
735 emit
tasksChanged( d
->mActiveTasks
);
740 void TaskView::clearActiveTasks()
742 d
->mActiveTasks
.clear();
745 void TaskView::stopAllTimers( const QDateTime
&when
)
747 kDebug(5970) << "Entering function";
748 KProgressDialog
dialog( this, 0, QString("Progress") );
749 dialog
.progressBar()->setMaximum( d
->mActiveTasks
.count() );
750 if ( d
->mActiveTasks
.count() > 1 ) dialog
.show();
752 foreach ( Task
*task
, d
->mActiveTasks
)
754 kapp
->processEvents();
755 task
->setRunning( false, d
->mStorage
, when
);
756 dialog
.progressBar()->setValue( dialog
.progressBar()->value() + 1 );
758 _idleTimeDetector
->stopIdleDetection();
759 FocusDetectorNotifier::instance()->detach( this );
760 d
->mActiveTasks
.clear();
761 emit
updateButtons();
762 emit
timersInactive();
763 emit
tasksChanged( d
->mActiveTasks
);
766 void TaskView::toggleFocusTracking()
768 d
->mFocusTrackingActive
= !d
->mFocusTrackingActive
;
770 if ( d
->mFocusTrackingActive
)
772 FocusDetectorNotifier::instance()->attach( this );
776 stopTimerFor( d
->mLastTaskWithFocus
);
777 FocusDetectorNotifier::instance()->detach( this );
780 emit
updateButtons();
783 void TaskView::startNewSession()
784 /* This procedure starts a new session. We speak of session times,
785 overalltimes (comprising all sessions) and total times (comprising all subtasks).
786 That is why there is also a total session time. */
788 kDebug(5970) <<"Entering TaskView::startNewSession";
789 QTreeWidgetItemIterator
item( this );
792 Task
* task
= (Task
*) *item
;
793 task
->startNewSession();
796 kDebug(5970) << "Leaving TaskView::startNewSession";
799 void TaskView::resetTimeForAllTasks()
800 /* This procedure resets all times (session and overall) for all tasks and subtasks. */
802 kDebug(5970) << "Entering function";
803 QTreeWidgetItemIterator
item( this );
806 Task
* task
= (Task
*) *item
;
810 storage()->deleteAllEvents();
811 kDebug(5970) << "Leaving function";
814 void TaskView::resetDisplayTimeForAllTasks()
815 /* This procedure resets all times (session and overall) for all tasks and subtasks. */
817 kDebug(5970) << "Entering function";
818 QTreeWidgetItemIterator
item( this );
821 Task
* task
= (Task
*) *item
;
825 kDebug(5970) << "Leaving function";
828 void TaskView::stopTimerFor(Task
* task
)
830 kDebug(5970) << "Entering function";
831 if ( task
!= 0 && d
->mActiveTasks
.indexOf(task
) != -1 )
833 d
->mActiveTasks
.removeAll(task
);
834 task
->setRunning(false, d
->mStorage
);
835 if ( d
->mActiveTasks
.count() == 0 )
837 _idleTimeDetector
->stopIdleDetection();
838 emit
timersInactive();
840 emit
updateButtons();
842 emit
tasksChanged( d
->mActiveTasks
);
845 void TaskView::stopCurrentTimer()
847 stopTimerFor( currentItem() );
848 if ( d
->mFocusTrackingActive
&& d
->mLastTaskWithFocus
== currentItem() )
850 toggleFocusTracking();
854 void TaskView::minuteUpdate()
856 addTimeToActiveTasks(1, false);
859 void TaskView::addTimeToActiveTasks(int minutes
, bool save_data
)
861 foreach ( Task
*task
, d
->mActiveTasks
)
862 task
->changeTime( minutes
, ( save_data
? d
->mStorage
: 0 ) );
865 void TaskView::newTask()
867 newTask(i18n("New Task"), 0);
870 void TaskView::newTask( const QString
&caption
, Task
*parent
)
872 EditTaskDialog
*dialog
= new EditTaskDialog( this, caption
, 0 );
873 long total
, totalDiff
, session
, sessionDiff
;
874 DesktopList desktopList
;
876 int result
= dialog
->exec();
877 if ( result
== QDialog::Accepted
)
879 QString taskName
= i18n( "Unnamed Task" );
880 if ( !dialog
->taskName().isEmpty()) taskName
= dialog
->taskName();
881 QString taskDescription
= dialog
->taskDescription();
883 total
= totalDiff
= session
= sessionDiff
= 0;
884 dialog
->status( &desktopList
);
886 // If all available desktops are checked, disable auto tracking,
887 // since it makes no sense to track for every desktop.
888 if ( desktopList
.size() == _desktopTracker
->desktopCount() )
891 QString uid
= addTask( taskName
, taskDescription
, total
, session
, desktopList
, parent
);
894 KMessageBox::error( 0, i18n(
895 "Error storing new task. Your changes were not saved. Make sure you can edit your iCalendar file. Also quit all applications using this file and remove any lock file related to its name from ~/.kde/share/apps/kabc/lock/ " ) );
898 emit
updateButtons();
901 QString
TaskView::addTask
902 ( const QString
& taskname
, const QString
& taskdescription
, long total
, long session
,
903 const DesktopList
& desktops
, Task
* parent
)
905 kDebug(5970) << "Entering function; taskname =" << taskname
;
906 setSortingEnabled(false);
908 if ( parent
) task
= new Task( taskname
, taskdescription
, total
, session
, desktops
, parent
);
909 else task
= new Task( taskname
, taskdescription
, total
, session
, desktops
, this );
911 task
->setUid( d
->mStorage
->addTask( task
, parent
) );
912 QString taskuid
=task
->uid();
913 if ( ! taskuid
.isNull() )
915 _desktopTracker
->registerForDesktops( task
, desktops
);
916 setCurrentItem( task
);
917 task
->setSelected( true );
918 task
->setPixmapProgress();
925 setSortingEnabled(true);
929 void TaskView::newSubTask()
931 Task
* task
= currentItem();
934 newTask(i18n("New Sub Task"), task
);
935 task
->setExpanded(true);
939 void TaskView::editTask()
941 kDebug(5970) <<"Entering editTask";
942 Task
*task
= currentItem();
945 DesktopList desktopList
= task
->desktops();
946 DesktopList oldDeskTopList
= desktopList
;
947 EditTaskDialog
*dialog
= new EditTaskDialog( this, i18n("Edit Task"), &desktopList
);
948 dialog
->setTask( task
->name() );
949 dialog
->setDescription( task
->description() );
950 int result
= dialog
->exec();
951 if (result
== QDialog::Accepted
)
953 QString taskName
= i18n("Unnamed Task");
954 if (!dialog
->taskName().isEmpty())
956 taskName
= dialog
->taskName();
958 // setName only does something if the new name is different
959 task
->setName(taskName
, d
->mStorage
);
960 task
->setDescription(dialog
->taskDescription());
961 // update session time as well if the time was changed
962 if (!dialog
->timeChange().isEmpty())
964 task
->changeTime(dialog
->timeChange().toInt(),d
->mStorage
);
966 dialog
->status( &desktopList
);
967 // If all available desktops are checked, disable auto tracking,
968 // since it makes no sense to track for every desktop.
969 if (desktopList
.size() == _desktopTracker
->desktopCount())
971 // only do something for autotracking if the new setting is different
972 if ( oldDeskTopList
!= desktopList
)
974 task
->setDesktopList(desktopList
);
975 _desktopTracker
->registerForDesktops( task
, desktopList
);
977 emit
updateButtons();
981 void TaskView::setPerCentComplete(int completion
)
983 Task
* task
= currentItem();
986 KMessageBox::information(0,i18n("No task selected."));
990 if (completion
<0) completion
=0;
993 task
->setPercentComplete(completion
, d
->mStorage
);
994 task
->setPixmapProgress();
996 emit
updateButtons();
1000 void TaskView::deleteTaskBatch( Task
* task
)
1002 QString uid
=task
->uid();
1003 task
->remove(d
->mStorage
);
1004 _preferences
->deleteEntry( uid
); // forget if the item was expanded or collapsed
1007 // Stop idle detection if no more counters are running
1008 if (d
->mActiveTasks
.count() == 0)
1010 _idleTimeDetector
->stopIdleDetection();
1011 emit
timersInactive();
1013 task
->delete_recursive();
1014 emit
tasksChanged( d
->mActiveTasks
);
1018 void TaskView::deleteTask( Task
* task
)
1019 /* Attention when popping up a window asking for confirmation.
1020 If you have "Track active applications" on, this window will create a new task and
1021 make this task running and selected. */
1023 kDebug(5970) << "Entering function";
1024 if (task
== 0) task
= currentItem();
1025 if (currentItem() == 0)
1027 KMessageBox::information(0,i18n("No task selected."));
1031 int response
= KMessageBox::Continue
;
1032 if (KTimeTrackerSettings::promptDelete())
1034 response
= KMessageBox::warningContinueCancel( 0,
1035 i18n( "Are you sure you want to delete the selected"
1036 " task and its entire history?\n"
1037 "NOTE: all subtasks and their history will also "
1039 i18n( "Deleting Task"), KStandardGuiItem::del());
1041 if (response
== KMessageBox::Continue
) deleteTaskBatch(task
);
1045 void TaskView::markTaskAsComplete()
1047 if (currentItem() == 0)
1049 KMessageBox::information(0,i18n("No task selected."));
1052 currentItem()->setPercentComplete(100, d
->mStorage
);
1053 currentItem()->setPixmapProgress();
1055 emit
updateButtons();
1058 void TaskView::subtractTime(int minutes
)
1060 addTimeToActiveTasks(-minutes
,false); // subtract time in memory, but do not store it
1063 void TaskView::deletingTask(Task
* deletedTask
)
1065 kDebug(5970) << "Entering function";
1066 DesktopList desktopList
;
1068 _desktopTracker
->registerForDesktops( deletedTask
, desktopList
);
1069 d
->mActiveTasks
.removeAll( deletedTask
);
1071 emit
tasksChanged( d
->mActiveTasks
);
1074 void TaskView::markTaskAsIncomplete()
1076 setPerCentComplete(50); // if it has been reopened, assume half-done
1079 QString
TaskView::clipTotals( const ReportCriteria
&rc
)
1080 // This function stores the user's tasks into the clipboard.
1081 // rc tells how the user wants his report, e.g. all times or session times
1083 kDebug(5970) << "Entering function";
1086 KApplication::clipboard()->setText(t
.totalsAsText(this, rc
));
1090 QString
TaskView::setClipBoardText(const QString
& s
)
1092 QString err
; // maybe we find possible errors later
1093 KApplication::clipboard()->setText(s
);
1097 void TaskView::slotItemDoubleClicked( QTreeWidgetItem
*item
, int )
1101 Task
*task
= static_cast<Task
*>( item
);
1104 if ( task
->isRunning() )
1108 else if ( !task
->isComplete() )
1111 startCurrentTimer();
1117 void TaskView::slotColumnToggled( int column
)
1122 KTimeTrackerSettings::setDisplaySessionTime( !isColumnHidden( 1 ) );
1125 KTimeTrackerSettings::setDisplayTime( !isColumnHidden( 2 ) );
1128 KTimeTrackerSettings::setDisplayTotalSessionTime( !isColumnHidden( 3 ) );
1131 KTimeTrackerSettings::setDisplayTotalTime( !isColumnHidden( 4 ) );
1134 KTimeTrackerSettings::setDisplayPriority( !isColumnHidden( 5 ) );
1137 KTimeTrackerSettings::setDisplayPercentComplete( !isColumnHidden( 6 ) );
1140 KTimeTrackerSettings::self()->writeConfig();
1143 void TaskView::slotCustomContextMenuRequested( const QPoint
&pos
)
1145 QPoint newPos
= viewport()->mapToGlobal( pos
);
1146 int column
= columnAt( pos
.x() );
1150 case 6: /* percentage */
1151 d
->mPopupPercentageMenu
->popup( newPos
);
1154 case 5: /* priority */
1155 d
->mPopupPriorityMenu
->popup( newPos
);
1159 emit
contextMenuRequested( newPos
);
1164 void TaskView::slotSetPercentage( QAction
*action
)
1166 if ( currentItem() )
1168 currentItem()->setPercentComplete( d
->mPercentage
[ action
], storage() );
1169 emit
updateButtons();
1173 void TaskView::slotSetPriority( QAction
*action
)
1175 if ( currentItem() )
1177 currentItem()->setPriority( d
->mPriority
[ action
] );
1181 bool TaskView::isFocusTrackingActive() const
1183 return d
->mFocusTrackingActive
;
1186 QList
< Task
* > TaskView::activeTasks() const
1188 return d
->mActiveTasks
;
1191 void TaskView::reconfigure()
1193 kDebug(5970) << "Entering function";
1195 setColumnHidden( 1, !KTimeTrackerSettings::displaySessionTime() );
1196 setColumnHidden( 2, !KTimeTrackerSettings::displayTime() );
1197 setColumnHidden( 3, !KTimeTrackerSettings::displayTotalSessionTime() );
1198 setColumnHidden( 4, !KTimeTrackerSettings::displayTotalTime() );
1199 setColumnHidden( 5, !KTimeTrackerSettings::displayPriority() );
1200 setColumnHidden( 6, !KTimeTrackerSettings::displayPercentComplete() );
1203 _idleTimeDetector
->setMaxIdle( KTimeTrackerSettings::period() );
1204 _idleTimeDetector
->toggleOverAllIdleDetection( KTimeTrackerSettings::enabled() );
1207 if ( KTimeTrackerSettings::autoSave() )
1209 _autoSaveTimer
->start(
1210 KTimeTrackerSettings::autoSavePeriod() * 1000 * secsPerMinute
1213 else if ( _autoSaveTimer
->isActive() )
1215 _autoSaveTimer
->stop();