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() ) {
One start/stop event that has been logged.
HistoryEvent()
Needed to be used in a value list.
Singleton to store/retrieve KArm data to/from persistent storage.
bool isNewStorage(const Preferences *preferences) const
Check if iCalendar file name in the preferences has changed since the last call to load.
TQString loadFromFlatFile(TaskView *taskview, const TQString &filename)
Read tasks and their total times from a text file (legacy storage).
TQString loadFromFlatFileCumulative(TaskView *taskview, const TQString &filename)
Reads tasks and their total times from text file (legacy).
bool isEmpty()
Check if the iCalendar file currently loaded has any Todos in it.
bool removeTask(Task *task)
Remove this task from iCalendar file.
bool bookTime(const Task *task, const TQDateTime &startDateTime, long durationInSeconds)
Book time to a task.
TQString addTask(const Task *task, const Task *parent)
Add this task from iCalendar file.
void stopTimer(const Task *task, TQDateTime when=TQDateTime::currentDateTime())
Log the event that the timer has stopped for this task.
TQString report(TaskView *taskview, const ReportCriteria &rc)
Output a report based on contents of ReportCriteria.
TQValueList< HistoryEvent > getHistory(const TQDate &from, const TQDate &to)
Return a list of start/stop events for the given date range.
void changeTime(const Task *task, const long deltaSeconds)
Log the change in a task's time.
void addComment(const Task *task, const TQString &comment)
Log a new comment for this task.
Provide an interface to the configuration options for the program.
Stores entries from export dialog.
bool decimalMinutes
True if the durations should be output in decimal hours.
TQString quote
The quote to use for text fields when outputting comma-seperated reports.
TQDate to
For history reports, the upper bound of the date range to report on.
bool allTasks
True if the report should contain all tasks in Karm.
KURL url
For reports that write to a file, the filename to write to.
TQString delimiter
The delimiter to use when outputting comma-seperated value reports.
REPORTTYPE reportType
The type of report we are running.
TQDate from
For history reports, the lower bound of the date range to report on.
Container and interface for the tasks.
Preferences * preferences()
Return preferences user selected on settings dialog.
Task * first_child() const
Return the first item in the view, cast to a Task pointer.
long count()
Return the total number if items in the view.
void refresh()
Used to refresh (e.g.
Task * current_item() const
Return the current item in the view, cast to a Task pointer.
Task * item_at_index(int i)
Return the i'th item (zero-based), cast to a Task pointer.
void clearActiveTasks()
clears all active tasks.
void startTimerFor(Task *task, TQDateTime startTime=TQDateTime::currentDateTime())
starts timer for task.
void scheduleSave()
Schedule that we should save very soon.
TQValueList< HistoryEvent > getHistory(const TQDate &from, const TQDate &to) const
Return list of start/stop events for given date range.
A class representing a task.
TQDateTime lastStart()
delivers when the task was started last
void changeTimes(long minutesSession, long minutes, KarmStorage *storage=0)
Add minutes to time and session time, and write to storage.
TQString name() const
returns the name of this task.
Task * firstChild() const
return parent Task or null in case of TaskView.
TQDateTime startTime() const
Return time the task was started.
KCal::Todo * asTodo(KCal::Todo *calendar) const
Load the todo passed in with this tasks info.
void setPixmapProgress()
Sets an appropriate icon for this task based on its level of completion.
TQString uid() const
Return unique iCalendar Todo ID for this task.
TaskView * taskView() const
Return task view for this task.
void move(Task *destination)
cut Task out of parent Task or the TaskView and into the destination Task
bool isRunning() const
return the state of a task - if it's running or not
void cut()
cut Task out of parent Task or the TaskView
TQString comment() const
Retrieve the entire comment for the task.
void setUid(const TQString uid)
Set unique id for the task.