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