kpilot Library API Documentation

abbrowser-conduit.cc

00001 /* abbrowser-conduit.cc                           KPilot
00002 **
00003 ** Copyright (C) 2000,2001 by Dan Pilone
00004 ** Copyright (C) 2002-2003 by Reinhold Kainhofer
00005 **
00006 ** The abbrowser conduit copies addresses from the Pilot's address book to 
00007 ** the KDE addressbook maintained via the kabc library.
00008 */
00009 
00010 /*
00011 ** This program is free software; you can redistribute it and/or modify
00012 ** it under the terms of the GNU General Public License as published by
00013 ** the Free Software Foundation; either version 2 of the License, or
00014 ** (at your option) any later version.
00015 **
00016 ** This program is distributed in the hope that it will be useful,
00017 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
00018 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00019 ** GNU General Public License for more details.
00020 **
00021 ** You should have received a copy of the GNU General Public License
00022 ** along with this program in a file called COPYING; if not, write to
00023 ** the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
00024 ** MA 02111-1307, USA.
00025 */
00026 
00027 /*
00028 ** Bug reports and questions can be sent to kde-pim@kde.org.
00029 */
00030 
00031 
00032 
00033 #include "options.h"
00034 #include "abbrowser-conduit.moc"
00035 
00036 #include <unistd.h>
00037 
00038 #include <qtimer.h>
00039 #include <qvbuttongroup.h>
00040 #include <qcheckbox.h>
00041 #include <qtextcodec.h>
00042 
00043 #include <kglobal.h>
00044 #include <kdebug.h>
00045 #include <kconfig.h>
00046 #include <kabc/addressbook.h>
00047 #include <kabc/stdaddressbook.h>
00048 
00049 #include <pilotUser.h>
00050 #include <pilotSerialDatabase.h>
00051 
00052 #include "abbrowser-factory.h"
00053 #include "resolutionDialog.h"
00054 
00055 // Something to allow us to check what revision
00056 // the modules are that make up a binary distribution.
00057 //
00058 //
00059 const char *abbrowser_conduit_id="$Id: abbrowser-conduit.cc,v 1.54.2.10 2003/07/30 23:46:20 kainhofe Exp $";
00060 
00061 const QString AbbrowserConduit::flagString=CSL1("Flag");
00062 const QString AbbrowserConduit::appString=CSL1("KPILOT");
00063 const QString AbbrowserConduit::idString=CSL1("RecordID");
00064 
00065 bool AbbrowserConduit::fPilotStreetHome=true;
00066 bool AbbrowserConduit::fPilotFaxHome=true;
00067 enum AbbrowserConduit::ePilotOtherEnum AbbrowserConduit::ePilotOther=AbbrowserConduit::eOtherPhone;
00068 
00069 
00070 
00071 /*********************************************************************
00072                         C O N S T R U C T O R
00073  *********************************************************************/
00074 
00075 
00076 
00077 
00078 
00079 AbbrowserConduit::AbbrowserConduit(KPilotDeviceLink * o, const char *n, const QStringList & a):
00080     ConduitAction(o, n, a),
00081     addresseeMap(),
00082     syncedIds(),
00083     aBook(0L),
00084     abiter()
00085 {
00086   FUNCTIONSETUP;
00087 #ifdef DEBUG
00088   DEBUGCONDUIT<<abbrowser_conduit_id<<endl;
00089 #endif
00090 }
00091 
00092 
00093 
00094 AbbrowserConduit::~AbbrowserConduit()
00095 {
00096   FUNCTIONSETUP;
00097 }
00098 
00099 
00100 
00101 
00102 
00103 /*********************************************************************
00104                 L O A D I N G   T H E   D A T A
00105  *********************************************************************/
00106 
00107 
00108 
00109 
00110 
00111 /* Builds the map which links record ids to uid's of KABC::Addressee
00112 */
00113 void AbbrowserConduit::_mapContactsToPilot(QMap < recordid_t, QString > &idContactMap) const
00114 {
00115         FUNCTIONSETUP;
00116 
00117         idContactMap.clear();
00118 
00119         for(KABC::AddressBook::Iterator contactIter = aBook->begin();
00120                 contactIter != aBook->end(); ++contactIter)
00121         {
00122                 KABC::Addressee aContact = *contactIter;
00123                 QString recid = aContact.custom(appString, idString);
00124                 if(!recid.isEmpty())
00125                 {
00126                         recordid_t id = recid.toULong();
00127                         idContactMap.insert(id, aContact.uid());
00128                 }
00129         }
00130 #ifdef DEBUG
00131         DEBUGCONDUIT << fname << ": Loaded " << idContactMap.size() << 
00132             " addresses from the addressbook " << 
00133                         dynamic_cast <KABC::StdAddressBook*>(aBook)->fileName() << endl;
00134 #endif
00135 }
00136 
00137 
00138 
00139 bool AbbrowserConduit::_prepare()
00140 {
00141         FUNCTIONSETUP;
00142 
00143         readConfig();
00144         syncedIds.clear();
00145 
00146         return true;
00147 }
00148 
00149 
00150 
00151 void AbbrowserConduit::readConfig()
00152 {
00153         FUNCTIONSETUP;
00154 
00155         KConfigGroupSaver g(fConfig, AbbrowserConduitFactory::group());
00156 
00157         fSmartMerge=fConfig->readBoolEntry(AbbrowserConduitFactory::smartMerge(), true);
00158         fConflictResolution=(EConflictResolution) fConfig->readNumEntry(AbbrowserConduitFactory::conflictResolution(), eUserChoose);
00159         fArchive=fConfig->readBoolEntry(AbbrowserConduitFactory::archiveDeletedRecs(), true);
00160         fPilotStreetHome=!fConfig->readBoolEntry(AbbrowserConduitFactory::streetType(), true);
00161         fPilotFaxHome=!fConfig->readBoolEntry(AbbrowserConduitFactory::faxType(), true);
00162         syncAction=fConfig->readNumEntry(AbbrowserConduitFactory::syncMode(), SYNC_FAST);
00163         fFirstTime=fConfig->readBoolEntry(AbbrowserConduitFactory::firstSync(), false);
00164         ePilotOther=(ePilotOtherEnum)(fConfig->readNumEntry(AbbrowserConduitFactory::otherField(), eOtherPhone));
00165 
00166 #ifdef DEBUG
00167         DEBUGCONDUIT << fname
00168                 << ": Settings "
00169                 << " fSmartMerge=" << fSmartMerge
00170                 << " fConflictResolution=" << fConflictResolution
00171                 << " fPilotStreetHome=" << fPilotStreetHome
00172                 << " fPilotFaxHome=" << fPilotFaxHome
00173                 << " syncAction=" << syncAction
00174                 << " fArchive=" << fArchive
00175                 << " fFirstTime=" << fFirstTime << endl;
00176 #endif
00177 }
00178 
00179 
00180 
00181 bool AbbrowserConduit::_loadAddressBook()
00182 {
00183         FUNCTIONSETUP;
00184         aBook = KABC::StdAddressBook::self();
00185         aBook->load();
00186         abChanged = false;
00187         // get the addresseMap which maps Pilot unique record(address) id's to
00188         // a Abbrowser KABC::Addressee; allows for easy lookup and comparisons
00189         if(aBook->begin() == aBook->end())
00190         {
00191                 fFirstTime = true;
00192         }
00193         else
00194         {
00195                 _mapContactsToPilot(addresseeMap);
00196         }
00197         return(aBook != 0L);
00198 }
00199 
00200 
00201 
00202 bool AbbrowserConduit::_saveAddressBook()
00203 {
00204         FUNCTIONSETUP;
00205 
00206         if(!abChanged) return true;
00207         return StdAddressBook::save();
00208 }
00209 
00210 
00211 
00212 void AbbrowserConduit::_setAppInfo()
00213 {
00214         FUNCTIONSETUP;
00215         // get the address application header information
00216         unsigned char *buffer = new unsigned char[PilotAddress::APP_BUFFER_SIZE];
00217         int appLen=fDatabase->readAppBlock(buffer, PilotAddress::APP_BUFFER_SIZE);
00218 
00219         unpack_AddressAppInfo(&fAddressAppInfo, buffer, appLen);
00220         delete[]buffer;
00221         buffer = NULL;
00222 
00223 #ifdef DEBUG
00224         DEBUGCONDUIT << fname << " lastUniqueId" << fAddressAppInfo.category.lastUniqueID << endl;
00225         for(int i = 0; i < 16; i++)
00226         {
00227                 DEBUGCONDUIT << fname << " cat " << i << " =" << fAddressAppInfo.category.name[i] << endl;
00228         }
00229 
00230         for(int x = 0; x < 8; x++)
00231         {
00232                 DEBUGCONDUIT << fname << " phone[" << x << "] = " << fAddressAppInfo.phoneLabels[x] << endl;
00233         }
00234 #endif
00235 }
00236 
00237 
00238 QString AbbrowserConduit::getOtherField(const KABC::Addressee & abEntry)
00239 {
00240         switch(ePilotOther)
00241         {
00242                 case eOtherPhone:
00243                         return abEntry.phoneNumber(0).number();
00244                 case eAssistant:
00245                         return abEntry.custom(CSL1("KADDRESSBOOK"), CSL1("AssistantsName"));
00246                 case eBusinessFax:
00247                         return abEntry.phoneNumber(KABC::PhoneNumber::Fax | KABC::PhoneNumber::Work).number();
00248                 case eCarPhone:
00249                         return abEntry.phoneNumber(KABC::PhoneNumber::Car).number();
00250                 case eEmail2:
00251                         return abEntry.emails().first();
00252                 case eHomeFax:
00253                         return abEntry.phoneNumber(KABC::PhoneNumber::Fax | KABC::PhoneNumber::Home).number();
00254                 case eTelex:
00255                         return abEntry.phoneNumber(KABC::PhoneNumber::Bbs).number();
00256                 case eTTYTTDPhone:
00257                         return abEntry.phoneNumber(KABC::PhoneNumber::Pcs).number();
00258                 default:
00259                         return QString::null;
00260         }
00261 }
00262 
00263 
00264 void AbbrowserConduit::setOtherField(KABC::Addressee & abEntry, QString nr)
00265 {
00266         KABC::PhoneNumber phone;
00267         switch(ePilotOther)
00268         {
00269                 case eOtherPhone:
00270                         phone = abEntry.phoneNumber(0);
00271                         phone.setNumber(nr);
00272                         abEntry.insertPhoneNumber(phone);
00273                         break;
00274                 case eAssistant:
00275                         abEntry.insertCustom(CSL1("KADDRESSBOOK"), CSL1("AssistantsName"), nr);
00276                         break;
00277                 case eBusinessFax:
00278                         phone = abEntry.phoneNumber(KABC::PhoneNumber::Fax | KABC::PhoneNumber::Work);
00279                         phone.setNumber(nr);
00280                         abEntry.insertPhoneNumber(phone);
00281                         break;
00282                 case eCarPhone:
00283                         phone = abEntry.phoneNumber(KABC::PhoneNumber::Car);
00284                         phone.setNumber(nr);
00285                         abEntry.insertPhoneNumber(phone);
00286                         break;
00287                 case eEmail2:
00288                         return abEntry.insertEmail(nr);
00289                 case eHomeFax:
00290                         phone = abEntry.phoneNumber(KABC::PhoneNumber::Fax | KABC::PhoneNumber::Home);
00291                         phone.setNumber(nr);
00292                         abEntry.insertPhoneNumber(phone);
00293                         break;
00294                 case eTelex:
00295                         phone = abEntry.phoneNumber(KABC::PhoneNumber::Bbs);
00296                         phone.setNumber(nr);
00297                         abEntry.insertPhoneNumber(phone);
00298                         break;
00299                 case eTTYTTDPhone:
00300                         phone = abEntry.phoneNumber(KABC::PhoneNumber::Pcs);
00301                         phone.setNumber(nr);
00302                         abEntry.insertPhoneNumber(phone);
00303                         break;
00304         }
00305 }
00306 
00307 
00308 KABC::PhoneNumber AbbrowserConduit::getFax(const KABC::Addressee & abEntry)
00309 {
00310         return abEntry.phoneNumber(KABC::PhoneNumber::Fax |
00311                 ( (fPilotFaxHome) ?(KABC::PhoneNumber::Home) :(KABC::PhoneNumber::Work)));
00312 }
00313 
00314 
00319 KABC::Address AbbrowserConduit::getAddress(const KABC::Addressee & abEntry)
00320 {
00321         int type=(fPilotStreetHome)?(KABC::Address::Home):(KABC::Address::Work);
00322         KABC::Address ad(abEntry.address(KABC::Address::Pref));
00323         if (!ad.isEmpty()) return ad;
00324         ad=abEntry.address(type);
00325         if (!ad.isEmpty()) return ad;
00326         ad=abEntry.address((fPilotStreetHome) ?(KABC::Address::Work):(KABC::Address::Home));
00327         if (!ad.isEmpty()) return ad;
00328 
00329         return abEntry.address(type | KABC::Address::Pref);
00330 }
00331 
00332 
00333 
00334 
00335 /*********************************************************************
00336                      D E B U G   O U T P U T
00337  *********************************************************************/
00338 
00339 
00340 
00341 
00342 
00343 #ifdef DEBUG
00344 void AbbrowserConduit::showAddressee(const KABC::Addressee & abAddress)
00345 {
00346         FUNCTIONSETUP;
00347         DEBUGCONDUIT << "\tAbbrowser Contact Entry" << endl;
00348         DEBUGCONDUIT << "\t\tLast name = " << abAddress.familyName() << endl;
00349         DEBUGCONDUIT << "\t\tFirst name = " << abAddress.givenName() << endl;
00350         DEBUGCONDUIT << "\t\tCompany = " << abAddress.organization() << endl;
00351         DEBUGCONDUIT << "\t\tJob Title = " << abAddress.title() << endl;
00352         DEBUGCONDUIT << "\t\tNote = " << abAddress.note() << endl;
00353         DEBUGCONDUIT << "\t\tHome phone = " << abAddress.phoneNumber(KABC::PhoneNumber::Home).number() << endl;
00354         DEBUGCONDUIT << "\t\tWork phone = " << abAddress.phoneNumber(KABC::PhoneNumber::Work).number() << endl;
00355         DEBUGCONDUIT << "\t\tMobile phone = " << abAddress.phoneNumber(KABC::PhoneNumber::Cell).number() << endl;
00356         DEBUGCONDUIT << "\t\tEmail = " << abAddress.preferredEmail() << endl;
00357         DEBUGCONDUIT << "\t\tFax = " << getFax(abAddress).number() << endl;
00358         DEBUGCONDUIT << "\t\tPager = " << abAddress.phoneNumber(KABC::PhoneNumber::Pager).number() << endl;
00359         DEBUGCONDUIT << "\t\tCategory = " << abAddress.categories().first() << endl;
00360 }
00361 
00362 
00363 
00364 void AbbrowserConduit::showPilotAddress(const PilotAddress & pilotAddress)
00365 {
00366         FUNCTIONSETUP;
00367         DEBUGCONDUIT << "\tPilot Address" << endl;
00368         DEBUGCONDUIT << "\t\tLast name = " << pilotAddress.getField(entryLastname) << endl;
00369         DEBUGCONDUIT << "\t\tFirst name = " << pilotAddress.getField(entryFirstname) << endl;
00370         DEBUGCONDUIT << "\t\tCompany = " << pilotAddress.getField(entryCompany) << endl;
00371         DEBUGCONDUIT << "\t\tJob Title = " << pilotAddress.getField(entryTitle) << endl;
00372         DEBUGCONDUIT << "\t\tNote = " << pilotAddress.getField(entryNote) << endl;
00373         DEBUGCONDUIT << "\t\tHome phone = " << pilotAddress.getPhoneField(PilotAddress::eHome) << endl;
00374         DEBUGCONDUIT << "\t\tWork phone = " << pilotAddress.getPhoneField(PilotAddress::eWork) << endl;
00375         DEBUGCONDUIT << "\t\tMobile phone = " << pilotAddress.getPhoneField(PilotAddress::eMobile) << endl;
00376         DEBUGCONDUIT << "\t\tEmail = " << pilotAddress.getPhoneField(PilotAddress::eEmail) << endl;
00377         DEBUGCONDUIT << "\t\tFax = " << pilotAddress.getPhoneField(PilotAddress::eFax) << endl;
00378         DEBUGCONDUIT << "\t\tPager = " << pilotAddress.getPhoneField(PilotAddress::ePager) << endl;
00379         DEBUGCONDUIT << "\t\tOther = " << pilotAddress.getPhoneField(PilotAddress::eOther) << endl;
00380         DEBUGCONDUIT << "\t\tCategory = " << pilotAddress.getCategoryLabel() << endl;
00381 }
00382 #endif
00383 
00384 
00385 
00386 
00387 
00388 /*********************************************************************
00389                 S Y N C   S T R U C T U R E
00390  *********************************************************************/
00391 
00392 
00393 
00394 
00395 
00396 /* virtual */ bool AbbrowserConduit::exec()
00397 {
00398         FUNCTIONSETUP;
00399         DEBUGCONDUIT<<abbrowser_conduit_id<<endl;
00400 
00401         KPilotUser *usr;
00402 
00403         if(!fConfig)
00404         {
00405                 kdWarning() << k_funcinfo << ": No config file was set!" << endl;
00406                 emit logError(i18n("Unable to load configuration of the addressbook conduit."));
00407                 return false;
00408         }
00409 
00410         _prepare();
00411 
00412         usr = fHandle->getPilotUser();
00413         // changing the PC or using a different Palm Desktop app causes a full sync
00414         // Use gethostid for this, since JPilot uses 1+(2000000000.0*random()/(RAND_MAX+1.0))
00415         // as PC_ID, so using JPilot and KPilot is the same as using two differenc PCs
00416         fFullSync =(syncAction == SYNC_FULL) ||
00417                 ((usr->getLastSyncPC() !=(unsigned long) gethostid())
00418                 && fConfig->readBoolEntry(AbbrowserConduitFactory::fullSyncOnPCChange(), true));
00419 
00420         fFirstTime = false;
00421         // Database names probably in latin1.
00422         if(!openDatabases(QString::fromLatin1("AddressDB"), &fFirstTime))
00423         {
00424                 emit logError(i18n("Unable to open the addressbook databases on the handheld."));
00425                 return false;
00426         }
00427         _setAppInfo();
00428         if(!_loadAddressBook())
00429         {
00430                 emit logError(i18n("Unable to open the addressbook."));
00431                 return false;
00432         }
00433         fFirstTime |=(aBook->begin() == aBook->end());
00434 
00435         // perform syncing from palm to abbrowser
00436         // iterate through all records in palm pilot
00437         pilotindex = 0;
00438 
00439 #ifdef DEBUG
00440         DEBUGCONDUIT << fname << ": fullsync=" << fFullSync << ", firstSync=" <<    fFirstTime << endl;
00441         DEBUGCONDUIT << fname << ": syncAction=" << syncAction << ", archive = " << fArchive << endl;
00442         DEBUGCONDUIT << fname << ": smartmerge=" << fSmartMerge << ", conflictRes="<< fConflictResolution << endl;
00443         DEBUGCONDUIT << fname << ": PilotStreetHome=" << fPilotStreetHome << ", PilotFaxHOme" << fPilotFaxHome << endl;
00444 #endif
00445 
00446         QTimer::singleShot(0, this, SLOT(syncPalmRecToPC()));
00447         // TODO: maybe start a second timer to kill the sync after, say, 5 Minutes(e.g. non existent slot called...)
00448         return true;
00449 }
00450 
00451 
00452 
00453 void AbbrowserConduit::syncPalmRecToPC()
00454 {
00455         FUNCTIONSETUP;
00456         PilotRecord *r = 0L, *s = 0L;
00457 
00458         if(fFirstTime || fFullSync)
00459         {
00460                 r = fDatabase->readRecordByIndex(pilotindex++);
00461         }
00462         else
00463         {
00464                 r = dynamic_cast <PilotSerialDatabase * >(fDatabase)->readNextModifiedRec();
00465         }
00466 
00467         if(!r)
00468         {
00469                 abiter = aBook->begin();
00470                 QTimer::singleShot(0, this, SLOT(syncPCRecToPalm()));
00471                 return;
00472         }
00473         else
00474         {
00475                 // already synced, so skip:
00476                 if(syncedIds.contains(r->getID()))
00477                 {
00478 #ifdef DEBUG
00479                         DEBUGCONDUIT << "already synced, so skipping" << endl;
00480 #endif
00481                         QTimer::singleShot(0, this, SLOT(syncPalmRecToPC()));
00482                         return;
00483                 }
00484         }
00485 
00486         bool archiveRecord =(r->getAttrib() & dlpRecAttrArchived);
00487 
00488         KABC::Addressee e;
00489         s = fLocalDatabase->readRecordById(r->getID());
00490         if(!s)
00491         {
00492                 e = _findMatch(PilotAddress(fAddressAppInfo, r));
00493         }
00494 
00495         if((!s && e.isEmpty()) || fFirstTime)
00496         {
00497                 // doesn't exist on PC. Either not deleted at all, or deleted with the archive flag on.
00498                 if(!r->isDeleted() ||(fArchive && archiveRecord))
00499                 {
00500                         e = _addToPC(r);
00501                         if(fArchive && archiveRecord && !e.isEmpty() )
00502                         {
00503                                 e.insertCustom(appString, flagString, QString::number(SYNCDEL));
00504                                 _saveAbEntry(e);
00505                         }
00506                 }
00507         }
00508         else
00509         {
00510                 // if the entry should be archived, it is not marked deleted!!!
00511                 if(r->isDeleted())
00512                 {
00513                         _checkDelete(r,s);
00514                 }
00515                 else
00516                 {
00517                         // if archived, either delete or change and add archived flag. Otherwise just change
00518                         if (archiveRecord && !fArchive)
00519                         {
00520                                 // Archived, but archived records are not supposed to be synced, so delete
00521                                 _checkDelete(r, s);
00522                         }
00523                         else
00524                         {
00525                                 e = _changeOnPC(r, s);
00526                                 if(fArchive && archiveRecord && !e.isEmpty() )
00527                                 {
00528                                         e.insertCustom(appString, flagString, QString::number(SYNCDEL));
00529                                         _saveAbEntry(e);
00530                                 }
00531                         }
00532                 }
00533         }
00534 
00535         syncedIds.append(r->getID());
00536         KPILOT_DELETE(r);
00537         KPILOT_DELETE(s);
00538 
00539         QTimer::singleShot(0, this, SLOT(syncPalmRecToPC()));
00540 }
00541 
00542 
00543 
00544 void AbbrowserConduit::syncPCRecToPalm()
00545 {
00546         FUNCTIONSETUP;
00547 
00548         if(abiter == aBook->end() ||(*abiter).isEmpty())
00549         {
00550                 pilotindex = 0;
00551                 QTimer::singleShot(0, this, SLOT(syncDeletedRecord()));
00552                 return;
00553         }
00554         bool ok;
00555         KABC::Addressee ad = *abiter;
00556         abiter++;
00557         QString recID(ad.custom(appString, idString));
00558         recordid_t rid = recID.toLong(&ok);
00559         if(recID.isEmpty() || !ok || !rid)
00560         {
00561                 // it's a new item(no record ID and not inserted by the Palm -> PC sync), so add it
00562                 _addToPalm(ad);
00563                 QTimer::singleShot(0, this, SLOT(syncPCRecToPalm()));
00564                 return;
00565         }
00566         // look into the list of already synced record ids to see if the addressee hasn't already been synced
00567         else if(syncedIds.contains(rid))
00568         {
00569 #ifdef DEBUG
00570                 DEBUGCONDUIT << fname << ": address with id " << rid <<
00571                         " already synced." << endl;
00572 #endif
00573                 QTimer::singleShot(0, this, SLOT(syncPCRecToPalm()));
00574                 return;
00575         }
00576         if(ad.custom(appString, flagString) == QString::number(SYNCDEL))
00577         {
00578 #ifdef DEBUG
00579                 DEBUGCONDUIT << fname << ": address with id " << rid <<
00580                         " marked archived, so don't sync." << endl;
00581 #endif
00582                 syncedIds.append(rid);
00583                 QTimer::singleShot(0, this, SLOT(syncPCRecToPalm()));
00584                 return;
00585         }
00586 
00587 
00588         PilotRecord *backup = fLocalDatabase->readRecordById(rid);
00589         // only update if no backup record or the backup record is not equal to the addresse
00590         PilotAddress pbackupadr(fAddressAppInfo, backup);
00591         if(!backup || !_equal(pbackupadr, ad) || fFirstTime)
00592         {
00593                 PilotRecord *rec = fDatabase->readRecordById(rid);
00594                 if (!rec && !backup)
00595                 {
00596                         // Entry exists neither on the handheld not in the backup database, although the addressbook entry has a KPilot-ID stored. Assume
00597                         // this comes from a sync with a different Handheld, so delete the handheld ID and restart work on the current item.
00598                         // It's rather unlikely that the entry was deleted from the handheld and the backup database at the same time without
00599                         // being deleted from the addressbook. This can only be the case if the last sync on this computer was done with a
00600                         // different addressbook. In this situation, however it's impossible to decide if that record comes from a sync
00601                         // with a different Handheld or from a deleted record that was synced with a different addressbook.
00602 
00603 #ifdef DEBUG
00604                         DEBUGCONDUIT<<"Addressbook entry "<<ad.realName()<<" has a non-existent Record-ID "<<ad.custom(appString, idString)<<", so disregard that ID"<<endl;
00605 #endif
00606                         ad.removeCustom(appString, idString);
00607                         _saveAbEntry(ad);
00608                         abiter--;
00609                         // No need to KPILOT_DELETE anything, as the records could not be created anyway
00610                         QTimer::singleShot(0, this, SLOT(syncPCRecToPalm()));
00611                         return;
00612                 }
00613 
00614                 if(!rec)
00615                 {
00616                         if(fFirstTime) _addToPalm(ad);
00617                         else _checkDelete(rec, backup);
00618                 }
00619                 else
00620                 {
00621                         // no conflict, just update the record on the handheld
00622                         _changeOnPalm(rec, backup, ad);
00623                 }
00624                 KPILOT_DELETE(rec);
00625         }
00626         KPILOT_DELETE(backup);
00627         syncedIds.append(rid);
00628         // done with the sync process, go on with the next one:
00629         QTimer::singleShot(0, this, SLOT(syncPCRecToPalm()));
00630 }
00631 
00632 
00633 
00634 void AbbrowserConduit::syncDeletedRecord()
00635 {
00636         FUNCTIONSETUP;
00637 
00638         PilotRecord *s = fLocalDatabase->readRecordByIndex(pilotindex++);
00639         if(!s || fFirstTime)
00640         {
00641                 QTimer::singleShot(0, this, SLOT(cleanup()));
00642                 return;
00643         }
00644 
00645         // already synced, so skip this record:
00646         if(syncedIds.contains(s->getID()))
00647         {
00648                 QTimer::singleShot(0, this, SLOT(syncDeletedRecord()));
00649                 return;
00650         }
00651 
00652         QString uid = addresseeMap[s->getID()];
00653         KABC::Addressee e = aBook->findByUid(uid);
00654         if(uid.isEmpty() || e.isEmpty())
00655         {
00656 #ifdef DEBUG
00657                 DEBUGCONDUIT << "Item " << s->getID() << " deleted from the PC, so delete from Palm too (or do deconfliction)!" << endl;
00658 #endif
00659 
00660                 // entry was deleted from addressbook, so delete it from the palm
00661                 // First find out if changed on Palm, and if so, do a deconfliction.
00662                 // This is the case if an entry was changed on the handheld and deleted from the pc.
00663                 // If the user chooses to ignore the conflict, on the next sync we have to check for that
00664                 // conflict here again...
00665                 PilotRecord*r=fDatabase->readRecordById(s->getID());
00666                 PilotAddress adr(fAddressAppInfo, r), backadr(fAddressAppInfo, s);
00667                 if (r && s && !(adr==backadr) )
00668                 {
00669                         _changeOnPC(r, s);
00670                 }
00671                 else
00672                 {
00673                         _deleteFromPalm(s);
00674                 }
00675                 KPILOT_DELETE(r);
00676         }
00677 
00678         KPILOT_DELETE(s);
00679         QTimer::singleShot(0, this, SLOT(syncDeletedRecord()));
00680 }
00681 
00682 void AbbrowserConduit::cleanup()
00683 {
00684         FUNCTIONSETUP;
00685 
00686         if(fDatabase)
00687         {
00688                 fDatabase->resetSyncFlags();
00689                 fDatabase->cleanup();
00690         }
00691         if(fLocalDatabase)
00692         {
00693                 fLocalDatabase->resetSyncFlags();
00694                 fLocalDatabase->cleanup();
00695         }
00696         KPILOT_DELETE(fDatabase);
00697         KPILOT_DELETE(fLocalDatabase);
00698         _saveAddressBook();
00699         // TODO: Do I need to free the addressbook?????
00700         emit syncDone(this);
00701 }
00702 
00703 
00704 
00705 
00706 
00707 /*********************************************************************
00708                  l o w - l e v e l   f u n c t i o n s   f o r
00709                    adding / removing palm/pc records
00710  *********************************************************************/
00711 
00712 
00713 
00714 
00715 
00716 void AbbrowserConduit::_removePilotAddress(PilotAddress & address)
00717 {
00718         FUNCTIONSETUP;
00719 
00720 #ifdef DEBUG
00721         DEBUGCONDUIT << fname << " deleting from palm pilot " << endl;
00722         showPilotAddress(address);
00723 #endif
00724 
00725         address.makeDeleted();
00726         PilotRecord *pilotRec = address.pack();
00727         _deleteFromPalm(pilotRec);
00728         delete pilotRec;
00729 }
00730 
00731 
00732 
00733 void AbbrowserConduit::_removeAbEntry(KABC::Addressee addressee)
00734 {
00735         FUNCTIONSETUP;
00736 
00737 #ifdef DEBUG
00738         DEBUGCONDUIT << fname << " removing " << addressee.formattedName() << endl;
00739 #endif
00740         abChanged = true;
00741         aBook->removeAddressee(addressee);
00742 }
00743 
00744 
00745 
00746 KABC::Addressee AbbrowserConduit::_saveAbEntry(KABC::Addressee & abEntry)
00747 {
00748         FUNCTIONSETUP;
00749 
00750 #ifdef DEBUG
00751         DEBUGCONDUIT<<"Before _saveAbEntry, abEntry.custom="<<abEntry.custom(appString, idString)<<endl;
00752 #endif
00753         if(!abEntry.custom(appString, idString).isEmpty())
00754         {
00755                 addresseeMap.insert(abEntry.custom(appString, idString).toLong(), abEntry.uid());
00756         }
00757 #ifdef DEBUG
00758         DEBUGCONDUIT<<"Before insertAddressee, abEntry.custom="<<abEntry.custom(appString, idString)<<endl;
00759 #endif
00760 
00761         aBook->insertAddressee(abEntry);
00762 #ifdef DEBUG
00763         DEBUGCONDUIT<<"After insertAddressee, abEntry.custom="<<abEntry.custom(appString, idString)<<endl;
00764 #endif
00765 
00766         abChanged = true;
00767         return abEntry;
00768 }
00769 
00770 
00771 
00772 bool AbbrowserConduit::_savePilotAddress(PilotAddress & address, KABC::Addressee & abEntry)
00773 {
00774         FUNCTIONSETUP;
00775 
00776 #ifdef DEBUG
00777         DEBUGCONDUIT << fname <<
00778                 " saving to pilot " << address.id()
00779                 << " " << address.getField(entryFirstname)
00780                 << " " << address.getField(entryLastname)
00781                 << " " << address.getField(entryCompany) << endl;
00782 #endif
00783 
00784         PilotRecord *pilotRec = address.pack();
00785 #ifdef DEBUG
00786         DEBUGCONDUIT<<"PilotRec vor writeRecord: ID="<<pilotRec->getID()<<", address.id="<<address.id()<<endl;
00787 #endif
00788         recordid_t pilotId = fDatabase->writeRecord(pilotRec);
00789 #ifdef DEBUG
00790         DEBUGCONDUIT<<"PilotRec nach writeRecord ("<<pilotId<<": ID="<<pilotRec->getID()<<endl;
00791 #endif
00792         pilotRec->setID(pilotId);
00793         fLocalDatabase->writeRecord(pilotRec);
00794         KPILOT_DELETE(pilotRec);
00795 
00796         // pilotId == 0 if using local db, so don't overwrite the valid id
00797         if(pilotId != 0) address.setID(pilotId);
00798 
00799         recordid_t abId = 0;
00800 
00801         abId = abEntry.custom(appString, idString).toUInt();
00802         if(abId != pilotId)
00803         {
00804                 abEntry.insertCustom(appString, idString, QString::number(pilotId));
00805                 return true;
00806         }
00807 
00808         return false;
00809 }
00810 
00811 
00812 
00813 
00814 
00815 
00816 bool AbbrowserConduit::_saveBackupAddress(PilotAddress & backup)
00817 {
00818         FUNCTIONSETUP;
00819 
00820 #ifdef DEBUG
00821         showPilotAddress(backup);
00822 #endif
00823         PilotRecord *pilotRec = backup.pack();
00824         fLocalDatabase->writeRecord(pilotRec);
00825         KPILOT_DELETE(pilotRec);
00826         return true;
00827 }
00828 
00829 
00830 
00831 
00832 
00833 
00834 KABC::Addressee AbbrowserConduit::_addToAbbrowser(const PilotAddress & address)
00835 {
00836         FUNCTIONSETUP;
00837         KABC::Addressee entry;
00838 
00839 // If a record has been deleted on pda without archiving option,
00840 // flags modify and deleted will be set but the contents are empty.
00841 // We shouldn't add such zombies to database:
00842         if((address.isModified() && address.isDeleted())
00843                         &&(address.getField(entryLastname).isEmpty())
00844                         &&(address.getField(entryFirstname).isEmpty()))
00845                 return entry;
00846 
00847         _copy(entry, address);
00848         return _saveAbEntry(entry);
00849 }
00850 
00851 
00852 
00853 
00854 
00855 /*********************************************************************
00856               A D D   /   C H A N G E   R E C O R D S
00857  *********************************************************************/
00858 
00859 
00860 
00861 
00862 
00863 // -----------------------------------------------------------
00864 // Palm => PC
00865 // -----------------------------------------------------------
00866 
00867 KABC::Addressee AbbrowserConduit::_addToPC(PilotRecord * r)
00868 {
00869         return _changeOnPC(r, NULL);
00870 }
00871 
00872 
00873 
00874 KABC::Addressee AbbrowserConduit::_changeOnPC(PilotRecord * rec, PilotRecord * backup)
00875 {
00876         FUNCTIONSETUP;
00877         PilotAddress padr(fAddressAppInfo, rec);
00878 #ifdef DEBUG
00879         showPilotAddress(padr);
00880 #endif
00881         struct AddressAppInfo ai = fAddressAppInfo;
00882         PilotAddress pbackupadr(ai, backup);
00883         KABC::Addressee ad;
00884 
00885 #ifdef DEBUG
00886         DEBUGCONDUIT << "---------------------------------" << endl;
00887         DEBUGCONDUIT << "Now syncing " <<
00888                 padr.getField(entryFirstname) << " " <<
00889                 padr.getField(entryLastname) << " / backup: " <<
00890                 pbackupadr.getField(entryFirstname) << " " <<
00891                 pbackupadr.getField(entryLastname) << endl;
00892 #endif
00893 
00894         if(backup) ad = _findMatch(pbackupadr);
00895         if(ad.isEmpty()) ad = _findMatch(padr);
00896 #ifdef DEBUG
00897         DEBUGCONDUIT << "ad.custom=" << ad.custom(appString, idString) << endl;
00898 #endif
00899 
00900         if(ad.isEmpty())
00901         {
00902 #ifdef DEBUG
00903                 DEBUGCONDUIT << "ad.isEmpty() " << endl;
00904 #endif
00905                 if(!backup)
00906                 {
00907                         // not found, so add
00908                         ad = _addToAbbrowser(padr);
00909                         fLocalDatabase->writeRecord(rec);
00910                 }
00911                 else
00912                 {
00913 #ifdef DEBUG
00914                         DEBUGCONDUIT <<
00915                                 "not a new entry, but PC entry does not exist => deconfliction of "
00916                                 << padr.getField(entryLastname) << endl;
00917 #endif
00918                         KABC::Addressee ab;
00919                         switch(getEntryResolution(ad, pbackupadr, padr))
00920                         {
00921                                 case ePilotOverides:
00922                                         _addToAbbrowser(padr);
00923                                         break;
00924                                 case eAbbrowserOverides:
00925                                         _removePilotAddress(padr);
00926                                         break;
00927                                 case eRevertToBackup:
00928                                         ab = _addToAbbrowser(pbackupadr);
00929                                         if(_savePilotAddress(pbackupadr, ab)) _saveAbEntry(ab);
00930                                         break;
00931                                 case eDoNotResolve:
00932                                 default:
00933                                         break;
00934                         }
00935                 }
00936         }
00937         else
00938         {
00939 #ifdef DEBUG
00940                 DEBUGCONDUIT << "!ad.isEmpty()" << endl;
00941                 showAddressee(ad);
00942 #endif
00943                 PilotAddress backupadr(fAddressAppInfo, backup);
00944                 _mergeEntries(padr, backupadr, ad);
00945 
00946         }
00947         return ad;
00948 }
00949 
00950 
00951 
00952 bool AbbrowserConduit::_deleteOnPC(PilotRecord * rec, PilotRecord * backup)
00953 {
00954         FUNCTIONSETUP;
00955         recordid_t id;
00956         if(rec) id = rec->getID();
00957         else if(backup) id = backup->getID();
00958         else id = 0;
00959 
00960 #ifdef DEBUG
00961         DEBUGCONDUIT << fname << ": deleting record with id " << id << ", rec " <<
00962                 (rec != NULL) << ", backup " <<(backup != NULL) << endl;
00963 #endif
00964         if(!id) return false;
00965 
00966         KABC::Addressee ad = aBook->findByUid(addresseeMap[id]);
00967         PilotAddress backupAdr(fAddressAppInfo, backup);
00968 
00969         if((!backup) || !_equal(backupAdr, ad))
00970         {
00971 #ifdef DEBUG
00972                 DEBUGCONDUIT << fname << ": record with id " << id <<
00973                         " is either new on PC or was changed, but is is requested to be removed. Ignoring this request"
00974                         << endl;
00975 #endif
00976                 // TODO: Conflict!!!
00977         }
00978         if(!ad.isEmpty())
00979         {
00980                 _removeAbEntry(ad);
00981                 //aBook->removeAddressee(ad);
00982         }
00983         if(!rec)
00984         {
00985                 backup->makeDeleted();  //setAttrib(backup->getAttrib() | dlpRecAttrDeleted );
00986                 fLocalDatabase->writeRecord(backup);
00987         }
00988         else
00989         {
00990                 fLocalDatabase->writeRecord(rec);
00991         }
00992         return true;
00993 }
00994 
00995 
00996 
00997 // -----------------------------------------------------------
00998 // PC => Palm
00999 // -----------------------------------------------------------
01000 
01001 
01002 
01003 void AbbrowserConduit::_addToPalm(KABC::Addressee & entry)
01004 {
01005         FUNCTIONSETUP;
01006         PilotAddress pilotAddress(fAddressAppInfo);
01007 
01008         _copy(pilotAddress, entry);
01009 #ifdef DEBUG
01010         DEBUGCONDUIT<<"pilotAddress.id="<<pilotAddress.getID()<<", abEntry.ID="<<entry.custom(appString, idString)<<endl;
01011 #endif
01012 
01013         if(_savePilotAddress(pilotAddress, entry))
01014         {
01015 #ifdef DEBUG
01016                 DEBUGCONDUIT<<"Vor _saveAbEntry, pilotAddress.id="<<pilotAddress.getID()<<", abEntry.ID="<<entry.custom(appString, idString)<<endl;
01017 #endif
01018                 _saveAbEntry(entry);
01019         }
01020 }
01021 
01022 
01023 
01024 void AbbrowserConduit::_changeOnPalm(PilotRecord * rec, PilotRecord * backuprec, KABC::Addressee & ad)
01025 {
01026         FUNCTIONSETUP;
01027         PilotAddress padr(fAddressAppInfo);
01028         PilotAddress pbackupadr(fAddressAppInfo);
01029 
01030         if(rec) padr = PilotAddress(fAddressAppInfo, rec);
01031         if(backuprec) pbackupadr = PilotAddress(fAddressAppInfo, backuprec);
01032 #ifdef DEBUG
01033         DEBUGCONDUIT << "---------------------------------" << endl;
01034         DEBUGCONDUIT << "Now syncing " << padr.getField(entryLastname) << " / backup: " << pbackupadr.getField(entryLastname) << endl;
01035 #endif
01036         _mergeEntries(padr, pbackupadr, ad);
01037 }
01038 
01039 
01040 
01041 void AbbrowserConduit::_deleteFromPalm(PilotRecord * rec)
01042 {
01043         FUNCTIONSETUP;
01044         rec->makeDeleted();
01045         recordid_t pilotId = fDatabase->writeRecord(rec);
01046         rec->setID(pilotId);
01047         fLocalDatabase->writeRecord(rec);
01048         syncedIds.append(rec->getID());
01049 }
01050 
01051 
01052 
01053 
01054 /*********************************************************************
01055                    C O P Y   R E C O R D S
01056  *********************************************************************/
01057 
01058 
01059 
01060 
01061 
01062 bool AbbrowserConduit::_equal(const PilotAddress & piAddress, KABC::Addressee & abEntry) const
01063 {
01064         if(_compare(abEntry.familyName(), piAddress.getField(entryLastname))) return false;
01065         if(_compare(abEntry.givenName(), piAddress.getField(entryFirstname))) return false;
01066         if(_compare(abEntry.title(), piAddress.getField(entryTitle))) return false;
01067         if(_compare(abEntry.organization(), piAddress.getField(entryCompany))) return false;
01068         if(_compare(abEntry.note(), piAddress.getField(entryNote))) return false;
01069         int cat = _getCat(abEntry.categories());
01070         // TODO: Add general getCategoryName() to pilotAppCategory to
01071         // retrieve category names.
01072         if(_compare(PilotAppCategory::codec()->toUnicode(fAddressAppInfo.category.name[cat]), 
01073                 piAddress.getCategoryLabel())) return false;
01074         if(_compare(abEntry.phoneNumber(KABC::PhoneNumber::Work).number(),
01075                         piAddress.getPhoneField(PilotAddress::eWork))) return false;
01076         if(_compare(abEntry.phoneNumber(KABC::PhoneNumber::Home).number(),
01077                         piAddress.getPhoneField(PilotAddress::eHome))) return false;
01078         if(_compare(getOtherField(abEntry),
01079                         piAddress.getPhoneField(PilotAddress::eOther))) return false;
01080         if(_compare(abEntry.preferredEmail(),
01081                         piAddress.getPhoneField(PilotAddress::eEmail))) return false;
01082         if(_compare(getFax(abEntry).number(),
01083                         piAddress.getPhoneField(PilotAddress::eFax))) return false;
01084         if(_compare(abEntry.phoneNumber(KABC::PhoneNumber::Cell).number(),
01085                         piAddress.getPhoneField(PilotAddress::eMobile))) return false;
01086 
01087         KABC::Address address = getAddress(abEntry);
01088         if(_compare(address.street(), piAddress.getField(entryAddress))) return false;
01089         if(_compare(address.locality(), piAddress.getField(entryCity))) return false;
01090         if(_compare(address.region(), piAddress.getField(entryState))) return false;
01091         if(_compare(address.postalCode(), piAddress.getField(entryZip))) return false;
01092         if(_compare(address.country(), piAddress.getField(entryCountry))) return false;
01093 
01094         if(
01095                 _compare(abEntry.custom(appString, CSL1("CUSTOM1")), piAddress.getField(entryCustom1)) ||
01096                 _compare(abEntry.custom(appString, CSL1("CUSTOM2")), piAddress.getField(entryCustom2)) ||
01097                 _compare(abEntry.custom(appString, CSL1("CUSTOM3")), piAddress.getField(entryCustom3)) ||
01098                 _compare(abEntry.custom(appString, CSL1("CUSTOM4")), piAddress.getField(entryCustom4)))
01099         {
01100                 return false;
01101         }
01102 
01103         if (piAddress.getCat()!=_getCat(abEntry.categories())) return false;    
01104         
01105         return true;
01106 }
01107 
01108 
01109 
01110 void AbbrowserConduit::_copy(PilotAddress & toPilotAddr, KABC::Addressee & fromAbEntry)
01111 {
01112         FUNCTIONSETUP;
01113         // don't do a reset since this could wipe out non copied info
01114         //toPilotAddr.reset();
01115         toPilotAddr.setField(entryLastname, fromAbEntry.familyName());
01116         QString firstAndMiddle = fromAbEntry.givenName();
01117         if(!fromAbEntry.additionalName().isEmpty()) firstAndMiddle += CSL1(" ") + fromAbEntry.additionalName();
01118         toPilotAddr.setField(entryFirstname, firstAndMiddle);
01119         toPilotAddr.setField(entryCompany, fromAbEntry.organization());
01120         toPilotAddr.setField(entryTitle, fromAbEntry.title());
01121         toPilotAddr.setField(entryNote, fromAbEntry.note());
01122 
01123         // do email first, to ensure its gets stored
01124         toPilotAddr.setPhoneField(PilotAddress::eEmail, fromAbEntry.preferredEmail());
01125         toPilotAddr.setPhoneField(PilotAddress::eWork,
01126                 fromAbEntry.phoneNumber(KABC::PhoneNumber::Work).number());
01127         toPilotAddr.setPhoneField(PilotAddress::eHome,
01128                 fromAbEntry.phoneNumber(KABC::PhoneNumber::Home).number());
01129         toPilotAddr.setPhoneField(PilotAddress::eMobile,
01130                 fromAbEntry.phoneNumber(KABC::PhoneNumber::Cell).number());
01131         toPilotAddr.setPhoneField(PilotAddress::eFax, getFax(fromAbEntry).number());
01132         toPilotAddr.setPhoneField(PilotAddress::ePager,
01133                 fromAbEntry.phoneNumber(KABC::PhoneNumber::Pager).number());
01134         toPilotAddr.setPhoneField(PilotAddress::eOther, getOtherField(fromAbEntry));
01135         toPilotAddr.setShownPhone(PilotAddress::eMobile);
01136 
01137         KABC::Address homeAddress = getAddress(fromAbEntry);
01138         _setPilotAddress(toPilotAddr, homeAddress);
01139 
01140         // Process the additional entries from the Palm(the palm database app block tells us the name of the fields)
01141         //
01142         // TODO: Remove the evil latin1() hack from here and support the
01143         // correct Palm codec within the PilotAddress class by using QString
01144         // arguments instead.
01145         //
01146         toPilotAddr.setField(entryCustom1, fromAbEntry.custom(appString, CSL1("CUSTOM1")));
01147         toPilotAddr.setField(entryCustom2, fromAbEntry.custom(appString, CSL1("CUSTOM2")));
01148         toPilotAddr.setField(entryCustom3, fromAbEntry.custom(appString, CSL1("CUSTOM3")));
01149         toPilotAddr.setField(entryCustom4, fromAbEntry.custom(appString, CSL1("CUSTOM4")));
01150 
01151         toPilotAddr.setCat(_getCat(fromAbEntry.categories()));
01152 }
01153 
01154 
01155 
01160 int AbbrowserConduit::_getCat(const QStringList cats) const
01161 {
01162 //      FUNCTIONSETUP;
01163         int j;
01164         for(QStringList::ConstIterator it = cats.begin(); it != cats.end(); ++it)
01165         {
01166                 for(j = 1; j <= 15; j++)
01167                 {
01168                         QString catName = PilotAppCategory::codec()->
01169                                 toUnicode(fAddressAppInfo.category.name[j]);
01170                         if(!(*it).isEmpty() && !_compare(*it, catName))
01171                         {
01172                                 return j;
01173                         }
01174                 }
01175         }
01176         return 0;
01177 }
01178 
01179 
01180 
01181 void AbbrowserConduit::_setPilotAddress(PilotAddress & toPilotAddr, const KABC::Address & abAddress)
01182 {
01183         toPilotAddr.setField(entryAddress, abAddress.street());
01184         toPilotAddr.setField(entryCity, abAddress.locality());
01185         toPilotAddr.setField(entryState, abAddress.region());
01186         toPilotAddr.setField(entryZip, abAddress.postalCode());
01187         toPilotAddr.setField(entryCountry, abAddress.country());
01188 }
01189 
01190 
01191 void AbbrowserConduit::_copyPhone(KABC::Addressee & toAbEntry,
01192                               KABC::PhoneNumber phone, QString palmphone)
01193 {
01194         if(!palmphone.isEmpty())
01195         {
01196                 phone.setNumber(palmphone);
01197                 toAbEntry.insertPhoneNumber(phone);
01198         }
01199         else
01200         {
01201                 toAbEntry.removePhoneNumber(phone);
01202         }
01203 }
01204 
01205 void AbbrowserConduit::_setCategory(Addressee & abEntry, QString cat)
01206 {
01207         for(int j = 1; j <= 15; j++)
01208         {
01209                 abEntry.removeCategory(
01210                         PilotAppCategory::codec()->toUnicode(fAddressAppInfo.category.name[j]));
01211         }
01212         if(!cat.isEmpty()) abEntry.insertCategory(cat);
01213 }
01214 
01215 void AbbrowserConduit::_copy(KABC::Addressee & toAbEntry, const PilotAddress & fromPiAddr)
01216 {
01217         FUNCTIONSETUP;
01218         // copy straight forward values
01219         toAbEntry.setFamilyName(fromPiAddr.getField(entryLastname));
01220         toAbEntry.setGivenName(fromPiAddr.getField(entryFirstname));
01221         toAbEntry.setOrganization(fromPiAddr.getField(entryCompany));
01222         toAbEntry.setTitle(fromPiAddr.getField(entryTitle));
01223         toAbEntry.setNote(fromPiAddr.getField(entryNote));
01224         
01225         // copy the phone stuff
01226         toAbEntry.removeEmail(toAbEntry.preferredEmail());
01227         toAbEntry.insertEmail(fromPiAddr.getPhoneField(PilotAddress::eEmail));
01228 
01229         _copyPhone(toAbEntry, 
01230                 toAbEntry.phoneNumber(KABC::PhoneNumber::Home), 
01231                 fromPiAddr.getPhoneField(PilotAddress::eHome));
01232         _copyPhone(toAbEntry, 
01233                 toAbEntry.phoneNumber(KABC::PhoneNumber::Work), 
01234                 fromPiAddr.getPhoneField(PilotAddress::eWork));
01235         _copyPhone(toAbEntry, 
01236                 toAbEntry.phoneNumber(KABC::PhoneNumber::Cell), 
01237                 fromPiAddr.getPhoneField(PilotAddress::eMobile));
01238         _copyPhone(toAbEntry, 
01239                 getFax(toAbEntry), 
01240                 fromPiAddr.getPhoneField(PilotAddress::eFax));
01241         _copyPhone(toAbEntry, 
01242                 toAbEntry.phoneNumber(KABC::PhoneNumber::Pager), 
01243                 fromPiAddr.getPhoneField(PilotAddress::ePager));
01244         setOtherField(toAbEntry, fromPiAddr.getPhoneField(PilotAddress::eOther));
01245 
01246         KABC::Address homeAddress = getAddress(toAbEntry);
01247         homeAddress.setStreet(fromPiAddr.getField(entryAddress));
01248         homeAddress.setLocality(fromPiAddr.getField(entryCity));
01249         homeAddress.setRegion(fromPiAddr.getField(entryState));
01250         homeAddress.setPostalCode(fromPiAddr.getField(entryZip));
01251         homeAddress.setCountry(fromPiAddr.getField(entryCountry));
01252         toAbEntry.insertAddress(homeAddress);
01253 
01254         toAbEntry.insertCustom(appString, CSL1("CUSTOM1"), fromPiAddr.getField(entryCustom1));
01255         toAbEntry.insertCustom(appString, CSL1("CUSTOM2"), fromPiAddr.getField(entryCustom2));
01256         toAbEntry.insertCustom(appString, CSL1("CUSTOM3"), fromPiAddr.getField(entryCustom3));
01257         toAbEntry.insertCustom(appString, CSL1("CUSTOM4"), fromPiAddr.getField(entryCustom4));
01258 
01259         // copy the fromPiAddr pilot id to the custom field KPilot_Id;
01260         // pilot id may be zero(since it could be new) but couldn't hurt
01261         // to even assign it to zero; let's us know what state the
01262         // toAbEntry is in
01263         toAbEntry.insertCustom(appString, idString, QString::number(fromPiAddr.getID()));
01264 
01265 
01266         int cat = fromPiAddr.getCat();
01267         QString category;
01268         if (0 < cat && cat <= 15) category = fAddressAppInfo.category.name[cat];
01269         _setCategory(toAbEntry, category);
01270 #ifdef DEBUG
01271         showAddressee(toAbEntry);
01272 #endif
01273 }
01274 
01275 
01276 
01277 
01278 
01279 /*********************************************************************
01280  C O N F L I C T   R E S O L U T I O N   a n d   M E R G I N G
01281  *********************************************************************/
01282 
01283 
01284 
01285 
01286 
01297 int AbbrowserConduit::_conflict(const QString & entry, const QString & field, const QString & palm,
01298         const QString & backup, const QString & pc, bool & mergeNeeded, QString & mergedStr)
01299 {
01300         FUNCTIONSETUP;
01301         mergeNeeded = false;
01302         QString bckup = backup;
01303 
01304         // if both entries are already the same, no need to do anything
01305         if(pc == palm) return CHANGED_NONE;
01306 
01307         // If this is a first sync, we don't have a bckup record, so
01308         if(fFirstTime)
01309         {
01310                 bckup = QString();
01311                 if(pc.isEmpty())
01312                 {
01313                         mergeNeeded = true;
01314                         mergedStr = palm;
01315                         return CHANGED_PC;
01316                 }
01317                 if(palm.isEmpty())
01318                 {
01319                         mergeNeeded = true;
01320                         mergedStr = pc;
01321                         return CHANGED_PALM;
01322                 }
01323         }
01324         else
01325         {
01326                 // only pc modified, so return that string, no conflict
01327                 if(palm == backup)
01328                 {
01329                         mergeNeeded = true;
01330                         mergedStr = pc;
01331                         return CHANGED_PALM;
01332                 }
01333                 // only palm modified, so return that string, no conflict
01334                 if(pc == backup)
01335                 {
01336                         mergeNeeded = true;
01337                         mergedStr = palm;
01338                         return CHANGED_PC;
01339                 }
01340         }
01341 
01342         // We need to do some deconfliction. Use already chosen resolution option if possible
01343         EConflictResolution fieldres = getFieldResolution(entry, field, palm, bckup, pc);
01344 #ifdef DEBUG
01345         DEBUGCONDUIT << "fieldres=" << fieldres << endl;
01346 #endif
01347         switch(fieldres)
01348         {
01349                 case eAbbrowserOverides:
01350                         mergeNeeded = true;
01351                         mergedStr = pc;
01352                         return CHANGED_PALM;
01353                         break;
01354                 case ePilotOverides:
01355                         mergeNeeded = true;
01356                         mergedStr = palm;
01357                         return CHANGED_PC;
01358                         break;
01359                 case eRevertToBackup:
01360                         mergeNeeded = true;
01361                         mergedStr = backup;
01362                         return CHANGED_BOTH;
01363                         break;
01364                 case eKeepBothInAbbrowser:
01365                         return CHANGED_DUPLICATE;
01366                 case eDoNotResolve:
01367                 default:
01368                         return CHANGED_NORES;
01369         }
01370         return CHANGED_NONE;
01371 }
01372 
01373 
01374 
01375 int AbbrowserConduit::_compare(const QString & str1, const QString & str2) const
01376 {
01377         if(str1.isEmpty() && str2.isEmpty()) return 0;
01378         else return str1.compare(str2);
01379 }
01380 
01381 
01382 int AbbrowserConduit::_smartMergePhone(KABC::Addressee & abEntry,
01383         const PilotAddress & backupAddress,
01384         PilotAddress & pilotAddress,
01385         PilotAddress::EPhoneType PalmFlag,
01386         KABC::PhoneNumber phone, QString thisName,
01387         QString name)
01388 {
01389         bool mergeNeeded = false;
01390         QString mergedStr;
01391 
01392         int res = _conflict(thisName, name, 
01393                 pilotAddress.getPhoneField(PalmFlag), 
01394                 backupAddress.getPhoneField(PalmFlag), 
01395                 phone.number(), mergeNeeded, mergedStr);
01396         if(res & CHANGED_NORES) return res;
01397         if(mergeNeeded)
01398         {
01399                 pilotAddress.setPhoneField(PalmFlag, mergedStr);
01400                 phone.setNumber(mergedStr);
01401                 abEntry.insertPhoneNumber(phone);
01402         }
01403         return -1;
01404 }
01405 
01406 int AbbrowserConduit::_smartMergeEntry(QString abEntry,
01407         const PilotAddress & backupAddress,
01408         PilotAddress & pilotAddress, int PalmFlag,
01409         QString thisName, QString name,
01410         QString & mergedString)
01411 {
01412         bool mergeNeeded = false;
01413         QString mergedStr;
01414         mergedString = QString();
01415 
01416         int res = _conflict(thisName, name, 
01417                 pilotAddress.getField(PalmFlag), 
01418                 backupAddress.getField(PalmFlag), 
01419                 abEntry, mergeNeeded, mergedStr);
01420         if(res & CHANGED_NORES) return res;
01421         if(mergeNeeded)
01422         {
01423 #ifdef DEBUG
01424                 DEBUGCONDUIT << "Merged string=" << mergedStr << endl;
01425 #endif
01426                 pilotAddress.setField(PalmFlag, mergedStr);
01427                 mergedString = mergedStr;
01428         }
01429         return -1;
01430 }
01431 
01432 
01433 int AbbrowserConduit::_smartMergeCategories(KABC::Addressee & abEntry,
01434         const PilotAddress & backupAddress,
01435         PilotAddress & pilotAddress,
01436         QString thisName, QString name,
01437         QString & mergedString)
01438 {
01439         int pccat = _getCat(abEntry.categories());
01440         QString abAddressCat =
01441                 PilotAppCategory::codec()->toUnicode(fAddressAppInfo.category.name[pccat]);
01442         bool mergeNeeded = false;
01443         QString mergedStr;
01444         mergedString = QString();
01445 
01446         int res = _conflict(thisName, name, 
01447                 pilotAddress.getCategoryLabel(), 
01448                 backupAddress.getCategoryLabel(), 
01449                 abAddressCat, mergeNeeded, mergedStr);
01450         if(res & CHANGED_NORES) return res;
01451         if(mergeNeeded)
01452         {
01453                 pilotAddress.setCategory(mergedStr);
01454                 _setCategory(abEntry, mergedStr);
01455                 mergedString = mergedStr;
01456         }
01457         return -1;
01458 }
01459 
01460 
01461 void AbbrowserConduit::showAdresses(PilotAddress & pilotAddress, 
01462         const PilotAddress & backupAddress, KABC::Addressee & abEntry)
01463 {
01464 #ifdef DEBUG
01465         DEBUGCONDUIT << "abEntry:" << endl;
01466         showAddressee(abEntry);
01467         DEBUGCONDUIT << "pilotAddress:" << endl;
01468         showPilotAddress(pilotAddress);
01469         DEBUGCONDUIT << "backupAddress:" << endl;
01470         showPilotAddress(backupAddress);
01471         DEBUGCONDUIT << "------------------------------------------------" << endl;
01472 #endif
01473 }
01474 
01475 int AbbrowserConduit::_smartMerge(PilotAddress & outPilotAddress, 
01476         const PilotAddress & backupAddress, KABC::Addressee & outAbEntry)
01477 {
01478         FUNCTIONSETUP;
01479         fEntryResolution = getResolveConflictOption();
01480         PilotAddress pilotAddress(outPilotAddress);
01481         KABC::Addressee abEntry(outAbEntry);
01482         QString thisName(outAbEntry.realName());
01483         bool mergeNeeded = false;
01484         QString mergedStr;
01485         int res;
01486 
01487         res = _smartMergeEntry(abEntry.familyName(), backupAddress, pilotAddress, 
01488                 (int) entryLastname, thisName, i18n("last name"), mergedStr);
01489         if(res >= 0) return res;
01490         else if(!mergedStr.isEmpty()) abEntry.setFamilyName(mergedStr);
01491 
01492         res = _smartMergeEntry(abEntry.givenName(), backupAddress, pilotAddress, 
01493                 (int) entryFirstname, thisName, i18n("first name"), mergedStr);
01494         if(res >= 0) return res;
01495         else if(!mergedStr.isEmpty()) abEntry.setGivenName(mergedStr);
01496 
01497         res = _smartMergeEntry(abEntry.organization(), backupAddress, pilotAddress, 
01498                 (int) entryCompany, thisName, i18n("organization"), mergedStr);
01499         if(res >= 0) return res;
01500         else if(!mergedStr.isEmpty()) abEntry.setOrganization(mergedStr);
01501 
01502         res = _smartMergeEntry(abEntry.title(), backupAddress, pilotAddress, 
01503                 (int) entryTitle, thisName, i18n("title"), mergedStr);
01504         if(res >= 0) return res;
01505         else if(!mergedStr.isEmpty()) abEntry.setTitle(mergedStr);
01506 
01507         res = _smartMergeEntry(abEntry.note(), backupAddress, pilotAddress, 
01508                 (int) entryNote, thisName, i18n("note"), mergedStr);
01509         if(res >= 0) return res;
01510         else if(!mergedStr.isEmpty()) abEntry.setNote(mergedStr);
01511 
01512 
01513 
01514 
01515 
01516 
01517 
01518 
01519 
01520         res = _smartMergeEntry(abEntry.custom(appString, CSL1("CUSTOM1")), backupAddress, pilotAddress, entryCustom1, thisName, i18n("custom 1"), mergedStr);
01521         if(res >= 0) return res;
01522         else if(!mergedStr.isEmpty()) abEntry.insertCustom(appString, CSL1("CUSTOM1"), mergedStr);
01523 
01524         res = _smartMergeEntry(abEntry.custom(appString, CSL1("CUSTOM2")), backupAddress, pilotAddress, entryCustom2, thisName, i18n("custom 2"), mergedStr);
01525         if(res >= 0) return res;
01526         else if(!mergedStr.isEmpty()) abEntry.insertCustom(appString, CSL1("CUSTOM2"), mergedStr);
01527 
01528         res = _smartMergeEntry(abEntry.custom(appString, CSL1("CUSTOM3")), backupAddress, pilotAddress, entryCustom3, thisName, i18n("custom 3"), mergedStr);
01529         if(res >= 0) return res;
01530         else if(!mergedStr.isEmpty()) abEntry.insertCustom(appString, CSL1("CUSTOM3"), mergedStr);
01531 
01532         res = _smartMergeEntry(abEntry.custom(appString, CSL1("CUSTOM4")), backupAddress, pilotAddress, entryCustom4, thisName, i18n("custom 4"), mergedStr);
01533         if(res >= 0) return res;
01534         else if(!mergedStr.isEmpty()) abEntry.insertCustom(appString, CSL1("CUSTOM4"), mergedStr);
01535 
01536 
01537 
01538 
01539 
01540 
01541 
01542 
01543 
01544 
01545         res = _smartMergePhone(abEntry, backupAddress, pilotAddress, 
01546                 PilotAddress::eWork, abEntry.phoneNumber(KABC::PhoneNumber::Work), 
01547                 thisName, i18n("work phone"));
01548         if(res >= 0) return res;
01549         res = _smartMergePhone(abEntry, backupAddress, pilotAddress, 
01550                 PilotAddress::eHome, abEntry.phoneNumber(KABC::PhoneNumber::Home), 
01551                 thisName, i18n("home phone"));
01552         if(res >= 0) return res;
01553         res = _smartMergePhone(abEntry, backupAddress, pilotAddress, 
01554                 PilotAddress::eMobile, abEntry.phoneNumber(KABC::PhoneNumber::Cell), 
01555                 thisName, i18n("mobile phone"));
01556         if(res >= 0) return res;
01557         res = _smartMergePhone(abEntry, backupAddress, pilotAddress, 
01558                 PilotAddress::eFax, getFax(abEntry), thisName, i18n("fax"));
01559         if(res >= 0) return res;
01560         res = _smartMergePhone(abEntry, backupAddress, pilotAddress, 
01561                 PilotAddress::ePager, abEntry.phoneNumber(KABC::PhoneNumber::Pager), 
01562                 thisName, i18n("pager"));
01563         if(res >= 0) return res;
01564 
01565         res = _conflict(thisName, i18n("other"), 
01566                 pilotAddress.getPhoneField(PilotAddress::eOther), 
01567                 backupAddress.getPhoneField(PilotAddress::eOther), 
01568                 getOtherField(abEntry), mergeNeeded, mergedStr);
01569         if(res & CHANGED_NORES) return res;
01570         if(mergeNeeded)
01571         {
01572                 pilotAddress.setPhoneField(PilotAddress::eOther, mergedStr);
01573                 setOtherField(abEntry, mergedStr);
01574         }
01575 
01576         res = _conflict(thisName, i18n("email"), 
01577                 pilotAddress.getPhoneField(PilotAddress::eEmail), 
01578                 backupAddress.getPhoneField(PilotAddress::eEmail), 
01579                 abEntry.preferredEmail(), mergeNeeded, mergedStr);
01580         if(res & CHANGED_NORES) return res;
01581         if(mergeNeeded)
01582         {
01583                 pilotAddress.setPhoneField(PilotAddress::eEmail, mergedStr);
01584                 abEntry.removeEmail(backupAddress.getPhoneField(PilotAddress::eEmail));
01585                 abEntry.removeEmail(pilotAddress.getPhoneField(PilotAddress::eEmail));
01586                 abEntry.insertEmail(mergedStr, true);
01587         }
01588 
01589 
01590 
01591         KABC::Address abAddress = getAddress(abEntry);
01592 
01593         res = _smartMergeEntry(abAddress.street(), 
01594                 backupAddress, pilotAddress, entryAddress, 
01595                 thisName, i18n("address"), mergedStr);
01596         if(res >= 0) return res;
01597         else if(!mergedStr.isEmpty()) abAddress.setStreet(mergedStr);
01598 
01599         res = _smartMergeEntry(abAddress.locality(), 
01600                 backupAddress, pilotAddress, entryCity, 
01601                 thisName, i18n("city"), mergedStr);
01602         if(res >= 0) return res;
01603         else if(!mergedStr.isEmpty()) abAddress.setLocality(mergedStr);
01604 
01605         res = _smartMergeEntry(abAddress.region(), 
01606                 backupAddress, pilotAddress, entryState, 
01607                 thisName, i18n("region"), mergedStr);
01608         if(res >= 0) return res;
01609         else if(!mergedStr.isEmpty()) abAddress.setRegion(mergedStr);
01610 
01611         res = _smartMergeEntry(abAddress.postalCode(), 
01612                 backupAddress, pilotAddress, entryZip, 
01613                 thisName, i18n("postal code"), mergedStr);
01614         if(res >= 0) return res;
01615         else if(!mergedStr.isEmpty()) abAddress.setPostalCode(mergedStr);
01616 
01617         res = _smartMergeEntry(abAddress.country(), 
01618                 backupAddress, pilotAddress, entryCountry, 
01619                 thisName, i18n("country"), mergedStr);
01620         if(res >= 0) return res;
01621         else if(!mergedStr.isEmpty()) abAddress.setCountry(mergedStr);
01622 
01623         abEntry.insertAddress(abAddress);
01624 
01625         res = _smartMergeCategories(abEntry, 
01626                 backupAddress, pilotAddress, 
01627                 thisName, i18n("category"), mergedStr);
01628         if(res >= 0) return res;
01629 
01630         abEntry.insertCustom(appString, idString, QString::number(pilotAddress.id()));
01631 
01632 
01633         outPilotAddress = pilotAddress;
01634         outAbEntry = abEntry;
01635 
01636         return CHANGED_BOTH;
01637 }
01638 
01639 
01645 int AbbrowserConduit::_mergeEntries(PilotAddress & pilotAddress, PilotAddress & backupAddress, KABC::Addressee & abEntry)
01646 {
01647         FUNCTIONSETUP;
01648 
01649         int res = _handleConflict(pilotAddress, backupAddress, abEntry);
01650         if(res & CHANGED_NORES)
01651         {
01652                 if(res & CHANGED_DUPLICATE)
01653                 {
01654                         if(res & CHANGED_PALM)
01655                         {
01656                                 // Set the Palm ID to 0 so we don't overwrite the existing record.
01657                                 abEntry.insertCustom(appString, idString, QString::number(0));
01658                                 _addToPalm(abEntry);
01659                         }
01660                         if(res & CHANGED_PC)
01661                         {
01662                                 _addToAbbrowser(pilotAddress);
01663                                 _saveBackupAddress(pilotAddress);
01664                         }
01665                 }
01666         }
01667         else
01668         {
01669                 if(res & CHANGED_PALM)
01670                 {
01671                         _savePilotAddress(pilotAddress, abEntry);
01672                 }
01673                 if(res & CHANGED_PC)
01674                 {
01675                         _saveAbEntry(abEntry);
01676                 }
01677                 _saveBackupAddress(pilotAddress);
01678 
01679                 // Duplication handles this on its own.
01680                 QString idStr(abEntry.custom(appString, idString));
01681 
01682                 if(idStr.isEmpty() ||(idStr != QString::number(pilotAddress.getID())))
01683                 {
01684                         abEntry.insertCustom(appString, idString, QString::number(pilotAddress.getID()));
01685                         _saveAbEntry(abEntry);
01686                 }
01687         }
01688         return 0;
01689 }
01690 
01691 
01696 int AbbrowserConduit::_handleConflict(PilotAddress & pilotAddress, PilotAddress & backupAddress, KABC::Addressee & abEntry)
01697 {
01698         FUNCTIONSETUP;
01699 
01700         if(abEntry.isEmpty())
01701         {
01702                 _copy(abEntry, pilotAddress);
01703                 return CHANGED_PC | CHANGED_ADD;
01704         }
01705         // TODO: if pilotAddress is empty????
01706 
01707         if(_equal(pilotAddress, abEntry)) return CHANGED_NONE;
01708 
01709         if(pilotAddress == backupAddress)
01710         {
01711                 if(!_equal(backupAddress, abEntry))
01712                 {
01713                         _copy(pilotAddress, abEntry);
01714                         return CHANGED_PALM;
01715                 }
01716                 else
01717                 {
01718                         // This should never be called, since that would mean 
01719                         // pilotAddress and backupAddress are equal, which we already checked!!!!
01720                         return CHANGED_NONE;
01721                 }
01722         }
01723         else
01724         {
01725                 if(_equal(backupAddress, abEntry))
01726                 {
01727                         _copy(abEntry, pilotAddress);
01728                         return CHANGED_PC;
01729                 }
01730                 else
01731                 {
01732                         // Both pc and palm were changed => merge, override, duplicate or ignore.
01733                         if(doSmartMerge())
01734                         {
01735                                 PilotAddress pAdr(pilotAddress);
01736                                 KABC::Addressee abEnt(abEntry);
01737                                 int res = _smartMerge(pilotAddress, backupAddress, abEntry);
01738                                 switch(res)
01739                                 {
01740                                         case CHANGED_NORES:
01741                                         case CHANGED_DUPLICATE:
01742                                                 pilotAddress = pAdr;
01743                                                 abEntry = abEnt;
01744                                 }
01745                                 return res;
01746                         }
01747                         else
01748                         {                       // no smart merge
01749                                 switch(getEntryResolution(abEntry, backupAddress, pilotAddress))
01750                                 {
01751                                         case eKeepBothInAbbrowser:
01752                                                 return CHANGED_DUPLICATE;
01753                                         case ePilotOverides:
01754                                                 _copy(abEntry, pilotAddress);
01755                                                 return CHANGED_PC;
01756                                         case eAbbrowserOverides:
01757                                                 _copy(pilotAddress, abEntry);
01758                                                 return CHANGED_PALM;
01759                                         case eRevertToBackup:
01760                                                 _copy(abEntry, backupAddress);
01761                                                 pilotAddress = backupAddress;
01762                                                 return CHANGED_BOTH;
01763                                         case eDoNotResolve:
01764                                                 return CHANGED_NORES;
01765                                                 default:
01766                                                 return CHANGED_NONE;
01767                                 }
01768                         }                       // smart merge
01769                 }                       // backup == abook
01770         }                               // pilot== backup
01771 
01772         return CHANGED_NONE;
01773 }
01774 
01775 AbbrowserConduit::EConflictResolution AbbrowserConduit::getFieldResolution(const QString & entry, const QString & field,
01776         const QString & palm, const QString & backup, const QString & pc)
01777 {
01778         FUNCTIONSETUP;
01779         EConflictResolution res = fEntryResolution;
01780         if(res == eUserChoose)
01781         {
01782                 res = getResolveConflictOption();
01783         }
01784         switch(res)
01785         {
01786                 case eKeepBothInAbbrowser:
01787                 case ePilotOverides:
01788                 case eAbbrowserOverides:
01789                 case eDoNotResolve:
01790                         return res;
01791                         break;
01792                 case eRevertToBackup:
01793                         if(backup.isNull()) return eDoNotResolve;
01794                         else return res;
01795                         break;
01796                 case eUserChoose:
01797                 default:
01798                         QStringList lst;
01799                         lst <<
01800                                 i18n("Leave untouched") <<
01801                                 i18n("Handheld overrides") <<
01802                                 i18n("PC overrides");
01803                         if(!backup.isNull()) lst << i18n("Use the value from the last sync");
01804                         lst << i18n("Duplicate both");
01805                         bool remember = FALSE;
01806                         res = ResolutionDialog(i18n("Address conflict"),
01807                                 i18n("<html><p>The field \"%1\" of the entry \"%2\" was changed on the handheld and on the PC.</p>"
01808                                 "<table border=0>"
01809                                 "<tr><td><b>Handheld:</b></td><td>%3</td></tr>"
01810                                 "<tr><td><b>PC:</b></td><td>%4</td></tr>"
01811                                 "<tr><td><b>last sync:</b></td><td>%5</td></tr>"
01812                                 "</table>"
01813                                 "<p>How should this conflict be resolved?</p></html>").arg(field).arg(entry).arg(palm).arg(pc).arg(backup),
01814                                 lst, i18n("Apply to all fields of this entry"), &remember);
01815                         if(backup.isNull() && res == 4) res =(EConflictResolution) 5;
01816                         if(remember)
01817                         {
01818                                 fEntryResolution = res;
01819                         }
01820                         return res;
01821         }
01822 }
01823 
01824 AbbrowserConduit::EConflictResolution AbbrowserConduit::getEntryResolution(const KABC::Addressee & abEntry, const PilotAddress &backupAddress, const PilotAddress & pilotAddress)
01825 {
01826         FUNCTIONSETUP;
01827         EConflictResolution res = getResolveConflictOption();
01828         switch(res)
01829         {
01830                 case eKeepBothInAbbrowser:
01831                 case ePilotOverides:
01832                 case eAbbrowserOverides:
01833                 case eRevertToBackup:
01834                 case eDoNotResolve:
01835                         return res;
01836                         break;
01837                 case eUserChoose:
01838                 default:
01839                         QStringList lst;
01840                         lst << i18n("Leave untouched") <<
01841                                 i18n("Handheld overrides") <<
01842                                 i18n("PC overrides") <<
01843                                 i18n("Use the value from the last sync");
01844 
01845                         bool remember = FALSE;
01846 
01847                         PilotAddress emptyAddress(fAddressAppInfo);
01848                         bool backupEmpty=backupAddress.isDeleted() || emptyAddress==backupAddress;
01849                         bool pilotEmpty=pilotAddress.isDeleted() || emptyAddress==pilotAddress;
01850                         QString backupEntryString;
01851                         if(!backupEmpty) backupEntryString=CSL1("%1 %2").arg(backupAddress.getField(entryFirstname)).arg(backupAddress.getField(entryLastname));
01852                         else backupEntryString=i18n("(deleted)");
01853                         QString pilotEntryString;
01854                         if(!pilotEmpty) pilotEntryString=CSL1("%1 %2").arg(pilotAddress.getField(entryFirstname)).arg(pilotAddress.getField(entryLastname));
01855                         else pilotEntryString=i18n("(deleted)");
01856 
01857                         if(!abEntry.isEmpty() && !pilotEmpty) lst << i18n("Duplicate both");
01858 
01859 #ifdef DEBUG
01860                         DEBUGCONDUIT<<"pilotAddress.firstName="<<pilotAddress.getField(entryFirstname)<<", pilotAddress.LastName="<<pilotAddress.getField(entryLastname)<<", pilotEmpty="<<pilotEmpty<<", pilotEntryString="<<pilotEntryString<<endl;
01861                         DEBUGCONDUIT<<"backupAddress.firstName="<<backupAddress.getField(entryFirstname)<<", backupAddress.LastName="<<backupAddress.getField(entryLastname)<<", backupEmpty="<<backupEmpty<<", backupEntryString="<<backupEntryString<<endl;
01862 #endif
01863                         res = ResolutionDialog(i18n("Address conflict"),
01864                                 i18n("<html><p>The following record was changed on the handheld and on the PC. </p>"
01865                                         "<table border=0>"
01866                                         "<tr><td><b>Handheld:</b></td><td>%1</td></tr>"
01867                                         "<tr><td><b>PC:</b></td><td>%2</td></tr>"
01868                                         "<tr><td><b>last sync:</b></td><td>%3</td></tr>"
01869                                         "</table>"
01870                                         "<p>How should this conflict be resolved?</p></html>")
01871                                         .arg(pilotEntryString)
01872                                         .arg(abEntry.isEmpty()? i18n("(deleted)") : abEntry.realName())
01873                                         .arg(backupEntryString)
01874                                 , lst, i18n("Remember my choice for all other records"), &remember);
01875                         if(remember)
01876                         {
01877                                 fConflictResolution = res;
01878                         }
01879                         return res;
01880         }
01881 }
01882 
01883 
01884 AbbrowserConduit::EConflictResolution AbbrowserConduit::ResolutionDialog(QString Title, QString Text, QStringList & lst, QString remember, bool * rem) const
01885 {
01886         FUNCTIONSETUP;
01887         ResolutionDlg *dlg = new ResolutionDlg(0L, fHandle, Title, Text, lst, remember);
01888         if(dlg->exec() == KDialogBase::Cancel)
01889         {
01890                 delete dlg;
01891                 return eDoNotResolve;
01892         }
01893         else
01894         {
01895                 EConflictResolution res = (EConflictResolution)(dlg->ResolutionButtonGroup->id(dlg->ResolutionButtonGroup->selected()) + 1);
01896 
01897                 if(!remember.isEmpty() && rem)
01898                 {
01899                         *rem = dlg->rememberCheck->isChecked();
01900                 }
01901                 delete dlg;
01902                 return res;
01903         }
01904 }
01905 
01906 
01907 // TODO: right now entries are equal if both first/last name and organization are
01908 //  equal. This rules out two entries for the same person(e.g. real home and weekend home)
01909 //  or two persons with the same name where you don't know the organization.!!!
01910 KABC::Addressee AbbrowserConduit::_findMatch(const PilotAddress & pilotAddress) const
01911 {
01912         FUNCTIONSETUP;
01913         // TODO: also search with the pilotID
01914         // first, use the pilotID to UID map to find the appropriate record
01915         if(!fFirstTime &&(pilotAddress.id() > 0))
01916         {
01917                 QString id(addresseeMap[pilotAddress.id()]);
01918 #ifdef DEBUG
01919                 DEBUGCONDUIT << fname << ": PilotRecord has id " << pilotAddress.id() << ", mapped to " << id << endl;
01920 #endif
01921                 if(!id.isEmpty())
01922                 {
01923                         KABC::Addressee res(aBook->findByUid(id));
01924                         if(!res.isEmpty()) return res;
01925 #ifdef DEBUG
01926                         DEBUGCONDUIT << fname << ": PilotRecord has id " << pilotAddress.id() << ", but could not be found in the addressbook" << endl;
01927 #endif
01928                 }
01929         }
01930 
01931         bool piFirstEmpty =(pilotAddress.getField(entryFirstname).isEmpty());
01932         bool piLastEmpty =(pilotAddress.getField(entryLastname).isEmpty());
01933         bool piCompanyEmpty =(pilotAddress.getField(entryCompany).isEmpty());
01934 
01935         // return not found if either one is empty on one side but not on the other
01936         if(piFirstEmpty && piLastEmpty && piCompanyEmpty)
01937         {
01938 #ifdef DEBUG
01939                 DEBUGCONDUIT << fname << ": entry has empty first, last name and company! Will not search in Addressbook" << endl;
01940 #endif
01941                 return KABC::Addressee();
01942         }
01943 
01944         // for now just loop throug all entries; in future, probably better
01945         // to create a map for first and last name, then just do O(1) calls
01946         for(KABC::AddressBook::Iterator iter = aBook->begin(); iter != aBook->end(); ++iter)
01947         {
01948                 KABC::Addressee abEntry = *iter;
01949                 // do quick empty check's
01950                 if(piFirstEmpty != abEntry.givenName().isEmpty() ||
01951                         piLastEmpty != abEntry.familyName().isEmpty() ||
01952                         piCompanyEmpty != abEntry.organization().isEmpty())
01953                 {
01954                         continue;
01955                 }
01956                 if(piFirstEmpty && piLastEmpty)
01957                 {
01958                         if(abEntry.organization() == pilotAddress.getField(entryCompany))
01959                         {
01960                                 return *iter;
01961                         }
01962                 }
01963                 else
01964                         // the first and last name must be equal; they are equal
01965                         // if they are both empty or the strings match
01966                         if(
01967                                 ((piLastEmpty && abEntry.familyName().isEmpty()) || (abEntry.familyName() == pilotAddress.getField(entryLastname))) &&
01968                                 ((piFirstEmpty && abEntry.givenName().isEmpty()) || (abEntry.givenName() == pilotAddress.getField(entryFirstname))) )
01969                         {
01970                                 return *iter;
01971                         }
01972 
01973         }
01974 #ifdef DEBUG
01975         DEBUGCONDUIT << fname << ": Could not find any addressbook enty matching " << pilotAddress.getField(entryLastname) << endl;
01976 #endif
01977         return KABC::Addressee();
01978 }
01979 
01980 
01981 
01982 void AbbrowserConduit::_checkDelete(PilotRecord* r, PilotRecord *s)
01983 {
01984         FUNCTIONSETUP;
01985         // Find out if changed on the PC, and if so, do a deconfliction.
01986         bool archiveRecord =(r)?(r->getAttrib() & dlpRecAttrArchived):false;
01987 
01988         KABC::Addressee e;
01989         PilotAddress adr(fAddressAppInfo, r), badr(fAddressAppInfo, s);
01990         if (r) e = _findMatch(adr);
01991         else if (s) e=_findMatch(badr);
01992         
01993         if (!e.isEmpty() && !_equal(badr, e))
01994         { // entry was changed on the PC => deconflict
01995 
01996                 switch(getEntryResolution(e, badr, adr))
01997                 {
01998                         case ePilotOverides:
01999                                 _deleteOnPC(r, s);
02000                                 break;
02001                         case eAbbrowserOverides:
02002                                 _copy(adr, e);
02003                                 // undelete the pilot record before changing to make sure it doesn't stay deleted!
02004                                 adr.setAttrib(adr.getAttrib() & ~dlpRecAttrDeleted);
02005                                 if (_savePilotAddress(adr, e)) _saveAbEntry(e);
02006                                 break;
02007                         case eRevertToBackup:
02008                                 _copy(e, badr);
02009                                 _savePilotAddress(badr, e);
02010                                 _saveAbEntry(e);
02011                                 break;
02012                         case eDoNotResolve:
02013                         default:
02014                                 break;
02015                 }
02016         }
02017         else
02018         {
02019                 if(fArchive && archiveRecord)
02020                 {
02021                         e = _changeOnPC(r, s);
02022                         e.insertCustom(appString, flagString, QString::number(SYNCDEL));
02023                         aBook->insertAddressee(e);
02024                 }
02025                 else
02026                 {
02027                         _deleteOnPC(r, s);
02028                 }
02029         }
02030 }
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