23 #include <sys/types.h>
33 #include <tqdatetime.h>
35 #include <tqstringlist.h>
37 #include "incidence.h"
38 #include "tdeapplication.h"
40 #include <tdeemailsettings.h>
41 #include <tdelocale.h>
42 #include <tdemessagebox.h>
43 #include <kprogress.h>
44 #include <tdetempfile.h>
45 #include <resourcecalendar.h>
46 #include <resourcelocal.h>
47 #include <resourceremote.h>
48 #include <kpimprefs.h>
51 #include <karmutility.h>
52 #include <tdeio/netaccess.h>
61 #include "karmstorage.h"
62 #include "preferences.h"
64 #include "reportcriteria.h"
78 KarmStorage::KarmStorage()
83 TQString KarmStorage::load (
TaskView* view,
const Preferences* preferences, TQString fileName )
96 KEMailSettings settings;
97 if ( fileName.isEmpty() ) fileName = preferences->iCalFile();
100 if ( fileName == _icalfile )
return err;
106 if ( ! remoteResource( _icalfile ) )
110 TQFile::encodeName( fileName ),
111 O_CREAT|O_EXCL|O_WRONLY,
112 S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH
114 if (handle != -1) close(handle);
121 _icalfile = fileName;
123 KCal::ResourceCached *resource;
124 if ( remoteResource( _icalfile ) )
126 KURL url( _icalfile );
127 resource =
new KCal::ResourceRemote( url, url );
131 resource =
new KCal::ResourceLocal( _icalfile );
133 _calendar = resource;
135 TQObject::connect (_calendar, TQ_SIGNAL(resourceChanged(ResourceCalendar *)),
136 view, TQ_SLOT(iCalFileModified(ResourceCalendar *)));
137 _calendar->setTimeZoneId( KPimPrefs::timezone() );
138 _calendar->setResourceName( TQString::fromLatin1(
"KArm") );
143 KCal::Person owner = resource->getOwner();
144 if ( owner.isEmpty() )
146 resource->setOwner( KCal::Person(
147 settings.getSetting( KEMailSettings::RealName ),
148 settings.getSetting( KEMailSettings::EmailAddress ) ) );
154 KCal::Todo::List todoList;
155 KCal::Todo::List::ConstIterator todo;
160 todoList = _calendar->rawTodos();
161 kdDebug(5970) <<
"KarmStorage::load "
162 <<
"rawTodo count (includes completed todos) ="
163 << todoList.count() << endl;
164 for( todo = todoList.begin(); todo != todoList.end(); ++todo )
184 map.insert( (*todo)->uid(), task );
185 view->setRootIsDecorated(
true);
190 for( todo = todoList.begin(); todo != todoList.end(); ++todo )
192 Task* task = map.find( (*todo)->uid() );
195 if ( (*todo)->relatedTo() )
197 Task* newParent = map.find( (*todo)->relatedToUid() );
201 err = i18n(
"Error loading \"%1\": could not find parent (uid=%2)")
203 .arg((*todo)->relatedToUid());
205 if (!err) task->
move( newParent);
209 kdDebug(5970) <<
"KarmStorage::load - loaded " << view->
count()
210 <<
" tasks from " << _icalfile << endl;
216 TQString KarmStorage::icalfile()
218 kdDebug(5970) <<
"Entering KarmStorage::icalfile" << endl;
222 TQString KarmStorage::buildTaskView(KCal::ResourceCalendar *rc,
TaskView *view)
226 KCal::Todo::List todoList;
227 KCal::Todo::List::ConstIterator todo;
229 vector<TQString> runningTasks;
230 vector<TQDateTime> startTimes;
233 for (
int i=0; i<view->
count(); i++)
249 todoList = rc->rawTodos();
250 for( todo = todoList.begin(); todo != todoList.end(); ++todo )
253 map.insert( (*todo)->uid(), task );
254 view->setRootIsDecorated(
true);
259 for( todo = todoList.begin(); todo != todoList.end(); ++todo )
261 Task* task = map.find( (*todo)->uid() );
264 if ( (*todo)->relatedTo() )
266 Task* newParent = map.find( (*todo)->relatedToUid() );
270 err = i18n(
"Error loading \"%1\": could not find parent (uid=%2)")
272 .arg((*todo)->relatedToUid());
274 if (!err) task->
move( newParent);
280 for (
int i=0; i<view->
count(); i++)
282 for (
unsigned int n=0; n<runningTasks.size(); n++)
296 void KarmStorage::closeStorage(
TaskView* view)
308 TQString KarmStorage::save(
TaskView* taskview)
310 kdDebug(5970) <<
"entering KarmStorage::save" << endl;
311 TQString err=TQString();
313 TQPtrStack< KCal::Todo > parents;
315 for (
Task* task=taskview->
first_child(); task; task = task->nextSibling())
317 err=writeTaskAsTodo(task, 1, parents );
320 if ( !saveCalendar() )
322 err=
"Could not save";
328 <<
"KarmStorage::save : wrote "
329 << taskview->
count() <<
" tasks to " << _icalfile << endl;
333 kdWarning(5970) <<
"KarmStorage::save : " << err << endl;
339 TQString KarmStorage::writeTaskAsTodo(
Task* task,
const int level,
340 TQPtrStack< KCal::Todo >& parents )
345 todo = _calendar->todo(task->
uid());
348 kdDebug(5970) <<
"Could not get todo from calendar" << endl;
349 return "Could not get todo from calendar";
352 if ( !parents.isEmpty() ) todo->setRelatedTo( parents.top() );
353 parents.push( todo );
356 nextTask = nextTask->nextSibling() )
358 err = writeTaskAsTodo(nextTask, level+1, parents );
367 KCal::Todo::List todoList;
369 todoList = _calendar->rawTodos();
370 return todoList.empty();
375 if ( !_icalfile.isNull() )
return preferences->iCalFile() != _icalfile;
385 const TQString& filename)
390 <<
"KarmStorage::loadFromFlatFile: " << filename << endl;
394 err = i18n(
"File \"%1\" not found.").arg(filename);
398 if( !f.open( IO_ReadOnly ) )
399 err = i18n(
"Could not open \"%1\".").arg(filename);
407 TQPtrStack<Task> stack;
410 TQTextStream stream(&f);
412 while( !stream.atEnd() ) {
417 line = stream.readLine();
418 kdDebug(5970) <<
"DEBUG: line: " << line <<
"\n";
426 DesktopList desktopList;
427 if (!parseLine(line, &minutes, &name, &level, &desktopList))
430 unsigned int stackLevel = stack.count();
431 for (
unsigned int i = level; i<=stackLevel ; i++) {
436 kdDebug(5970) <<
"KarmStorage::loadFromFlatFile - toplevel task: "
437 << name <<
" min: " << minutes <<
"\n";
438 task =
new Task(name, minutes, 0, desktopList, taskview);
439 task->
setUid(addTask(task, 0));
442 Task *parent = stack.top();
443 kdDebug(5970) <<
"KarmStorage::loadFromFlatFile - task: " << name
444 <<
" min: " << minutes <<
" parent" << parent->
name() <<
"\n";
445 task =
new Task(name, minutes, 0, desktopList, parent);
447 task->
setUid(addTask(task, parent));
451 taskview->setRootIsDecorated(
true);
452 parent->setOpen(
true);
454 if (!task->
uid().isNull())
468 const TQString& filename)
470 TQString err = loadFromFlatFile(taskview, filename);
474 task = task->nextSibling())
476 adjustFromLegacyFileFormat(task);
482 bool KarmStorage::parseLine(TQString line,
long *time, TQString *name,
483 int *level, DesktopList* desktopList)
485 if (line.find(
'#') == 0) {
490 int index = line.find(
'\t');
496 TQString levelStr = line.left(index);
497 TQString rest = line.remove(0,index+1);
499 index = rest.find(
'\t');
505 TQString timeStr = rest.left(index);
506 rest = rest.remove(0,index+1);
510 index = rest.find(
'\t');
512 *name = rest.left(index);
513 TQString deskLine = rest.remove(0,index+1);
519 int commaIdx = deskLine.find(
',');
520 while (commaIdx >= 0) {
521 ds = deskLine.left(commaIdx);
526 desktopList->push_back(d);
527 deskLine.remove(0,commaIdx+1);
528 commaIdx = deskLine.find(
',');
531 d = deskLine.toInt(&ok);
536 desktopList->push_back(d);
539 *name = rest.remove(0,index+1);
542 *time = timeStr.toLong(&ok);
548 *level = levelStr.toInt(&ok);
557 void KarmStorage::adjustFromLegacyFileFormat(
Task* task)
560 if ( task->parent() )
561 task->parent()->
changeTimes(-task->sessionTime(), -task->time());
568 subtask = subtask->nextSibling() )
569 adjustFromLegacyFileFormat(subtask);
575 TQString KarmStorage::exportcsvFile(
TaskView *taskview,
579 TQString dquote = rc.
quote;
580 TQString double_dquote = dquote + dquote;
581 bool to_quote =
true;
588 <<
"KarmStorage::exportcsvFile: " << rc.
url << endl;
590 TQString title = i18n(
"Export Progress");
591 KProgressDialog dialog( taskview, 0, title );
592 dialog.setAutoClose(
true );
593 dialog.setAllowCancel(
true );
594 dialog.progressBar()->setTotalSteps( 2 * taskview->
count() );
597 int width = taskview->fontMetrics().width(title) * 3;
599 dialogsize.setWidth(width);
600 dialog.setInitialSize( dialogsize,
true );
602 if ( taskview->
count() > 1 ) dialog.show();
608 while ( tasknr < taskview->count() && !dialog.wasCancelled() )
610 dialog.progressBar()->advance( 1 );
611 if ( tasknr % 15 == 0 ) kapp->processEvents();
619 while ( tasknr < taskview->count() && !dialog.wasCancelled() )
622 dialog.progressBar()->advance( 1 );
623 if ( tasknr % 15 == 0 ) kapp->processEvents();
626 for (
int i=0; i < task->depth(); ++i ) retval += delim;
643 retval += task->
name().replace( dquote, double_dquote );
649 for (
int i = 0; i < maxdepth - task->depth(); ++i ) retval += delim;
651 retval += delim + formatTime( task->sessionTime(),
653 + delim + formatTime( task->time(),
655 + delim + formatTime( task->totalSessionTime(),
657 + delim + formatTime( task->totalTime(),
664 if ((rc.
url.isLocalFile()) || (!rc.
url.url().contains(
"/")))
666 TQString filename=rc.
url.path();
667 if (filename.isEmpty()) filename=rc.
url.url();
668 TQFile f( filename );
669 if( !f.open( IO_WriteOnly ) ) {
670 err = i18n(
"Could not open \"%1\"." ).arg( filename );
674 TQTextStream stream(&f);
683 if ( tmpFile.status() != 0 ) err = TQString::fromLatin1(
"Unable to get temporary file" );
686 TQTextStream *stream=tmpFile.textStream();
689 if (!TDEIO::NetAccess::upload( tmpFile.name(), rc.
url, 0 )) err=TQString::fromLatin1(
"Could not upload");
709 todo =
new KCal::Todo();
710 if ( _calendar->addTodo( todo ) )
714 todo->setRelatedTo(_calendar->todo(parent->
uid()));
731 KCal::Event::List eventList = _calendar->rawEvents();
732 for(KCal::Event::List::iterator i = eventList.begin();
733 i != eventList.end();
740 if ( (*i)->relatedToUid() == task->
uid()
741 || ( (*i)->relatedTo()
742 && (*i)->relatedTo()->uid() == task->
uid()))
744 _calendar->deleteEvent(*i);
749 KCal::Todo *todo = _calendar->todo(task->
uid());
750 _calendar->deleteTodo(todo);
763 todo = _calendar->todo(task->
uid());
767 TQString s = comment;
772 todo->setDescription(task->
comment());
777 long KarmStorage::printTaskHistory (
779 const TQMap<TQString,long> &taskdaytotals,
780 TQMap<TQString,long> &daytotals,
784 vector <TQString> &matrix,
788 long ownline=linenr++;
790 vector <TQString> cell;
793 TQString dquote = rc.
quote;
794 TQString double_dquote = dquote + dquote;
795 bool to_quote =
true;
797 const TQString cr = TQString::fromLatin1(
"\n");
799 TQString daytaskkey, daykey;
803 if ( !task )
return 0;
810 daykey = day.toString(TQString::fromLatin1(
"yyyyMMdd"));
811 daytaskkey = TQString::fromLatin1(
"%1_%2")
815 if (taskdaytotals.contains(daytaskkey))
817 cell.push_back(TQString::fromLatin1(
"%1")
818 .arg(formatTime(taskdaytotals[daytaskkey]/60, rc.
decimalMinutes)));
819 sum += taskdaytotals[daytaskkey];
821 if (daytotals.contains(daykey))
822 daytotals.replace(daykey, daytotals[daykey]+taskdaytotals[daytaskkey]);
824 daytotals.insert(daykey, taskdaytotals[daytaskkey]);
826 cell.push_back(delim);
828 day = day.addDays(1);
832 cell.push_back(TQString::fromLatin1(
"%1").arg(formatTime(sum/60, rc.
decimalMinutes)));
835 cell.push_back(delim);
836 colrectot = cell.size();
837 cell.push_back(
"???");
838 cell.push_back(delim);
841 for (
int i = level + 1; i > 0; i-- ) cell.push_back(delim);
850 if ( to_quote) cell.push_back(dquote);
854 cell.push_back(task->
name().replace( dquote, double_dquote ));
856 if ( to_quote) cell.push_back(dquote);
863 subTask = subTask->nextSibling())
865 add += printTaskHistory( subTask, taskdaytotals, daytotals, from, to , level+1, matrix,
868 cell[colrectot]=(TQString::fromLatin1(
"%1").arg(formatTime((add+sum)/60, rc.
decimalMinutes )));
869 for (
unsigned int i=0; i < cell.size(); i++) matrix[ownline]+=cell[i];
876 if ( rc.
reportType == ReportCriteria::CSVHistoryExport )
877 err = exportcsvHistory( taskview, rc.
from, rc.
to, rc );
878 else if ( rc.
reportType == ReportCriteria::CSVTotalsExport )
879 err = exportcsvFile( taskview, rc );
887 TQString KarmStorage::exportcsvHistory (
TaskView *taskview,
893 const TQString cr = TQString::fromLatin1(
"\n");
898 TQString taskhdr, totalhdr;
902 TQValueList<HistoryEvent> events;
903 TQValueList<HistoryEvent>::iterator event;
904 TQMap<TQString, long> taskdaytotals;
905 TQMap<TQString, long> daytotals;
906 TQString daytaskkey, daykey;
913 err = TQString::fromLatin1 (
914 "'to' has to be a date later than or equal to 'from'.");
918 retval += i18n(
"Task History\n");
919 retval += i18n(
"From %1 to %2")
920 .arg(TDEGlobal::locale()->formatDate(from))
921 .arg(TDEGlobal::locale()->formatDate(to));
923 retval += i18n(
"Printed on: %1")
924 .arg(TDEGlobal::locale()->formatDateTime(TQDateTime::currentDateTime()));
929 taskdaytotals.clear();
937 for (event = events.begin(); event != events.end(); ++event)
939 daykey = (*event).start().date().toString(TQString::fromLatin1(
"yyyyMMdd"));
940 daytaskkey = TQString(TQString::fromLatin1(
"%1_%2"))
942 .arg((*event).todoUid());
944 if (taskdaytotals.contains(daytaskkey))
945 taskdaytotals.replace(daytaskkey,
946 taskdaytotals[daytaskkey] + (*event).duration());
948 taskdaytotals.insert(daytaskkey, (*event).duration());
953 while ( dayheading <= to )
956 retval += dayheading.toString(TQString::fromLatin1(
"yyyy-MM-dd"));
958 dayheading=dayheading.addDays(1);
960 retval += i18n(
"Sum") + delim + i18n(
"Total Sum") + delim + i18n(
"Task Hierarchy");
965 vector <TQString> matrix;
967 for (
int i=0; i<=taskview->
count()+1; i++) matrix.push_back(
"");
970 retval += i18n(
" No hours logged.");
977 task; task= task->nextSibling() )
979 printTaskHistory( task, taskdaytotals, daytotals, from, to, 0,
985 printTaskHistory( taskview->
current_item(), taskdaytotals, daytotals,
986 from, to, 0, matrix, rc );
988 for (
unsigned int i=0; i<matrix.size(); i++) retval+=matrix[i];
996 daykey = day.toString(TQString::fromLatin1(
"yyyyMMdd"));
998 if (daytotals.contains(daykey))
1000 retval += TQString::fromLatin1(
"%1")
1002 sum += daytotals[daykey];
1005 day = day.addDays(1);
1008 retval += TQString::fromLatin1(
"%1%2%3%4")
1010 .arg( delim ).arg( delim )
1011 .arg( i18n(
"Total" ) );
1018 if ((rc.
url.isLocalFile()) || (!rc.
url.url().contains(
"/")))
1020 TQString filename=rc.
url.path();
1021 if (filename.isEmpty()) filename=rc.
url.url();
1022 TQFile f( filename );
1023 if( !f.open( IO_WriteOnly ) ) {
1024 err = i18n(
"Could not open \"%1\"." ).arg( filename );
1028 TQTextStream stream(&f);
1037 if ( tmpFile.status() != 0 )
1039 err = TQString::fromLatin1(
"Unable to get temporary file" );
1043 TQTextStream *stream=tmpFile.textStream();
1046 if (!TDEIO::NetAccess::upload( tmpFile.name(), rc.
url, 0 )) err=TQString::fromLatin1(
"Could not upload");
1054 kdDebug(5970) <<
"Entering KarmStorage::stopTimer" << endl;
1055 long delta = task->
startTime().secsTo(when);
1056 changeTime(task, delta);
1060 const TQDateTime& startDateTime,
1061 const long durationInSeconds)
1067 e = baseEvent( task );
1068 e->setDtStart( startDateTime );
1069 e->setDtEnd( startDateTime.addSecs( durationInSeconds ) );
1072 e->setCustomProperty( kapp->instanceName(),
1073 TQCString(
"duration"),
1074 TQString::number(durationInSeconds));
1076 return _calendar->addEvent(e);
1081 kdDebug(5970) <<
"Entering KarmStorage::changeTime ( " << task->
name() <<
"," << deltaSeconds <<
" )" << endl;
1089 e = baseEvent(task);
1094 if ( deltaSeconds > 0 ) end = task->
startTime().addSecs(deltaSeconds);
1098 e->setCustomProperty( kapp->instanceName(),
1099 TQCString(
"duration"),
1100 TQString::number(deltaSeconds));
1102 _calendar->addEvent(e);
1116 KCal::Event* KarmStorage::baseEvent(
const Task * task)
1119 TQStringList categories;
1121 e =
new KCal::Event;
1122 e->setSummary(task->
name());
1125 e->setRelatedTo(_calendar->todo(task->
uid()));
1128 assert(e->relatedTo()->uid() == task->
uid());
1131 e->setFloats(
false);
1135 categories.append(i18n(
"KArm"));
1136 e->setCategories(categories);
1142 TQDateTime start, TQDateTime stop, TQString todoUid)
1146 _duration = duration;
1156 TQValueList<HistoryEvent> retval;
1157 TQStringList processed;
1158 KCal::Event::List events;
1159 KCal::Event::List::iterator event;
1162 for(TQDate d = from; d <= to; d = d.addDays(1))
1164 events = _calendar->rawEventsForDate( d );
1165 for (event = events.begin(); event != events.end(); ++event)
1169 if (! processed.contains( (*event)->uid()))
1176 processed.append( (*event)->uid());
1178 duration = (*event)->customProperty(kapp->instanceName(),
1179 TQCString(
"duration"));
1180 if ( ! duration.isNull() )
1182 if ( (*event)->relatedTo()
1183 && ! (*event)->relatedTo()->uid().isEmpty() )
1187 (*event)->summary(),
1189 (*event)->dtStart(),
1191 (*event)->relatedTo()->uid()
1198 kdDebug(5970) <<
"KarmStorage::getHistory(): "
1199 <<
"The event " << (*event)->uid()
1200 <<
" is not related to a todo. Dropped." << endl;
1209 bool KarmStorage::remoteResource(
const TQString& file )
const
1211 TQString f = file.lower();
1212 bool rval = f.startsWith(
"http://" ) || f.startsWith(
"ftp://" );
1214 kdDebug(5970) <<
"KarmStorage::remoteResource( " << file <<
" ) returns " << rval << endl;
1218 bool KarmStorage::saveCalendar()
1220 kdDebug(5970) <<
"KarmStorage::saveCalendar" << endl;
1223 Event::List evl=_calendar->rawEvents();
1224 kdDebug(5970) <<
"summary - dtStart - dtEnd" << endl;
1225 for (
unsigned int i=0; i<evl.count(); i++)
1227 kdDebug() << evl[i]->summary() << evl[i]->dtStart() << evl[i]->dtEnd() << endl;
1230 TDEABC::Lock *lock = _calendar->lock();
1231 if ( !lock || !lock->lock() )
1234 if ( _calendar && _calendar->save() ) {