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"
00012 #include <kconfig.h>
00013 #include <kdebug.h>
00014 #include <klocale.h>
00015 #include <kfiledialog.h>
00016 #include <kmessagebox.h>
00017
00018 #include "csvexportdialog.h"
00019 #include "desktoptracker.h"
00020 #include "edittaskdialog.h"
00021 #include "idletimedetector.h"
00022 #include "karmstorage.h"
00023 #include "preferences.h"
00024 #include "printdialog.h"
00025 #include "reportcriteria.h"
00026 #include "task.h"
00027 #include "taskview.h"
00028 #include "timekard.h"
00029
00030 #define T_LINESIZE 1023
00031 #define HIDDEN_COLUMN -10
00032
00033 class DesktopTracker;
00034
00035 TaskView::TaskView(QWidget *parent, const char *name):KListView(parent,name)
00036 {
00037 _preferences = Preferences::instance();
00038 _storage = KarmStorage::instance();
00039
00040 connect(this, SIGNAL( doubleClicked( QListViewItem * )),
00041 this, SLOT( changeTimer( QListViewItem * )));
00042
00043 connect(this, SIGNAL( delete( QListViewItem * )),
00044 this, SLOT( deleteItemState( QListViewItem * )));
00045 connect( this, SIGNAL( expanded( QListViewItem * ) ),
00046 this, SLOT( itemStateChanged( QListViewItem * ) ) );
00047 connect( this, SIGNAL( collapsed( QListViewItem * ) ),
00048 this, SLOT( itemStateChanged( QListViewItem * ) ) );
00049
00050
00051 previousColumnWidths[0] = previousColumnWidths[1]
00052 = previousColumnWidths[2] = previousColumnWidths[3] = HIDDEN_COLUMN;
00053
00054 addColumn( i18n("Task Name") );
00055 addColumn( i18n("Session Time") );
00056 addColumn( i18n("Time") );
00057 addColumn( i18n("Total Session Time") );
00058 addColumn( i18n("Total Time") );
00059 setColumnAlignment( 1, Qt::AlignRight );
00060 setColumnAlignment( 2, Qt::AlignRight );
00061 setColumnAlignment( 3, Qt::AlignRight );
00062 setColumnAlignment( 4, Qt::AlignRight );
00063 adaptColumns();
00064 setAllColumnsShowFocus( true );
00065
00066
00067 _minuteTimer = new QTimer(this);
00068 connect( _minuteTimer, SIGNAL( timeout() ), this, SLOT( minuteUpdate() ));
00069 _minuteTimer->start(1000 * secsPerMinute);
00070
00071
00072 connect(_preferences, SIGNAL(iCalFile(QString)),
00073 this, SLOT(iCalFileChanged(QString)));
00074
00075
00076 connect(_preferences, SIGNAL( setupChanged() ), this,SLOT( adaptColumns() ));
00077
00078 _minuteTimer->start(1000 * secsPerMinute);
00079
00080
00081 _idleTimeDetector = new IdleTimeDetector( _preferences->idlenessTimeout() );
00082 connect( _idleTimeDetector, SIGNAL( extractTime(int) ),
00083 this, SLOT( extractTime(int) ));
00084 connect( _idleTimeDetector, SIGNAL( stopAllTimers() ),
00085 this, SLOT( stopAllTimers() ));
00086 connect( _preferences, SIGNAL( idlenessTimeout(int) ),
00087 _idleTimeDetector, SLOT( setMaxIdle(int) ));
00088 connect( _preferences, SIGNAL( detectIdleness(bool) ),
00089 _idleTimeDetector, SLOT( toggleOverAllIdleDetection(bool) ));
00090 if (!_idleTimeDetector->isIdleDetectionPossible())
00091 _preferences->disableIdleDetection();
00092
00093
00094 _autoSaveTimer = new QTimer(this);
00095 connect( _preferences, SIGNAL( autoSave(bool) ),
00096 this, SLOT( autoSaveChanged(bool) ));
00097 connect( _preferences, SIGNAL( autoSavePeriod(int) ),
00098 this, SLOT( autoSavePeriodChanged(int) ));
00099 connect( _autoSaveTimer, SIGNAL( timeout() ), this, SLOT( save() ));
00100
00101
00102 _manualSaveTimer = new QTimer(this);
00103 connect( _manualSaveTimer, SIGNAL( timeout() ), this, SLOT( save() ));
00104
00105
00106 _desktopTracker = new DesktopTracker();
00107 connect( _desktopTracker, SIGNAL( reachedtActiveDesktop( Task* ) ),
00108 this, SLOT( startTimerFor(Task*) ));
00109 connect( _desktopTracker, SIGNAL( leftActiveDesktop( Task* ) ),
00110 this, SLOT( stopTimerFor(Task*) ));
00111 }
00112
00113 TaskView::~TaskView()
00114 {
00115 _preferences->save();
00116 }
00117
00118 Task* TaskView::first_child() const
00119 {
00120 return static_cast<Task*>(firstChild());
00121 }
00122
00123 Task* TaskView::current_item() const
00124 {
00125 return static_cast<Task*>(currentItem());
00126 }
00127
00128 Task* TaskView::item_at_index(int i)
00129 {
00130 return static_cast<Task*>(itemAtIndex(i));
00131 }
00132
00133 void TaskView::load()
00134 {
00135 _isloading = true;
00136 QString err = _storage->load(this, _preferences);
00137
00138 if (!err.isEmpty())
00139 {
00140 KMessageBox::error(this, err);
00141 _isloading = false;
00142 return;
00143 }
00144
00145
00146 int i = 0;
00147 for ( Task* t = item_at_index(i); t; t = item_at_index(++i) )
00148 _desktopTracker->registerForDesktops( t, t->getDesktops() );
00149
00150 restoreItemState( first_child() );
00151
00152 setSelected(first_child(), true);
00153 setCurrentItem(first_child());
00154 _desktopTracker->startTracking();
00155 _isloading = false;
00156 }
00157
00158 void TaskView::restoreItemState( QListViewItem *item )
00159 {
00160 while( item )
00161 {
00162 Task *t = (Task *)item;
00163 t->setOpen( _preferences->readBoolEntry( t->uid() ) );
00164 if( item->childCount() > 0 ) restoreItemState( item->firstChild() );
00165 item = item->nextSibling();
00166 }
00167 }
00168
00169 void TaskView::itemStateChanged( QListViewItem *item )
00170 {
00171 if ( !item || _isloading ) return;
00172 Task *t = (Task *)item;
00173 kdDebug(5970) << "TaskView::itemStateChanged()"
00174 << " uid=" << t->uid() << " state=" << t->isOpen()
00175 << endl;
00176 if( _preferences ) _preferences->writeEntry( t->uid(), t->isOpen() );
00177 }
00178
00179 void TaskView::deleteItemState( QListViewItem *item )
00180 {
00181 if ( !item ) return;
00182 Task *t = (Task *)item;
00183 kdDebug(5970) << "TaskView:deleteItemState()"
00184 << " uid=" << t->uid() << endl;
00185 if( _preferences ) _preferences->deleteEntry( t->uid() );
00186 }
00187
00188 void TaskView::closeStorage() { _storage->closeStorage( this ); }
00189
00190
00191 void TaskView::loadFromFlatFile()
00192 {
00193 kdDebug(5970) << "TaskView::loadFromFlatFile()" << endl;
00194
00195
00196
00197 QString fileName(KFileDialog::getOpenFileName(QString::null, QString::null,
00198 0));
00199 if (!fileName.isEmpty()) {
00200 QString err = _storage->loadFromFlatFile(this, fileName);
00201 if (!err.isEmpty())
00202 {
00203 KMessageBox::error(this, err);
00204 return;
00205 }
00206
00207
00208 int task_idx = 0;
00209 Task* task = item_at_index(task_idx++);
00210 while (task)
00211 {
00212
00213 _desktopTracker->registerForDesktops( task, task->getDesktops() );
00214 task = item_at_index(task_idx++);
00215 }
00216
00217 setSelected(first_child(), true);
00218 setCurrentItem(first_child());
00219
00220 _desktopTracker->startTracking();
00221 }
00222 }
00223
00224 void TaskView::exportcsvFile()
00225 {
00226 kdDebug(5970) << "TaskView::exportcsvFile()" << endl;
00227
00228 CSVExportDialog dialog( ReportCriteria::CSVTotalsExport, this );
00229 if ( current_item() && current_item()->isRoot() )
00230 dialog.enableTasksToExportQuestion();
00231
00232 if ( dialog.exec() ) {
00233 QString err = _storage->report( this, dialog.reportCriteria() );
00234 if ( !err.isEmpty() ) KMessageBox::error( this, err );
00235 }
00236 }
00237
00238 void TaskView::exportcsvHistory()
00239 {
00240 kdDebug(5970) << "TaskView::exportcsvHistory()" << endl;
00241
00242 CSVExportDialog dialog( ReportCriteria::CSVHistoryExport, this );
00243 if ( current_item() && current_item()->isRoot() )
00244 dialog.enableTasksToExportQuestion();
00245
00246 if ( dialog.exec() ) {
00247 QString err = _storage->report( this, dialog.reportCriteria() );
00248 if ( !err.isEmpty() ) KMessageBox::error( this, err );
00249 }
00250 }
00251
00252 void TaskView::scheduleSave()
00253 {
00254 _manualSaveTimer->start( 10, true );
00255 }
00256
00257 Preferences* TaskView::preferences() { return _preferences; }
00258
00259 void TaskView::save()
00260 {
00261
00262
00263
00264
00265
00266 #if 0
00267
00268
00269
00270
00271
00272
00273
00274 for (unsigned int i = 0; i < activeTasks.count(); i++)
00275 {
00276 activeTasks.at(i)->setRunning(false, _storage);
00277 activeTasks.at(i)->setRunning(true, _storage);
00278 }
00279
00280
00281 if (activeTasks.count() == 0)
00282 #endif
00283 {
00284 _storage->save(this);
00285 }
00286 }
00287
00288 void TaskView::startCurrentTimer()
00289 {
00290 startTimerFor( current_item() );
00291 }
00292
00293 long TaskView::count()
00294 {
00295 long n = 0;
00296 for (Task* t = item_at_index(n); t; t=item_at_index(++n));
00297 return n;
00298 }
00299
00300 void TaskView::startTimerFor(Task* task)
00301 {
00302 if (task != 0 && activeTasks.findRef(task) == -1) {
00303 _idleTimeDetector->startIdleDetection();
00304 task->setRunning(true, _storage);
00305 activeTasks.append(task);
00306 emit updateButtons();
00307 if ( activeTasks.count() == 1 )
00308 emit timersActive();
00309
00310 emit tasksChanged( activeTasks);
00311 }
00312 }
00313
00314 void TaskView::stopAllTimers()
00315 {
00316 for ( unsigned int i = 0; i < activeTasks.count(); i++ )
00317 activeTasks.at(i)->setRunning(false, _storage);
00318
00319 _idleTimeDetector->stopIdleDetection();
00320 activeTasks.clear();
00321 emit updateButtons();
00322 emit timersInactive();
00323 emit tasksChanged( activeTasks);
00324 }
00325
00326 void TaskView::startNewSession()
00327 {
00328 QListViewItemIterator item( first_child());
00329 for ( ; item.current(); ++item ) {
00330 Task * task = (Task *) item.current();
00331 task->startNewSession();
00332 }
00333 }
00334
00335 void TaskView::resetTimeForAllTasks()
00336 {
00337 QListViewItemIterator item( first_child());
00338 for ( ; item.current(); ++item ) {
00339 Task * task = (Task *) item.current();
00340 task->resetTimes();
00341 }
00342 }
00343
00344 void TaskView::stopTimerFor(Task* task)
00345 {
00346 if ( task != 0 && activeTasks.findRef(task) != -1 ) {
00347 activeTasks.removeRef(task);
00348 task->setRunning(false, _storage);
00349 if ( activeTasks.count() == 0 ) {
00350 _idleTimeDetector->stopIdleDetection();
00351 emit timersInactive();
00352 }
00353 emit updateButtons();
00354 }
00355 emit tasksChanged( activeTasks);
00356 }
00357
00358 void TaskView::stopCurrentTimer()
00359 {
00360 stopTimerFor( current_item());
00361 }
00362
00363
00364 void TaskView::changeTimer(QListViewItem *)
00365 {
00366 if ( isReadOnly() )
00367 return;
00368 Task *task = current_item();
00369
00370 if ( task != 0 && activeTasks.findRef(task) == -1 )
00371 {
00372
00373 for (unsigned int i=0; i<activeTasks.count();i++)
00374 (activeTasks.at(i))->setRunning(false, _storage);
00375 activeTasks.clear();
00376
00377
00378 startCurrentTimer();
00379 }
00380 else stopCurrentTimer();
00381 }
00382
00383 void TaskView::minuteUpdate()
00384 {
00385 addTimeToActiveTasks(1, false);
00386 }
00387
00388 void TaskView::addTimeToActiveTasks(int minutes, bool save_data)
00389 {
00390 for( unsigned int i = 0; i < activeTasks.count(); i++ )
00391 activeTasks.at(i)->changeTime(minutes, ( save_data ? _storage : 0 ) );
00392 }
00393
00394 void TaskView::newTask()
00395 {
00396 newTask(i18n("New Task"), 0);
00397 }
00398
00399 void TaskView::newTask(QString caption, Task *parent)
00400 {
00401 EditTaskDialog *dialog = new EditTaskDialog(caption, false);
00402 long total, totalDiff, session, sessionDiff;
00403 DesktopList desktopList;
00404 Task *task;
00405
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
00413 total = totalDiff = session = sessionDiff = 0;
00414 dialog->status( &total, &totalDiff, &session, &sessionDiff, &desktopList);
00415
00416
00417
00418 if (desktopList.size() == (unsigned int)_desktopTracker->desktopCount())
00419 desktopList.clear();
00420
00421 if (parent == 0)
00422 {
00423 task = new Task(taskName, total, session, desktopList, this);
00424 task->setUid(_storage->addTask(task, 0));
00425 }
00426 else
00427 {
00428 task = new Task(taskName, total, session, desktopList, parent);
00429 task->setUid(_storage->addTask(task, parent));
00430 }
00431
00432 if (!task->uid().isNull())
00433 {
00434 _desktopTracker->registerForDesktops( task, desktopList );
00435
00436 setCurrentItem( task );
00437 setSelected( task, true );
00438
00439 save();
00440 }
00441 else
00442 {
00443 delete task;
00444 KMessageBox::error(0,i18n(
00445 "Error storing new task. Your changes were not saved."));
00446 }
00447 }
00448
00449 delete dialog;
00450 }
00451
00452 void TaskView::newSubTask()
00453 {
00454 Task* task = current_item();
00455 if(!task)
00456 return;
00457 newTask(i18n("New Sub Task"), task);
00458 task->setOpen(true);
00459 setRootIsDecorated(true);
00460 }
00461
00462 void TaskView::editTask()
00463 {
00464 Task *task = current_item();
00465 if (!task)
00466 return;
00467
00468 DesktopList desktopList = task->getDesktops();
00469 EditTaskDialog *dialog = new EditTaskDialog(i18n("Edit Task"), true, &desktopList);
00470 dialog->setTask( task->name(),
00471 task->time(),
00472 task->sessionTime() );
00473 int result = dialog->exec();
00474 if (result == QDialog::Accepted) {
00475 QString taskName = i18n("Unnamed Task");
00476 if (!dialog->taskName().isEmpty()) {
00477 taskName = dialog->taskName();
00478 }
00479
00480 task->setName(taskName, _storage);
00481
00482
00483 long total, session, totalDiff, sessionDiff;
00484 total = totalDiff = session = sessionDiff = 0;
00485 DesktopList desktopList;
00486 dialog->status( &total, &totalDiff, &session, &sessionDiff, &desktopList);
00487
00488 if( totalDiff != 0 || sessionDiff != 0)
00489 task->changeTimes( sessionDiff, totalDiff, _storage );
00490
00491
00492
00493 if (desktopList.size() == (unsigned int)_desktopTracker->desktopCount())
00494 desktopList.clear();
00495
00496 task->setDesktopList(desktopList);
00497
00498 _desktopTracker->registerForDesktops( task, desktopList );
00499
00500 emit updateButtons();
00501 }
00502 delete dialog;
00503 }
00504
00505
00506
00507
00508
00509
00510
00511
00512
00513
00514
00515
00516
00517
00518
00519
00520 void TaskView::deleteTask(bool markingascomplete)
00521 {
00522 Task *task = current_item();
00523 if (task == 0) {
00524 KMessageBox::information(0,i18n("No task selected."));
00525 return;
00526 }
00527
00528 int response = KMessageBox::Yes;
00529 if (!markingascomplete && _preferences->promptDelete()) {
00530 if (task->childCount() == 0) {
00531 response = KMessageBox::warningYesNo( 0,
00532 i18n( "Are you sure you want to delete "
00533 "the task named\n\"%1\" and its entire history?")
00534 .arg(task->name()),
00535 i18n( "Deleting Task"));
00536 }
00537 else {
00538 response = KMessageBox::warningYesNo( 0,
00539 i18n( "Are you sure you want to delete the task named"
00540 "\n\"%1\" and its entire history?\n"
00541 "NOTE: all its subtasks and their history will also "
00542 "be deleted.").arg(task->name()),
00543 i18n( "Deleting Task"));
00544 }
00545 }
00546
00547 if (response == KMessageBox::Yes)
00548 {
00549 if (markingascomplete)
00550 {
00551 task->setPercentComplete(100, _storage);
00552 save();
00553
00554
00555
00556
00557
00558
00559 }
00560 else
00561 {
00562 task->remove(activeTasks, _storage);
00563 task->removeFromView();
00564 save();
00565 }
00566
00567
00568 bool anyChilds = false;
00569 for(Task* child = first_child();
00570 child;
00571 child = child->nextSibling()) {
00572 if (child->childCount() != 0) {
00573 anyChilds = true;
00574 break;
00575 }
00576 }
00577 if (!anyChilds) {
00578 setRootIsDecorated(false);
00579 }
00580
00581
00582 if (activeTasks.count() == 0) {
00583 _idleTimeDetector->stopIdleDetection();
00584 emit timersInactive();
00585 }
00586
00587 emit tasksChanged( activeTasks );
00588 }
00589 }
00590
00591 void TaskView::extractTime(int minutes)
00592 {
00593 addTimeToActiveTasks(-minutes);
00594 }
00595
00596 void TaskView::autoSaveChanged(bool on)
00597 {
00598 if (on) _autoSaveTimer->start(_preferences->autoSavePeriod()*1000*secsPerMinute);
00599 else if (_autoSaveTimer->isActive()) _autoSaveTimer->stop();
00600 }
00601
00602 void TaskView::autoSavePeriodChanged(int )
00603 {
00604 autoSaveChanged(_preferences->autoSave());
00605 }
00606
00607 void TaskView::adaptColumns()
00608 {
00609
00610
00611
00612
00613
00614
00615
00616
00617
00618 for( int x=1; x <= 4; x++) {
00619
00620 if( _preferences->displayColumn(x-1)
00621 && previousColumnWidths[x-1] != HIDDEN_COLUMN )
00622 {
00623 setColumnWidth( x, previousColumnWidths[x-1] );
00624 previousColumnWidths[x-1] = HIDDEN_COLUMN;
00625 setColumnWidthMode( x, QListView::Maximum );
00626 }
00627
00628 else
00629 if( ! _preferences->displayColumn(x-1)
00630 && previousColumnWidths[x-1] == HIDDEN_COLUMN )
00631 {
00632 setColumnWidthMode( x, QListView::Manual );
00633
00634 previousColumnWidths[x-1] = columnWidth( x );
00635 setColumnWidth( x, 0 );
00636 }
00637 }
00638 }
00639
00640 void TaskView::deletingTask(Task* deletedTask)
00641 {
00642 DesktopList desktopList;
00643
00644 _desktopTracker->registerForDesktops( deletedTask, desktopList );
00645 activeTasks.removeRef( deletedTask );
00646
00647 emit tasksChanged( activeTasks);
00648 }
00649
00650 void TaskView::iCalFileChanged(QString file)
00651 {
00652 kdDebug(5970) << "TaskView:iCalFileChanged: " << file << endl;
00653 stopAllTimers();
00654 _storage->save(this);
00655 load();
00656 }
00657
00658 QValueList<HistoryEvent> TaskView::getHistory(const QDate& from,
00659 const QDate& to) const
00660 {
00661 return _storage->getHistory(from, to);
00662 }
00663
00664 void TaskView::markTaskAsComplete()
00665 {
00666 if (current_item())
00667 kdDebug(5970) << "TaskView::markTaskAsComplete: "
00668 << current_item()->uid() << endl;
00669 else
00670 kdDebug(5970) << "TaskView::markTaskAsComplete: null current_item()" << endl;
00671
00672 bool markingascomplete = true;
00673 deleteTask(markingascomplete);
00674 }
00675
00676 void TaskView::clipTotals()
00677 {
00678 TimeKard *t = new TimeKard();
00679 if (current_item() && current_item()->isRoot())
00680 {
00681 int response = KMessageBox::questionYesNo( 0,
00682 i18n("Copy totals for just this task and its subtasks, or copy totals for all tasks?"),
00683 i18n("Copy Totals to Clipboard"),
00684 i18n("Copy This Task"), i18n("Copy All Tasks") );
00685 if (response == KMessageBox::Yes)
00686 {
00687 KApplication::clipboard()->setText(t->totalsAsText(this));
00688 }
00689 else
00690 {
00691 KApplication::clipboard()->setText(t->totalsAsText(this, false));
00692 }
00693 }
00694 else
00695 {
00696 KApplication::clipboard()->setText(t->totalsAsText(this));
00697 }
00698 }
00699
00700 void TaskView::clipHistory()
00701 {
00702
00703 PrintDialog *dialog = new PrintDialog();
00704 if (dialog->exec()== QDialog::Accepted)
00705 {
00706 TimeKard *t = new TimeKard();
00707 KApplication::clipboard()->
00708 setText(t->historyAsText(this, dialog->from(), dialog->to()));
00709 }
00710 }
00711
00712 #include "taskview.moc"