kpilot Library API Documentation

knotes-action.cc

00001 /* knotes-action.cc                      KPilot
00002 **
00003 ** Copyright (C) 2001,2002,2003 by Dan Pilone
00004 **
00005 ** This file defines the SyncAction for the knotes-conduit plugin.
00006 */
00007 
00008 /*
00009 ** This program is free software; you can redistribute it and/or modify
00010 ** it under the terms of the GNU General Public License as published by
00011 ** the Free Software Foundation; either version 2 of the License, or
00012 ** (at your option) any later version.
00013 **
00014 ** This program is distributed in the hope that it will be useful,
00015 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00017 ** GNU General Public License for more details.
00018 **
00019 ** You should have received a copy of the GNU General Public License
00020 ** along with this program in a file called COPYING; if not, write to
00021 ** the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
00022 ** MA 02111-1307, USA.
00023 */
00024 
00025 /*
00026 ** Bug reports and questions can be sent to kde-pim@kde.org
00027 */
00028 
00029 #include "options.h"
00030 
00031 
00032 #include <qmap.h>
00033 #include <qtimer.h>
00034 
00035 #include <kapplication.h>
00036 
00037 #include <kconfig.h>
00038 #include <dcopclient.h>
00039 
00040 #include <time.h>  // required by pilot-link includes
00041 
00042 #include <pi-memo.h>
00043 
00044 #include "pilotMemo.h"
00045 #include "pilotSerialDatabase.h"
00046 
00047 #include "KNotesIface_stub.h"
00048 
00049 #include "knotes-factory.h"
00050 
00051 #include "knotes-action.moc"
00052 
00053 
00054 typedef QString KNoteID_t;
00055 typedef const QString &KNoteID_pt;
00056 
00057 class NoteAndMemo
00058 {
00059 public:
00060     NoteAndMemo() : noteId(),memoId(-1) { } ;
00061     NoteAndMemo(KNoteID_pt noteid,int memoid) : noteId(noteid),memoId(memoid) { } ;
00062 
00063     int memo() const { return memoId; } ;
00064     KNoteID_t note() const { return noteId; } ;
00065     bool valid() const { return (!noteId.isEmpty()) && (memoId>0); } ;
00066     QString toString() const { return CSL1("<%1,%2>").arg(noteId).arg(memoId); } ;
00067 
00068     static NoteAndMemo findNote(const QValueList<NoteAndMemo> &,KNoteID_pt note);
00069     static NoteAndMemo findMemo(const QValueList<NoteAndMemo> &,int memo);
00070 
00071 protected:
00072     KNoteID_t noteId;
00073     int memoId;
00074 } ;
00075 
00076 NoteAndMemo NoteAndMemo::findNote(const QValueList<NoteAndMemo> &l ,KNoteID_pt note)
00077 {
00078     FUNCTIONSETUP;
00079 
00080     for (QValueList<NoteAndMemo>::ConstIterator it =l.begin();
00081         it != l.end();
00082         ++it)
00083     {
00084         if ((*it).note()==note) return *it;
00085     }
00086 
00087     return NoteAndMemo();
00088 }
00089 
00090 NoteAndMemo NoteAndMemo::findMemo(const QValueList<NoteAndMemo> &l ,int memo)
00091 {
00092     FUNCTIONSETUP;
00093 
00094     for (QValueList<NoteAndMemo>::ConstIterator it =l.begin();
00095         it != l.end();
00096         ++it)
00097     {
00098         if ((*it).memo()==memo) return *it;
00099     }
00100 
00101     return NoteAndMemo();
00102 }
00103 
00104 class KNotesAction::KNotesActionPrivate
00105 {
00106 public:
00107     KNotesActionPrivate() :
00108         fDCOP(0L),
00109         fKNotes(0L),
00110         fTimer(0L),
00111         // fDatabase(0L),
00112         fCounter(0)
00113     { } ;
00114     ~KNotesActionPrivate()
00115     {
00116         KPILOT_DELETE(fKNotes);
00117         KPILOT_DELETE(fTimer);
00118     }
00119 
00120     // This is the collection of  notes held by KNotes and
00121         // returned by the notes() DCOP call.
00122     QMap <KNoteID_t,QString> fNotes;
00123     // This iterates through that list; it's in here because
00124     // we use slots to process one item at a time and need
00125     // to keep track of where we are between slot calls.
00126     QMap <KNoteID_t,QString>::ConstIterator fIndex;
00127     // The DCOP client for this application, and the KNotes stub.
00128     DCOPClient *fDCOP;
00129     KNotesIface_stub *fKNotes;
00130     // The timer for invoking process() to do some more work.
00131     QTimer *fTimer;
00132     // The database we're working with (MemoDB)
00133     // PilotSerialDatabase *fDatabase;
00134     // Some counter that needs to be preserved between calls to
00135     // process(). Typically used to note how much work is done.
00136     int fCounter;
00137 
00138     // We need to translate between the ids that KNotes uses and
00139     // Pilot id's, so we make a list of pairs.
00140     //
00141     QValueList<NoteAndMemo> fIdList;
00142 } ;
00143 
00144 
00145 /* static */ const char * const KNotesAction::noteIdsKey="KNoteIds";
00146 /* static */ const char * const KNotesAction::memoIdsKey="MemoIds";
00147 
00148 
00149 KNotesAction::KNotesAction(KPilotDeviceLink *o,
00150     const char *n, const QStringList &a) :
00151     ConduitAction(o,!n ? "knotes-conduit" : n,a),
00152     fP(new KNotesActionPrivate)
00153 {
00154     FUNCTIONSETUP;
00155 
00156 
00157     if (fP) fP->fDCOP = KApplication::kApplication()->dcopClient();
00158 
00159     if (fP && !fP->fDCOP)
00160     {
00161         kdWarning() << k_funcinfo
00162             << ": Can't get DCOP client."
00163             << endl;
00164     }
00165 }
00166 
00167 /* virtual */ KNotesAction::~KNotesAction()
00168 {
00169     FUNCTIONSETUP;
00170 
00171     KPILOT_DELETE(fP);
00172 }
00173 
00174 /* virtual */ bool KNotesAction::exec()
00175 {
00176     FUNCTIONSETUP;
00177 
00178     QString e;
00179     if (!fP || !fP->fDCOP)
00180     {
00181         emit logError(i18n("No DCOP connection could be made. The "
00182             "conduit cannot function like this."));
00183         return false;
00184 
00185     }
00186     if (!PluginUtility::isRunning("knotes"))
00187     {
00188         emit logError(i18n("KNotes is not running. The conduit must "
00189             "be able to make a DCOP connection to KNotes "
00190             "for synchronization to take place. "
00191             "Please start KNotes and try again."));
00192         return false;
00193     }
00194 
00195     if (!fConfig) return false;
00196 
00197     fP->fKNotes = new KNotesIface_stub("knotes","KNotesIface");
00198 
00199     fP->fNotes = fP->fKNotes->notes();
00200 
00201     // Database names seem to be latin1
00202     openDatabases(QString::fromLatin1("MemoDB"));
00203 
00204     if (isTest())
00205     {
00206         listNotes();
00207     }
00208     else
00209     {
00210         fP->fTimer = new QTimer(this);
00211         fStatus = Init;
00212         resetIndexes();
00213 
00214         connect(fP->fTimer,SIGNAL(timeout()),SLOT(process()));
00215 
00216         fP->fTimer->start(0,false);
00217     }
00218 
00219     return true;
00220 }
00221 
00222 void KNotesAction::resetIndexes()
00223 {
00224     FUNCTIONSETUP;
00225 
00226     fP->fCounter = 0;
00227     fP->fIndex = fP->fNotes.begin();
00228 }
00229 
00230 void KNotesAction::listNotes()
00231 {
00232     FUNCTIONSETUP;
00233 
00234     QMap<KNoteID_t,QString>::ConstIterator i = fP->fNotes.begin();
00235     while (i != fP->fNotes.end())
00236     {
00237 #ifdef DEBUG
00238         DEBUGCONDUIT << fname
00239             << ": "
00240             << i.key()
00241             << "->"
00242             << i.data()
00243             << (fP->fKNotes->isNew(CSL1("kpilot"),i.key()) ?
00244                 " (new)" : "" )
00245             << endl;
00246 #endif
00247         i++;
00248     }
00249 
00250     emit syncDone(this);
00251 }
00252 
00253 /* slot */ void KNotesAction::process()
00254 {
00255     FUNCTIONSETUP;
00256 #ifdef DEBUG
00257     DEBUGCONDUIT << fname
00258         << ": Now in state " << fStatus << endl;
00259 #endif
00260 
00261     switch(fStatus)
00262     {
00263     case Init:
00264         getAppInfo();
00265         getConfigInfo();
00266         break;
00267     case ModifiedNotesToPilot :
00268         if (modifyNoteOnPilot())
00269         {
00270             resetIndexes();
00271             fStatus = NewNotesToPilot;
00272         }
00273         break;
00274     case NewNotesToPilot :
00275         if (addNewNoteToPilot())
00276         {
00277             resetIndexes();
00278             fStatus = MemosToKNotes;
00279             fDatabase->resetDBIndex();
00280         }
00281         break;
00282     case MemosToKNotes :
00283         if (syncMemoToKNotes())
00284         {
00285             fStatus=Cleanup;
00286         }
00287         break;
00288     case Cleanup :
00289         cleanupMemos();
00290         break;
00291     default :
00292         if (fP->fTimer) fP->fTimer->stop();
00293         emit syncDone(this);
00294     }
00295 }
00296 
00297 
00298 void KNotesAction::getConfigInfo()
00299 {
00300     FUNCTIONSETUP;
00301 
00302     if (fConfig)
00303     {
00304         KConfigGroupSaver g(fConfig,KNotesConduitFactory::group);
00305 
00306         QValueList<KNoteID_t> notes;
00307         QValueList<int> memos;
00308 
00309 
00310                 // Make this match the type of KNoteID_t !
00311         notes=fConfig->readListEntry(noteIdsKey);
00312         memos=fConfig->readIntListEntry(memoIdsKey);
00313 
00314         if (notes.count() != memos.count())
00315         {
00316             kdWarning() << k_funcinfo
00317                 << ": Notes and memo id lists don't match ("
00318                 << notes.count()
00319                 << ","
00320                 << memos.count()
00321                 << ")"
00322                 << endl;
00323             notes.clear();
00324             memos.clear();
00325             fFirstSync = true;
00326         }
00327 
00328         QValueList<KNoteID_t>::ConstIterator iNotes = notes.begin();
00329         QValueList<int>::ConstIterator iMemos = memos.begin();
00330 
00331         while((iNotes != notes.end()) && (iMemos != memos.end()))
00332         {
00333             fP->fIdList.append(NoteAndMemo(*iNotes,*iMemos));
00334             ++iNotes;
00335             ++iMemos;
00336         }
00337     }
00338 }
00339 
00340 void KNotesAction::getAppInfo()
00341 {
00342     FUNCTIONSETUP;
00343 
00344 
00345     unsigned char buffer[PilotDatabase::MAX_APPINFO_SIZE];
00346     int appInfoSize = fDatabase->readAppBlock(buffer,PilotDatabase::MAX_APPINFO_SIZE);
00347     struct MemoAppInfo memoInfo;
00348 
00349     if (appInfoSize<0)
00350     {
00351         fStatus=Error;
00352         return;
00353     }
00354 
00355     unpack_MemoAppInfo(&memoInfo,buffer,appInfoSize);
00356     PilotDatabase::listAppInfo(&memoInfo.category);
00357 
00358     resetIndexes();
00359     fStatus=ModifiedNotesToPilot;
00360 
00361     addSyncLogEntry(i18n("[KNotes conduit: "));
00362 }
00363 
00364 
00365 bool KNotesAction::modifyNoteOnPilot()
00366 {
00367     FUNCTIONSETUP;
00368 
00369     if (fP->fIndex == fP->fNotes.end())
00370     {
00371         if (fP->fCounter)
00372         {
00373             addSyncLogEntry(i18n("Modified one memo.",
00374                 "Modified %n memos.",
00375                 fP->fCounter));
00376         }
00377         else
00378         {
00379             addSyncLogEntry(TODO_I18N("No memos were changed."));
00380         }
00381         return true;
00382     }
00383 
00384     if (fP->fKNotes->isModified(CSL1("kpilot"),fP->fIndex.key()))
00385     {
00386 #ifdef DEBUG
00387         DEBUGCONDUIT << fname
00388             << ": The note #"
00389             << fP->fIndex.key()
00390             << " with name "
00391             << fP->fIndex.data()
00392             << " is modified in KNotes."
00393             << endl;
00394 #endif
00395 
00396         NoteAndMemo nm = NoteAndMemo::findNote(fP->fIdList,
00397             fP->fIndex.key());
00398 
00399         if (nm.valid())
00400         {
00401             QString text = fP->fIndex.data() + CSL1("\n") ;
00402             text.append(fP->fKNotes->text(fP->fIndex.key()));
00403 
00404             PilotMemo *a = new PilotMemo(text);
00405             PilotRecord *r = a->pack();
00406             r->setID(nm.memo());
00407 
00408             int newid = fDatabase->writeRecord(r);
00409 
00410             if (newid != nm.memo())
00411             {
00412                 kdWarning() << k_funcinfo
00413                     << ": Memo id changed during write? "
00414                     << "From "
00415                     << nm.memo()
00416                     << " to "
00417                     << newid
00418                     << endl;
00419             }
00420         }
00421         else
00422         {
00423             kdWarning() << ": Modified note unknown to Pilot" << endl;
00424         }
00425 
00426         fP->fCounter++;
00427     }
00428 
00429     ++(fP->fIndex);
00430     return false;
00431 }
00432 
00433 bool KNotesAction::addNewNoteToPilot()
00434 {
00435     FUNCTIONSETUP;
00436 
00437     if (fP->fIndex == fP->fNotes.end())
00438     {
00439         if (fP->fCounter)
00440         {
00441             addSyncLogEntry(i18n("Added one new memo.",
00442                 "Added %n new memos.",
00443                 fP->fCounter));
00444         }
00445         else
00446         {
00447             addSyncLogEntry(TODO_I18N("No memos were added."));
00448         }
00449         return true;
00450     }
00451 
00452     if (fP->fKNotes->isNew(CSL1("kpilot"),fP->fIndex.key()))
00453     {
00454 #ifdef DEBUG
00455         DEBUGCONDUIT << fname
00456             << ": The note #"
00457             << fP->fIndex.key()
00458             << " with name "
00459             << fP->fIndex.data()
00460             << " is new to the Pilot."
00461             << endl;
00462 #endif
00463 
00464         QString text = fP->fIndex.data() + CSL1("\n") ;
00465         text.append(fP->fKNotes->text(fP->fIndex.key()));
00466 
00467         PilotMemo *a = new PilotMemo(text);
00468         PilotRecord *r = a->pack();
00469 
00470         int newid = fDatabase->writeRecord(r);
00471 
00472         fP->fIdList.append(NoteAndMemo(fP->fIndex.key(),newid));
00473 
00474         delete r;
00475         delete a;
00476 
00477         fP->fCounter++;
00478     }
00479 
00480     ++(fP->fIndex);
00481     return false;
00482 }
00483 
00484 bool KNotesAction::syncMemoToKNotes()
00485 {
00486     FUNCTIONSETUP;
00487 
00488     PilotRecord *rec = fDatabase->readNextModifiedRec();
00489     if (!rec)
00490     {
00491         if (fP->fCounter)
00492         {
00493             addSyncLogEntry(i18n("Added one memo to KNotes.",
00494                 "Added %n memos to KNotes.",fP->fCounter));
00495         }
00496         else
00497         {
00498             addSyncLogEntry(TODO_I18N("No memos added to KNotes."));
00499         }
00500         return true;
00501     }
00502 
00503     fP->fCounter++;
00504 
00505     PilotMemo *memo = new PilotMemo(rec);
00506     NoteAndMemo m = NoteAndMemo::findMemo(fP->fIdList,memo->id());
00507 
00508 #ifdef DEBUG
00509     DEBUGCONDUIT << fname << ": Looking at memo "
00510         << memo->id()
00511         << " which was found "
00512         << m.toString()
00513         << endl;
00514 #endif
00515 
00516     if (m.valid())
00517     {
00518         // We knew about the note already, but it
00519         // has changed on the Pilot.
00520         //
00521         //
00522         if (memo->isDeleted())
00523         {
00524 #ifdef DEBUG
00525             DEBUGCONDUIT << fname << ": It's been deleted." << endl;
00526 #endif
00527             fP->fKNotes->killNote(m.note());
00528         }
00529         else
00530         {
00531 #ifdef DEBUG
00532             DEBUGCONDUIT << fname << ": It's just modified." << endl;
00533             DEBUGCONDUIT << fname << ": <"
00534                 << fP->fNotes[m.note()]
00535                 << "> <"
00536                 << memo->shortTitle()
00537                 << ">"
00538                 << endl;
00539 #endif
00540             if (fP->fNotes[m.note()] != memo->shortTitle())
00541             {
00542                 // Name changed. KNotes might complain though.
00543                 fP->fKNotes->setName(m.note(),memo->shortTitle());
00544             }
00545             fP->fKNotes->setText(m.note(),memo->text());
00546         }
00547     }
00548     else
00549     {
00550         if (memo->isDeleted())
00551         {
00552 #ifdef DEBUG
00553             DEBUGCONDUIT << fname << ": It's new and deleted." << endl;
00554 #endif
00555             // Do nothing, it's new and deleted at the same time
00556         }
00557         else
00558         {
00559             KNoteID_t i = fP->fKNotes->newNote(memo->shortTitle(),memo->text());
00560             fP->fIdList.append(NoteAndMemo(i,memo->id()));
00561 #ifdef DEBUG
00562             DEBUGCONDUIT << fname << ": It's new with knote id " << i << endl;
00563 #endif
00564         }
00565     }
00566 
00567     if (memo) delete memo;
00568     if (rec) delete rec;
00569 
00570     return false;
00571 }
00572 
00573 
00574 void KNotesAction::cleanupMemos()
00575 {
00576     FUNCTIONSETUP;
00577 
00578     // Tell KNotes we're up-to-date
00579     fP->fKNotes->sync(CSL1("kpilot"));
00580 
00581     if (fConfig)
00582     {
00583 #ifdef DEBUG
00584         DEBUGCONDUIT << fname
00585             << ": Writing "
00586             << fP->fIdList.count()
00587             << " pairs to the config file."
00588             << endl;
00589         DEBUGCONDUIT << fname
00590             << ": The config file is read-only: "
00591             << fConfig->isReadOnly()
00592             << endl;
00593 #endif
00594 
00595         KConfigGroupSaver g(fConfig,KNotesConduitFactory::group);
00596 
00597         QValueList<KNoteID_t> notes;
00598         QValueList<int> memos;
00599 
00600         for (QValueList<NoteAndMemo>::ConstIterator i =
00601             fP->fIdList.begin();
00602             i!=fP->fIdList.end();
00603             ++i)
00604         {
00605             notes.append((*i).note());
00606             memos.append((*i).memo());
00607         }
00608 
00609         fConfig->writeEntry(noteIdsKey,notes);
00610         fConfig->writeEntry(memoIdsKey,memos);
00611         fConfig->sync();
00612     }
00613 
00614     fStatus=Done;
00615     fDatabase->cleanup();
00616     fDatabase->resetSyncFlags();
00617 
00618     addSyncLogEntry(CSL1("]\n"));
00619 }
00620 
00621 
00622 /* virtual */ QString KNotesAction::statusString() const
00623 {
00624     switch(fStatus)
00625     {
00626     case Init : return CSL1("Init");
00627     case NewNotesToPilot :
00628         return CSL1("NewNotesToPilot key=%1")
00629             .arg(fP->fIndex.key());
00630     case Done :
00631         return CSL1("Done");
00632     default :
00633         return CSL1("Unknown (%1)").arg(fStatus);
00634     }
00635 }
00636 
00637 
00638 
KDE Logo
This file is part of the documentation for kpilot Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat May 1 11:36:47 2004 by doxygen 1.2.15 written by Dimitri van Heesch, © 1997-2003