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( const QString
& ) ),
177 this, SLOT( newFocusWindowDetected ( const 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( extractTime(int) ),
203 this, SLOT( extractTime(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( const QPoint
& ) ),
268 this, SLOT( slotCustomContextMenuRequested( const 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() { d
->mStorage
->closeStorage(); }
505 bool TaskView::allEventsHaveEndTiMe()
507 return d
->mStorage
->allEventsHaveEndTiMe();
510 void TaskView::iCalFileModified(ResourceCalendar
*rc
)
512 kDebug(5970) << "entering function";
513 kDebug(5970) << rc
->infoText();
515 d
->mStorage
->buildTaskView(rc
,this);
516 kDebug(5970) << "exiting iCalFileModified";
519 void TaskView::refresh()
521 kDebug(5970) << "entering function";
523 for ( Task
* t
= itemAt(i
); t
; t
= itemAt(++i
) )
525 t
->setPixmapProgress();
526 t
->update(); // maybe there was a change in the times's format
529 // remove root decoration if there is no more child.
531 while ( itemAt( ++i
) && ( itemAt( i
)->depth() == 0 ) ){};
532 //setRootIsDecorated( itemAt( i ) && ( itemAt( i )->depth() != 0 ) );
533 // FIXME workaround? seems that the QItemDelegate for the procent column only
534 // works properly if rootIsDecorated == true.
535 setRootIsDecorated( true );
537 emit
updateButtons();
538 kDebug(5970) << "exiting TaskView::refresh()";
541 QString
TaskView::reFreshTimes()
542 /** Refresh the times of the tasks, e.g. when the history has been changed by the user */
544 kDebug(5970) << "Entering function";
546 // re-calculate the time for every task based on events in the history
547 KCal::Event::List eventList
= storage()->rawevents(); // get all events (!= tasks)
549 resetDisplayTimeForAllTasks();
551 while (itemAt(++n
)) // loop over all tasks
553 for( KCal::Event::List::iterator i
= eventList
.begin(); i
!= eventList
.end(); ++i
) // loop over all events
555 if ( (*i
)->relatedToUid() == itemAt(n
)->uid() ) // if event i belongs to task n
557 KDateTime kdatetimestart
= (*i
)->dtStart();
558 KDateTime kdatetimeend
= (*i
)->dtEnd();
559 KDateTime eventstart
= KDateTime::fromString(kdatetimestart
.toString().remove("Z"));
560 KDateTime eventend
= KDateTime::fromString(kdatetimeend
.toString().remove("Z"));
561 int duration
=eventstart
.secsTo( eventend
)/60;
562 itemAt(n
)->addTime( duration
);
563 emit
totalTimesChanged( 0, duration
);
564 kDebug(5970) << "duration is " << duration
;
566 if ( itemAt(n
)->sessionStartTiMe().isValid() )
568 // if there is a session
569 if ((itemAt(n
)->sessionStartTiMe().secsTo( eventstart
)>0) &&
570 (itemAt(n
)->sessionStartTiMe().secsTo( eventend
)>0))
571 // if the event is after the session start
573 int sessionTime
=eventstart
.secsTo( eventend
)/60;
574 itemAt(n
)->setSessionTime( itemAt(n
)->sessionTime()+sessionTime
);
578 // so there is no session at all
580 itemAt(n
)->addSessionTime( duration
);
581 emit
totalTimesChanged( duration
, 0 );
587 for (int i
=0; i
<count(); i
++) itemAt(i
)->recalculatetotaltime();
588 for (int i
=0; i
<count(); i
++) itemAt(i
)->recalculatetotalsessiontime();
591 kDebug(5970) << "Leaving TaskView::reFreshTimes()";
595 void TaskView::importPlanner( const QString
&fileName
)
597 kDebug( 5970 ) << "entering importPlanner";
598 PlannerParser
*handler
= new PlannerParser( this );
599 QString lFileName
= fileName
;
600 if ( lFileName
.isEmpty() )
601 lFileName
= KFileDialog::getOpenFileName( QString(), QString(), 0 );
602 QFile
xmlFile( lFileName
);
603 QXmlInputSource
source( &xmlFile
);
604 QXmlSimpleReader reader
;
605 reader
.setContentHandler( handler
);
606 reader
.parse( source
);
610 QString
TaskView::report( const ReportCriteria
& rc
)
612 return d
->mStorage
->report( this, rc
);
615 void TaskView::exportcsvFile()
617 kDebug(5970) << "TaskView::exportcsvFile()";
619 CSVExportDialog
dialog( ReportCriteria::CSVTotalsExport
, this );
620 if ( currentItem() && currentItem()->isRoot() )
621 dialog
.enableTasksToExportQuestion();
622 dialog
.urlExportTo
->KUrlRequester::setMode(KFile::File
);
625 QString err
= d
->mStorage
->report( this, dialog
.reportCriteria() );
626 if ( !err
.isEmpty() ) KMessageBox::error( this, i18n(err
.toAscii()) );
630 QString
TaskView::exportcsvHistory()
632 kDebug(5970) << "TaskView::exportcsvHistory()";
635 CSVExportDialog
dialog( ReportCriteria::CSVHistoryExport
, this );
636 if ( currentItem() && currentItem()->isRoot() )
637 dialog
.enableTasksToExportQuestion();
638 dialog
.urlExportTo
->KUrlRequester::setMode(KFile::File
);
641 err
= d
->mStorage
->report( this, dialog
.reportCriteria() );
646 long TaskView::count()
649 QTreeWidgetItemIterator
item( this );
658 QStringList
TaskView::tasks()
664 result
<< itemAt(i
)->name();
670 Task
* TaskView::task( const QString
& taskId
)
674 while ( itemAt(++i
) )
676 if ( itemAt( i
)->uid() == taskId
) result
=itemAt( i
);
680 void TaskView::dropEvent ( QDropEvent
* event
)
682 QTreeWidget::dropEvent(event
);
686 void TaskView::scheduleSave()
688 _manualSaveTimer
->start( 10 );
691 void TaskView::save()
693 kDebug(5970) <<"Entering TaskView::save()";
694 QString err
=d
->mStorage
->save(this);
698 QString errMsg
= d
->mStorage
->icalfile() + ":\n";
700 if (err
==QString("Could not save. Could not lock file."))
701 errMsg
+= i18n("Could not save. Disk full?");
703 errMsg
+= i18n("Could not save.");
705 KMessageBox::error(this, errMsg
);
709 void TaskView::startCurrentTimer()
711 startTimerFor( currentItem() );
714 void TaskView::startTimerFor( Task
* task
, const QDateTime
&startTime
)
716 kDebug(5970) << "Entering function";
717 if (task
!= 0 && d
->mActiveTasks
.indexOf(task
) == -1)
719 if (!task
->isComplete())
721 if ( KTimeTrackerSettings::uniTasking() ) stopAllTimers();
722 _idleTimeDetector
->startIdleDetection();
723 task
->setRunning(true, d
->mStorage
, startTime
);
724 d
->mActiveTasks
.append(task
);
725 emit
updateButtons();
726 if ( d
->mActiveTasks
.count() == 1 )
728 emit
tasksChanged( d
->mActiveTasks
);
733 void TaskView::clearActiveTasks()
735 d
->mActiveTasks
.clear();
738 void TaskView::stopAllTimers( const QDateTime
&when
)
740 kDebug(5970) << "Entering function";
741 KProgressDialog
dialog( this, 0, QString("Progress") );
742 dialog
.progressBar()->setMaximum( d
->mActiveTasks
.count() );
743 if ( d
->mActiveTasks
.count() > 1 ) dialog
.show();
745 foreach ( Task
*task
, d
->mActiveTasks
)
747 kapp
->processEvents();
748 task
->setRunning( false, d
->mStorage
, when
);
749 dialog
.progressBar()->setValue( dialog
.progressBar()->value() + 1 );
751 _idleTimeDetector
->stopIdleDetection();
752 FocusDetectorNotifier::instance()->detach( this );
753 d
->mActiveTasks
.clear();
754 emit
updateButtons();
755 emit
timersInactive();
756 emit
tasksChanged( d
->mActiveTasks
);
759 void TaskView::toggleFocusTracking()
761 d
->mFocusTrackingActive
= !d
->mFocusTrackingActive
;
763 if ( d
->mFocusTrackingActive
)
765 FocusDetectorNotifier::instance()->attach( this );
769 stopTimerFor( d
->mLastTaskWithFocus
);
770 FocusDetectorNotifier::instance()->detach( this );
773 emit
updateButtons();
776 void TaskView::startNewSession()
777 /* This procedure starts a new session. We speak of session times,
778 overalltimes (comprising all sessions) and total times (comprising all subtasks).
779 That is why there is also a total session time. */
781 kDebug(5970) <<"Entering TaskView::startNewSession";
782 QTreeWidgetItemIterator
item( this );
785 Task
* task
= (Task
*) *item
;
786 task
->startNewSession();
789 kDebug(5970) << "Leaving TaskView::startNewSession";
792 void TaskView::resetTimeForAllTasks()
793 /* This procedure resets all times (session and overall) for all tasks and subtasks. */
795 kDebug(5970) << "Entering function";
796 QTreeWidgetItemIterator
item( this );
799 Task
* task
= (Task
*) *item
;
803 storage()->deleteAllEvents();
804 kDebug(5970) << "Leaving function";
807 void TaskView::resetDisplayTimeForAllTasks()
808 /* This procedure resets all times (session and overall) for all tasks and subtasks. */
810 kDebug(5970) << "Entering function";
811 QTreeWidgetItemIterator
item( this );
814 Task
* task
= (Task
*) *item
;
818 kDebug(5970) << "Leaving function";
821 void TaskView::stopTimerFor(Task
* task
)
823 kDebug(5970) << "Entering function";
824 if ( task
!= 0 && d
->mActiveTasks
.indexOf(task
) != -1 )
826 d
->mActiveTasks
.removeAll(task
);
827 task
->setRunning(false, d
->mStorage
);
828 if ( d
->mActiveTasks
.count() == 0 )
830 _idleTimeDetector
->stopIdleDetection();
831 emit
timersInactive();
833 emit
updateButtons();
835 emit
tasksChanged( d
->mActiveTasks
);
838 void TaskView::stopCurrentTimer()
840 stopTimerFor( currentItem() );
841 if ( d
->mFocusTrackingActive
&& d
->mLastTaskWithFocus
== currentItem() )
843 toggleFocusTracking();
847 void TaskView::minuteUpdate()
849 addTimeToActiveTasks(1, false);
852 void TaskView::addTimeToActiveTasks(int minutes
, bool save_data
)
854 foreach ( Task
*task
, d
->mActiveTasks
)
855 task
->changeTime( minutes
, ( save_data
? d
->mStorage
: 0 ) );
858 void TaskView::newTask()
860 newTask(i18n("New Task"), 0);
863 void TaskView::newTask( const QString
&caption
, Task
*parent
)
865 EditTaskDialog
*dialog
= new EditTaskDialog( this, caption
, false );
866 long total
, totalDiff
, session
, sessionDiff
;
867 DesktopList desktopList
;
869 int result
= dialog
->exec();
870 if ( result
== QDialog::Accepted
)
872 QString taskName
= i18n( "Unnamed Task" );
873 if ( !dialog
->taskName().isEmpty()) taskName
= dialog
->taskName();
875 total
= totalDiff
= session
= sessionDiff
= 0;
876 dialog
->status( &desktopList
);
878 // If all available desktops are checked, disable auto tracking,
879 // since it makes no sense to track for every desktop.
880 if ( desktopList
.size() == _desktopTracker
->desktopCount() )
883 QString uid
= addTask( taskName
, total
, session
, desktopList
, parent
);
886 KMessageBox::error( 0, i18n(
887 "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/ " ) );
890 emit
updateButtons();
893 QString
TaskView::addTask
894 ( const QString
& taskname
, long total
, long session
,
895 const DesktopList
& desktops
, Task
* parent
)
897 kDebug(5970) << "Entering function; taskname =" << taskname
;
898 setSortingEnabled(false);
900 if ( parent
) task
= new Task( taskname
, total
, session
, desktops
, parent
);
901 else task
= new Task( taskname
, total
, session
, desktops
, this );
903 task
->setUid( d
->mStorage
->addTask( task
, parent
) );
904 QString taskuid
=task
->uid();
905 if ( ! taskuid
.isNull() )
907 _desktopTracker
->registerForDesktops( task
, desktops
);
908 setCurrentItem( task
);
909 task
->setSelected( true );
910 task
->setPixmapProgress();
917 setSortingEnabled(true);
921 void TaskView::newSubTask()
923 Task
* task
= currentItem();
926 newTask(i18n("New Sub Task"), task
);
927 task
->setExpanded(true);
931 void TaskView::editTask()
933 kDebug(5970) <<"Entering editTask";
934 Task
*task
= currentItem();
938 DesktopList desktopList
= task
->desktops();
939 DesktopList oldDeskTopList
= desktopList
;
940 EditTaskDialog
*dialog
= new EditTaskDialog( this, i18n("Edit Task"), &desktopList
);
941 dialog
->setTask( task
->name() );
942 int result
= dialog
->exec();
943 if (result
== QDialog::Accepted
)
945 QString taskName
= i18n("Unnamed Task");
946 if (!dialog
->taskName().isEmpty())
948 taskName
= dialog
->taskName();
950 // setName only does something if the new name is different
951 task
->setName(taskName
, d
->mStorage
);
953 // update session time as well if the time was changed
954 if (!dialog
->timeChange().isEmpty())
956 task
->changeTime(dialog
->timeChange().toInt(),d
->mStorage
);
959 // If all available desktops are checked, disable auto tracking,
960 // since it makes no sense to track for every desktop.
961 if (desktopList
.size() == _desktopTracker
->desktopCount())
963 // only do something for autotracking if the new setting is different
964 if ( oldDeskTopList
!= desktopList
)
966 task
->setDesktopList(desktopList
);
967 _desktopTracker
->registerForDesktops( task
, desktopList
);
969 emit
updateButtons();
973 void TaskView::setPerCentComplete(int completion
)
975 Task
* task
= currentItem();
978 KMessageBox::information(0,i18n("No task selected."));
982 if (completion
<0) completion
=0;
985 task
->setPercentComplete(completion
, d
->mStorage
);
986 task
->setPixmapProgress();
988 emit
updateButtons();
992 void TaskView::deleteTaskBatch( Task
* task
)
994 QString uid
=task
->uid();
995 task
->remove(d
->mStorage
);
996 _preferences
->deleteEntry( uid
); // forget if the item was expanded or collapsed
999 // Stop idle detection if no more counters are running
1000 if (d
->mActiveTasks
.count() == 0)
1002 _idleTimeDetector
->stopIdleDetection();
1003 emit
timersInactive();
1005 task
->delete_recursive();
1006 emit
tasksChanged( d
->mActiveTasks
);
1010 void TaskView::deleteTask( Task
* task
)
1011 /* Attention when popping up a window asking for confirmation.
1012 If you have "Track active applications" on, this window will create a new task and
1013 make this task running and selected. */
1015 kDebug(5970) << "Entering function";
1016 if (task
== 0) task
= currentItem();
1017 if (currentItem() == 0)
1019 KMessageBox::information(0,i18n("No task selected."));
1023 int response
= KMessageBox::Continue
;
1024 if (KTimeTrackerSettings::promptDelete())
1026 response
= KMessageBox::warningContinueCancel( 0,
1027 i18n( "Are you sure you want to delete the selected"
1028 " task and its entire history?\n"
1029 "NOTE: all subtasks and their history will also "
1031 i18n( "Deleting Task"), KStandardGuiItem::del());
1033 if (response
== KMessageBox::Continue
) deleteTaskBatch(task
);
1037 void TaskView::markTaskAsComplete()
1039 if (currentItem() == 0)
1041 KMessageBox::information(0,i18n("No task selected."));
1044 currentItem()->setPercentComplete(100, d
->mStorage
);
1045 currentItem()->setPixmapProgress();
1047 emit
updateButtons();
1050 void TaskView::extractTime(int minutes
)
1052 addTimeToActiveTasks(-minutes
,false); // subtract time in memory, but do not store it
1055 void TaskView::deletingTask(Task
* deletedTask
)
1057 kDebug(5970) << "Entering function";
1058 DesktopList desktopList
;
1060 _desktopTracker
->registerForDesktops( deletedTask
, desktopList
);
1061 d
->mActiveTasks
.removeAll( deletedTask
);
1063 emit
tasksChanged( d
->mActiveTasks
);
1066 void TaskView::markTaskAsIncomplete()
1068 setPerCentComplete(50); // if it has been reopened, assume half-done
1071 QString
TaskView::clipTotals( const ReportCriteria
&rc
)
1072 // This function stores the user's tasks into the clipboard.
1073 // rc tells how the user wants his report, e.g. all times or session times
1075 kDebug(5970) << "Entering function";
1078 KApplication::clipboard()->setText(t
.totalsAsText(this, rc
));
1082 QString
TaskView::setClipBoardText(const QString
& s
)
1084 QString err
; // maybe we find possible errors later
1085 KApplication::clipboard()->setText(s
);
1089 void TaskView::slotItemDoubleClicked( QTreeWidgetItem
*item
, int )
1093 Task
*task
= static_cast<Task
*>( item
);
1096 if ( task
->isRunning() )
1100 if ( !task
->isComplete() )
1103 startCurrentTimer();
1109 void TaskView::slotColumnToggled( int column
)
1114 KTimeTrackerSettings::setDisplaySessionTime( !isColumnHidden( 1 ) );
1117 KTimeTrackerSettings::setDisplayTime( !isColumnHidden( 2 ) );
1120 KTimeTrackerSettings::setDisplayTotalSessionTime( !isColumnHidden( 3 ) );
1123 KTimeTrackerSettings::setDisplayTotalTime( !isColumnHidden( 4 ) );
1126 KTimeTrackerSettings::setDisplayPriority( !isColumnHidden( 5 ) );
1129 KTimeTrackerSettings::setDisplayPercentComplete( !isColumnHidden( 6 ) );
1132 KTimeTrackerSettings::self()->writeConfig();
1135 void TaskView::slotCustomContextMenuRequested( const QPoint
&pos
)
1137 QPoint newPos
= viewport()->mapToGlobal( pos
);
1138 int column
= columnAt( pos
.x() );
1142 case 6: /* percentage */
1143 d
->mPopupPercentageMenu
->popup( newPos
);
1146 case 5: /* priority */
1147 d
->mPopupPriorityMenu
->popup( newPos
);
1151 emit
contextMenuRequested( newPos
);
1156 void TaskView::slotSetPercentage( QAction
*action
)
1158 if ( currentItem() )
1160 currentItem()->setPercentComplete( d
->mPercentage
[ action
], storage() );
1161 emit
updateButtons();
1165 void TaskView::slotSetPriority( QAction
*action
)
1167 if ( currentItem() )
1169 currentItem()->setPriority( d
->mPriority
[ action
] );
1173 bool TaskView::isFocusTrackingActive() const
1175 return d
->mFocusTrackingActive
;
1178 QList
< Task
* > TaskView::activeTasks() const
1180 return d
->mActiveTasks
;
1183 void TaskView::reconfigure()
1185 kDebug(5970) << "Entering function";
1187 setColumnHidden( 1, !KTimeTrackerSettings::displaySessionTime() );
1188 setColumnHidden( 2, !KTimeTrackerSettings::displayTime() );
1189 setColumnHidden( 3, !KTimeTrackerSettings::displayTotalSessionTime() );
1190 setColumnHidden( 4, !KTimeTrackerSettings::displayTotalTime() );
1191 setColumnHidden( 5, !KTimeTrackerSettings::displayPriority() );
1192 setColumnHidden( 6, !KTimeTrackerSettings::displayPercentComplete() );
1195 _idleTimeDetector
->setMaxIdle( KTimeTrackerSettings::period() );
1196 _idleTimeDetector
->toggleOverAllIdleDetection( KTimeTrackerSettings::enabled() );
1199 if ( KTimeTrackerSettings::autoSave() )
1201 _autoSaveTimer
->start(
1202 KTimeTrackerSettings::autoSavePeriod() * 1000 * secsPerMinute
1205 else if ( _autoSaveTimer
->isActive() )
1207 _autoSaveTimer
->stop();
1213 #include "taskview.moc"