SVN_SILENT made messages (.desktop file)
[kdepim.git] / ktimetracker / taskview.cpp
blob28f93f322415aeb3ef6728bafb00f67b61b95a62
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(QString)),
177 this, SLOT(newFocusWindowDetected(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(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)) );
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(QPoint)),
268 this, SLOT(slotCustomContextMenuRequested(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()
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;
518 } else {
519 kDebug(5970) << "entering function";
520 calendar->reload();
521 d->mStorage->buildTaskView( calendar->weakPointer().toStrongRef(), this );
522 kDebug(5970) << "exiting iCalFileModified";
526 void TaskView::refresh()
528 kDebug(5970) << "entering function";
529 int i = 0;
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.
537 i = 0;
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";
552 QString err;
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)
555 int n=-1;
556 resetDisplayTimeForAllTasks();
557 emit reSetTimes();
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 );
584 else
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();
597 refresh();
598 kDebug(5970) << "Leaving TaskView::reFreshTimes()";
599 return err;
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 );
614 refresh();
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);
630 if ( dialog.exec() )
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()";
640 QString err;
642 CSVExportDialog dialog( ReportCriteria::CSVHistoryExport, this );
643 if ( currentItem() && currentItem()->isRoot() )
644 dialog.enableTasksToExportQuestion();
645 dialog.urlExportTo->KUrlRequester::setMode(KFile::File);
646 if ( dialog.exec() )
648 err = d->mStorage->report( this, dialog.reportCriteria() );
650 return err;
653 long TaskView::count()
655 long n = 0;
656 QTreeWidgetItemIterator item( this );
657 while( *item )
659 ++item;
660 ++n;
662 return n;
665 QStringList TaskView::tasks()
667 QStringList result;
668 int i=0;
669 while ( itemAt(i) )
671 result << itemAt(i)->name();
672 ++i;
674 return result;
677 Task* TaskView::task( const QString& taskId )
679 Task* result=0;
680 int i=-1;
681 while ( itemAt(++i) )
682 if ( itemAt( i ) )
683 if ( itemAt( i )->uid() == taskId ) result=itemAt( i );
684 return result;
687 void TaskView::dropEvent ( QDropEvent * event )
689 QTreeWidget::dropEvent(event);
690 reFreshTimes();
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);
703 if (!err.isNull())
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?");
709 else
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 )
734 emit timersActive();
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 );
774 else
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 );
790 while ( *item )
792 Task * task = (Task *) *item;
793 task->startNewSession();
794 ++item;
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 );
804 while ( *item )
806 Task * task = (Task *) *item;
807 task->resetTimes();
808 ++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 );
819 while ( *item )
821 Task * task = (Task *) *item;
822 task->resetTimes();
823 ++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() )
889 desktopList.clear();
891 QString uid = addTask( taskName, taskDescription, total, session, desktopList, parent );
892 if ( uid.isNull() )
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);
907 Task *task;
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();
919 save();
921 else
923 delete task;
925 setSortingEnabled(true);
926 return taskuid;
929 void TaskView::newSubTask()
931 Task* task = currentItem();
932 if(!task)
933 return;
934 newTask(i18n("New Sub Task"), task);
935 task->setExpanded(true);
936 refresh();
939 void TaskView::editTask()
941 kDebug(5970) <<"Entering editTask";
942 Task *task = currentItem();
943 if (!task) return;
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())
970 desktopList.clear();
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();
984 if (task == 0)
986 KMessageBox::information(0,i18n("No task selected."));
987 return;
990 if (completion<0) completion=0;
991 if (completion<100)
993 task->setPercentComplete(completion, d->mStorage);
994 task->setPixmapProgress();
995 save();
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
1005 save();
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."));
1029 else
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 "
1038 "be deleted."),
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."));
1050 return;
1052 currentItem()->setPercentComplete(100, d->mStorage);
1053 currentItem()->setPixmapProgress();
1054 save();
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";
1084 QString err;
1085 TimeKard t;
1086 KApplication::clipboard()->setText(t.totalsAsText(this, rc));
1087 return err;
1090 QString TaskView::setClipBoardText(const QString& s)
1092 QString err; // maybe we find possible errors later
1093 KApplication::clipboard()->setText(s);
1094 return err;
1097 void TaskView::slotItemDoubleClicked( QTreeWidgetItem *item, int )
1099 if (item)
1101 Task *task = static_cast<Task*>( item );
1102 if ( task )
1104 if ( task->isRunning() )
1106 stopCurrentTimer();
1108 else if ( !task->isComplete() )
1110 stopAllTimers();
1111 startCurrentTimer();
1117 void TaskView::slotColumnToggled( int column )
1119 switch ( column )
1121 case 1:
1122 KTimeTrackerSettings::setDisplaySessionTime( !isColumnHidden( 1 ) );
1123 break;
1124 case 2:
1125 KTimeTrackerSettings::setDisplayTime( !isColumnHidden( 2 ) );
1126 break;
1127 case 3:
1128 KTimeTrackerSettings::setDisplayTotalSessionTime( !isColumnHidden( 3 ) );
1129 break;
1130 case 4:
1131 KTimeTrackerSettings::setDisplayTotalTime( !isColumnHidden( 4 ) );
1132 break;
1133 case 5:
1134 KTimeTrackerSettings::setDisplayPriority( !isColumnHidden( 5 ) );
1135 break;
1136 case 6:
1137 KTimeTrackerSettings::setDisplayPercentComplete( !isColumnHidden( 6 ) );
1138 break;
1140 KTimeTrackerSettings::self()->writeConfig();
1143 void TaskView::slotCustomContextMenuRequested( const QPoint &pos )
1145 QPoint newPos = viewport()->mapToGlobal( pos );
1146 int column = columnAt( pos.x() );
1148 switch (column)
1150 case 6: /* percentage */
1151 d->mPopupPercentageMenu->popup( newPos );
1152 break;
1154 case 5: /* priority */
1155 d->mPopupPriorityMenu->popup( newPos );
1156 break;
1158 default:
1159 emit contextMenuRequested( newPos );
1160 break;
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";
1194 /* Adapt columns */
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() );
1202 /* idleness */
1203 _idleTimeDetector->setMaxIdle( KTimeTrackerSettings::period() );
1204 _idleTimeDetector->toggleOverAllIdleDetection( KTimeTrackerSettings::enabled() );
1206 /* auto save */
1207 if ( KTimeTrackerSettings::autoSave() )
1209 _autoSaveTimer->start(
1210 KTimeTrackerSettings::autoSavePeriod() * 1000 * secsPerMinute
1213 else if ( _autoSaveTimer->isActive() )
1215 _autoSaveTimer->stop();
1218 refresh();