kpilot Library API Documentation

Organizer-conduit.cc

00001 /* Organizer-conduit.cc  Organizer-Conduit for syncing KPilot and KOrganizer
00002 **
00003 ** Copyright (C) 1998-2001 Dan Pilone
00004 ** Copyright (C) 1998-2000 Preston Brown
00005 ** Copyright (C) 1998 Herwin-Jan Steehouwer
00006 ** Copyright (C) 2001 Cornelius Schumacher
00007 ** Copyright (C) 2002 by Reinhold Kainhofer
00008 **
00009 ** This file is part of the Organizer conduit, a conduit for KPilot that
00010 ** synchronises the Pilot's Organizer application with the outside world,
00011 ** which currently means KOrganizer.
00012 */
00013 
00014 /*
00015 ** This program is free software; you can redistribute it and/or modify
00016 ** it under the terms of the GNU General Public License as published by
00017 ** the Free Software Foundation; either version 2 of the License, or
00018 ** (at your option) any later version.
00019 **
00020 ** This program is distributed in the hope that it will be useful,
00021 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
00022 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00023 ** GNU General Public License for more details.
00024 **
00025 ** You should have received a copy of the GNU General Public License
00026 ** along with this program in a file called COPYING; if not, write to
00027 ** the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
00028 ** MA 02111-1307, USA.
00029 */
00030 
00031 #include "options.h"
00032 #include <stdio.h>
00033 #include <stdlib.h>
00034 
00035 #if QT_VERSION < 300
00036 #include <qmsgbox.h>
00037 #else
00038 #include <qmessagebox.h>
00039 #endif
00040 
00041 #include <qtimer.h>
00042 #include <kconfig.h>
00043 #include <kmessagebox.h>
00044 #include <kstandarddirs.h>
00045 #include <pilotSerialDatabase.h>
00046 #include <calendarlocal.h>
00047 #include "Organizer-conduit.h"
00048 #include "DatabaseAction.h"
00049 
00050 using namespace KCal;
00051 
00052 static const char *Organizer_conduit_id = "$Id: Organizer-conduit.cc,v 1.4.4.1 2003/03/12 23:31:10 adridg Exp $";
00053 
00054 
00055 /*****************************************************************
00056  *          OrgConduit::VCalPrivate helper class                 *
00057  *****************************************************************/
00058 
00059 class OrganizerConduit::VCalPrivate {
00060 private:
00061         bool reading;
00062         KCal::Calendar*fCalendar;
00063 public:
00064         VCalPrivate(KCal::Calendar *buddy);
00065         #ifdef KDE2
00066                 QList<KCal::Todo> fAllTodos;
00067         #else
00068                 QPtrList<KCal::Todo> fAllTodos;
00069         #endif
00070         int updateTodos();
00071         int count() {return fAllTodos.count();};
00072         void addTodo(KCal::Todo*);
00073         void removeTodo(KCal::Todo*);
00074         void insertTodo(KCal::Todo*);
00075         KCal::Todo *findTodo(recordid_t);
00076         KCal::Todo *getNextTodo();
00077         KCal::Todo *getNextModifiedTodo();
00078 };
00079 
00080 
00081 
00082 OrganizerConduit::VCalPrivate::VCalPrivate(KCal::Calendar *b) : fCalendar(b) {
00083         fAllTodos.setAutoDelete(false);
00084 }
00085 
00086 void OrganizerConduit::VCalPrivate::addTodo(KCal::Todo*e)
00087 {
00088         fAllTodos.append(e);
00089         fCalendar->addTodo(e);
00090 }
00091 
00092 int OrganizerConduit::VCalPrivate::updateTodos() {
00093         fAllTodos=fCalendar->getTodoList();
00094         fAllTodos.setAutoDelete(false);
00095         return fAllTodos.count();
00096 }
00097 
00098 void OrganizerConduit::VCalPrivate::removeTodo(KCal::Todo*e) {
00099         fAllTodos.remove(e);
00100         fCalendar->deleteTodo(e);
00101 }
00102 
00103 void OrganizerConduit::VCalPrivate::insertTodo(KCal::Todo*e) {
00104         fAllTodos.append(e);
00105 }
00106 
00107 KCal::Todo*OrganizerConduit::VCalPrivate::findTodo(recordid_t id) {
00108         KCal::Todo *todo = fAllTodos.first();
00109         while (todo) {
00110                 if ((recordid_t)todo->pilotId()==id) return todo;
00111                 todo=fAllTodos.next();
00112         }
00113         return 0L;
00114 }
00115 
00116 KCal::Todo *OrganizerConduit::VCalPrivate::getNextTodo()
00117 {
00118         if (reading) return fAllTodos.next();
00119         reading=true;
00120         return fAllTodos.first();
00121 }
00122 
00123 KCal::Todo *OrganizerConduit::VCalPrivate::getNextModifiedTodo()
00124 {
00125         KCal::Todo*e=0L;
00126         if (!reading) {
00127                 reading=true;
00128                 e=fAllTodos.first();
00129         } else {
00130                 e=fAllTodos.next();
00131         }
00132         while (e && e->syncStatus()==KCal::Todo::SYNCNONE) {
00133                 e=fAllTodos.next();
00134         }
00135         return e;
00136 }
00137 
00138 
00139 /*****************************************************************************
00140  *                 OrganizerConduit main class                        *
00141  *****************************************************************************/
00142 OrganizerConduit::OrganizerConduit(KPilotDeviceLink *d, const char *n, const QStringList &l, SyncTypeList_t *tps) :
00143                 MultiDBConduit(d,n,l,tps), fCalendar(0L), fP(0L) {
00144         FUNCTIONSETUP;
00145         for (int i=0; i<7; i++) { levelparent[i]=0;}
00146         previd=0;
00147         (void)Organizer_conduit_id;
00148 }
00149 
00150 
00151 OrganizerConduit::~OrganizerConduit() {
00152         FUNCTIONSETUP;
00153         cleanup();
00154 }
00155 
00156 bool OrganizerConduit::exec() {
00157         FUNCTIONSETUP;
00158         KConfig korgcfg( locate( "config", "korganizerrc" ) );
00159         QString tz;
00160         // this part taken from adcalendarbase.cpp:
00161         korgcfg.setGroup( "Time & Date" );
00162         tz = korgcfg.readEntry( "TimeZoneId" );
00163 #ifdef DEBUG
00164         DEBUGCONDUIT << fname<<": KOrganizer's time zone = "<<tz<<endl;
00165 #endif
00166 
00167         return MultiDBConduit::exec();
00168 }
00169 
00170 void OrganizerConduit::cleanup() {
00171         FUNCTIONSETUP;
00172         MultiDBConduit::cleanup();
00173         KPILOT_DELETE(fP);
00174 }
00175 
00176 void OrganizerConduit::cleanupDB() {
00177         FUNCTIONSETUP;
00178         MultiDBConduit::cleanupDB();
00179         KPILOT_DELETE(fCalendar);
00180 }
00181 
00182 
00183 void OrganizerConduit::syncNextRecord() {
00184         PilotRecord*rec=fCurrentDatabase->readRecordByIndex(Palmix);
00185         KCal::Todo*todo=fP->fAllTodos.at(PCix);
00186 
00187         // if any of them could not be found, we are at the end, just add the rest:
00188         if (!rec) {
00189                 while ( (todo=fP->fAllTodos.at(PCix++)) ) {
00190                         insertRecordToPalm(Palmix++, todo);
00191                 }
00192                 QTimer::singleShot(0, this, SLOT(finishedDB()));
00193                 return;
00194         }
00195         if (!todo) {
00196                 while ( (rec=fCurrentDatabase->readRecordByIndex(Palmix++)) ) {
00197                         insertRecordToPC(PCix++, rec);
00198                 }
00199                 QTimer::singleShot(0, this, SLOT(finishedDB()));
00200                 return;
00201         }
00202 
00203 
00204         // Check if the two items are the same and only need to be updated but not moved:
00205         if ((recordid_t)rec->getID()==(recordid_t)todo->pilotId()) {
00206                 /* There is just one case that both the current palm and pc entries are
00207                    in the correct order. For this the following needs to hold:
00208                    1) neither the palm nor the pc entry have been changed
00209                    2) the pc and palm entry are the same (check the palm ID)
00210                    3) if the entries are numbered, no entries have been inserted on the palm by the sync */
00211                 // if any of them does not hold, update the two entries:
00212                 if (  !(todo->syncStatus()==Incidence::SYNCNONE) ||
00213                       (rec->getAttrib() & dlpRecAttrDirty) ||
00214                       (inserted && (flags()&FLG_NUMBERED)) ) {
00215                         updateRecords(Palmix, rec, PCix, todo);
00216                 }
00217                 PCix++; Palmix++;
00218                 QTimer::singleShot(0, this, SLOT(syncNextRecord()));
00219                 return;
00220         }
00221 
00222         // The two records don't describe the same entry so find out what to insert/move:
00223         // First, find the corresponding entry in the PC db:
00224         KCal::Todo*t=fP->findTodo(rec->getID());
00225         if (!t) { // there is not item on the PC, so insert
00226                 insertRecordToPC(PCix++, rec);
00227                 QTimer::singleShot(0, this, SLOT(syncNextRecord()));
00228                 return;
00229         }
00230 
00231         // if there are only unsynced entries before the next synced PC entry, insert them on the palm:
00232         if ((recordid_t)todo->pilotId()==0)
00233         while ((recordid_t)todo->pilotId() ==0) {
00234                 insertRecordToPalm(Palmix++, todo);
00235                 if (!(todo=fP->fAllTodos.at(PCix++))) {
00236                         QTimer::singleShot(0, this, SLOT(syncNextRecord()));
00237                         return;
00238                 }
00239         }
00240         // the next PC item has a SyncID, so find which has been changed and accordingly move/update:
00241         // if the PC entry has been removed on the palm, remove it from the PC, too
00242         PilotRecord*rec1=fCurrentDatabase->readRecordById(todo->pilotId());
00243         if (!rec1) {
00244                 fP->removeTodo(todo);
00245                 QTimer::singleShot(0, this, SLOT(syncNextRecord()));
00246                 return;
00247         }
00248         if ((t->syncStatus()==Incidence::SYNCNONE) || (rec->getAttrib() & dlpRecAttrDirty)) {
00249                 // PC entry was not changed, so use position on Palm
00250                 movePCRecord(fP->fAllTodos.find(t), PCix);
00251                 updateRecords(Palmix, rec, PCix, t);
00252                 Palmix++; PCix++;
00253                 QTimer::singleShot(0, this, SLOT(syncNextRecord()));
00254                 return;
00255         }
00256         // Palm entry was not changed, only the PC entry, so move the Palm entry and stay at the same position
00257         movePalmRecord(Palmix, Palmix + (fP->fAllTodos.find(t)-Palmix));
00258         QTimer::singleShot(0, this, SLOT(syncNextRecord()));
00259         return;
00260 }
00261 
00262 void OrganizerConduit::updateRecords(int pid, PilotRecord*rec, int  pcid, KCal::Todo*todo) {
00263         // TODO: merge the two records in a sensitive way and write them out.
00264 }
00265 
00266 void OrganizerConduit::insertRecordToPC(int pos, PilotRecord*rec) {
00267         PilotOrganizerEntry*poe=createOrganizerEntry(rec);
00268         KCal::Todo*todo=poe->getTodo();
00269         fP->fAllTodos.insert(pos, todo);
00270         delete poe;
00271 }
00272 void OrganizerConduit::insertRecordToPalm(int pos, KCal::Todo*todo) {
00273         PilotOrganizerEntry*poe=createOrganizerEntry(todo);
00274         poe->setNumber(pos);
00275         PilotRecord*rec=poe->pack();
00276         // TODO!!!
00277 //      fCurrentDatabase->insertRecord(pos, rec);
00278         KPILOT_DELETE(rec);
00279 }
00280 
00281 void OrganizerConduit::movePCRecord(int frompos, int topos) {
00282         // move the Record on the PC:
00283         if (frompos>topos) {
00284                 // TODO
00285         } else {
00286                 // TODO
00287         }
00288 }
00289 void OrganizerConduit::movePalmRecord(int frompos, int topos) {
00290         // move the Record on the Palm:
00291         if (frompos>topos) {
00292                 // TODO
00293         } else {
00294                 // TODO
00295         }
00296 }
00297 
00298 
00299 /* preSyncAction is used to initialize locally needed files and databases.
00300         e.g. open the local vCalendar file here and if that fails, return false.
00301    if false is returned, the database will be skipped, so use the return value!!! */
00302 bool OrganizerConduit::preSyncAction(DBSyncInfo*dbinfo) {
00303         FUNCTIONSETUP;
00304         PCix=0;
00305         Palmix=0;
00306         inserted=false;
00307         switch (syncinfo.syncaction) {
00308                 case st_vcal:
00309                         fCalendar=new KCal::CalendarLocal(timezone);
00310                         if (!fCalendar) return false;
00311                         
00312                         // if there is no calendar yet, use a first sync..
00313                         // the calendar is initialized, so nothing more to do...
00314                         if (!fCalendar->load(dbinfo->filename) ) {
00315 #ifdef DEBUG
00316                                 DEBUGCONDUIT << "calendar file "<<fCalendarFile<<" could not be opened. Will create a new one"<<endl;
00317 #endif
00318                                 fFullSync=true;
00319                         }
00320 
00321                         fP = new VCalPrivate(fCalendar);
00322                         if (!fP) {
00323                                 emit logError(i18n("Could not load the calendar "+dbinfo->filename));
00324                                 kdWarning() << k_funcinfo << ": Couldn't load calendar "<<dbinfo->filename<<" from local harddrive" << endl;
00325                                 return false;
00326                         }
00327                         fP->updateTodos();
00328                         if (fP->count()<1) fFullSync=true;
00329 
00330                         return true;
00331                         break;
00332                 case st_pdb:
00333                         #ifdef DEBUG
00334                         DEBUGCONDUIT<<"  skipping database "<<dbinfo->filename<<". PDB not yet implemented." <<endl;
00335                         #endif
00336                         // TODO
00337                         return false;
00338                         break;
00339                 default:
00340                         return MultiDBConduit::preSyncAction(dbinfo);
00341                         break;
00342         }
00343         return MultiDBConduit::preSyncAction(dbinfo);
00344 }
00345 
00346 
00347 /*
00348  * Given a pilot record, check to see what needs to be done to the
00349  * analogous vobject to bring things into sync.
00350  */
00351 void OrganizerConduit::updateLocalEntry(PilotRecord *rec, bool force) {
00352         FUNCTIONSETUP;
00353 
00354         PilotOrganizerEntry*entry=createOrganizerEntry(rec);
00355 
00356 #ifdef DEBUG
00357         DEBUGCONDUIT << fname
00358                 << ": Using rec @" << (int) rec
00359                 << " with ID " << ( rec ? rec->getID() : 0xffffffff)
00360                 << endl;
00361 #endif
00362 
00363 #ifdef DEBUG
00364         if (entry->getLevel()>hierlevel+1) {
00365                 DEBUGCONDUIT<<fname << ": skipped one hierarchy level. Jumped from "<<hierlevel<<" to "<<entry->getLevel()<<endl;
00366         }
00367 #endif
00368         
00369         // if we jumped one hierarchy level down, get the parent record. If the parent record was modified, we already have it, so we don't need to search
00370         // This could actually be even more optimized (most of the time we don't need the level parent, because nothing below it was changed)
00371         if (hierlevel<entry->getLevel() && levelparent[hierlevel]==0) {
00372                 levelparent[hierlevel]=fP->findTodo(previd);
00373         }
00374         hierlevel=entry->getLevel();
00375         previd=rec->getID();
00376         
00377         // we have to search the new item only if it was changed, or the force flag is set
00378         if ( (rec->getAttrib() & dlpRecAttrDirty) || force ) {
00379                 KCal::Todo *vtodo=fP->findTodo(rec->getID());
00380                 if (!vtodo) {
00381                         // no event was found, so we need to add one with some initial info
00382                         vtodo = new KCal::Todo;
00383                         fP->insertTodo(vtodo);
00384 
00385                         vtodo->setPilotId(entry->getID());
00386                         vtodo->setSyncStatus(Incidence::SYNCNONE);
00387                 }
00388                 levelparent[hierlevel]=vtodo;
00389                 // todo has a parent element, so link it with it's parent and vice versa:
00390                 if (hierlevel>0 && levelparent[hierlevel-1] ) {
00391                         vtodo->setRelatedTo(levelparent[hierlevel-1]);
00392                         if (! levelparent[hierlevel-1]->relations().containsRef(vtodo)) {
00393                                 levelparent[hierlevel-1]->addRelation(vtodo);
00394                         }
00395                 }
00396 
00397                 // we don't want to modify the vobject with pilot info, because it has
00398                 // already been  modified on the desktop.  The VObject's modified state
00399                 // overrides the PilotRec's modified state.
00400 
00401                 // otherwise, the vObject hasn't been touched.  Updated it with the
00402                 // info from the PilotRec.
00403                 
00404                 if (entry->hasDate(DATE_CREATED)) vtodo->setCreated(entry->getDate(DATE_CREATED));
00405 
00406                 if (entry->hasDate(DATE_STARTED)) vtodo->setDtStart(entry->getDate(DATE_STARTED));
00407                 else vtodo->setHasStartDate(false);
00408 
00409                 if (entry->hasDate(DATE_FINISHED)) vtodo->setCompleted(entry->getDate(DATE_FINISHED));
00410                 else vtodo->setCompleted(false);
00411 
00412                 if (entry->hasDate(DATE_DUE)) vtodo->setDtDue(entry->getDate(DATE_DUE));
00413                 else vtodo->setHasDueDate(false);
00414 
00415                 // Completed in %
00416                 vtodo->setPercentComplete(entry->getProgress());
00417                 vtodo->setCompleted(entry->getFlag(IS_CHECKED));
00418                 
00419                 // expanded does not have a counterpart: IS_EXPANDED    0x0800, IS_VISIBLE      0x0400
00420 
00421                 // PRIORITY //
00422                 vtodo->setPriority(entry->getPriority());
00423 
00424                 // TODO: NUMBERING not yet implemented //
00425 //              int nr;
00426 
00427                 vtodo->setSummary(entry->getDescription());
00428                 vtodo->setDescription(entry->getNote());
00429 
00430                 // now let the child classes do it's custom initialization
00431                 setCustomFields(vtodo, entry);
00432                 
00433                 vtodo->setSyncStatus(Incidence::SYNCNONE);
00434         }
00435         delete(entry);
00436 }
00437 
00438 
00439 
00440 
00441 
00442 // $Log: Organizer-conduit.cc,v $
00443 // Revision 1.4.4.1  2003/03/12 23:31:10  adridg
00444 // CVS_SILENT: FSF address change
00445 //
00446 // Revision 1.4  2002/08/23 22:59:29  kainhofe
00447 // Implemented Adriaan's change 'signal: void exec()' -> 'bool exec()' for "my" conduits
00448 //
00449 // Revision 1.3  2002/07/05 00:15:22  kainhofe
00450 // Added KPilotDeviceLink::tickle(), Changelog update, compile fixes
00451 //
00452 // Revision 1.2  2002/04/07 20:19:48  cschumac
00453 // Compile fixes.
00454 //
00455 // Revision 1.1  2002/04/07 12:09:43  kainhofe
00456 // Initial checkin of the conduit. The gui works mostly, but syncing crashes KPilot...
00457 //
00458 // Revision 1.1  2002/04/07 01:03:52  reinhold
00459 // the list of possible actions is now created dynamically
00460 //
00461 // Revision 1.12  2002/04/06 00:51:08  reinhold
00462 // Finally the conduit compiles again... Still have a lot of TODOS
00463 //
00464 // Revision 1.11  2002/04/05 21:17:00  reinhold
00465 // *** empty log message ***
00466 //
00467 // Revision 1.10  2002/04/01 14:36:49  reinhold
00468 // edit KListViewItems for DB setup inline
00469 // use QStringList instead of QStrList
00470 //
00471 // Revision 1.9  2002/04/01 09:22:11  reinhold
00472 // Implemented the syncNextRecord routine
00473 //
00474 // Revision 1.8  2002/03/28 13:47:53  reinhold
00475 // Added the list of synctypes, aboutbox is now directly passed on to the setup dlg (instead of being a static var)
00476 //
00477 // Revision 1.6  2002/03/23 21:46:42  reinhold
00478 // config  dlg works, but the last changes crash the plugin itself
00479 //
00480 // Revision 1.5  2002/03/23 18:21:14  reinhold
00481 // Cleaned up the structure. Works with QTimer instead of loops.
00482 //
00483 // Revision 1.4  2002/03/15 20:43:17  reinhold
00484 // Fixed the crash on loading (member function not defined)...
00485 //
00486 // Revision 1.3  2002/03/10 23:58:32  reinhold
00487 // Made the conduit compile...
00488 //
00489 // Revision 1.2  2002/03/10 16:06:43  reinhold
00490 // Cleaned up the class hierarchy, implemented some more features (should be quite finished now...)
00491 //
00492 // Revision 1.1.1.1  2002/03/09 15:38:45  reinhold
00493 // Initial checin of the  project manager / List manager conduit.
00494 //
00495 //
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.4.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Sat Oct 18 02:47:14 2003 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001