karm Library API Documentation

taskview.cpp

00001 #include <qfile.h>
00002 #include <qclipboard.h>
00003 #include <qlayout.h>
00004 #include <qlistbox.h>
00005 #include <qptrlist.h>
00006 #include <qptrstack.h>
00007 #include <qtextstream.h>
00008 #include <qlistview.h>
00009 #include <qtimer.h>
00010 
00011 #include "kapplication.h"       // kapp
00012 #include <kconfig.h>
00013 #include <kdebug.h>
00014 #include <klocale.h>            // i18n
00015 #include <kfiledialog.h>
00016 #include <klineeditdlg.h>
00017 #include <kmessagebox.h>
00018 
00019 #include "desktoptracker.h"
00020 #include "edittaskdialog.h"
00021 #include "idletimedetector.h"
00022 #include "preferences.h"
00023 #include "task.h"
00024 #include "taskview.h"
00025 #include "timekard.h"
00026 #include "karmstorage.h"
00027 #include "printdialog.h"
00028 
00029 #define T_LINESIZE 1023
00030 #define HIDDEN_COLUMN -10
00031 
00032 class DesktopTracker;
00033 
00034 TaskView::TaskView(QWidget *parent, const char *name):KListView(parent,name)
00035 {
00036   _preferences = Preferences::instance();
00037   _storage = KarmStorage::instance();
00038 
00039   connect(this, SIGNAL( doubleClicked( QListViewItem * )),
00040           this, SLOT( changeTimer( QListViewItem * )));
00041 
00042   // setup default values
00043   previousColumnWidths[0] = previousColumnWidths[1]
00044   = previousColumnWidths[2] = previousColumnWidths[3] = HIDDEN_COLUMN;
00045 
00046   addColumn( i18n("Task Name") );
00047   addColumn( i18n("Session Time") );
00048   addColumn( i18n("Time") );
00049   addColumn( i18n("Total Session Time") );
00050   addColumn( i18n("Total Time") );
00051   setColumnAlignment( 1, Qt::AlignRight );
00052   setColumnAlignment( 2, Qt::AlignRight );
00053   setColumnAlignment( 3, Qt::AlignRight );
00054   setColumnAlignment( 4, Qt::AlignRight );
00055   adaptColumns();
00056   setAllColumnsShowFocus( true );
00057 
00058   // set up the minuteTimer
00059   _minuteTimer = new QTimer(this);
00060   connect( _minuteTimer, SIGNAL( timeout() ), this, SLOT( minuteUpdate() ));
00061   _minuteTimer->start(1000 * secsPerMinute);
00062 
00063   // React when user changes iCalFile
00064   connect(_preferences, SIGNAL(iCalFile(QString)),
00065       this, SLOT(iCalFileChanged(QString)));
00066 
00067   // resize columns when config is changed
00068   connect(_preferences, SIGNAL( setupChanged() ), this,SLOT( adaptColumns() ));
00069 
00070   _minuteTimer->start(1000 * secsPerMinute);
00071 
00072   // Set up the idle detection.
00073   _idleTimeDetector = new IdleTimeDetector( _preferences->idlenessTimeout() );
00074   connect( _idleTimeDetector, SIGNAL( extractTime(int) ),
00075            this, SLOT( extractTime(int) ));
00076   connect( _idleTimeDetector, SIGNAL( stopAllTimers() ),
00077            this, SLOT( stopAllTimers() ));
00078   connect( _preferences, SIGNAL( idlenessTimeout(int) ),
00079            _idleTimeDetector, SLOT( setMaxIdle(int) ));
00080   connect( _preferences, SIGNAL( detectIdleness(bool) ),
00081            _idleTimeDetector, SLOT( toggleOverAllIdleDetection(bool) ));
00082   if (!_idleTimeDetector->isIdleDetectionPossible())
00083     _preferences->disableIdleDetection();
00084 
00085   // Setup auto save timer
00086   _autoSaveTimer = new QTimer(this);
00087   connect( _preferences, SIGNAL( autoSave(bool) ),
00088            this, SLOT( autoSaveChanged(bool) ));
00089   connect( _preferences, SIGNAL( autoSavePeriod(int) ),
00090            this, SLOT( autoSavePeriodChanged(int) ));
00091   connect( _autoSaveTimer, SIGNAL( timeout() ), this, SLOT( save() ));
00092 
00093   // Setup manual save timer (to save changes a little while after they happen)
00094   _manualSaveTimer = new QTimer(this);
00095   connect( _manualSaveTimer, SIGNAL( timeout() ), this, SLOT( save() ));
00096 
00097   // Connect desktop tracker events to task starting/stopping
00098   _desktopTracker = new DesktopTracker();
00099   connect( _desktopTracker, SIGNAL( reachedtActiveDesktop( Task* ) ),
00100            this, SLOT( startTimerFor(Task*) ));
00101   connect( _desktopTracker, SIGNAL( leftActiveDesktop( Task* ) ),
00102            this, SLOT( stopTimerFor(Task*) ));
00103 }
00104 
00105 TaskView::~TaskView()
00106 {
00107   _preferences->save();
00108 }
00109 
00110 Task* TaskView::first_child() const
00111 {
00112   return static_cast<Task*>(firstChild());
00113 }
00114 
00115 Task* TaskView::current_item() const
00116 {
00117   return static_cast<Task*>(currentItem());
00118 }
00119 
00120 Task* TaskView::item_at_index(int i)
00121 {
00122   return static_cast<Task*>(itemAtIndex(i));
00123 }
00124 
00125 void TaskView::load()
00126 {
00127 
00128   QString err = _storage->load(this, _preferences);
00129 
00130   if (!err.isEmpty())
00131   {
00132     KMessageBox::error(this, err);
00133     return;
00134   }
00135 
00136   // TODO: If empty, ask if user wants to import tasks from another file
00137   //if (_storage.isEmpty())
00138   //{
00139   //}
00140 
00141   // Register tasks with desktop tracker
00142   int task_idx = 0;
00143   for (Task* task = item_at_index(task_idx);
00144       task;
00145       task = item_at_index(++task_idx))
00146   {
00147     _desktopTracker->registerForDesktops( task, task->getDesktops() );
00148   }
00149 
00150   setSelected(first_child(), true);
00151   setCurrentItem(first_child());
00152   _desktopTracker->startTracking();
00153 }
00154 
00155 void TaskView::loadFromFlatFile()
00156 {
00157   kdDebug() << "TaskView::loadFromFlatFile()" << endl;
00158 
00159   //KFileDialog::getSaveFileName("icalout.ics",i18n("*.ics|ICalendars"),this);
00160 
00161   QString fileName(KFileDialog::getOpenFileName(QString::null, QString::null,
00162         0));
00163   if (!fileName.isEmpty()) {
00164     QString err = _storage->loadFromFlatFile(this, fileName);
00165     if (!err.isEmpty())
00166     {
00167       KMessageBox::error(this, err);
00168       return;
00169     }
00170 
00171     // Register tasks with desktop tracker
00172     int task_idx = 0;
00173     Task* task = item_at_index(task_idx++);
00174     while (task)
00175     {
00176       // item_at_index returns 0 where no more items.
00177       _desktopTracker->registerForDesktops( task, task->getDesktops() );
00178       task = item_at_index(task_idx++);
00179     }
00180 
00181     setSelected(first_child(), true);
00182     setCurrentItem(first_child());
00183 
00184     _desktopTracker->startTracking();
00185   }
00186 }
00187 
00188 void TaskView::scheduleSave()
00189 {
00190     _manualSaveTimer->start( 10, true /*single-shot*/ );
00191 }
00192 
00193 void TaskView::save()
00194 {
00195     // DF: this code created a new event for the running task(s),
00196     // at every call (very frequent with autosave) !!!
00197     // -> if one wants autosave to save the current event, then
00198     // Task needs to store the "current event" and we need to update
00199     // it before calling save.
00200 #if 0
00201   // Stop then start all timers so history entries are written.  This is
00202   // inefficient if more than one task running, but it is correct.  It is
00203   // inefficient because the iCalendar file is saved every time a task's
00204   // setRunning(false, ...) is called.  For a big ics file, this could be a
00205   // drag.  However, it does ensure that the data will be consistent.  And
00206   // if the most common use case is that one task is running most of the time,
00207   // it won't make any difference.
00208   for (unsigned int i = 0; i < activeTasks.count(); i++)
00209   {
00210     activeTasks.at(i)->setRunning(false, _storage);
00211     activeTasks.at(i)->setRunning(true, _storage);
00212   }
00213 
00214   // If there was an active task, the iCal file has already been saved.
00215   if (activeTasks.count() == 0)
00216 #endif
00217   {
00218     _storage->save(this);
00219   }
00220 }
00221 
00222 void TaskView::startCurrentTimer()
00223 {
00224   startTimerFor( current_item() );
00225 }
00226 
00227 long TaskView::count()
00228 {
00229   long n = 0;
00230   for (Task* t = item_at_index(n); t; t=item_at_index(++n));
00231   return n;
00232 }
00233 
00234 void TaskView::startTimerFor(Task* task)
00235 {
00236   if (task != 0 && activeTasks.findRef(task) == -1) {
00237     _idleTimeDetector->startIdleDetection();
00238     task->setRunning(true, _storage);
00239     activeTasks.append(task);
00240     emit updateButtons();
00241     if ( activeTasks.count() == 1 )
00242         emit timersActive();
00243 
00244     emit tasksChanged( activeTasks);
00245   }
00246 }
00247 
00248 void TaskView::stopAllTimers()
00249 {
00250   for (unsigned int i=0; i<activeTasks.count();i++) {
00251     activeTasks.at(i)->setRunning(false, _storage);
00252   }
00253   _idleTimeDetector->stopIdleDetection();
00254   activeTasks.clear();
00255   emit updateButtons();
00256   emit timersInactive();
00257   emit tasksChanged( activeTasks);
00258 }
00259 
00260 void TaskView::startNewSession()
00261 {
00262   QListViewItemIterator item( first_child());
00263   for ( ; item.current(); ++item ) {
00264     Task * task = (Task *) item.current();
00265     task->startNewSession();
00266   }
00267 }
00268 
00269 void TaskView::resetTimeForAllTasks()
00270 {
00271   QListViewItemIterator item( first_child());
00272   for ( ; item.current(); ++item ) {
00273     Task * task = (Task *) item.current();
00274     task->resetTimes();
00275   }
00276 }
00277 
00278 void TaskView::stopTimerFor(Task* task)
00279 {
00280   if (task != 0 && activeTasks.findRef(task) != -1) {
00281     activeTasks.removeRef(task);
00282     task->setRunning(false, _storage);
00283     if (activeTasks.count()== 0) {
00284       _idleTimeDetector->stopIdleDetection();
00285       emit timersInactive();
00286     }
00287     emit updateButtons();
00288   }
00289   emit tasksChanged( activeTasks);
00290 }
00291 
00292 void TaskView::stopCurrentTimer()
00293 {
00294   stopTimerFor( current_item());
00295 }
00296 
00297 
00298 void TaskView::changeTimer(QListViewItem *)
00299 {
00300   Task *task = current_item();
00301   if (task != 0 && activeTasks.findRef(task) == -1) {
00302     // Stop all the other timers.
00303     for (unsigned int i=0; i<activeTasks.count();i++) {
00304       (activeTasks.at(i))->setRunning(false, _storage);
00305     }
00306     activeTasks.clear();
00307 
00308     // Start the new timer.
00309     startCurrentTimer();
00310   }
00311   else {
00312     stopCurrentTimer();
00313   }
00314 }
00315 
00316 void TaskView::minuteUpdate()
00317 {
00318   addTimeToActiveTasks(1, false);
00319 }
00320 
00321 void TaskView::addTimeToActiveTasks(int minutes, bool do_logging)
00322 {
00323   for(unsigned int i=0; i<activeTasks.count();i++)
00324     activeTasks.at(i)->changeTime(minutes, do_logging, _storage);
00325 }
00326 
00327 void TaskView::newTask()
00328 {
00329   newTask(i18n("New Task"), 0);
00330 }
00331 
00332 void TaskView::newTask(QString caption, Task *parent)
00333 {
00334   EditTaskDialog *dialog = new EditTaskDialog(caption, false);
00335   long total, totalDiff, session, sessionDiff;
00336   DesktopList desktopList;
00337   Task *task;
00338 
00339   int result = dialog->exec();
00340   if (result == QDialog::Accepted) {
00341     QString taskName = i18n("Unnamed Task");
00342     if (!dialog->taskName().isEmpty()) {
00343       taskName = dialog->taskName();
00344     }
00345 
00346     total = totalDiff = session = sessionDiff = 0;
00347     dialog->status( &total, &totalDiff, &session, &sessionDiff, &desktopList);
00348 
00349     // If all available desktops are checked, disable auto tracking,
00350     // since it makes no sense to track for every desktop.
00351     if (desktopList.size() == (unsigned int)_desktopTracker->desktopCount())
00352       desktopList.clear();
00353 
00354     if (parent == 0)
00355     {
00356       task = new Task(taskName, total, session, desktopList, this);
00357       task->setUid(_storage->addTask(task, 0));
00358     }
00359     else
00360     {
00361       task = new Task(taskName, total, session, desktopList, parent);
00362       task->setUid(_storage->addTask(task, parent));
00363     }
00364 
00365     if (!task->uid().isNull())
00366     {
00367       _desktopTracker->registerForDesktops( task, desktopList );
00368 
00369       setCurrentItem( task );
00370       setSelected( task, true );
00371 
00372       save();
00373     }
00374     else
00375     {
00376       delete task;
00377       KMessageBox::error(0,i18n(
00378             "Error storing new task--your changes were not saved."));
00379     }
00380   }
00381 
00382   delete dialog;
00383 }
00384 
00385 void TaskView::newSubTask()
00386 {
00387   Task* task = current_item();
00388   if(!task)
00389     return;
00390   newTask(i18n("New Sub Task"), task);
00391   task->setOpen(true);
00392   setRootIsDecorated(true);
00393 }
00394 
00395 void TaskView::editTask()
00396 {
00397   Task *task = current_item();
00398   if (!task)
00399     return;
00400 
00401   DesktopList desktopList = task->getDesktops();
00402   EditTaskDialog *dialog = new EditTaskDialog(i18n("Edit Task"), true, &desktopList);
00403   dialog->setTask( task->name(),
00404                    task->time(),
00405                    task->sessionTime() );
00406   int result = dialog->exec();
00407   if (result == QDialog::Accepted) {
00408     QString taskName = i18n("Unnamed Task");
00409     if (!dialog->taskName().isEmpty()) {
00410       taskName = dialog->taskName();
00411     }
00412     // setName only does something if the new name is different
00413     task->setName(taskName, _storage);
00414 
00415     // update session time as well if the time was changed
00416     long total, session, totalDiff, sessionDiff;
00417     total = totalDiff = session = sessionDiff = 0;
00418     DesktopList desktopList;
00419     dialog->status( &total, &totalDiff, &session, &sessionDiff, &desktopList);
00420 
00421     if( totalDiff != 0 || sessionDiff != 0)
00422       task->changeTimes( sessionDiff ,totalDiff, true, _storage );
00423 
00424     // If all available desktops are checked, disable auto tracking,
00425     // since it makes no sense to track for every desktop.
00426     if (desktopList.size() == (unsigned int)_desktopTracker->desktopCount())
00427       desktopList.clear();
00428 
00429     task->setDesktopList(desktopList);
00430 
00431     _desktopTracker->registerForDesktops( task, desktopList );
00432 
00433     emit updateButtons();
00434   }
00435   delete dialog;
00436 }
00437 
00438 //void TaskView::addCommentToTask()
00439 //{
00440 //  Task *task = current_item();
00441 //  if (!task)
00442 //    return;
00443 
00444 //  bool ok;
00445 //  QString comment = KLineEditDlg::getText(i18n("Comment"),
00446 //                       i18n("Log comment for task '%1':").arg(task->name()),
00447 //                       QString(), &ok, this);
00448 //  if ( ok )
00449 //    task->addComment( comment, _storage );
00450 //}
00451 
00452 
00453 void TaskView::deleteTask(bool markingascomplete)
00454 {
00455   Task *task = current_item();
00456   if (task == 0) {
00457     KMessageBox::information(0,i18n("No task selected."));
00458     return;
00459   }
00460 
00461   int response = KMessageBox::Yes;
00462   if (!markingascomplete && _preferences->promptDelete()) {
00463     if (task->childCount() == 0) {
00464       response = KMessageBox::warningYesNo( 0,
00465           i18n( "Are you sure you want to delete "
00466           "the task named\n\"%1\" and its entire history?")
00467           .arg(task->name()),
00468           i18n( "Deleting Task"));
00469     }
00470     else {
00471       response = KMessageBox::warningYesNo( 0,
00472           i18n( "Are you sure you want to delete the task named"
00473           "\n\"%1\" and its entire history?\n"
00474           "NOTE: all its subtasks and their history will also "
00475           "be deleted!").arg(task->name()),
00476           i18n( "Deleting Task"));
00477     }
00478   }
00479 
00480   if (response == KMessageBox::Yes)
00481   {
00482     if (markingascomplete)
00483     {
00484       task->setPercentComplete(100, _storage);
00485       save();
00486 
00487       // Have to remove after saving, as the save routine only affects tasks
00488       // that are in the view.  Otherwise, the new percent complete does not
00489       // get saved.   (No longer remove when marked as complete.)
00490       //task->removeFromView();
00491 
00492     }
00493     else
00494     {
00495       task->remove(activeTasks, _storage);
00496       task->removeFromView();
00497       save();
00498     }
00499 
00500     // remove root decoration if there is no more children.
00501     bool anyChilds = false;
00502     for(Task* child = first_child();
00503               child;
00504               child = child->nextSibling()) {
00505       if (child->childCount() != 0) {
00506         anyChilds = true;
00507         break;
00508       }
00509     }
00510     if (!anyChilds) {
00511       setRootIsDecorated(false);
00512     }
00513 
00514     // Stop idle detection if no more counters are running
00515     if (activeTasks.count() == 0) {
00516       _idleTimeDetector->stopIdleDetection();
00517       emit timersInactive();
00518     }
00519 
00520     emit tasksChanged( activeTasks );
00521   }
00522 }
00523 
00524 void TaskView::extractTime(int minutes)
00525 {
00526   addTimeToActiveTasks(-minutes, true);
00527 }
00528 
00529 void TaskView::autoSaveChanged(bool on)
00530 {
00531   if (on) {
00532     _autoSaveTimer->start(_preferences->autoSavePeriod()*1000*secsPerMinute);
00533   }
00534   else {
00535     if (_autoSaveTimer->isActive()) {
00536       _autoSaveTimer->stop();
00537     }
00538   }
00539 }
00540 
00541 void TaskView::autoSavePeriodChanged(int /*minutes*/)
00542 {
00543   autoSaveChanged(_preferences->autoSave());
00544 }
00545 
00546 void TaskView::adaptColumns()
00547 {
00548   // to hide a column X we set it's width to 0
00549   // at that moment we'll remember the original column within
00550   // previousColumnWidths[X]
00551   //
00552   // When unhiding a previously hidden column
00553   // (previousColumnWidths[X] != HIDDEN_COLUMN !)
00554   // we restore it's width from the saved value and set
00555   // previousColumnWidths[X] to HIDDEN_COLUMN
00556 
00557   for( int x=1; x <= 4; x++) {
00558     // the column was invisible before and were switching it on now
00559     if(   _preferences->displayColumn(x-1)
00560        && previousColumnWidths[x-1] != HIDDEN_COLUMN )
00561     {
00562       setColumnWidth( x, previousColumnWidths[x-1] );
00563       previousColumnWidths[x-1] = HIDDEN_COLUMN;
00564       setColumnWidthMode( x, QListView::Maximum );
00565     }
00566     // the column was visible before and were switching it off now
00567     else
00568     if( ! _preferences->displayColumn(x-1)
00569        && previousColumnWidths[x-1] == HIDDEN_COLUMN )
00570     {
00571       setColumnWidthMode( x, QListView::Manual ); // we don't want update()
00572                                                   // to resize/unhide the col
00573       previousColumnWidths[x-1] = columnWidth( x );
00574       setColumnWidth( x, 0 );
00575     }
00576   }
00577 }
00578 
00579 void TaskView::deletingTask(Task* deletedTask)
00580 {
00581   DesktopList desktopList;
00582 
00583   _desktopTracker->registerForDesktops( deletedTask, desktopList );
00584   activeTasks.removeRef( deletedTask );
00585 
00586   emit tasksChanged( activeTasks);
00587 }
00588 
00589 void TaskView::iCalFileChanged(QString file)
00590 {
00591   kdDebug() << "TaskView:iCalFileChanged: " << file << endl;
00592   load();
00593 }
00594 
00595 QValueList<HistoryEvent> TaskView::getHistory(const QDate& from,
00596     const QDate& to) const
00597 {
00598   return _storage->getHistory(from, to);
00599 }
00600 
00601 void TaskView::markTaskAsComplete()
00602 {
00603   if (current_item())
00604     kdDebug() << "TaskView::markTaskAsComplete: "
00605       << current_item()->uid() << endl;
00606   else
00607     kdDebug() << "TaskView::markTaskAsComplete: null current_item()" << endl;
00608 
00609   bool markingascomplete = true;
00610   deleteTask(markingascomplete);
00611 }
00612 
00613 void TaskView::clipTotals()
00614 {
00615   TimeKard *t = new TimeKard();
00616   if (current_item() && current_item()->isRoot())
00617   {
00618     int response = KMessageBox::questionYesNo( 0,
00619         i18n("Copy totals for just this task and its subtasks?"
00620           "  (Click No to copy totals for all tasks.)"));
00621     if (response == KMessageBox::Yes)
00622     {
00623       KApplication::clipboard()->setText(t->totalsAsText(this));
00624     }
00625     else
00626     {
00627       KApplication::clipboard()->setText(t->totalsAsText(this, false));
00628     }
00629   }
00630   else
00631   {
00632     KApplication::clipboard()->setText(t->totalsAsText(this));
00633   }
00634 }
00635 
00636 void TaskView::clipHistory()
00637 {
00638 
00639   PrintDialog *dialog = new PrintDialog();
00640   if (dialog->exec()== QDialog::Accepted)
00641   {
00642     TimeKard *t = new TimeKard();
00643     KApplication::clipboard()->
00644       setText(t->historyAsText(this, dialog->from(), dialog->to()));
00645   }
00646 }
00647 
00648 #include "taskview.moc"
KDE Logo
This file is part of the documentation for karm Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat May 1 11:37:53 2004 by doxygen 1.2.15 written by Dimitri van Heesch, © 1997-2003