Make it possible to use a distinct selection model in the foldertreewidget.
[kdepim.git] / ktimetracker / taskview.cpp
blob55bc9750eaa650bf998a68be2167c506d4aa6ab9
1 /*
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.
22 #include "taskview.h"
24 #include <cassert>
26 #include <QFile>
27 #include <QStyledItemDelegate>
28 #include <QMenu>
29 #include <QPainter>
30 #include <QString>
31 #include <QTimer>
32 #include <QMouseEvent>
33 #include <QList>
34 #include <QClipboard>
36 #include <KApplication> // kapp
37 #include <KDebug>
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"
51 #include "task.h"
52 #include "timekard.h"
53 #include "treeviewheadercontextmenu.h"
54 #include "focusdetector.h"
55 #include "focusdetectornotifier.h"
56 #include "storageadaptor.h"
58 #define T_LINESIZE 1023
60 class DesktopTracker;
62 //BEGIN TaskViewDelegate (custom painting of the progress column)
63 class TaskViewDelegate : public QStyledItemDelegate {
64 public:
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 );
88 if (newWidth > width)
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 );
102 else
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) + " %" );
129 else
131 QStyledItemDelegate::paint( painter, option, index );
135 //END
137 //BEGIN Private Data
138 //@cond PRIVATE
139 class TaskView::Private
141 public:
142 Private() :
143 mStorage( new timetrackerstorage() ),
144 mFocusTrackingActive( false ) {}
146 ~Private()
148 delete mStorage;
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;
160 //@endcond
161 //END
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 & ) ) );
179 QStringList labels;
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)) );
229 // Context Menu
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 )
242 QString label;
243 switch ( i )
245 case 0:
246 label = i18n( "unspecified" );
247 break;
248 case 1:
249 label = i18nc( "combox entry for highest priority", "1 (highest)" );
250 break;
251 case 5:
252 label = i18nc( "combox entry for medium priority", "5 (medium)" );
253 break;
254 case 9:
255 label = i18nc( "combox entry for lowest priority", "9 (lowest)" );
256 break;
257 default:
258 label = QString( "%1" ).arg( i );
259 break;
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 & ) ) );
270 reconfigure();
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 );
284 int i = 0;
285 for ( Task* task = itemAt( i ); task; task = itemAt( ++i ) )
287 if ( task->name() == newTaskName )
289 found = true;
290 startTimerFor( task );
291 d->mLastTaskWithFocus = task;
294 if ( !found )
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/ " ) );
302 i = 0;
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;
326 if ( delta >= 5 )
328 newValue += (10 - delta);
329 } else
331 newValue -= delta;
334 QTreeWidgetItem *item = itemFromIndex( index );
335 if (item && item->isSelected())
337 Task *task = static_cast<Task*>(item);
338 if (task)
340 task->setPercentComplete( newValue, d->mStorage );
341 emit updateButtons();
345 else
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 );
361 if (item)
363 Task *task = static_cast<Task*>(item);
364 if (task)
366 if (task->isComplete()) task->setPercentComplete( 0, d->mStorage );
367 else
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()
389 return d->mStorage;
392 TaskView::~TaskView()
394 FocusDetectorNotifier::instance()->detach( this );
395 delete d;
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);
416 if ( !( *item ) )
417 return 0;
418 else
419 return (Task*)*item;
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";
429 _isloading = true;
430 QString err = d->mStorage->load(this, fileName);
432 if (!err.isEmpty())
434 KMessageBox::error(this, err);
435 _isloading = false;
436 kDebug(5970) << "Leaving TaskView::load";
437 return;
440 // Register tasks with desktop tracker
441 int i = 0;
442 for ( Task* t = itemAt(i); t; t = itemAt(++i) )
443 _desktopTracker->registerForDesktops( t, t->desktops() );
444 // till here
445 // Start all tasks that have an event without endtime
446 i = 0;
447 for ( Task* t = itemAt(i); t; t = itemAt(++i) )
449 if ( !d->mStorage->allEventsHaveEndTiMe( t ) )
451 t->resumeRunning();
452 d->mActiveTasks.append(t);
453 emit updateButtons();
454 if ( d->mActiveTasks.count() == 1 )
455 emit timersActive();
456 emit tasksChanged( d->mActiveTasks );
459 // till here
461 if ( topLevelItemCount() > 0 )
463 restoreItemState();
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" ) );
467 _isloading = false;
468 refresh();
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 );
484 while( *item )
486 Task *t = (Task *) *item;
487 t->setExpanded( _preferences->readBoolEntry( t->uid() ) );
488 ++item;
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();
514 rc->dump();
515 d->mStorage->buildTaskView(rc,this);
516 kDebug(5970) << "exiting iCalFileModified";
519 void TaskView::refresh()
521 kDebug(5970) << "entering function";
522 int i = 0;
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.
530 i = 0;
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";
545 QString err;
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)
548 int n=-1;
549 resetDisplayTimeForAllTasks();
550 emit reSetTimes();
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 );
577 else
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();
590 refresh();
591 kDebug(5970) << "Leaving TaskView::reFreshTimes()";
592 return err;
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 );
607 refresh();
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);
623 if ( dialog.exec() )
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()";
633 QString err;
635 CSVExportDialog dialog( ReportCriteria::CSVHistoryExport, this );
636 if ( currentItem() && currentItem()->isRoot() )
637 dialog.enableTasksToExportQuestion();
638 dialog.urlExportTo->KUrlRequester::setMode(KFile::File);
639 if ( dialog.exec() )
641 err = d->mStorage->report( this, dialog.reportCriteria() );
643 return err;
646 long TaskView::count()
648 long n = 0;
649 QTreeWidgetItemIterator item( this );
650 while( *item )
652 ++item;
653 ++n;
655 return n;
658 QStringList TaskView::tasks()
660 QStringList result;
661 int i=0;
662 while ( itemAt(i) )
664 result << itemAt(i)->name();
665 ++i;
667 return result;
670 Task* TaskView::task( const QString& taskId )
672 Task* result=0;
673 int i=-1;
674 while ( itemAt(++i) )
675 if ( itemAt( i ) )
676 if ( itemAt( i )->uid() == taskId ) result=itemAt( i );
677 return result;
680 void TaskView::dropEvent ( QDropEvent * event )
682 QTreeWidget::dropEvent(event);
683 reFreshTimes();
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);
696 if (!err.isNull())
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?");
702 else
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 )
727 emit timersActive();
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 );
767 else
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 );
783 while ( *item )
785 Task * task = (Task *) *item;
786 task->startNewSession();
787 ++item;
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 );
797 while ( *item )
799 Task * task = (Task *) *item;
800 task->resetTimes();
801 ++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 );
812 while ( *item )
814 Task * task = (Task *) *item;
815 task->resetTimes();
816 ++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() )
881 desktopList.clear();
883 QString uid = addTask( taskName, total, session, desktopList, parent );
884 if ( uid.isNull() )
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);
899 Task *task;
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();
911 save();
913 else
915 delete task;
917 setSortingEnabled(true);
918 return taskuid;
921 void TaskView::newSubTask()
923 Task* task = currentItem();
924 if(!task)
925 return;
926 newTask(i18n("New Sub Task"), task);
927 task->setExpanded(true);
928 refresh();
931 void TaskView::editTask()
933 kDebug(5970) <<"Entering editTask";
934 Task *task = currentItem();
935 if (!task)
936 return;
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())
962 desktopList.clear();
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();
976 if (task == 0)
978 KMessageBox::information(0,i18n("No task selected."));
979 return;
982 if (completion<0) completion=0;
983 if (completion<100)
985 task->setPercentComplete(completion, d->mStorage);
986 task->setPixmapProgress();
987 save();
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
997 save();
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."));
1021 else
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 "
1030 "be deleted."),
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."));
1042 return;
1044 currentItem()->setPercentComplete(100, d->mStorage);
1045 currentItem()->setPixmapProgress();
1046 save();
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";
1076 QString err;
1077 TimeKard t;
1078 KApplication::clipboard()->setText(t.totalsAsText(this, rc));
1079 return err;
1082 QString TaskView::setClipBoardText(const QString& s)
1084 QString err; // maybe we find possible errors later
1085 KApplication::clipboard()->setText(s);
1086 return err;
1089 void TaskView::slotItemDoubleClicked( QTreeWidgetItem *item, int )
1091 if (item)
1093 Task *task = static_cast<Task*>( item );
1094 if ( task )
1096 if ( task->isRunning() )
1098 stopCurrentTimer();
1099 } else
1100 if ( !task->isComplete() )
1102 stopAllTimers();
1103 startCurrentTimer();
1109 void TaskView::slotColumnToggled( int column )
1111 switch ( column )
1113 case 1:
1114 KTimeTrackerSettings::setDisplaySessionTime( !isColumnHidden( 1 ) );
1115 break;
1116 case 2:
1117 KTimeTrackerSettings::setDisplayTime( !isColumnHidden( 2 ) );
1118 break;
1119 case 3:
1120 KTimeTrackerSettings::setDisplayTotalSessionTime( !isColumnHidden( 3 ) );
1121 break;
1122 case 4:
1123 KTimeTrackerSettings::setDisplayTotalTime( !isColumnHidden( 4 ) );
1124 break;
1125 case 5:
1126 KTimeTrackerSettings::setDisplayPriority( !isColumnHidden( 5 ) );
1127 break;
1128 case 6:
1129 KTimeTrackerSettings::setDisplayPercentComplete( !isColumnHidden( 6 ) );
1130 break;
1132 KTimeTrackerSettings::self()->writeConfig();
1135 void TaskView::slotCustomContextMenuRequested( const QPoint &pos )
1137 QPoint newPos = viewport()->mapToGlobal( pos );
1138 int column = columnAt( pos.x() );
1140 switch (column)
1142 case 6: /* percentage */
1143 d->mPopupPercentageMenu->popup( newPos );
1144 break;
1146 case 5: /* priority */
1147 d->mPopupPriorityMenu->popup( newPos );
1148 break;
1150 default:
1151 emit contextMenuRequested( newPos );
1152 break;
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";
1186 /* Adapt columns */
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() );
1194 /* idleness */
1195 _idleTimeDetector->setMaxIdle( KTimeTrackerSettings::period() );
1196 _idleTimeDetector->toggleOverAllIdleDetection( KTimeTrackerSettings::enabled() );
1198 /* auto save */
1199 if ( KTimeTrackerSettings::autoSave() )
1201 _autoSaveTimer->start(
1202 KTimeTrackerSettings::autoSavePeriod() * 1000 * secsPerMinute
1205 else if ( _autoSaveTimer->isActive() )
1207 _autoSaveTimer->stop();
1210 refresh();
1213 #include "taskview.moc"