Waste less space in nested layouts.
[kdepim.git] / ktimetracker / timetrackerstorage.cpp
blob9a733a1a9ad7623d595b618c0b709fe77ad48ffb
1 /*
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"
35 #include "task.h"
36 #include "taskview.h"
37 #include "timekard.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
47 #include <KDebug>
48 #include <KLocale> // i18n
49 #include <KMessageBox>
50 #include <KProgressDialog>
51 #include <KSystemTimeZones>
52 #include <KTemporaryFile>
53 #include <KUrl>
55 #include <QByteArray>
56 #include <QDateTime>
57 #include <QFile>
58 #include <QList>
59 #include <QMultiHash>
60 #include <QSize>
61 #include <QString>
62 #include <QStringList>
63 #include <QTableWidget>
64 #include <QTextStream>
66 #include <sys/stat.h>
67 #include <sys/types.h>
68 #include <unistd.h>
69 #include <cassert>
70 #include <fcntl.h>
73 //@cond PRIVATE
74 class timetrackerstorage::Private
76 public:
77 Private() : mCalendar( 0 ) {}
78 ~Private()
80 delete mCalendar;
82 KCal::ResourceCalendar *mCalendar;
83 QString mICalFile;
85 //@endcond
87 timetrackerstorage::timetrackerstorage() : d( new Private() )
91 timetrackerstorage::~timetrackerstorage()
93 delete d;
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";
101 QString err;
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 ) ) )
116 int handle;
117 handle = open ( QFile::encodeName( lFileName ), O_CREAT | O_EXCL | O_WRONLY,
118 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH );
119 if ( handle != -1 )
121 close( handle );
124 if ( d->mCalendar )
125 closeStorage();
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
135 else
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
158 if (!err.isEmpty())
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) ="
169 << todoList.count();
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
188 if ( !newParent )
189 err = i18n("Error loading \"%1\": could not find parent (uid=%2)",
190 task->name(),
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);
202 return err;
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();
214 Task* result=0;
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;
220 return result;
223 QString timetrackerstorage::icalfile()
225 kDebug(5970) << "Entering function";
226 return d->mICalFile;
229 QString timetrackerstorage::buildTaskView(KCal::ResourceCalendar *rc, TaskView *view)
230 // makes *view contain the tasks out of *rc.
232 kDebug(5970) << "Entering function";
233 QString err;
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 );
242 while ( *it )
244 Task *task = static_cast< Task* >( *it );
245 if ( task->isRunning() )
247 runningTasks.append( task->uid() );
248 startTimes.append( task->startTime() );
250 ++it;
253 view->clear();
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
274 if ( !newParent )
275 err = i18n("Error loading \"%1\": could not find parent (uid=%2)",
276 task->name(),
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] );
295 view->refresh();
296 return err;
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";
308 if ( d->mCalendar )
310 d->mCalendar->close();
311 delete d->mCalendar;
312 d->mCalendar = 0;
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;
343 return true;
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;
355 return true;
358 QString timetrackerstorage::deleteAllEvents()
360 kDebug(5970) << "Entering function";
361 QString err;
362 KCal::Event::List eventList = d->mCalendar->rawEvents();
363 d->mCalendar->deleteAllEvents();
364 return err;
367 QString timetrackerstorage::save(TaskView* taskview)
369 kDebug(5970) << "Entering function";
370 QString err;
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 );
383 err=saveCalendar();
385 if ( err.isEmpty() )
387 kDebug(5970) << "timetrackerstorage::save : wrote tasks to" << d->mICalFile;
389 else
391 kWarning(5970) << "timetrackerstorage::save :" << err;
393 return err;
396 QString timetrackerstorage::setTaskParent( Task* task, Task* parent )
398 kDebug(5970) << "Entering function";
399 QString err;
400 KCal::Todo* toDo;
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";
405 return err;
408 QString timetrackerstorage::writeTaskAsTodo(Task* task, QStack<KCal::Todo*>& parents )
410 kDebug(5970) << "Entering function";
411 QString err;
412 KCal::Todo* todo;
414 todo = d->mCalendar->todo(task->uid());
415 if ( !todo )
417 kDebug(5970) << "Could not get todo from calendar";
418 return "Could not get todo from calendar";
420 task->asTodo(todo);
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 );
430 parents.pop();
431 return err;
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";
449 KCal::Todo* todo;
450 QString uid;
452 todo = new KCal::Todo();
453 if ( d->mCalendar->addTodo( todo ) )
455 task->asTodo( todo );
456 if (parent)
457 todo->setRelatedTo(d->mCalendar->todo(parent->uid()));
458 uid = todo->uid();
460 else
462 // Most likely a lock could not be pulled, although there are other
463 // possiblities (like a really confused resource manager).
464 uid = "";
466 return uid;
469 QStringList timetrackerstorage::taskidsfromname(QString taskname)
471 kDebug(5970) << "Entering function";
472 QStringList result;
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();
480 return result;
483 QStringList timetrackerstorage::taskNames() const
485 kDebug(5970) << "Entering function";
486 QStringList result;
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();
490 return result;
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);
506 return err;
509 bool timetrackerstorage::removeTask(Task* task)
511 kDebug(5970) << "Entering function";
512 // delete history
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);
525 // delete todo
526 KCal::Todo *todo = d->mCalendar->todo(task->uid());
527 d->mCalendar->deleteTodo(todo);
528 // Save entire file
529 saveCalendar();
531 return true;
534 bool timetrackerstorage::removeTask(QString taskid)
536 kDebug(5970) << "Entering function";
537 // delete history
538 KCal::Event::List eventList = d->mCalendar->rawEvents();
539 for(KCal::Event::List::iterator i = eventList.begin();
540 i != eventList.end();
541 ++i)
543 if ( (*i)->relatedToUid() == taskid
544 || ( (*i)->relatedTo()
545 && (*i)->relatedTo()->uid() == taskid))
547 d->mCalendar->deleteEvent(*i);
551 // delete todo
552 KCal::Todo *todo = d->mCalendar->todo(taskid);
553 d->mCalendar->deleteTodo(todo);
555 // Save entire file
556 saveCalendar();
558 return true;
561 void timetrackerstorage::addComment(const Task* task, const QString& comment)
563 kDebug(5970) << "Entering function";
564 KCal::Todo* todo;
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.
570 QString s = comment;
572 // TODO: Use libkcal comments
573 // todo->addComment(comment);
574 // temporary
575 todo->setDescription(task->comment());
577 saveCalendar();
580 QString timetrackerstorage::report( TaskView *taskview, const ReportCriteria &rc )
582 kDebug(5970) << "Entering function";
583 QString err;
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 );
592 else
593 err = taskview->clipTotals( rc );
595 return err;
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;
610 QString err;
611 Task* task;
612 int maxdepth=0;
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;
621 QSize dialogsize;
622 dialogsize.setWidth(width);
623 dialog.setInitialSize( dialogsize );
625 if ( taskview->count() > 1 ) dialog.show();
626 QString retval;
628 // Find max task depth
629 int tasknr = 0;
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();
636 tasknr++;
639 // Export to file
640 tasknr = 0;
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;
651 // CSV compliance
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))
655 to_quote = true;
656 else
657 to_quote = false;
659 to_quote = true;
661 if (to_quote)
662 retval += dquote;
664 // Double quotes replaced by a pair of consecutive double quotes
665 retval += task->name().replace( dquote, double_dquote );
667 if (to_quote)
668 retval += 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(),
674 rc.decimalMinutes )
675 + delim + formatTime( task->time(),
676 rc.decimalMinutes )
677 + delim + formatTime( task->totalSessionTime(),
678 rc.decimalMinutes )
679 + delim + formatTime( task->totalTime(),
680 rc.decimalMinutes )
681 + '\n';
682 tasknr++;
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();
690 QFile f( filename );
691 if( !f.open( QIODevice::WriteOnly ) )
693 err = i18n( "Could not open \"%1\".", filename );
695 if (err.length()==0)
697 QTextStream stream(&f);
698 // Export to file
699 stream << retval;
700 f.close();
703 else // use remote file
705 KTemporaryFile tmpFile;
706 if ( !tmpFile.open() ) err = QString::fromLatin1( "Unable to get temporary file" );
707 else
709 QTextStream stream ( &tmpFile );
710 stream << retval;
711 stream.flush();
712 if (!KIO::NetAccess::upload( tmpFile.fileName(), rc.url, 0 )) err=QString::fromLatin1("Could not upload");
716 return err;
719 // export history report as csv, all tasks X all dates in one block
720 QString timetrackerstorage::exportcsvHistory ( TaskView *taskview,
721 const QDate &from,
722 const QDate &to,
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;
729 QString retval;
730 Task* task;
732 QTableWidget* itab=new QTableWidget(); // hello, ABAP
733 itab->setRowCount(taskview->count()+1);
734 itab->setColumnCount(from.daysTo(to)+2);
735 // parameter-plausi
736 if ( from > to )
738 err = QString("'to' has to be a date later than or equal to 'from'.");
740 else
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
779 secondsToAdd=86400;
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);
795 if (y>0)
797 if (itab->item(y,x))
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 ));
803 else // heading
805 retval.append(itab->item(y,x)->text());
808 retval.append("\n");
810 kDebug() << "Retval is \n" << retval;
812 // itab->show(); // GREAT for debugging purposes :)
813 if (rc.bExPortToClipBoard)
814 taskview->setClipBoardText(retval);
815 else
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();
823 QFile f( filename );
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;
830 if (err.length()==0)
832 QTextStream stream(&f);
833 kDebug(5970) << "Writing to file: " << retval;
834 // Export to file
835 stream << retval;
836 f.close();
839 else // use remote file
841 KTemporaryFile tmpFile;
842 if ( !tmpFile.open() )
844 err = QString::fromLatin1( "Unable to get temporary file" );
846 else
848 QTextStream stream ( &tmpFile );
849 stream << retval;
850 stream.flush();
851 if (!KIO::NetAccess::upload( tmpFile.fileName(), rc.url, 0 )) err=QString::fromLatin1("Could not upload");
855 return err;
858 void timetrackerstorage::startTimer( const Task* task, const KDateTime &when )
860 kDebug(5970) << "Entering function; when=" << when;
861 KCal::Event* e;
862 e = baseEvent(task);
863 e->setDtStart(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";
881 KCal::Event* e;
882 e = baseEvent((*todo));
883 e->setDtStart(KDateTime::currentLocalDateTime());
884 d->mCalendar->addEvent(e);
887 saveCalendar();
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();
896 ++i)
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);
909 else
911 kDebug(5970) << "this event has an enddate";
912 kDebug(5970) << "end date is " << (*i)->dtEnd();
916 saveCalendar();
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.
925 KCal::Event* e;
926 QDateTime end;
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;
943 KCal::Event* e;
944 QDateTime end;
945 e = baseEvent(task);
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";
966 KCal::Event* e;
967 QStringList categories;
968 e = new KCal::Event;
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.
978 e->setAllDay(false);
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);
985 return e;
988 KCal::Event* timetrackerstorage::baseEvent(const Todo* todo)
990 kDebug(5970) << "Entering function";
991 KCal::Event* e;
992 QStringList categories;
994 e = new KCal::Event;
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);
1008 return e;
1011 HistoryEvent::HistoryEvent( const QString &uid, const QString &name,
1012 long duration, const KDateTime &start,
1013 const KDateTime &stop, const QString &todoUid )
1015 _uid = uid;
1016 _name = name;
1017 _duration = duration;
1018 _start = start;
1019 _stop = stop;
1020 _todoUid = todoUid;
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;
1029 return rval;
1032 QString timetrackerstorage::saveCalendar()
1034 kDebug(5970) << "Entering function";
1035 QString err;
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() )
1040 lock->unlock();
1042 else err=QString("Could not save. Could lock file.");
1043 lock->unlock();
1044 return err;