2 * Copyright (C) 2003, 2004 by Mark Bucciarelli <mark@hubcapconsutling.com>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the
16 * Free Software Foundation, Inc.
17 * 51 Franklin Street, Fifth Floor
18 * Boston, MA 02110-1301 USA.
22 /** timetrackerstorage
23 * This class cares for the storage of ktimetracker's data.
24 * ktimetracker's data is
25 * - tasks like "programming for customer foo"
26 * - events like "from 2009-09-07, 8pm till 10pm programming for customer foo"
27 * tasks are like the items on your todo list, events are dates when you worked on them.
28 * ktimetracker's data is stored in a ResourceCalendar object hold by mCalendar.
31 #include "timetrackerstorage.h"
32 #include "ktimetrackerutility.h"
33 #include "ktimetracker.h"
34 #include "storageadaptor.h"
39 #include <kemailsettings.h>
40 #include <kio/netaccess.h>
41 #include <kcal/resourcecalendar.h>
42 #include <kcal/resourcelocal.h>
43 #include <resourceremote.h>
44 #include <kcal/incidence.h>
46 #include <KApplication> // kapp
48 #include <KLocale> // i18n
49 #include <KMessageBox>
50 #include <KProgressDialog>
51 #include <KSystemTimeZones>
52 #include <KTemporaryFile>
62 #include <QStringList>
63 #include <QTableWidget>
64 #include <QTextStream>
67 #include <sys/types.h>
74 class timetrackerstorage::Private
77 Private() : mCalendar( 0 ) {}
82 KCal::ResourceCalendar
*mCalendar
;
87 timetrackerstorage::timetrackerstorage() : d( new Private() )
91 timetrackerstorage::~timetrackerstorage()
96 QString
timetrackerstorage::load( TaskView
* view
, const QString
&fileName
)
97 // loads data from filename into view. If no filename is given, filename from preferences is used.
98 // filename might be of use if this program is run as embedded konqueror plugin.
100 kDebug(5970) << "Entering function";
102 KEMailSettings settings
;
103 QString lFileName
= fileName
;
105 assert( !( lFileName
.isEmpty() ) );
107 // If same file, don't reload
108 if ( lFileName
== d
->mICalFile
) return err
;
111 // If file doesn't exist, create a blank one to avoid ResourceLocal load
112 // error. We make it user and group read/write, others read. This is
113 // masked by the users umask. (See man creat)
114 if ( !( remoteResource( lFileName
) ) )
117 handle
= open ( QFile::encodeName( lFileName
), O_CREAT
| O_EXCL
| O_WRONLY
,
118 S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IWGRP
| S_IROTH
);
126 // Create local file resource and add to resources
127 d
->mICalFile
= lFileName
;
129 KCal::ResourceCached
*resource
;
130 if ( remoteResource( d
->mICalFile
) )
132 KUrl
url( d
->mICalFile
);
133 resource
= new KCal::ResourceRemote( url
, url
); // same url for upload and download
137 resource
= new KCal::ResourceLocal( d
->mICalFile
);
139 d
->mCalendar
= resource
;
141 QObject::connect (d
->mCalendar
, SIGNAL(resourceChanged(ResourceCalendar
*)),
142 view
, SLOT(iCalFileModified(ResourceCalendar
*)));
143 d
->mCalendar
->setTimeSpec( KSystemTimeZones::local() );
144 d
->mCalendar
->setResourceName( QString::fromLatin1("KTimeTracker") );
145 d
->mCalendar
->open();
146 d
->mCalendar
->load();
148 // Claim ownership of iCalendar file if no one else has.
149 KCal::Person owner
= resource
->owner();
150 if ( owner
.isEmpty() )
152 resource
->setOwner( KCal::Person(
153 settings
.getSetting( KEMailSettings::RealName
),
154 settings
.getSetting( KEMailSettings::EmailAddress
) ) );
157 // Build task view from iCal data
160 KCal::Todo::List todoList
;
161 KCal::Todo::List::ConstIterator todo
;
162 QMultiHash
< QString
, Task
* > map
;
164 // Build dictionary to look up Task object from Todo uid. Each task is a
165 // QListViewItem, and is initially added with the view as the parent.
166 todoList
= d
->mCalendar
->rawTodos();
167 kDebug(5970) << "timetrackerstorage::load"
168 << "rawTodo count (includes completed todos) ="
170 for( todo
= todoList
.constBegin(); todo
!= todoList
.constEnd(); ++todo
)
172 Task
* task
= new Task(*todo
, view
);
173 map
.insert( (*todo
)->uid(), task
);
174 view
->setRootIsDecorated(true);
175 task
->setPixmapProgress();
178 // Load each task under it's parent task.
179 for( todo
= todoList
.constBegin(); todo
!= todoList
.constEnd(); ++todo
)
181 Task
* task
= map
.value( (*todo
)->uid() );
182 // No relatedTo incident just means this is a top-level task.
183 if ( (*todo
)->relatedTo() )
185 Task
* newParent
= map
.value( (*todo
)->relatedToUid() );
187 // Complete the loading but return a message
189 err
= i18n("Error loading \"%1\": could not find parent (uid=%2)",
191 (*todo
)->relatedToUid());
193 if (!err
.isEmpty()) task
->move( newParent
);
197 kDebug(5970) << "timetrackerstorage::load - loaded" << view
->count()
198 << "tasks from" << d
->mICalFile
;
201 if ( view
) buildTaskView(d
->mCalendar
, view
);
205 Task
* timetrackerstorage::task( const QString
& uid
, TaskView
* view
)
206 // return the tasks with the uid uid out of view.
207 // If !view, return the todo with the uid uid.
209 kDebug(5970) << "Entering function";
210 KCal::Todo::List todoList
;
211 KCal::Todo::List::ConstIterator todo
;
212 todoList
= d
->mCalendar
->rawTodos();
213 todo
= todoList
.constBegin();
215 bool konsolemode
=false;
216 if ( view
== 0 ) konsolemode
=true;
217 while ( todo
!= todoList
.constEnd() && ( (*todo
)->uid() != uid
) ) ++todo
;
218 if ( todo
!= todoList
.constEnd() ) result
= new Task((*todo
), view
, konsolemode
);
219 kDebug(5970) << "Leaving function, returning " << result
;
223 QString
timetrackerstorage::icalfile()
225 kDebug(5970) << "Entering function";
229 QString
timetrackerstorage::buildTaskView(KCal::ResourceCalendar
*rc
, TaskView
*view
)
230 // makes *view contain the tasks out of *rc.
232 kDebug(5970) << "Entering function";
234 KCal::Todo::List todoList
;
235 KCal::Todo::List::ConstIterator todo
;
236 QMultiHash
< QString
, Task
* > map
;
237 QVector
<QString
> runningTasks
;
238 QVector
<QDateTime
> startTimes
;
240 // remember tasks that are running and their start times
241 QTreeWidgetItemIterator
it( view
);
244 Task
*task
= static_cast< Task
* >( *it
);
245 if ( task
->isRunning() )
247 runningTasks
.append( task
->uid() );
248 startTimes
.append( task
->startTime() );
254 todoList
= rc
->rawTodos();
255 for ( todo
= todoList
.constBegin(); todo
!= todoList
.constEnd(); ++todo
)
257 Task
* task
= new Task(*todo
, view
);
258 task
->setWhatsThis(0,i18n("The task name is what you call the task, it can be chosen freely."));
259 task
->setWhatsThis(1,i18n("The session time is the time since you last chose \"start new session.\""));
260 map
.insert( (*todo
)->uid(), task
);
261 view
->setRootIsDecorated(true);
262 task
->setPixmapProgress();
265 // 1.1. Load each task under it's parent task.
266 for( todo
= todoList
.constBegin(); todo
!= todoList
.constEnd(); ++todo
)
268 Task
* task
= map
.value( (*todo
)->uid() );
269 // No relatedTo incident just means this is a top-level task.
270 if ( (*todo
)->relatedTo() )
272 Task
* newParent
= map
.value( (*todo
)->relatedToUid() );
273 // Complete the loading but return a message
275 err
= i18n("Error loading \"%1\": could not find parent (uid=%2)",
277 (*todo
)->relatedToUid());
278 else task
->move( newParent
);
282 view
->clearActiveTasks();
283 // restart tasks that have been running with their start times
284 for ( int i
=0; i
<view
->count(); i
++)
286 for ( int n
= 0; n
< runningTasks
.count(); ++n
)
288 if ( runningTasks
[n
] == view
->itemAt(i
)->uid() )
290 view
->startTimerFor( view
->itemAt(i
), startTimes
[n
] );
299 QString
timetrackerstorage::buildTaskView(TaskView
*view
)
300 // makes *view contain the tasks out of mCalendar
302 return buildTaskView(d
->mCalendar
, view
);
305 void timetrackerstorage::closeStorage()
307 kDebug(5970) << "Entering function";
310 d
->mCalendar
->close();
314 kDebug(5970) << "Leaving function";
317 KCal::Event::List
timetrackerstorage::rawevents()
319 kDebug(5970) << "Entering function";
320 return d
->mCalendar
->rawEvents();
323 KCal::Todo::List
timetrackerstorage::rawtodos()
325 kDebug(5970) << "Entering function";
326 return d
->mCalendar
->rawTodos();
329 bool timetrackerstorage::allEventsHaveEndTiMe(Task
* task
)
331 kDebug(5970) << "Entering function";
332 KCal::Event::List eventList
= d
->mCalendar
->rawEvents();
333 for(KCal::Event::List::iterator i
= eventList
.begin();
334 i
!= eventList
.end(); ++i
)
336 if ( (*i
)->relatedToUid() == task
->uid()
337 || ( (*i
)->relatedTo()
338 && (*i
)->relatedTo()->uid() == task
->uid() ) )
340 if ( !(*i
)->hasEndDate() ) return false;
346 bool timetrackerstorage::allEventsHaveEndTiMe()
348 kDebug(5970) << "Entering function";
349 KCal::Event::List eventList
= d
->mCalendar
->rawEvents();
350 for(KCal::Event::List::iterator i
= eventList
.begin();
351 i
!= eventList
.end(); ++i
)
353 if ( !(*i
)->hasEndDate() ) return false;
358 QString
timetrackerstorage::deleteAllEvents()
360 kDebug(5970) << "Entering function";
362 KCal::Event::List eventList
= d
->mCalendar
->rawEvents();
363 d
->mCalendar
->deleteAllEvents();
367 QString
timetrackerstorage::save(TaskView
* taskview
)
369 kDebug(5970) << "Entering function";
372 QStack
<KCal::Todo
*> parents
;
373 if ( taskview
) // we may also be in konsole mode
375 for (int i
= 0; i
< taskview
->topLevelItemCount(); ++i
)
377 Task
*task
= static_cast< Task
* >( taskview
->topLevelItem( i
) );
378 kDebug( 5970 ) << "write task" << task
->name();
379 err
= writeTaskAsTodo( task
, parents
);
387 kDebug(5970) << "timetrackerstorage::save : wrote tasks to" << d
->mICalFile
;
391 kWarning(5970) << "timetrackerstorage::save :" << err
;
396 QString
timetrackerstorage::setTaskParent( Task
* task
, Task
* parent
)
398 kDebug(5970) << "Entering function";
401 toDo
= d
->mCalendar
->todo(task
->uid());
402 if (parent
==0) toDo
->removeRelation(toDo
->relatedTo());
403 else toDo
->setRelatedTo(d
->mCalendar
->todo(parent
->uid()));
404 kDebug(5970) << "Leaving function";
408 QString
timetrackerstorage::writeTaskAsTodo(Task
* task
, QStack
<KCal::Todo
*>& parents
)
410 kDebug(5970) << "Entering function";
414 todo
= d
->mCalendar
->todo(task
->uid());
417 kDebug(5970) << "Could not get todo from calendar";
418 return "Could not get todo from calendar";
421 if ( !parents
.isEmpty() ) todo
->setRelatedTo( parents
.top() );
422 parents
.push( todo
);
424 for ( int i
= 0; i
< task
->childCount(); ++i
)
426 Task
*nextTask
= static_cast< Task
* >( task
->child( i
) );
427 err
= writeTaskAsTodo( nextTask
, parents
);
434 bool timetrackerstorage::isEmpty()
436 kDebug(5970) << "Entering function";
437 KCal::Todo::List todoList
;
438 todoList
= d
->mCalendar
->rawTodos();
439 return todoList
.empty();
442 //----------------------------------------------------------------------------
443 // Routines that handle logging ktimetracker history
446 QString
timetrackerstorage::addTask(const Task
* task
, const Task
* parent
)
448 kDebug(5970) << "Entering function";
452 todo
= new KCal::Todo();
453 if ( d
->mCalendar
->addTodo( todo
) )
455 task
->asTodo( todo
);
457 todo
->setRelatedTo(d
->mCalendar
->todo(parent
->uid()));
462 // Most likely a lock could not be pulled, although there are other
463 // possiblities (like a really confused resource manager).
469 QStringList
timetrackerstorage::taskidsfromname(QString taskname
)
471 kDebug(5970) << "Entering function";
473 KCal::Todo::List todoList
= d
->mCalendar
->rawTodos();
474 for(KCal::Todo::List::iterator i
= todoList
.begin();
475 i
!= todoList
.end(); ++i
)
477 kDebug(5970) << (*i
)->uid();
478 if ( (*i
)->summary() == taskname
) result
<< (*i
)->uid();
483 QStringList
timetrackerstorage::taskNames() const
485 kDebug(5970) << "Entering function";
487 KCal::Todo::List todoList
= d
->mCalendar
->rawTodos();
488 for(KCal::Todo::List::iterator i
= todoList
.begin();
489 i
!= todoList
.end(); ++i
) result
<< (*i
)->summary();
493 QString
timetrackerstorage::removeEvent(QString uid
)
495 kDebug(5970) << "Entering function";
496 QString err
=QString();
497 KCal::Event::List eventList
= d
->mCalendar
->rawEvents();
498 for(KCal::Event::List::iterator i
= eventList
.begin();
499 i
!= eventList
.end(); ++i
)
501 if ( (*i
)->uid() == uid
)
503 d
->mCalendar
->deleteEvent(*i
);
509 bool timetrackerstorage::removeTask(Task
* task
)
511 kDebug(5970) << "Entering function";
513 KCal::Event::List eventList
= d
->mCalendar
->rawEvents();
514 for(KCal::Event::List::iterator i
= eventList
.begin();
515 i
!= eventList
.end(); ++i
)
517 if ( (*i
)->relatedToUid() == task
->uid()
518 || ( (*i
)->relatedTo()
519 && (*i
)->relatedTo()->uid() == task
->uid()))
521 d
->mCalendar
->deleteEvent(*i
);
526 KCal::Todo
*todo
= d
->mCalendar
->todo(task
->uid());
527 d
->mCalendar
->deleteTodo(todo
);
534 bool timetrackerstorage::removeTask(QString taskid
)
536 kDebug(5970) << "Entering function";
538 KCal::Event::List eventList
= d
->mCalendar
->rawEvents();
539 for(KCal::Event::List::iterator i
= eventList
.begin();
540 i
!= eventList
.end();
543 if ( (*i
)->relatedToUid() == taskid
544 || ( (*i
)->relatedTo()
545 && (*i
)->relatedTo()->uid() == taskid
))
547 d
->mCalendar
->deleteEvent(*i
);
552 KCal::Todo
*todo
= d
->mCalendar
->todo(taskid
);
553 d
->mCalendar
->deleteTodo(todo
);
561 void timetrackerstorage::addComment(const Task
* task
, const QString
& comment
)
563 kDebug(5970) << "Entering function";
566 todo
= d
->mCalendar
->todo(task
->uid());
568 // Do this to avoid compiler warnings about comment not being used. once we
569 // transition to using the addComment method, we need this second param.
572 // TODO: Use libkcal comments
573 // todo->addComment(comment);
575 todo
->setDescription(task
->comment());
580 QString
timetrackerstorage::report( TaskView
*taskview
, const ReportCriteria
&rc
)
582 kDebug(5970) << "Entering function";
584 if ( rc
.reportType
== ReportCriteria::CSVHistoryExport
)
586 err
= exportcsvHistory( taskview
, rc
.from
, rc
.to
, rc
);
588 else // if ( rc.reportType == ReportCriteria::CSVTotalsExport )
590 if ( !rc
.bExPortToClipBoard
)
591 err
= exportcsvFile( taskview
, rc
);
593 err
= taskview
->clipTotals( rc
);
599 //----------------------------------------------------------------------------
600 // Routines that handle Comma-Separated Values export file format.
602 QString
timetrackerstorage::exportcsvFile( TaskView
*taskview
,
603 const ReportCriteria
&rc
)
605 kDebug(5970) << "Entering function";
606 QString delim
= rc
.delimiter
;
607 QString dquote
= rc
.quote
;
608 QString double_dquote
= dquote
+ dquote
;
609 bool to_quote
= true;
613 QString title
= i18n("Export Progress");
614 KProgressDialog
dialog( taskview
, 0, title
);
615 dialog
.setAutoClose( true );
616 dialog
.setAllowCancel( true );
617 dialog
.progressBar()->setMaximum( 2 * taskview
->count() );
619 // The default dialog was not displaying all the text in the title bar.
620 int width
= taskview
->fontMetrics().width(title
) * 3;
622 dialogsize
.setWidth(width
);
623 dialog
.setInitialSize( dialogsize
);
625 if ( taskview
->count() > 1 ) dialog
.show();
628 // Find max task depth
630 while ( tasknr
< taskview
->count() && !dialog
.wasCancelled() )
632 dialog
.progressBar()->setValue( dialog
.progressBar()->value() + 1 );
633 if ( tasknr
% 15 == 0 ) kapp
->processEvents(); // repainting is slow
634 if ( taskview
->itemAt(tasknr
)->depth() > maxdepth
)
635 maxdepth
= taskview
->itemAt(tasknr
)->depth();
641 while ( tasknr
< taskview
->count() && !dialog
.wasCancelled() )
643 task
= taskview
->itemAt( tasknr
);
644 dialog
.progressBar()->setValue( dialog
.progressBar()->value() + 1 );
645 if ( tasknr
% 15 == 0 ) kapp
->processEvents();
647 // indent the task in the csv-file:
648 for ( int i
=0; i
< task
->depth(); ++i
) retval
+= delim
;
652 // Surround the field with quotes if the field contains
653 // a comma (delim) or a double quote
654 if (task->name().contains(delim) || task->name().contains(dquote))
664 // Double quotes replaced by a pair of consecutive double quotes
665 retval
+= task
->name().replace( dquote
, double_dquote
);
670 // maybe other tasks are more indented, so to align the columns:
671 for ( int i
= 0; i
< maxdepth
- task
->depth(); ++i
) retval
+= delim
;
673 retval
+= delim
+ formatTime( task
->sessionTime(),
675 + delim
+ formatTime( task
->time(),
677 + delim
+ formatTime( task
->totalSessionTime(),
679 + delim
+ formatTime( task
->totalTime(),
685 // save, either locally or remote
686 if ((rc
.url
.isLocalFile()) || (!rc
.url
.url().contains("/")))
688 QString filename
=rc
.url
.toLocalFile();
689 if (filename
.isEmpty()) filename
=rc
.url
.url();
691 if( !f
.open( QIODevice::WriteOnly
) )
693 err
= i18n( "Could not open \"%1\".", filename
);
697 QTextStream
stream(&f
);
703 else // use remote file
705 KTemporaryFile tmpFile
;
706 if ( !tmpFile
.open() ) err
= QString::fromLatin1( "Unable to get temporary file" );
709 QTextStream
stream ( &tmpFile
);
712 if (!KIO::NetAccess::upload( tmpFile
.fileName(), rc
.url
, 0 )) err
=QString::fromLatin1("Could not upload");
719 // export history report as csv, all tasks X all dates in one block
720 QString
timetrackerstorage::exportcsvHistory ( TaskView
*taskview
,
723 const ReportCriteria
&rc
)
725 kDebug(5970) << "Entering function";
726 QString delim
= rc
.delimiter
;
727 const QString cr
= QString::fromLatin1("\n");
728 QString err
=QString::null
;
732 QTableWidget
* itab
=new QTableWidget(); // hello, ABAP
733 itab
->setRowCount(taskview
->count()+1);
734 itab
->setColumnCount(from
.daysTo(to
)+2);
738 err
= QString("'to' has to be a date later than or equal to 'from'.");
742 itab
->setItem(0,0,new QTableWidgetItem("Task name"));
743 for ( QDate mdate
=from
; mdate
.daysTo(to
)>=0; mdate
=mdate
.addDays(1) )
745 kDebug(5970) << mdate
.toString();
746 itab
->setItem(0,1+from
.daysTo(mdate
),new QTableWidgetItem(mdate
.toString()));
747 for ( int n
=0; n
<taskview
->count(); n
++ )
749 task
=taskview
->itemAt(n
);
750 itab
->setItem(n
+1,0,new QTableWidgetItem(taskview
->itemAt(n
)->name()));
751 KCal::Event::List eventList
= d
->mCalendar
->rawEvents();
752 for(KCal::Event::List::iterator i
= eventList
.begin();
753 i
!= eventList
.end(); ++i
)
755 if ( (*i
)->relatedToUid() == task
->uid() )
757 kDebug(5970) << "found an event for task, event=" << (*i
)->uid();
758 // dtStart is stored like DTSTART;TZID=Europe/Berlin:20080327T231056
759 // dtEnd is stored like DTEND:20080327T231509Z
760 // we need to subtract the offset from UTC.
761 KDateTime startTime
=(*i
)->dtStart().addSecs((*i
)->dtStart().utcOffset());
762 KDateTime endTime
=(*i
)->dtEnd().addSecs((*i
)->dtEnd().utcOffset());
763 KDateTime NextMidNight
=startTime
;
764 NextMidNight
.setTime(QTime ( 0,0 ));
765 NextMidNight
=NextMidNight
.addDays(1);
766 // LastMidNight := mdate.setTime(0:00) as it would read in a decent programming language
767 KDateTime LastMidNight
=KDateTime::currentLocalDateTime();
768 LastMidNight
.setDate(mdate
);
769 LastMidNight
.setTime(QTime(0,0));
770 int secsstartTillMidNight
=startTime
.secsTo(NextMidNight
);
771 int secondsToAdd
=0; // seconds that need to be added to the actual cell
772 if ( (startTime
.date()==mdate
) && ((*i
)->dtEnd().date()==mdate
) ) // all the event occurred today
773 secondsToAdd
=startTime
.secsTo(endTime
);
774 if ( (startTime
.date()==mdate
) && (endTime
.date()>mdate
) ) // the event started today, but ended later
775 secondsToAdd
=secsstartTillMidNight
;
776 if ( (startTime
.date()<mdate
) && (endTime
.date()==mdate
) ) // the event started before today and ended today
777 secondsToAdd
=LastMidNight
.secsTo((*i
)->dtEnd());
778 if ( (startTime
.date()<mdate
) && (endTime
.date()>mdate
) ) // the event started before today and ended after
780 int secondsSum
=secondsToAdd
;
781 if (itab
->item(n
+1,from
.daysTo(mdate
)+1))
782 secondsSum
=itab
->item(n
+1,from
.daysTo(mdate
)+1)->text().toInt()+secondsToAdd
;
783 itab
->setItem(n
+1,from
.daysTo(mdate
)+1,new QTableWidgetItem(QString::number(secondsSum
)));
788 // use the internal table itab to create the return value retval
789 for ( int y
=0; y
<=(taskview
->count()); y
++ )
791 if (itab
->item(y
,0)) retval
.append("\"").append(itab
->item(y
,0)->text().replace("\"","\"\"")).append("\""); // task names
792 for ( int x
=1; x
<=from
.daysTo(to
)+1; x
++ )
794 retval
.append(rc
.delimiter
);
799 kDebug(5970) << "itab->item(y,x)=" << itab
->item(y
,x
)->text();
800 retval
.append(formatTime( itab
->item(y
,x
)->text().toInt()/60.0, rc
.decimalMinutes
));
805 retval
.append(itab
->item(y
,x
)->text());
810 kDebug() << "Retval is \n" << retval
;
812 // itab->show(); // GREAT for debugging purposes :)
813 if (rc
.bExPortToClipBoard
)
814 taskview
->setClipBoardText(retval
);
817 // store the file locally or remote
818 if ((rc
.url
.isLocalFile()) || (!rc
.url
.url().contains("/")))
820 kDebug(5970) << "storing a local file";
821 QString filename
=rc
.url
.toLocalFile();
822 if (filename
.isEmpty()) filename
=rc
.url
.url();
824 if( !f
.open( QIODevice::WriteOnly
) )
826 err
= i18n( "Could not open \"%1\".", filename
);
827 kDebug(5970) << "Could not open file";
829 kDebug() << "Err is " << err
;
832 QTextStream
stream(&f
);
833 kDebug(5970) << "Writing to file: " << retval
;
839 else // use remote file
841 KTemporaryFile tmpFile
;
842 if ( !tmpFile
.open() )
844 err
= QString::fromLatin1( "Unable to get temporary file" );
848 QTextStream
stream ( &tmpFile
);
851 if (!KIO::NetAccess::upload( tmpFile
.fileName(), rc
.url
, 0 )) err
=QString::fromLatin1("Could not upload");
858 void timetrackerstorage::startTimer( const Task
* task
, const KDateTime
&when
)
860 kDebug(5970) << "Entering function; when=" << when
;
864 d
->mCalendar
->addEvent(e
);
865 task
->taskView()->scheduleSave();
868 void timetrackerstorage::startTimer( QString taskID
)
870 kDebug(5970) << "Entering function";
871 KCal::Todo::List todoList
;
872 KCal::Todo::List::ConstIterator todo
;
873 todoList
= d
->mCalendar
->rawTodos();
874 for( todo
= todoList
.constBegin(); todo
!= todoList
.constEnd(); ++todo
)
876 kDebug(5970) << (*todo
)->uid();
877 kDebug(5970) << taskID
;
878 if ( (*todo
)->uid() == taskID
)
880 kDebug(5970) << "adding event";
882 e
= baseEvent((*todo
));
883 e
->setDtStart(KDateTime::currentLocalDateTime());
884 d
->mCalendar
->addEvent(e
);
890 void timetrackerstorage::stopTimer( const Task
* task
, const QDateTime
&when
)
892 kDebug(5970) << "Entering function; when=" << when
;
893 KCal::Event::List eventList
= d
->mCalendar
->rawEvents();
894 for(KCal::Event::List::iterator i
= eventList
.begin();
895 i
!= eventList
.end();
898 if ( (*i
)->relatedToUid() == task
->uid() )
900 kDebug(5970) << "found an event for task, event=" << (*i
)->uid();
901 if (!(*i
)->hasEndDate())
903 kDebug(5970) << "this event has no enddate";
904 QString s
=when
.toString("yyyy-MM-ddThh:mm:ss.zzzZ"); // need the KDE standard from the ISO standard, not the QT one
905 KDateTime kwhen
=KDateTime::fromString(s
);
906 kDebug() << "kwhen ==" << kwhen
;
907 (*i
)->setDtEnd(kwhen
);
911 kDebug(5970) << "this event has an enddate";
912 kDebug(5970) << "end date is " << (*i
)->dtEnd();
919 bool timetrackerstorage::bookTime(const Task
* task
,
920 const QDateTime
& startDateTime
,
921 const long durationInSeconds
)
923 kDebug(5970) << "Entering function";
924 // Ignores preferences setting re: logging history.
927 KDateTime
start( startDateTime
, KDateTime::Spec::LocalZone() ); //??? is LocalZone correct ???
929 e
= baseEvent( task
);
930 e
->setDtStart( start
);
931 e
->setDtEnd( start
.addSecs( durationInSeconds
) );
933 // Use a custom property to keep a record of negative durations
934 e
->setCustomProperty( KGlobal::mainComponent().componentName().toUtf8(),
935 QByteArray("duration"),
936 QString::number(durationInSeconds
));
937 return d
->mCalendar
->addEvent(e
);
940 void timetrackerstorage::changeTime(const Task
* task
, const long deltaSeconds
)
942 kDebug(5970) << "Entering function; deltaSeconds=" << deltaSeconds
;
947 // Don't use duration, as ICalFormatImpl::writeIncidence never writes a
948 // duration, even though it looks like it's used in event.cpp.
949 end
= task
->startTime();
950 if ( deltaSeconds
> 0 ) end
= task
->startTime().addSecs(deltaSeconds
);
951 e
->setDtEnd(KDateTime(end
, KDateTime::Spec::LocalZone()));
953 // Use a custom property to keep a record of negative durations
954 e
->setCustomProperty( KGlobal::mainComponent().componentName().toUtf8(),
955 QByteArray("duration"),
956 QString::number(deltaSeconds
));
958 d
->mCalendar
->addEvent(e
);
959 task
->taskView()->scheduleSave();
963 KCal::Event
* timetrackerstorage::baseEvent(const Task
* task
)
965 kDebug(5970) << "Entering function";
967 QStringList categories
;
969 e
->setSummary(task
->name());
971 // Can't use setRelatedToUid()--no error, but no RelatedTo written to disk
972 e
->setRelatedTo(d
->mCalendar
->todo(task
->uid()));
974 // Debugging: some events where not getting a related-to field written.
975 assert(e
->relatedTo()->uid() == task
->uid());
977 // Have to turn this off to get datetimes in date fields.
979 e
->setDtStart(KDateTime(task
->startTime(), KDateTime::Spec::LocalZone()));
981 // So someone can filter this mess out of their calendar display
982 categories
.append(i18n("KTimeTracker"));
983 e
->setCategories(categories
);
988 KCal::Event
* timetrackerstorage::baseEvent(const Todo
* todo
)
990 kDebug(5970) << "Entering function";
992 QStringList categories
;
995 e
->setSummary(todo
->summary());
997 // Can't use setRelatedToUid()--no error, but no RelatedTo written to disk
998 e
->setRelatedTo(d
->mCalendar
->todo(todo
->uid()));
1000 // Have to turn this off to get datetimes in date fields.
1001 e
->setAllDay(false);
1002 e
->setDtStart(KDateTime(todo
->dtStart()));
1004 // So someone can filter this mess out of their calendar display
1005 categories
.append(i18n("KTimeTracker"));
1006 e
->setCategories(categories
);
1011 HistoryEvent::HistoryEvent( const QString
&uid
, const QString
&name
,
1012 long duration
, const KDateTime
&start
,
1013 const KDateTime
&stop
, const QString
&todoUid
)
1017 _duration
= duration
;
1023 bool timetrackerstorage::remoteResource( const QString
& file
) const
1025 kDebug(5970) << "Entering function";
1026 QString f
= file
.toLower();
1027 bool rval
= f
.startsWith( QLatin1String("http://") ) || f
.startsWith( QLatin1String("ftp://") );
1028 kDebug(5970) << "timetrackerstorage::remoteResource(" << file
<<" ) returns" << rval
;
1032 QString
timetrackerstorage::saveCalendar()
1034 kDebug(5970) << "Entering function";
1036 KABC::Lock
*lock
= d
->mCalendar
->lock();
1037 if ( !lock
|| !lock
->lock() ) err
=QString("Could not save. Could not lock file.");
1038 if ( d
->mCalendar
->save() )
1042 else err
=QString("Could not save. Could lock file.");