• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdepim
  • Sitemap
  • Contact Us
 

ktimetracker

taskview.cpp

Go to the documentation of this file.
00001 /*
00002  *     Copyright (C) 2003 by Scott Monachello <smonach@cox.net>
00003  *
00004  *   This program is free software; you can redistribute it and/or modify
00005  *   it under the terms of the GNU General Public License as published by
00006  *   the Free Software Foundation; either version 2 of the License, or
00007  *   (at your option) any later version.
00008  *
00009  *   This program is distributed in the hope that it will be useful,
00010  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *   GNU General Public License for more details.
00013  *
00014  *   You should have received a copy of the GNU General Public License along
00015  *   with this program; if not, write to the
00016  *      Free Software Foundation, Inc.
00017  *      51 Franklin Street, Fifth Floor
00018  *      Boston, MA  02110-1301  USA.
00019  *
00020  */
00021 
00022 #include "taskview.h"
00023 
00024 #include <cassert>
00025 
00026 #include <QFile>
00027 #include <QStyledItemDelegate>
00028 #include <QMenu>
00029 #include <QPainter>
00030 #include <QString>
00031 #include <QTimer>
00032 #include <QMouseEvent>
00033 #include <QList>
00034 #include <QClipboard>
00035 
00036 #include <KApplication>       // kapp
00037 #include <KDebug>
00038 #include <KFileDialog>
00039 #include <KLocale>            // i18n
00040 #include <KMessageBox>
00041 #include <KUrlRequester>
00042 
00043 #include "csvexportdialog.h"
00044 #include "desktoptracker.h"
00045 #include "edittaskdialog.h"
00046 #include "idletimedetector.h"
00047 #include "plannerparser.h"
00048 #include "preferences.h"
00049 #include "ktimetracker.h"
00050 #include "task.h"
00051 #include "timekard.h"
00052 #include "treeviewheadercontextmenu.h"
00053 #include "focusdetector.h"
00054 #include "focusdetectornotifier.h"
00055 #include "storageadaptor.h"
00056 
00057 #define T_LINESIZE 1023
00058 
00059 class DesktopTracker;
00060 
00061 //BEGIN TaskViewDelegate (custom painting of the progress column)
00062 class TaskViewDelegate : public QStyledItemDelegate {
00063 public:
00064   TaskViewDelegate( QObject *parent = 0 ) : QStyledItemDelegate( parent ) {}
00065 
00066   void paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const {
00067     if (index.column () == 6) {
00068       QApplication::style()->drawControl( QStyle::CE_ItemViewItem, &option, painter );
00069       int rX = option.rect.x() + 2;
00070       int rY = option.rect.y() + 2;
00071       int rWidth = option.rect.width() - 4;
00072       int rHeight = option.rect.height() - 4;
00073 
00074       int value = index.model()->data( index ).toInt();
00075       int newWidth = (int)(rWidth * (value / 100.));
00076 
00077       if(QApplication::isLeftToRight())
00078       {
00079          int mid = rY + rHeight / 2;
00080          int width = rWidth / 2;
00081          QLinearGradient gradient1( rX, mid, rX + width, mid);
00082          gradient1.setColorAt( 0, Qt::red );
00083          gradient1.setColorAt( 1, Qt::yellow );
00084          painter->fillRect( rX, rY, (newWidth < width) ? newWidth : width, rHeight, gradient1 );
00085 
00086          if (newWidth > width) {
00087             QLinearGradient gradient2( rX + width, mid, rX + 2 * width, mid);
00088             gradient2.setColorAt( 0, Qt::yellow );
00089             gradient2.setColorAt( 1, Qt::green );
00090             painter->fillRect( rX + width, rY, newWidth - width, rHeight, gradient2 );
00091          }
00092 
00093          painter->setPen( option.state & QStyle::State_Selected ? option.palette.highlight().color() : option.palette.background().color() );
00094          for (int x = rHeight; x < newWidth; x += rHeight) {
00095             painter->drawLine( rX + x, rY, rX + x, rY + rHeight - 1 );
00096          }
00097       }
00098       else
00099       {
00100          int mid = option.rect.height() - rHeight / 2;
00101          int width = rWidth / 2;
00102          QLinearGradient gradient1( rX, mid, rX + width, mid);
00103          gradient1.setColorAt( 0, Qt::red );
00104          gradient1.setColorAt( 1, Qt::yellow );
00105          painter->fillRect( option.rect.height(), rY, (newWidth < width) ? newWidth : width, rHeight, gradient1 );
00106 
00107          if (newWidth > width) {
00108             QLinearGradient gradient2( rX + width, mid, rX + 2 * width, mid);
00109             gradient2.setColorAt( 0, Qt::yellow );
00110             gradient2.setColorAt( 1, Qt::green );
00111             painter->fillRect( rX + width, rY, newWidth - width, rHeight, gradient2 );
00112          }
00113 
00114          painter->setPen( option.state & QStyle::State_Selected ? option.palette.highlight().color() : option.palette.background().color() );
00115          for (int x = rWidth- rHeight; x > newWidth; x -= rHeight) {
00116             painter->drawLine( rWidth - x, rY, rWidth - x, rY + rHeight - 1 );
00117          }
00118 
00119       }
00120       painter->setPen( Qt::black );
00121       painter->drawText( option.rect, Qt::AlignCenter | Qt::AlignVCenter, QString::number(value) + " %" );
00122     } else {
00123       QStyledItemDelegate::paint( painter, option, index );
00124     }
00125   }
00126 };
00127 //END
00128 
00129 //BEGIN Private Data
00130 //@cond PRIVATE
00131 class TaskView::Private {
00132   public:
00133     Private() : 
00134       mStorage( new KarmStorage() ), 
00135       mFocusTrackingActive( false ) {}
00136 
00137     ~Private() {
00138       delete mStorage; 
00139     }
00140     KarmStorage *mStorage;
00141     bool mFocusTrackingActive;
00142     Task* mLastTaskWithFocus;
00143     QList<Task*> mActiveTasks;
00144 
00145     QMenu *mPopupPercentageMenu;
00146     QMap<QAction*, int> mPercentage;
00147     QMenu *mPopupPriorityMenu;
00148     QMap<QAction*, int> mPriority;
00149 };
00150 //@endcond
00151 //END
00152 
00153 TaskView::TaskView( QWidget *parent ) : QTreeWidget(parent), d( new Private() )
00154 {
00155   _preferences = Preferences::instance();
00156     new StorageAdaptor( this );
00157     QDBusConnection::sessionBus().registerObject( "/ktimetrackerstorage", this );
00158 
00159   connect( this, SIGNAL(itemExpanded(QTreeWidgetItem*)),
00160            this, SLOT(itemStateChanged(QTreeWidgetItem*)) );
00161   connect( this, SIGNAL(itemCollapsed(QTreeWidgetItem*)),
00162            this, SLOT(itemStateChanged(QTreeWidgetItem*)) );
00163   connect( this, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)),
00164            this, SLOT(slotItemDoubleClicked(QTreeWidgetItem*, int)) );
00165   connect( FocusDetectorNotifier::instance()->focusDetector(), 
00166            SIGNAL( newFocus( const QString & ) ), 
00167            this, SLOT( newFocusWindowDetected ( const QString & ) ) ); 
00168 
00169   QStringList labels;
00170   setWindowFlags( windowFlags() | Qt::WindowContextHelpButtonHint );
00171   labels << i18n( "Task Name" ) << i18n( "Session Time" ) << i18n( "Time" ) 
00172          << i18n( "Total Session Time" ) << i18n( "Total Time" ) 
00173          << i18n( "Priority" ) << i18n( "Percent Complete" );
00174   setHeaderLabels( labels );
00175   headerItem()->setWhatsThis(0,"The task name is how you call the task, it can be chosen freely.");
00176   headerItem()->setWhatsThis(1,"The session time is the time since you last chose \"start new session.\"");
00177   headerItem()->setWhatsThis(3,"The total session time is the session time of this task and all its subtasks.");
00178   headerItem()->setWhatsThis(4,"The total time is the time of this task and all its subtasks.");
00179   setAllColumnsShowFocus( true );
00180   setSortingEnabled( true );
00181   setAlternatingRowColors( true );
00182   setDragDropMode( QAbstractItemView::InternalMove );
00183   setItemDelegateForColumn( 6, new TaskViewDelegate(this) );
00184 
00185   // set up the minuteTimer
00186   _minuteTimer = new QTimer(this);
00187   connect( _minuteTimer, SIGNAL( timeout() ), this, SLOT( minuteUpdate() ));
00188   _minuteTimer->start(1000 * secsPerMinute);
00189 
00190   // Set up the idle detection.
00191   _idleTimeDetector = new IdleTimeDetector( KTimeTrackerSettings::period() );
00192   connect( _idleTimeDetector, SIGNAL( extractTime(int) ),
00193            this, SLOT( extractTime(int) ));
00194   connect( _idleTimeDetector, SIGNAL( stopAllTimers(QDateTime) ),
00195            this, SLOT( stopAllTimers(QDateTime) ));
00196   if (!_idleTimeDetector->isIdleDetectionPossible())
00197     KTimeTrackerSettings::setEnabled( false );
00198 
00199   // Setup auto save timer
00200   _autoSaveTimer = new QTimer(this);
00201   connect( _autoSaveTimer, SIGNAL( timeout() ), this, SLOT( save() ));
00202 
00203   // Setup manual save timer (to save changes a little while after they happen)
00204   _manualSaveTimer = new QTimer(this);
00205   _manualSaveTimer->setSingleShot( true );
00206   connect( _manualSaveTimer, SIGNAL( timeout() ), this, SLOT( save() ));
00207 
00208   // Connect desktop tracker events to task starting/stopping
00209   _desktopTracker = new DesktopTracker();
00210   connect( _desktopTracker, SIGNAL( reachedActiveDesktop( Task* ) ),
00211            this, SLOT( startTimerFor(Task*) ));
00212   connect( _desktopTracker, SIGNAL( leftActiveDesktop( Task* ) ),
00213            this, SLOT( stopTimerFor(Task*) ));
00214 
00215   // Header context menu
00216   TreeViewHeaderContextMenu *headerContextMenu = new TreeViewHeaderContextMenu( this, this, TreeViewHeaderContextMenu::AlwaysCheckBox, QVector<int>() << 0 );
00217   connect( headerContextMenu, SIGNAL(columnToggled(int)), this, SLOT(slotColumnToggled(int)) );
00218 
00219   // Context Menu
00220   d->mPopupPercentageMenu = new QMenu( this );
00221   for ( int i = 0; i <= 100; i += 10 )
00222   {
00223     QString label = i18n( "%1 %" , i );
00224     d->mPercentage[ d->mPopupPercentageMenu->addAction( label ) ] = i;
00225   }
00226   connect( d->mPopupPercentageMenu, SIGNAL( triggered( QAction * ) ),
00227            this, SLOT( slotSetPercentage( QAction * ) ) );
00228 
00229   d->mPopupPriorityMenu = new QMenu( this );
00230   for ( int i = 0; i <= 9; ++i ) {
00231     QString label;
00232     switch ( i ) {
00233       case 0:
00234         label = i18n( "unspecified" );
00235         break;
00236       case 1:
00237         label = i18nc( "combox entry for highest priority", "1 (highest)" );
00238         break;
00239       case 5:
00240         label = i18nc( "combox entry for medium priority", "5 (medium)" );
00241         break;
00242       case 9:
00243         label = i18nc( "combox entry for lowest priority", "9 (lowest)" );
00244         break;
00245       default:
00246         label = QString( "%1" ).arg( i );
00247         break;
00248     }
00249     d->mPriority[ d->mPopupPriorityMenu->addAction( label ) ] = i;
00250   }
00251   connect( d->mPopupPriorityMenu, SIGNAL( triggered( QAction * ) ),
00252            this, SLOT( slotSetPriority( QAction * ) ) );
00253 
00254   setContextMenuPolicy( Qt::CustomContextMenu );
00255   connect( this, SIGNAL( customContextMenuRequested( const QPoint & ) ),
00256            this, SLOT( slotCustomContextMenuRequested( const QPoint & ) ) );
00257 
00258   reconfigure();
00259   sortByColumn( 0, Qt::AscendingOrder );
00260   for (int i=0; i<=columnCount(); i++) resizeColumnToContents(i);
00261 }
00262 
00263 void TaskView::newFocusWindowDetected( const QString &taskName )
00264 {
00265   QString newTaskName = taskName;
00266   newTaskName.replace( "\n", "" );
00267 
00268   if ( d->mFocusTrackingActive ) {
00269     bool found = false;  // has taskName been found in our tasks
00270     stopTimerFor( d->mLastTaskWithFocus );
00271     int i = 0;
00272     for ( Task* task = itemAt( i ); task; task = itemAt( ++i ) ) {
00273       if ( task->name() == newTaskName ) {
00274          found = true;
00275          startTimerFor( task );
00276          d->mLastTaskWithFocus = task;
00277       }
00278     }
00279     if ( !found ) {
00280       QString taskuid = addTask( newTaskName );
00281       if ( taskuid.isNull() ) {
00282         KMessageBox::error( 0, i18n(
00283         "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/ " ) );
00284       }
00285       i = 0;
00286       for ( Task* task = itemAt( i ); task; task = itemAt( ++i ) ) {
00287         if (task->name() == newTaskName) {
00288           startTimerFor( task );
00289           d->mLastTaskWithFocus = task;
00290         }
00291       }
00292     }
00293     emit updateButtons();
00294   } // focustrackingactive
00295 }
00296 
00297 void TaskView::mouseMoveEvent( QMouseEvent *event ) 
00298 {
00299   QModelIndex index = indexAt( event->pos() );
00300 
00301   if (index.isValid() && index.column() == 6) 
00302   {
00303     int newValue = (int)((event->pos().x() - visualRect(index).x()) / (double)(visualRect(index).width()) * 100);
00304 
00305     if ( event->modifiers() & Qt::ShiftModifier ) {
00306       int delta = newValue % 10;
00307       if ( delta >= 5 ) {
00308         newValue += (10 - delta);
00309       } else {
00310         newValue -= delta;
00311       }
00312     }
00313 
00314     QTreeWidgetItem *item = itemFromIndex( index );
00315     if (item && item->isSelected()) 
00316     {
00317       Task *task = static_cast<Task*>(item);
00318       if (task) 
00319       {
00320         task->setPercentComplete( newValue, d->mStorage );
00321 
00322         emit updateButtons();
00323       }
00324     }
00325   } 
00326   else 
00327   {
00328     QTreeWidget::mouseMoveEvent( event );
00329   }
00330 }
00331 
00332 void TaskView::mousePressEvent( QMouseEvent *event )
00333 {
00334   QModelIndex index = indexAt( event->pos() );
00335 
00336   // if the user toggles a task as complete/incomplete
00337   if (   index.isValid() 
00338       && index.column() == 0
00339       && visualRect( index ).x() <= event->pos().x()
00340       && event->pos().x() < visualRect( index ).x() + 19)
00341   {
00342     QTreeWidgetItem *item = itemFromIndex( index );
00343     if (item)
00344     {
00345       Task *task = static_cast<Task*>(item);
00346       if (task) 
00347       {
00348         if (task->isComplete()) task->setPercentComplete( 0, d->mStorage );
00349         else 
00350         {
00351           task->setPercentComplete( 100, d->mStorage );
00352         }
00353         
00354         emit updateButtons();
00355       }
00356     }
00357   }
00358   else
00359   {
00360     if ( KTimeTrackerSettings::configPDA() )
00361     // if you have a touchscreen, you cannot right-click. So, display context menu on any click.
00362     {
00363       QPoint newPos = viewport()->mapToGlobal( event->pos() );
00364       emit contextMenuRequested( newPos );
00365     }
00366     QTreeWidget::mousePressEvent( event );
00367   }
00368 }
00369 
00370 KarmStorage* TaskView::storage()
00371 {
00372   return d->mStorage;
00373 }
00374 
00375 TaskView::~TaskView()
00376 {
00377   FocusDetectorNotifier::instance()->detach( this );
00378   delete d;
00379   KTimeTrackerSettings::self()->writeConfig();
00380 }
00381 
00382 Task* TaskView::currentItem() const
00383 {
00384   kDebug(5970) << "Entering function";
00385   return static_cast< Task* >( QTreeWidget::currentItem() );
00386 }
00387 
00388 Task* TaskView::itemAt(int i)
00389 /* This procedure delivers the item at the position i in the KTreeWidget.
00390 Every item is a task. The items are counted linearily. The uppermost item
00391 has the number i=0. */
00392 {
00393   if ( topLevelItemCount() == 0 ) return 0;
00394   
00395   QTreeWidgetItemIterator item( this );
00396   while( *item && i-- ) ++item;
00397   
00398   kDebug( 5970 ) << "Leaving TaskView::itemAt" << "returning " << (*item==0);
00399   if ( !( *item ) )
00400     return 0;
00401   else
00402     return (Task*)*item;
00403 }
00404 
00405 void TaskView::load( const QString &fileName )
00406 {
00407   assert( !( fileName.isEmpty() ) );
00408 
00409   // if the program is used as an embedded plugin for konqueror, there may be a need
00410   // to load from a file without touching the preferences.
00411   kDebug(5970) << "Entering function";
00412   _isloading = true;
00413   QString err = d->mStorage->load(this, fileName);
00414 
00415   if (!err.isEmpty())
00416   {
00417     KMessageBox::error(this, err);
00418     _isloading = false;
00419     kDebug(5970) << "Leaving TaskView::load";
00420     return;
00421   }
00422 
00423   // Register tasks with desktop tracker
00424   int i = 0;
00425   for ( Task* t = itemAt(i); t; t = itemAt(++i) )
00426     _desktopTracker->registerForDesktops( t, t->desktops() );
00427   // till here
00428   // Start all tasks that have an event without endtime
00429   i = 0;
00430   for ( Task* t = itemAt(i); t; t = itemAt(++i) )
00431   {
00432     if ( !d->mStorage->allEventsHaveEndTiMe( t ) )
00433     {
00434       t->resumeRunning();
00435       d->mActiveTasks.append(t);
00436       emit updateButtons();
00437       if ( d->mActiveTasks.count() == 1 )
00438         emit timersActive();  
00439       emit tasksChanged( d->mActiveTasks );
00440     }
00441   }
00442   // till here
00443 
00444   if ( topLevelItemCount() > 0 )
00445   {
00446     restoreItemState();
00447     setCurrentItem(topLevelItem( 0 ));
00448     if ( !_desktopTracker->startTracking().isEmpty() )
00449       KMessageBox::error( 0, i18n( "Your virtual desktop number is too high, desktop tracking will not work" ) );
00450     _isloading = false;
00451     refresh();
00452   }
00453   for (int i=0; i<=columnCount(); i++) resizeColumnToContents(i);
00454   kDebug(5970) << "Leaving function";
00455 }
00456 
00457 void TaskView::restoreItemState()
00458 /* Restores the item state of every item. An item is a task in the list.
00459 Its state is whether it is expanded or not. If a task shall be expanded
00460 is stored in the _preferences object. */
00461 {
00462   kDebug(5970) << "Entering function";
00463   
00464   if ( topLevelItemCount() > 0 ) {
00465     QTreeWidgetItemIterator item( this );
00466     while( *item ) 
00467     {
00468       Task *t = (Task *) *item;
00469       t->setExpanded( _preferences->readBoolEntry( t->uid() ) );
00470       ++item;
00471     }
00472   }
00473   kDebug(5970) << "Leaving function";
00474 }
00475 
00476 void TaskView::itemStateChanged( QTreeWidgetItem *item )
00477 {
00478   kDebug() << "Entering function";
00479   if ( !item || _isloading ) return;
00480   Task *t = (Task *)item;
00481   kDebug(5970) <<"TaskView::itemStateChanged()" <<" uid=" << t->uid() <<" state=" << t->isExpanded();
00482   if( _preferences ) _preferences->writeEntry( t->uid(), t->isExpanded() );
00483 }
00484 
00485 void TaskView::closeStorage() { d->mStorage->closeStorage(); }
00486 
00487 bool TaskView::allEventsHaveEndTiMe()
00488 {
00489   return d->mStorage->allEventsHaveEndTiMe();
00490 }
00491 
00492 void TaskView::iCalFileModified(ResourceCalendar *rc)
00493 {
00494   kDebug(5970) << "entering function";
00495   kDebug(5970) << rc->infoText();
00496   rc->dump();
00497   d->mStorage->buildTaskView(rc,this);
00498   kDebug(5970) << "exiting iCalFileModified";
00499 }
00500 
00501 void TaskView::refresh()
00502 {
00503   kDebug(5970) << "entering function";
00504   int i = 0;
00505   for ( Task* t = itemAt(i); t; t = itemAt(++i) )
00506   {
00507     t->setPixmapProgress();
00508     t->update();  // maybe there was a change in the times's format
00509   }
00510 
00511   // remove root decoration if there is no more child.
00512   i = 0;
00513   while ( itemAt( ++i ) && ( itemAt( i )->depth() == 0 ) ){};
00514   //setRootIsDecorated( itemAt( i ) && ( itemAt( i )->depth() != 0 ) );
00515   // FIXME workaround? seems that the QItemDelegate for the procent column only
00516   // works properly if rootIsDecorated == true.
00517   setRootIsDecorated( true );
00518 
00519   emit updateButtons();
00520   kDebug(5970) << "exiting TaskView::refresh()";
00521 }
00522 
00523 QString TaskView::reFreshTimes()
00525 {
00526   kDebug(5970) << "Entering function";
00527   QString err;
00528 
00529   // re-calculate the time for every task based on events in the history
00530 
00531   KCal::Event::List eventList = storage()->rawevents(); // get all events (!= tasks)
00532   int n=-1;
00533   resetTimeForAllTasks();
00534   emit reSetTimes();
00535   while (itemAt(++n)) // loop over all tasks
00536   {
00537     for( KCal::Event::List::iterator i = eventList.begin(); i != eventList.end(); ++i ) // loop over all events
00538     {
00539       if ( (*i)->relatedToUid() == itemAt(n)->uid() ) // if event i belongs to task n
00540       {
00541         KDateTime kdatetimestart = (*i)->dtStart();
00542         KDateTime kdatetimeend = (*i)->dtEnd();
00543         KDateTime eventstart = KDateTime::fromString(kdatetimestart.toString().replace("Z",""));
00544         KDateTime eventend = KDateTime::fromString(kdatetimeend.toString().replace("Z",""));
00545         int duration=eventstart.secsTo( eventend )/60;
00546         itemAt(n)->addTime( duration );
00547         emit totalTimesChanged( 0, duration );
00548         kDebug(5970) << "duration is " << duration;
00549 
00550         if ( itemAt(n)->sessionStartTiMe().isValid() )
00551         {
00552         // if there is a session
00553           if ((itemAt(n)->sessionStartTiMe().secsTo( eventstart )>0) &&        
00554               (itemAt(n)->sessionStartTiMe().secsTo( eventend )>0))
00555           // if the event is after the session start
00556           {
00557             int sessionTime=eventstart.secsTo( eventend )/60;
00558             itemAt(n)->setSessionTime( itemAt(n)->sessionTime()+sessionTime );
00559           }
00560         }
00561     else 
00562     // so there is no session at all
00563         {
00564           itemAt(n)->addSessionTime( duration );
00565           emit totalTimesChanged( duration, 0 );
00566         };
00567       }
00568     }
00569   }
00570   refresh();
00571   kDebug(5970) << "Leaving TaskView::reFreshTimes()";
00572   return err;
00573 }
00574 
00575 void TaskView::importPlanner( const QString &fileName )
00576 {
00577   kDebug( 5970 ) << "entering importPlanner";
00578   PlannerParser *handler = new PlannerParser( this );
00579   QString lFileName = fileName;
00580   if ( lFileName.isEmpty() ) 
00581     lFileName = KFileDialog::getOpenFileName( QString(), QString(), 0 );
00582   QFile xmlFile( lFileName );
00583   QXmlInputSource source( &xmlFile );
00584   QXmlSimpleReader reader;
00585   reader.setContentHandler( handler );
00586   reader.parse( source );
00587   refresh();
00588 }
00589 
00590 QString TaskView::report( const ReportCriteria& rc )
00591 {
00592   return d->mStorage->report( this, rc );
00593 }
00594 
00595 void TaskView::exportcsvFile()
00596 {
00597   kDebug(5970) << "TaskView::exportcsvFile()";
00598 
00599   CSVExportDialog dialog( ReportCriteria::CSVTotalsExport, this );
00600   if ( currentItem() && currentItem()->isRoot() )
00601     dialog.enableTasksToExportQuestion();
00602   dialog.urlExportTo->KUrlRequester::setMode(KFile::File);
00603   if ( dialog.exec() ) {
00604     QString err = d->mStorage->report( this, dialog.reportCriteria() );
00605     if ( !err.isEmpty() ) KMessageBox::error( this, i18n(err.toAscii()) );
00606   }
00607 }
00608 
00609 QString TaskView::exportcsvHistory()
00610 {
00611   kDebug(5970) << "TaskView::exportcsvHistory()";
00612   QString err;
00613   
00614   CSVExportDialog dialog( ReportCriteria::CSVHistoryExport, this );
00615   if ( currentItem() && currentItem()->isRoot() )
00616     dialog.enableTasksToExportQuestion();
00617   dialog.urlExportTo->KUrlRequester::setMode(KFile::File);
00618   if ( dialog.exec() ) {
00619     err = d->mStorage->report( this, dialog.reportCriteria() );
00620   }
00621   return err;
00622 }
00623 
00624 long TaskView::count()
00625 {
00626   long n = 0;
00627   for (Task* t = itemAt(n); t; t=itemAt(++n)){};
00628   return n;
00629 }
00630 
00631 QStringList TaskView::tasks()
00632 {
00633   QStringList result;
00634   int i=0;
00635   while ( itemAt(i) ) 
00636   {
00637     result << itemAt(i)->name();
00638     ++i;
00639   }
00640   return result;
00641 }
00642 
00643 Task* TaskView::task( const QString& taskId )
00644 {
00645   Task* result=0;
00646   int i=-1;
00647   while ( itemAt(++i) ) 
00648     if ( itemAt( i ) ) 
00649       if ( itemAt( i )->uid() == taskId ) result=itemAt( i );
00650   return result;
00651 }
00652 
00653 void TaskView::scheduleSave()
00654 {
00655     _manualSaveTimer->start( 10 );
00656 }
00657 
00658 QString TaskView::save()
00659 {
00660   kDebug(5970) <<"Entering TaskView::save()";
00661   QString err=d->mStorage->save(this);
00662 
00663   if (err.isNull()) emit setStatusBarText( i18n("Successfully saved file ").append( d->mStorage->icalfile() ));
00664   else
00665     if (err==QString("Could not save. Could not lock file.")) emit setStatusBarText( i18n("Could not save. Disk full ?") );
00666     else emit setStatusBarText( i18n("Could not save.") );
00667   return err;
00668 }
00669 
00670 void TaskView::startCurrentTimer()
00671 {
00672   startTimerFor( currentItem() );
00673 }
00674 
00675 void TaskView::startTimerFor( Task* task, const QDateTime &startTime )
00676 {
00677   kDebug(5970) << "Entering function";
00678   if (task != 0 && d->mActiveTasks.indexOf(task) == -1) 
00679   {
00680     if (!task->isComplete())
00681     {
00682       if ( KTimeTrackerSettings::uniTasking() ) stopAllTimers();
00683       _idleTimeDetector->startIdleDetection();
00684       task->setRunning(true, d->mStorage, startTime);
00685       d->mActiveTasks.append(task);
00686       emit updateButtons();
00687       if ( d->mActiveTasks.count() == 1 )
00688           emit timersActive();
00689       emit tasksChanged( d->mActiveTasks );
00690     }
00691   }
00692 }
00693 
00694 void TaskView::clearActiveTasks()
00695 {
00696   d->mActiveTasks.clear(); 
00697 }
00698 
00699 void TaskView::stopAllTimers( const QDateTime &when )
00700 {
00701   kDebug(5970) << "Entering function";
00702   foreach ( Task *task, d->mActiveTasks )
00703     task->setRunning( false, d->mStorage, when );
00704 
00705   _idleTimeDetector->stopIdleDetection();
00706   FocusDetectorNotifier::instance()->detach( this );
00707   d->mActiveTasks.clear();
00708   emit updateButtons();
00709   emit timersInactive();
00710   emit tasksChanged( d->mActiveTasks );
00711 }
00712 
00713 void TaskView::toggleFocusTracking()
00714 {
00715   d->mFocusTrackingActive = !d->mFocusTrackingActive;
00716 
00717   if ( d->mFocusTrackingActive ) 
00718   {
00719     FocusDetectorNotifier::instance()->attach( this );
00720   } 
00721   else  
00722   {
00723     stopTimerFor( d->mLastTaskWithFocus );
00724     FocusDetectorNotifier::instance()->detach( this );
00725   }
00726 
00727   emit updateButtons();
00728 }
00729 
00730 void TaskView::startNewSession()
00731 /* This procedure starts a new session. We speak of session times, 
00732 overalltimes (comprising all sessions) and total times (comprising all subtasks).
00733 That is why there is also a total session time. */
00734 {
00735   kDebug(5970) <<"Entering TaskView::startNewSession";
00736   QTreeWidgetItemIterator item( this );
00737   while ( *item )
00738   {
00739     Task * task = (Task *) *item;
00740     task->startNewSession();
00741     ++item;
00742   }
00743   kDebug(5970) << "Leaving TaskView::startNewSession";
00744 }
00745 
00746 void TaskView::resetTimeForAllTasks()
00747 /* This procedure resets all times (session and overall) for all tasks and subtasks. */
00748 {
00749   kDebug(5970) << "Entering TaskView::resetTimeForAllTasks";
00750   QTreeWidgetItemIterator item( this );
00751   while ( *item ) 
00752   {
00753     Task * task = (Task *) *item;
00754     task->resetTimes();
00755     ++item;
00756   }
00757   kDebug(5970) << "Leaving TaskView::resetTimeForAllTasks";
00758 }
00759 
00760 void TaskView::stopTimerFor(Task* task)
00761 {
00762   kDebug(5970) << "Entering function";
00763   if ( task != 0 && d->mActiveTasks.indexOf(task) != -1 ) 
00764   {
00765     d->mActiveTasks.removeAll(task);
00766     task->setRunning(false, d->mStorage);
00767     if ( d->mActiveTasks.count() == 0 ) 
00768     {
00769       _idleTimeDetector->stopIdleDetection();
00770       emit timersInactive();
00771     }
00772     emit updateButtons();
00773   }
00774   emit tasksChanged( d->mActiveTasks );
00775 }
00776 
00777 void TaskView::stopCurrentTimer()
00778 {
00779   stopTimerFor( currentItem() );
00780   if ( d->mFocusTrackingActive && d->mLastTaskWithFocus == currentItem() ) 
00781   {
00782     toggleFocusTracking();
00783   }
00784 }
00785 
00786 void TaskView::minuteUpdate()
00787 {
00788   addTimeToActiveTasks(1, false);
00789 }
00790 
00791 void TaskView::addTimeToActiveTasks(int minutes, bool save_data)
00792 {
00793   foreach ( Task *task, d->mActiveTasks )
00794     task->changeTime( minutes, ( save_data ? d->mStorage : 0 ) );
00795 }
00796 
00797 void TaskView::newTask()
00798 {
00799   newTask(i18n("New Task"), 0);
00800 }
00801 
00802 void TaskView::newTask( const QString &caption, Task *parent )
00803 {
00804   EditTaskDialog *dialog = new EditTaskDialog( this, caption, false );
00805   long total, totalDiff, session, sessionDiff;
00806   DesktopList desktopList;
00807 
00808   int result = dialog->exec();
00809   if ( result == QDialog::Accepted ) 
00810   {
00811     QString taskName = i18n( "Unnamed Task" );
00812     if ( !dialog->taskName().isEmpty()) taskName = dialog->taskName();
00813 
00814     total = totalDiff = session = sessionDiff = 0;
00815     dialog->status( &total, &totalDiff, &session, &sessionDiff, &desktopList );
00816 
00817     // If all available desktops are checked, disable auto tracking,
00818     // since it makes no sense to track for every desktop.
00819     if ( desktopList.size() ==  _desktopTracker->desktopCount() )
00820       desktopList.clear();
00821 
00822     QString uid = addTask( taskName, total, session, desktopList, parent );
00823     if ( uid.isNull() )
00824     {
00825       KMessageBox::error( 0, i18n(
00826             "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/ " ) );
00827     }
00828   }
00829   emit updateButtons();
00830 }
00831 
00832 QString TaskView::addTask
00833 ( const QString& taskname, long total, long session, 
00834   const DesktopList& desktops, Task* parent )
00835 {
00836   kDebug(5970) << "Entering function; taskname =" << taskname;
00837   setSortingEnabled(false);
00838   Task *task;
00839   if ( parent ) task = new Task( taskname, total, session, desktops, parent );
00840   else          task = new Task( taskname, total, session, desktops, this );
00841 
00842   task->setUid( d->mStorage->addTask( task, parent ) );
00843   QString taskuid=task->uid();
00844   if ( ! taskuid.isNull() )
00845   {
00846     _desktopTracker->registerForDesktops( task, desktops );
00847     setCurrentItem( task );
00848     task->setSelected( true );
00849     task->setPixmapProgress();
00850     save();
00851   }
00852   else
00853   {
00854     delete task;
00855   }
00856   setSortingEnabled(true);
00857   return taskuid;
00858 }
00859 
00860 void TaskView::newSubTask()
00861 {
00862   Task* task = currentItem();
00863   if(!task)
00864     return;
00865   newTask(i18n("New Sub Task"), task);
00866   task->setExpanded(true);
00867   refresh();
00868 }
00869 
00870 void TaskView::editTask()
00871 {
00872   kDebug(5970) <<"Entering editTask";
00873   Task *task = currentItem();
00874   if (!task)
00875     return;
00876 
00877   DesktopList desktopList = task->desktops();
00878   DesktopList oldDeskTopList = desktopList;
00879   EditTaskDialog *dialog = new EditTaskDialog( this, i18n("Edit Task"), true, &desktopList );
00880   dialog->setTask( task->name(),
00881                    task->time(),
00882                    task->sessionTime() );
00883   int result = dialog->exec();
00884   if (result == QDialog::Accepted) 
00885   {
00886     QString taskName = i18n("Unnamed Task");
00887     if (!dialog->taskName().isEmpty()) 
00888     {
00889       taskName = dialog->taskName();
00890     }
00891     // setName only does something if the new name is different
00892     task->setName(taskName, d->mStorage);
00893 
00894     // update session time as well if the time was changed
00895     long total, session, totalDiff, sessionDiff;
00896     total = totalDiff = session = sessionDiff = 0;
00897     DesktopList desktopList;
00898     dialog->status( &total, &totalDiff, &session, &sessionDiff, &desktopList);
00899 
00900     if( totalDiff != 0 || sessionDiff != 0)
00901       task->changeTimes( sessionDiff, totalDiff, d->mStorage );
00902 
00903     // If all available desktops are checked, disable auto tracking,
00904     // since it makes no sense to track for every desktop.
00905     if (desktopList.size() == _desktopTracker->desktopCount())
00906       desktopList.clear();
00907     // only do something for autotracking if the new setting is different
00908     if ( oldDeskTopList != desktopList )
00909     { 
00910       task->setDesktopList(desktopList);
00911       _desktopTracker->registerForDesktops( task, desktopList );
00912     }
00913     emit updateButtons();
00914   }
00915 }
00916 
00917 void TaskView::reinstateTask(int completion)
00918 {
00919   Task* task = currentItem();
00920   if (task == 0) {
00921     KMessageBox::information(0,i18n("No task selected."));
00922     return;
00923   }
00924 
00925   if (completion<0) completion=0;
00926   if (completion<100)
00927   {
00928     task->setPercentComplete(completion, d->mStorage);
00929     task->setPixmapProgress();
00930     save();
00931     emit