00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include <sys/types.h>
00024 #include <sys/stat.h>
00025 #include <fcntl.h>
00026 #include <unistd.h>
00027
00028 #include <cassert>
00029
00030 #include <qfile.h>
00031 #include <qdict.h>
00032 #include <qdatetime.h>
00033 #include <qstringlist.h>
00034
00035 #include "kapplication.h"
00036 #include <kdebug.h>
00037 #include <kemailsettings.h>
00038 #include <klocale.h>
00039 #include <taskview.h>
00040
00041 #include "incidence.h"
00042
00043
00044
00045
00046
00047
00048 #include "karmstorage.h"
00049 #include "preferences.h"
00050 #include "task.h"
00051
00052
00053 KarmStorage *KarmStorage::_instance = 0;
00054
00055 KarmStorage *KarmStorage::instance()
00056 {
00057 if (_instance == 0) {
00058 _instance = new KarmStorage();
00059 }
00060 return _instance;
00061 }
00062
00063 KarmStorage::KarmStorage() { }
00064
00065 QString KarmStorage::load(TaskView* view, const Preferences* preferences)
00066 {
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077 QString err;
00078 KEMailSettings settings;
00079 int handle;
00080
00081
00082 if (preferences->iCalFile() == _icalfile)
00083 return err;
00084
00085
00086
00087
00088
00089 handle = open(QFile::encodeName(preferences->iCalFile()),
00090 O_CREAT|O_EXCL|O_WRONLY,
00091 S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);
00092
00093 if (handle != -1)
00094 {
00095 close(handle);
00096 }
00097
00098
00099 view->clear();
00100 _calendar.close();
00101
00102
00103 _icalfile = preferences->iCalFile();
00104 kdDebug() << "KarmStorage::load - loading " << _icalfile << endl;
00105 _calendar.setEmail( settings.getSetting( KEMailSettings::EmailAddress ) );
00106 _calendar.setOwner( settings.getSetting( KEMailSettings::RealName ) );
00107 if (!_calendar.load(_icalfile))
00108 err = i18n("Error loading file \"%1\"")
00109 .arg(_icalfile);
00110
00111
00112 if (!err)
00113 {
00114 KCal::Todo::List todoList;
00115 KCal::Todo::List::ConstIterator todo;
00116 QDict< Task > map;
00117
00118
00119
00120 todoList = _calendar.rawTodos();
00121 kdDebug() << "KarmStorage::load "
00122 << "rawTodo count (includes completed todos) ="
00123 << todoList.count() << endl;
00124 for( todo = todoList.begin(); todo != todoList.end(); ++todo )
00125 {
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143 Task* task = new Task(*todo, view);
00144 map.insert( (*todo)->uid(), task );
00145 view->setRootIsDecorated(true);
00146 if ((*todo)->isCompleted())
00147 {
00148 task->setEnabled(false);
00149 task->setOpen(false);
00150 }
00151 else
00152 task->setOpen(true);
00153
00154 }
00155
00156
00157 for( todo = todoList.begin(); todo != todoList.end(); ++todo )
00158 {
00159 Task* task = map.find( (*todo)->uid() );
00160
00161
00162 if ( (*todo)->relatedTo() )
00163 {
00164 Task* newParent = map.find( (*todo)->relatedToUid() );
00165
00166
00167 if ( !newParent )
00168 err = i18n("Error loading \"%1\": could not find parent (uid=%2)")
00169 .arg(task->name())
00170 .arg((*todo)->relatedToUid());
00171
00172 if (!err)
00173 task->move( newParent);
00174 }
00175 }
00176
00177 kdDebug() << "KarmStorage::load - loaded " << view->count()
00178 << " tasks from " << _icalfile << endl;
00179 }
00180
00181 return err;
00182 }
00183
00184 void KarmStorage::save(TaskView* taskview)
00185 {
00186 QPtrStack< KCal::Todo > parents;
00187
00188 for (Task* task=taskview->first_child(); task; task = task->nextSibling())
00189 {
00190 writeTaskAsTodo(task, 1, parents );
00191 }
00192
00193 _calendar.save(_icalfile);
00194
00195 kdDebug()
00196 << "KarmStorage::save : wrote "
00197 << taskview->count() << " tasks to " << _icalfile << endl;
00198 }
00199
00200 void KarmStorage::writeTaskAsTodo(Task* task, const int level,
00201 QPtrStack< KCal::Todo >& parents )
00202 {
00203
00204 KCal::Todo* todo;
00205
00206 todo = _calendar.todo(task->uid());
00207 task->asTodo(todo);
00208
00209 if ( !parents.isEmpty() )
00210 todo->setRelatedTo( parents.top() );
00211
00212 parents.push( todo );
00213
00214 for (Task* nextTask = task->firstChild(); nextTask;
00215 nextTask = nextTask->nextSibling() )
00216 {
00217 writeTaskAsTodo(nextTask, level+1, parents );
00218 }
00219
00220 parents.pop();
00221 }
00222
00223 bool KarmStorage::isEmpty()
00224 {
00225 KCal::Todo::List todoList;
00226
00227 todoList = _calendar.rawTodos();
00228 return todoList.empty();
00229 }
00230
00231 bool KarmStorage::isNewStorage(const Preferences* preferences) const
00232 {
00233 if (!_icalfile.isNull())
00234 return preferences->iCalFile() != _icalfile;
00235 else
00236 return false;
00237 }
00238
00239
00240
00241
00242
00243
00244 QString KarmStorage::loadFromFlatFile(TaskView* taskview,
00245 const QString& filename)
00246 {
00247 QString err;
00248
00249 kdDebug()
00250 << "KarmStorage::loadFromFlatFile: " << filename << endl;
00251
00252 QFile f(filename);
00253 if( !f.exists() )
00254 err = i18n("File \"%1\" not found.").arg(filename);
00255
00256 if (!err)
00257 {
00258 if( !f.open( IO_ReadOnly ) )
00259 err = i18n("Could not open \"%1\".").arg(filename);
00260 }
00261
00262 if (!err)
00263 {
00264
00265 QString line;
00266
00267 QPtrStack<Task> stack;
00268 Task *task;
00269
00270 QTextStream stream(&f);
00271
00272 while( !stream.atEnd() ) {
00273
00274
00275
00276
00277 line = stream.readLine();
00278 kdDebug() << "DEBUG: line: " << line << "\n";
00279
00280 if (line.isNull())
00281 break;
00282
00283 long minutes;
00284 int level;
00285 QString name;
00286 DesktopList desktopList;
00287 if (!parseLine(line, &minutes, &name, &level, &desktopList))
00288 continue;
00289
00290 unsigned int stackLevel = stack.count();
00291 for (unsigned int i = level; i<=stackLevel ; i++) {
00292 stack.pop();
00293 }
00294
00295 if (level == 1) {
00296 kdDebug() << "KarmStorage::loadFromFlatFile - toplevel task: "
00297 << name << " min: " << minutes << "\n";
00298 task = new Task(name, minutes, 0, desktopList, taskview);
00299 task->setUid(addTask(task, 0));
00300 }
00301 else {
00302 Task *parent = stack.top();
00303 kdDebug() << "KarmStorage::loadFromFlatFile - task: " << name
00304 << " min: " << minutes << " parent" << parent->name() << "\n";
00305 task = new Task(name, minutes, 0, desktopList, parent);
00306
00307 task->setUid(addTask(task, parent));
00308
00309
00310 parent->changeTimes(0, -minutes, false);
00311 taskview->setRootIsDecorated(true);
00312 parent->setOpen(true);
00313 }
00314 if (!task->uid().isNull())
00315 stack.push(task);
00316 else
00317 delete task;
00318 }
00319
00320 f.close();
00321
00322 }
00323
00324 return err;
00325 }
00326
00327 QString KarmStorage::loadFromFlatFileCumulative(TaskView* taskview,
00328 const QString& filename)
00329 {
00330 QString err = loadFromFlatFile(taskview, filename);
00331 if (!err)
00332 {
00333 for (Task* task = taskview->first_child(); task;
00334 task = task->nextSibling())
00335 {
00336 adjustFromLegacyFileFormat(task);
00337 }
00338 }
00339 return err;
00340 }
00341
00342 bool KarmStorage::parseLine(QString line, long *time, QString *name,
00343 int *level, DesktopList* desktopList)
00344 {
00345 if (line.find('#') == 0) {
00346
00347 return false;
00348 }
00349
00350 int index = line.find('\t');
00351 if (index == -1) {
00352
00353 return false;
00354 }
00355
00356 QString levelStr = line.left(index);
00357 QString rest = line.remove(0,index+1);
00358
00359 index = rest.find('\t');
00360 if (index == -1) {
00361
00362 return false;
00363 }
00364
00365 QString timeStr = rest.left(index);
00366 rest = rest.remove(0,index+1);
00367
00368 bool ok;
00369
00370 index = rest.find('\t');
00371 if (index >= 0) {
00372 *name = rest.left(index);
00373 QString deskLine = rest.remove(0,index+1);
00374
00375
00376
00377 QString ds;
00378 int d;
00379 int commaIdx = deskLine.find(',');
00380 while (commaIdx >= 0) {
00381 ds = deskLine.left(commaIdx);
00382 d = ds.toInt(&ok);
00383 if (!ok)
00384 return false;
00385
00386 desktopList->push_back(d);
00387 deskLine.remove(0,commaIdx+1);
00388 commaIdx = deskLine.find(',');
00389 }
00390
00391 d = deskLine.toInt(&ok);
00392
00393 if (!ok)
00394 return false;
00395
00396 desktopList->push_back(d);
00397 }
00398 else {
00399 *name = rest.remove(0,index+1);
00400 }
00401
00402 *time = timeStr.toLong(&ok);
00403
00404 if (!ok) {
00405
00406 return false;
00407 }
00408 *level = levelStr.toInt(&ok);
00409 if (!ok) {
00410
00411 return false;
00412 }
00413
00414 return true;
00415 }
00416
00417 void KarmStorage::adjustFromLegacyFileFormat(Task* task)
00418 {
00419
00420 if ( task->parent() )
00421 task->parent()->changeTimes(-task->sessionTime(), -task->time(), false);
00422
00423
00424
00425
00426
00427 for ( Task* subtask = task->firstChild(); subtask;
00428 subtask = subtask->nextSibling() )
00429 adjustFromLegacyFileFormat(subtask);
00430 }
00431
00432
00433
00434
00435
00436
00437
00438
00439
00440 QString KarmStorage::addTask(const Task* task, const Task* parent)
00441 {
00442 KCal::Todo* todo;
00443 QString uid;
00444
00445 todo = new KCal::Todo();
00446 if (_calendar.addTodo(todo))
00447 {
00448 task->asTodo(todo);
00449 if (parent)
00450 todo->setRelatedTo(_calendar.todo(parent->uid()));
00451 uid = todo->uid();
00452 }
00453
00454 return uid;
00455 }
00456
00457 bool KarmStorage::removeTask(Task* task)
00458 {
00459
00460
00461 KCal::Event::List eventList = _calendar.rawEvents();
00462 for(KCal::Event::List::iterator i = eventList.begin();
00463 i != eventList.end();
00464 ++i)
00465 {
00466
00467
00468
00469
00470 if ( (*i)->relatedToUid() == task->uid()
00471 || ( (*i)->relatedTo()
00472 && (*i)->relatedTo()->uid() == task->uid()))
00473 {
00474 _calendar.deleteEvent(*i);
00475 }
00476 }
00477
00478
00479 KCal::Todo *todo = _calendar.todo(task->uid());
00480 _calendar.deleteTodo(todo);
00481
00482
00483 _calendar.save(_icalfile);
00484
00485 return true;
00486 }
00487
00488 void KarmStorage::addComment(const Task* task, const QString& comment)
00489 {
00490 KCal::Todo* todo;
00491
00492 todo = _calendar.todo(task->uid());
00493
00494
00495
00496 QString s = comment;
00497
00498
00499
00500
00501
00502
00503 todo->setDescription(task->comment());
00504
00505 _calendar.save(_icalfile);
00506 }
00507
00508 void KarmStorage::stopTimer(const Task* task)
00509 {
00510 long delta = task->startTime().secsTo(QDateTime::currentDateTime());
00511 changeTime(task, delta);
00512 }
00513
00514 void KarmStorage::changeTime(const Task* task, const long deltaSeconds)
00515 {
00516
00517 KCal::Event* e;
00518 QDateTime end;
00519
00520 e = baseEvent(task);
00521
00522
00523
00524 end = task->startTime();
00525 if (deltaSeconds > 0)
00526 end = task->startTime().addSecs(deltaSeconds);
00527 e->setDtEnd(end);
00528
00529
00530 e->setCustomProperty( kapp->instanceName(),
00531 QCString("duration"),
00532 QString::number(deltaSeconds));
00533
00534 _calendar.addEvent(e);
00535
00536
00537
00538
00539
00540
00541
00542
00543
00544 task->taskView()->scheduleSave();
00545 }
00546
00547
00548 KCal::Event* KarmStorage::baseEvent(const Task * task)
00549 {
00550 KCal::Event* e;
00551 QStringList categories;
00552
00553 e = new KCal::Event;
00554 e->setSummary(task->name());
00555
00556
00557 e->setRelatedTo(_calendar.todo(task->uid()));
00558
00559
00560 assert(e->relatedTo()->uid() == task->uid());
00561
00562
00563 e->setFloats(false);
00564 e->setDtStart(task->startTime());
00565
00566
00567 categories.append(i18n("KArm"));
00568 e->setCategories(categories);
00569
00570 return e;
00571 }
00572
00573 HistoryEvent::HistoryEvent(QString uid, QString name, long duration,
00574 QDateTime start, QDateTime stop, QString todoUid)
00575 {
00576 _uid = uid;
00577 _name = name;
00578 _duration = duration;
00579 _start = start;
00580 _stop = stop;
00581 _todoUid = todoUid;
00582 }
00583
00584
00585 QValueList<HistoryEvent> KarmStorage::getHistory(const QDate& from,
00586 const QDate& to)
00587 {
00588 QValueList<HistoryEvent> retval;
00589 QStringList processed;
00590 KCal::Event::List events;
00591 KCal::Event::List::iterator event;
00592 QString duration;
00593
00594 for(QDate d = from; d <= to; d = d.addDays(1))
00595 {
00596 events = _calendar.rawEventsForDate(d);
00597 for (event = events.begin(); event != events.end(); ++event)
00598 {
00599
00600
00601 if (! processed.contains( (*event)->uid()))
00602 {
00603
00604
00605
00606
00607
00608 processed.append( (*event)->uid());
00609
00610 duration = (*event)->customProperty(kapp->instanceName(),
00611 QCString("duration"));
00612 if ( ! duration.isNull() )
00613 {
00614 if ( (*event)->relatedTo()
00615 && ! (*event)->relatedTo()->uid().isEmpty() )
00616 {
00617 retval.append(HistoryEvent(
00618 (*event)->uid(),
00619 (*event)->summary(),
00620 duration.toLong(),
00621 (*event)->dtStart(),
00622 (*event)->dtEnd(),
00623 (*event)->relatedTo()->uid()
00624 ));
00625 }
00626 else
00627
00628
00629
00630 kdDebug() << "KarmStorage::getHistory(): "
00631 << "The event " << (*event)->uid()
00632 << " is not related to a todo. Dropped." << endl;
00633 }
00634 }
00635 }
00636 }
00637
00638 return retval;
00639 }
00640
00641
00642
00643
00644
00645
00646
00647
00648
00649
00650
00651
00652
00653
00654
00655
00656
00657
00658
00659
00660
00661
00662
00663
00664
00665
00666
00667
00668
00669
00670
00671
00672
00673
00674
00675
00676
00677
00678
00679
00680
00681
00682
00683
00684
00685
00686
00687
00688
00689
00690
00691
00692
00693
00694
00695
00696