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
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>
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
00112 fCounter(0)
00113 { } ;
00114 ~KNotesActionPrivate()
00115 {
00116 KPILOT_DELETE(fKNotes);
00117 KPILOT_DELETE(fTimer);
00118 }
00119
00120
00121
00122 QMap <KNoteID_t,QString> fNotes;
00123
00124
00125
00126 QMap <KNoteID_t,QString>::ConstIterator fIndex;
00127
00128 DCOPClient *fDCOP;
00129 KNotesIface_stub *fKNotes;
00130
00131 QTimer *fTimer;
00132
00133
00134
00135
00136 int fCounter;
00137
00138
00139
00140
00141 QValueList<NoteAndMemo> fIdList;
00142 } ;
00143
00144
00145 const char * const KNotesAction::noteIdsKey="KNoteIds";
00146 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 KNotesAction::~KNotesAction()
00168 {
00169 FUNCTIONSETUP;
00170
00171 KPILOT_DELETE(fP);
00172 }
00173
00174 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
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 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
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
00519
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
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
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
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 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