diff options
Diffstat (limited to 'conduits/abbrowserconduit')
19 files changed, 5308 insertions, 0 deletions
diff --git a/conduits/abbrowserconduit/CMakeLists.txt b/conduits/abbrowserconduit/CMakeLists.txt new file mode 100644 index 0000000..2459d1d --- /dev/null +++ b/conduits/abbrowserconduit/CMakeLists.txt @@ -0,0 +1,46 @@ +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} +) + +set(conduit_abbrowser_SRCS + resolutionDialog.cc + abbrowser-factory.cc + abbrowser-setup.cc + kabcRecord.cc + abbrowser-conduit.cc +) + +set(conduit_abbrowser_UIS + resolutionDialog_base.ui + kaddressbookConduit.ui +) + +set(conduit_abbrowser_KCFGS + abbrowserSettings.kcfgc +) + +kde3_add_kcfg_files(conduit_abbrowser_SRCS ${conduit_abbrowser_KCFGS}) +kde3_add_ui_files(conduit_abbrowser_SRCS ${conduit_abbrowser_UIS}) +kde3_automoc(${conduit_abbrowser_SRCS}) +add_library(conduit_address SHARED ${conduit_abbrowser_SRCS}) +target_link_libraries(conduit_address kabc_file kabc) +set_target_properties(conduit_address PROPERTIES LOCATION ${KDE3_PLUGIN_INSTALL_DIR} + INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/lib + PREFIX "" +) + +kde3_install_libtool_file(conduit_address) + +install( + TARGETS conduit_address + LIBRARY DESTINATION ${KDE3_PLUGIN_INSTALL_DIR} +) + +install( + FILES abbrowser_conduit.desktop DESTINATION ${KDE3_SERVICES_DIR} +) + +install( + FILES abbrowserconduit.kcfg DESTINATION ${KDE3_KCFG_DIR} +) + diff --git a/conduits/abbrowserconduit/KPilotCustomFieldEditor.ui b/conduits/abbrowserconduit/KPilotCustomFieldEditor.ui new file mode 100644 index 0000000..832864d --- /dev/null +++ b/conduits/abbrowserconduit/KPilotCustomFieldEditor.ui @@ -0,0 +1,276 @@ +<!DOCTYPE UI><UI version="3.2" stdsetdef="1"> +<class>KPilotCustomFields</class> +<comment>This form lets you edit the custom fields synced from PalmOS handhelds by the addressbook conduit of KPilot.</comment> +<author>Reinhold Kainhofer <[email protected]></author> +<widget class="QWidget"> + <property name="name"> + <cstring>KPILOT</cstring> + </property> + <property name="tqgeometry"> + <rect> + <x>0</x> + <y>0</y> + <width>461</width> + <height>409</height> + </rect> + </property> + <property name="caption"> + <string>KPilot Custom Fields</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>mCustomFieldsGroups</cstring> + </property> + <property name="title"> + <string>KPilot Custom Fields</string> + </property> + <property name="whatsThis" stdset="0"> + <string>On your handheld, each address also provides four custom fields for your personal use. KPilot can sync these either to birthdate, URL, IM address, or just store them as a custom field on your PC with no special meaning. In the last case, you can change the values here. Note, however, that for all other settings the values entered here will have no effect.</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>mCustom3Label</cstring> + </property> + <property name="text"> + <string>Custom &3:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>X_CUSTOM3</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string><qt>Edit or enter the value of the third custom field here. Using KPilot, you can synchronize these values with the handheld's Address application custom fields.</qt></string> + </property> + </widget> + <widget class="QLabel" row="4" column="0"> + <property name="name"> + <cstring>mCustom4Label</cstring> + </property> + <property name="text"> + <string>Custom &4:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>X_CUSTOM3</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string><qt>Edit or enter the value of the fourth custom field here. Using KPilot, you can synchronize these values with the handheld's Address application custom fields.</qt></string> + </property> + </widget> + <widget class="QLineEdit" row="3" column="1"> + <property name="name"> + <cstring>X_CUSTOM2</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string><qt>Edit or enter the value of the third custom field here. Using KPilot, you can synchronize these values with the handheld's Address application custom fields.</qt></string> + </property> + </widget> + <widget class="QLineEdit" row="2" column="1"> + <property name="name"> + <cstring>X_CUSTOM1</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string><qt>Edit or enter the value of the second custom field here. Using KPilot, you can synchronize these values with the handheld's Address application custom fields.</qt></string> + </property> + </widget> + <widget class="QLineEdit" row="4" column="1"> + <property name="name"> + <cstring>X_CUSTOM3</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string><qt>Edit or enter the value of the fourth custom field here. Using KPilot, you can synchronize these values with the handheld's Address application custom fields.</qt></string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>mCustom2Label</cstring> + </property> + <property name="text"> + <string>Custom &2:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>X_CUSTOM2</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string><qt>Edit or enter the value of the second custom field here. Using KPilot, you can synchronize these values with the handheld's Address application custom fields.</qt></string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>mCustom1Label</cstring> + </property> + <property name="text"> + <string>Custom &1:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>X_CUSTOM1</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string><qt>Edit or enter the value of the first custom field here. Using KPilot, you can synchronize these values with the handheld's Address application custom fields.</qt></string> + </property> + </widget> + <widget class="QLineEdit" row="1" column="1"> + <property name="name"> + <cstring>X_CUSTOM0</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="whatsThis" stdset="0"> + <string><qt>Edit or enter the value of the first custom field here. Using KPilot, you can synchronize these values with the handheld's Address application custom fields.</qt></string> + </property> + </widget> + <widget class="QLabel" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>mCustomFieldsExplanation</cstring> + </property> + <property name="text"> + <string>If you let KPilot sync the handheld's custom fields as custom fields on the PC, you can change the values here. Note, however, that for all other settings the values entered here will have no effect.</string> + </property> + <property name="tqalignment"> + <set>WordBreak|AlignVCenter</set> + </property> + </widget> + </grid> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>mMetaSyncGroup</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="title"> + <string>KPilot's Private (meta-sync) Settings</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>mRecordIDLabel</cstring> + </property> + <property name="text"> + <string>Record&ID:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>X_RecordID</cstring> + </property> + </widget> + <widget class="QLabel" row="1" column="3"> + <property name="name"> + <cstring>mSyncFlagLabel</cstring> + </property> + <property name="text"> + <string>Sync &flag:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>X_Flag</cstring> + </property> + </widget> + <spacer row="1" column="5"> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="tqsizeHint"> + <size> + <width>41</width> + <height>20</height> + </size> + </property> + </spacer> + <spacer row="1" column="2"> + <property name="name"> + <cstring>spacer3</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="tqsizeHint"> + <size> + <width>31</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLabel" row="0" column="0" rowspan="1" colspan="6"> + <property name="name"> + <cstring>mMetaSyncSettingsWarning</cstring> + </property> + <property name="text"> + <string>These values indicate the state of the record for KPilot, and connect an entry on the handheld with an entry on the PC. +Do NOT change these values: doing so will almost certainly result in data loss when you next do a sync.</string> + </property> + <property name="tqalignment"> + <set>WordBreak|AlignVCenter</set> + </property> + </widget> + <widget class="QSpinBox" row="1" column="1"> + <property name="name"> + <cstring>X_RecordID</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="maxValue"> + <number>2147483647</number> + </property> + </widget> + <widget class="QSpinBox" row="1" column="4"> + <property name="name"> + <cstring>X_Flag</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="maxValue"> + <number>3</number> + </property> + </widget> + </grid> + </widget> + <spacer> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="tqsizeHint"> + <size> + <width>20</width> + <height>210</height> + </size> + </property> + </spacer> + </vbox> +</widget> +<tqlayoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/conduits/abbrowserconduit/Makefile.am b/conduits/abbrowserconduit/Makefile.am new file mode 100644 index 0000000..b012765 --- /dev/null +++ b/conduits/abbrowserconduit/Makefile.am @@ -0,0 +1,24 @@ +INCLUDES= $(PISOCK_INCLUDE) -I$(top_srcdir)/kpilot/lib $(all_includes) + +kde_module_LTLIBRARIES = conduit_address.la + +conduit_address_la_SOURCES = \ + resolutionDialog_base.ui \ + kaddressbookConduit.ui \ + abbrowserSettings.kcfgc \ + resolutionDialog.cc \ + abbrowser-factory.cc \ + abbrowser-setup.cc \ + kabcRecord.cc \ + abbrowser-conduit.cc +conduit_address_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) $(LIB_QT) -lkdecore -lkio -lkdeui +conduit_address_la_LIBADD = ../../lib/libkpilot.la -lkabc -lkabc_file + +servicedir = $(kde_servicesdir) +service_DATA = abbrowser_conduit.desktop +kde_kcfg_DATA = abbrowserconduit.kcfg + +kabcustompagedir = $(kde_datadir)/kaddressbook/contacteditorpages +kabcustompage_DATA = KPilotCustomFieldEditor.ui + +METASOURCES = AUTO diff --git a/conduits/abbrowserconduit/abbrowser-conduit.cc b/conduits/abbrowserconduit/abbrowser-conduit.cc new file mode 100644 index 0000000..e4f674a --- /dev/null +++ b/conduits/abbrowserconduit/abbrowser-conduit.cc @@ -0,0 +1,1897 @@ +/* KPilot +** +** Copyright (C) 2000,2001 by Dan Pilone +** Copyright (C) 2002-2003 by Reinhold Kainhofer +** Copyright (C) 2007 by Adriaan de Groot <[email protected]> +** +** The abbrowser conduit copies addresses from the Pilot's address book to +** the KDE addressbook maintained via the kabc library. +*/ + +/* +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program in a file called COPYING; if not, write to +** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +** MA 02110-1301, USA. +*/ + +/* +** Bug reports and questions can be sent to [email protected]. +*/ + + + +#include "options.h" + +#include <tqtimer.h> +#include <tqtextcodec.h> +#include <tqfile.h> +#include <tqregexp.h> + +#include <kabc/stdaddressbook.h> +#include <kabc/resourcefile.h> +#include <kio/netaccess.h> +#include <ksavefile.h> + +#include <pilotSerialDatabase.h> +#include <pilotLocalDatabase.h> + +#include "resolutionDialog.h" +#include "resolutionTable.h" +#include "abbrowserSettings.h" +#include "kabcRecord.h" + +#include "abbrowser-conduit.moc" + +// Something to allow us to check what revision +// the modules are that make up a binary distribution. +// +// +extern "C" +{ +unsigned long version_conduit_address = Pilot::PLUGIN_API; +} + + +/* This is partly stolen from the boost libraries, partly from +* "Modern C++ design" for doing compile time checks; we need +* to make sure that the enum values in KABCSync:: and in the +* AbbrowserSettings class are the same so that both interpret +* configuration values the same way. +*/ +template<bool> struct EnumerationMismatch; +template<> struct EnumerationMismatch<true>{}; + +#define CHECK_ENUM(a) (void)sizeof(EnumerationMismatch<((int)KABCSync::a)==((int)AbbrowserSettings::a)>) + +static inline void compile_time_check() +{ + // Mappings for other phone + CHECK_ENUM(eOtherPhone); + CHECK_ENUM(eOtherPhone); + CHECK_ENUM(eAssistant); + CHECK_ENUM(eBusinessFax); + CHECK_ENUM(eCarPhone); + CHECK_ENUM(eEmail2); + CHECK_ENUM(eHomeFax); + CHECK_ENUM(eTelex); + CHECK_ENUM(eTTYTTDPhone); + + // Mappings for custom fields + CHECK_ENUM(eCustomField); + CHECK_ENUM(eCustomBirthdate); + CHECK_ENUM(eCustomURL); + CHECK_ENUM(eCustomIM); +} + +inline int faxTypeOnPC() +{ + return KABC::PhoneNumber::Fax | + ( (AbbrowserSettings::pilotFax()==0) ? + KABC::PhoneNumber::Home : + KABC::PhoneNumber::Work ); +} + + +using namespace KABC; + +/********************************************************************* + C O N S T R U C T O R + *********************************************************************/ + + +AbbrowserConduit::AbbrowserConduit(KPilotLink * o, const char *n, const TQStringList & a): + ConduitAction(o, n, a), + aBook(0L), + fAddressAppInfo(0L), + addresseeMap(), + syncedIds(), + abiter(), + fTicket(0L), + fCreatedBook(false), + fBookResource(0L) +{ + FUNCTIONSETUP; + fConduitName=i18n("Addressbook"); +} + + + +AbbrowserConduit::~AbbrowserConduit() +{ + FUNCTIONSETUP; + + if (fTicket) + { + DEBUGKPILOT << fname << ": Releasing ticket" << endl; + aBook->releaseSaveTicket(fTicket); + fTicket=0L; + } + + _cleanupAddressBookPointer(); + // unused function warnings. + compile_time_check(); +} + + + +/********************************************************************* + L O A D I N G T H E D A T A + *********************************************************************/ + + + +/* Builds the map which links record ids to uid's of Addressee +*/ +void AbbrowserConduit::_mapContactsToPilot(TQMap < recordid_t, TQString > &idContactMap) +{ + FUNCTIONSETUP; + + idContactMap.clear(); + + for(AddressBook::Iterator contactIter = aBook->begin(); + contactIter != aBook->end(); ++contactIter) + { + Addressee aContact = *contactIter; + TQString recid = aContact.custom(KABCSync::appString, KABCSync::idString); + if(!recid.isEmpty()) + { + recordid_t id = recid.toULong(); + // safety check: make sure that we don't already have a map for this pilot id. + // if we do (this can come from a copy/paste in kaddressbook, etc.), then we need + // to reset our Addressee so that we can assign him a new pilot Id later and sync + // him properly. if we don't do this, we'll lose one of these on the pilot. + if (!idContactMap.tqcontains(id)) + { + idContactMap.insert(id, aContact.uid()); + } + else + { + DEBUGKPILOT << fname << ": found duplicate pilot key: [" + << id << "], removing pilot id from addressee: [" + << aContact.realName() << "]" << endl; + aContact.removeCustom(KABCSync::appString, KABCSync::idString); + aBook->insertAddressee(aContact); + abChanged = true; + } + } + } + DEBUGKPILOT << fname << ": Loaded " << idContactMap.size() << + " addresses from the addressbook. " << endl; +} + + + +bool AbbrowserConduit::_prepare() +{ + FUNCTIONSETUP; + + readConfig(); + syncedIds.clear(); + pilotindex = 0; + + return true; +} + + + +void AbbrowserConduit::readConfig() +{ + FUNCTIONSETUP; + AbbrowserSettings::self()->readConfig(); + + // Conflict page + SyncAction::ConflictResolution res = (SyncAction::ConflictResolution)AbbrowserSettings::conflictResolution(); + setConflictResolution(res); + + DEBUGKPILOT << fname + << ": Reading addressbook " + << ( AbbrowserSettings::addressbookType() == AbbrowserSettings::eAbookFile ? + AbbrowserSettings::fileName() : CSL1("Standard") ) + << endl; + DEBUGKPILOT << fname + << ": " + << " fConflictResolution=" << getConflictResolution() + << " fArchive=" << AbbrowserSettings::archiveDeleted() + << " fFirstTime=" << isFirstSync() + << endl; + DEBUGKPILOT << fname + << ": " + << " fPilotStreetHome=" << AbbrowserSettings::pilotStreet() + << " fPilotFaxHome=" << AbbrowserSettings::pilotFax() + << " eCustom[0]=" << AbbrowserSettings::custom0() + << " eCustom[1]=" << AbbrowserSettings::custom1() + << " eCustom[2]=" << AbbrowserSettings::custom2() + << " eCustom[3]=" << AbbrowserSettings::custom3() + << endl; +} + + + +bool isDeleted(const PilotAddress *addr) +{ + if (!addr) + { + return true; + } + if (addr->isDeleted() && !addr->isArchived()) + { + return true; + } + if (addr->isArchived()) + { + return !AbbrowserSettings::archiveDeleted(); + } + return false; +} + +bool isArchived(const PilotAddress *addr) +{ + if (addr && addr->isArchived()) + { + return AbbrowserSettings::archiveDeleted(); + } + else + { + return false; + } +} + + + +bool AbbrowserConduit::_loadAddressBook() +{ + FUNCTIONSETUP; + + startTickle(); + switch ( AbbrowserSettings::addressbookType() ) + { + case AbbrowserSettings::eAbookResource: + DEBUGKPILOT<<"Loading standard addressbook"<<endl; + aBook = StdAddressBook::self( true ); + fCreatedBook=false; + break; + case AbbrowserSettings::eAbookFile: + { // initialize the abook with the given file + DEBUGKPILOT<<"Loading custom addressbook"<<endl; + KURL kurl(AbbrowserSettings::fileName()); + if(!KIO::NetAccess::download(AbbrowserSettings::fileName(), fABookFile, 0L) && + !kurl.isLocalFile()) + { + emit logError(i18n("You chose to sync with the file \"%1\", which " + "cannot be opened. Please make sure to supply a " + "valid file name in the conduit's configuration dialog. " + "Aborting the conduit.").arg(AbbrowserSettings::fileName())); + KIO::NetAccess::removeTempFile(fABookFile); + stopTickle(); + return false; + } + + aBook = new AddressBook(); + if (!aBook) + { + stopTickle(); + return false; + } + fBookResource = new ResourceFile(fABookFile, CSL1("vcard") ); + + bool r = aBook->addResource( fBookResource ); + if ( !r ) + { + DEBUGKPILOT << "Unable to open resource for file " << fABookFile << endl; + KPILOT_DELETE( aBook ); + stopTickle(); + return false; + } + fCreatedBook=true; + break; + } + default: break; + } + // find out if this can fail for reasons other than a non-existent + // vcf file. If so, how can I determine if the missing file was the problem + // or something more serious: + if ( !aBook || !aBook->load() ) + { + // Something went wrong, so tell the user and return false to exit the conduit + emit logError(i18n("Unable to initialize and load the addressbook for the sync.") ); + addSyncLogEntry(i18n("Unable to initialize and load the addressbook for the sync.") ); + WARNINGKPILOT << "Unable to initialize the addressbook for the sync." << endl; + _cleanupAddressBookPointer(); + stopTickle(); + return false; + } + abChanged = false; + + fTicket=aBook->requestSaveTicket(); + if (!fTicket) + { + WARNINGKPILOT << "Unable to lock addressbook for writing " << endl; + emit logError(i18n("Unable to lock addressbook for writing. Can't sync!")); + addSyncLogEntry(i18n("Unable to lock addressbook for writing. Can't sync!")); + _cleanupAddressBookPointer(); + stopTickle(); + return false; + } + + fCtrPC->setStartCount(aBook->allAddressees().count()); + + // get the addresseMap which maps Pilot unique record(address) id's to + // a Abbrowser Addressee; allows for easy lookup and comparisons + if(aBook->begin() == aBook->end()) + { + setFirstSync( true ); + } + else + { + _mapContactsToPilot(addresseeMap); + } + stopTickle(); + return(aBook != 0L); +} + +bool AbbrowserConduit::_saveAddressBook() +{ + FUNCTIONSETUP; + + bool saveSuccessful = false; + + fCtrPC->setEndCount(aBook->allAddressees().count()); + + Q_ASSERT(fTicket); + + if (abChanged) + { + saveSuccessful = aBook->save(fTicket); + } + else + { + DEBUGKPILOT << fname + << "Addressbook not changed, no need to save it" << endl; + } + // XXX: KDE4: release ticket in all cases (save no longer releases it) + if ( !saveSuccessful ) // didn't save, delete ticket manually + { + aBook->releaseSaveTicket(fTicket); + } + fTicket=0L; + + if ( AbbrowserSettings::addressbookType()!= AbbrowserSettings::eAbookResource ) + { + KURL kurl(AbbrowserSettings::fileName()); + if(!kurl.isLocalFile()) + { + DEBUGKPILOT << fname << "Deleting local addressbook tempfile" << endl; + if(!KIO::NetAccess::upload(fABookFile, AbbrowserSettings::fileName(), 0L)) { + emit logError(i18n("An error occurred while uploading \"%1\". You can try to upload " + "the temporary local file \"%2\" manually") + .arg(AbbrowserSettings::fileName()).arg(fABookFile)); + } + else { + KIO::NetAccess::removeTempFile(fABookFile); + } + TQFile backup(fABookFile + CSL1("~")); + backup.remove(); + } + + } + + // now try to remove the resource from the addressbook... + if (fBookResource) + { + bool r = aBook->removeResource( fBookResource ); + if ( !r ) + { + DEBUGKPILOT << fname <<": Unable to close resource." << endl; + } + } + + return saveSuccessful; +} + + + +void AbbrowserConduit::_getAppInfo() +{ + FUNCTIONSETUP; + + delete fAddressAppInfo; + fAddressAppInfo = new PilotAddressInfo(fDatabase); + fAddressAppInfo->dump(); +} + +void AbbrowserConduit::_setAppInfo() +{ + FUNCTIONSETUP; + if (fDatabase) fAddressAppInfo->writeTo(fDatabase); + if (fLocalDatabase) fAddressAppInfo->writeTo(fLocalDatabase); +} + + +void AbbrowserConduit::_cleanupAddressBookPointer() +{ + if (fCreatedBook) + { + KPILOT_DELETE(aBook); + fCreatedBook=false; + } + else + { + aBook=0L; + } +} + + + + +/********************************************************************* + D E B U G O U T P U T + *********************************************************************/ + + + + + +void AbbrowserConduit::showPilotAddress(const PilotAddress *pilotAddress) +{ + FUNCTIONSETUPL(3); + if (debug_level < 3) + { + return; + } + if (!pilotAddress) + { + DEBUGKPILOT<< fname << "| EMPTY"<<endl; + return; + } + DEBUGKPILOT << fname << "\n" + << pilotAddress->getTextRepresentation( + fAddressAppInfo,Qt::PlainText) << endl; +} + + +void AbbrowserConduit::showAddresses( + const Addressee &pcAddr, + const PilotAddress *backupAddr, + const PilotAddress *palmAddr) +{ + FUNCTIONSETUPL(3); + if (debug_level >= 3) + { + DEBUGKPILOT << fname << "abEntry:" << endl; + KABCSync::showAddressee(pcAddr); + DEBUGKPILOT << fname << "pilotAddress:" << endl; + showPilotAddress(palmAddr); + DEBUGKPILOT << fname << "backupAddress:" << endl; + showPilotAddress(backupAddr); + DEBUGKPILOT << fname << "------------------------------------------------" << endl; + } +} + + + +/********************************************************************* + S Y N C S T R U C T U R E + *********************************************************************/ + + + +/* virtual */ bool AbbrowserConduit::exec() +{ + FUNCTIONSETUP; + + _prepare(); + + bool retrieved = false; + if(!openDatabases(CSL1("AddressDB"), &retrieved)) + { + emit logError(i18n("Unable to open the addressbook databases on the handheld.")); + return false; + } + setFirstSync( retrieved ); + + _getAppInfo(); + + // Local block + { + TQString dbpath = fLocalDatabase->dbPathName(); + DEBUGKPILOT << fname << ": Local database path " << dbpath << endl; + } + + if ( syncMode().isTest() ) + { + TQTimer::singleShot(0, this, TQT_SLOT(slotTestRecord())); + return true; + } + + if(!_loadAddressBook()) + { + emit logError(i18n("Unable to open the addressbook.")); + return false; + } + setFirstSync( isFirstSync() || (aBook->begin() == aBook->end()) ); + + DEBUGKPILOT << fname << ": First sync now " << isFirstSync() + << " and addressbook is " + << ((aBook->begin() == aBook->end()) ? "" : "non-") + << "empty." << endl; + + // perform syncing from palm to abbrowser + // iterate through all records in palm pilot + + DEBUGKPILOT << fname << ": fullsync=" << isFullSync() << ", firstSync=" << isFirstSync() << endl; + DEBUGKPILOT << fname << ": " + << "syncDirection=" << syncMode().name() << ", " + << "archive = " << AbbrowserSettings::archiveDeleted() << endl; + DEBUGKPILOT << fname << ": conflictRes="<< getConflictResolution() << endl; + DEBUGKPILOT << fname << ": PilotStreetHome=" << AbbrowserSettings::pilotStreet() << ", PilotFaxHOme" << AbbrowserSettings::pilotFax() << endl; + + if (!isFirstSync()) + { + allIds=fDatabase->idList(); + } + + TQValueVector<int> v(4); + v[0] = AbbrowserSettings::custom0(); + v[1] = AbbrowserSettings::custom1(); + v[2] = AbbrowserSettings::custom2(); + v[3] = AbbrowserSettings::custom3(); + + fSyncSettings.setCustomMapping(v); + fSyncSettings.setFieldForOtherPhone(AbbrowserSettings::pilotOther()); + fSyncSettings.setDateFormat(AbbrowserSettings::customDateFormat()); + fSyncSettings.setPreferHome(AbbrowserSettings::pilotStreet()==0); + fSyncSettings.setFaxTypeOnPC(faxTypeOnPC()); + + /* Note: + if eCopyPCToHH or eCopyHHToPC, first sync everything, then lookup + those entries on the receiving side that are not yet syncced and delete + them. Use slotDeleteUnsyncedPCRecords and slotDeleteUnsyncedHHRecords + for this, and no longer purge the whole addressbook before the sync to + prevent data loss in case of connection loss. */ + + TQTimer::singleShot(0, this, TQT_SLOT(slotPalmRecToPC())); + + return true; +} + + + +void AbbrowserConduit::slotPalmRecToPC() +{ + FUNCTIONSETUP; + PilotRecord *palmRec = 0L, *backupRec = 0L; + + if ( syncMode() == SyncMode::eCopyPCToHH ) + { + DEBUGKPILOT << fname << ": Done; change to PCtoHH phase." << endl; + abiter = aBook->begin(); + TQTimer::singleShot(0, this, TQT_SLOT(slotPCRecToPalm())); + return; + } + + if(isFullSync()) + { + palmRec = fDatabase->readRecordByIndex(pilotindex++); + } + else + { + palmRec = fDatabase->readNextModifiedRec(); + } + + // no record means we're done going in this direction, so switch to + // PC->Palm + if(!palmRec) + { + abiter = aBook->begin(); + TQTimer::singleShot(0, this, TQT_SLOT(slotPCRecToPalm())); + return; + } + + // already synced, so skip: + if(syncedIds.tqcontains(palmRec->id())) + { + KPILOT_DELETE(palmRec); + TQTimer::singleShot(0, this, TQT_SLOT(slotPalmRecToPC())); + return; + } + + backupRec = fLocalDatabase->readRecordById(palmRec->id()); + PilotRecord*compareRec=(backupRec)?(backupRec):(palmRec); + Addressee e = _findMatch(PilotAddress(compareRec)); + + PilotAddress*backupAddr=0L; + if (backupRec) + { + backupAddr=new PilotAddress(backupRec); + } + + PilotAddress*palmAddr=0L; + if (palmRec) + { + palmAddr=new PilotAddress(palmRec); + } + + syncAddressee(e, backupAddr, palmAddr); + + syncedIds.append(palmRec->id()); + KPILOT_DELETE(palmAddr); + KPILOT_DELETE(backupAddr); + KPILOT_DELETE(palmRec); + KPILOT_DELETE(backupRec); + + TQTimer::singleShot(0, this, TQT_SLOT(slotPalmRecToPC())); +} + + + +void AbbrowserConduit::slotPCRecToPalm() +{ + FUNCTIONSETUP; + + if ( (syncMode()==SyncMode::eCopyHHToPC) || + abiter == aBook->end() || (*abiter).isEmpty() ) + { + DEBUGKPILOT << fname << ": Done; change to delete records." << endl; + pilotindex = 0; + TQTimer::singleShot(0, this, TQT_SLOT(slotDeletedRecord())); + return; + } + + PilotRecord *palmRec=0L, *backupRec=0L; + Addressee ad = *abiter; + + abiter++; + + // If marked as archived, don't sync! + if (KABCSync::isArchived(ad)) + { + DEBUGKPILOT << fname << ": address with id " << ad.uid() << + " marked archived, so don't sync." << endl; + TQTimer::singleShot(0, this, TQT_SLOT(slotPCRecToPalm())); + return; + } + + + TQString recID(ad.custom(KABCSync::appString, KABCSync::idString)); + bool ok; + recordid_t rid = recID.toLong(&ok); + if (recID.isEmpty() || !ok || !rid) + { + DEBUGKPILOT << fname << ": This is a new record." << endl; + // it's a new item(no record ID and not inserted by the Palm -> PC sync), so add it + syncAddressee(ad, 0L, 0L); + TQTimer::singleShot(0, this, TQT_SLOT(slotPCRecToPalm())); + return; + } + + // look into the list of already synced record ids to see if the addressee hasn't already been synced + if (syncedIds.tqcontains(rid)) + { + DEBUGKPILOT << ": address with id " << rid << " already synced." << endl; + TQTimer::singleShot(0, this, TQT_SLOT(slotPCRecToPalm())); + return; + } + + + backupRec = fLocalDatabase->readRecordById(rid); + // only update if no backup record or the backup record is not equal to the addressee + + PilotAddress*backupAddr=0L; + if (backupRec) + { + backupAddr=new PilotAddress(backupRec); + } + if(!backupRec || isFirstSync() || !_equal(backupAddr, ad) ) + { + DEBUGKPILOT << fname << ": Updating entry." << endl; + palmRec = fDatabase->readRecordById(rid); + PilotAddress *palmAddr = 0L; + if (palmRec) + { + palmAddr = new PilotAddress(palmRec); + } + else + { + DEBUGKPILOT << fname << ": No HH record with id " << rid << endl; + } + syncAddressee(ad, backupAddr, palmAddr); + // update the id just in case it changed + if (palmRec) rid=palmRec->id(); + KPILOT_DELETE(palmRec); + KPILOT_DELETE(palmAddr); + } + else + { + DEBUGKPILOT << fname << ": Entry not updated." << endl; + } + KPILOT_DELETE(backupAddr); + KPILOT_DELETE(backupRec); + + DEBUGKPILOT << fname << ": adding id:["<< rid << "] to syncedIds." << endl; + + syncedIds.append(rid); + // done with the sync process, go on with the next one: + TQTimer::singleShot(0, this, TQT_SLOT(slotPCRecToPalm())); +} + + + +void AbbrowserConduit::slotDeletedRecord() +{ + FUNCTIONSETUP; + + PilotRecord *backupRec = fLocalDatabase->readRecordByIndex(pilotindex++); + if(!backupRec || isFirstSync() ) + { + KPILOT_DELETE(backupRec); + TQTimer::singleShot(0, this, TQT_SLOT(slotDeleteUnsyncedPCRecords())); + return; + } + + recordid_t id = backupRec->id(); + + TQString uid = addresseeMap[id]; + Addressee e = aBook->tqfindByUid(uid); + + DEBUGKPILOT << fname << ": now looking at palm id: [" + << id << "], kabc uid: [" << uid << "]." << endl; + + PilotAddress*backupAddr=0L; + if (backupRec) + { + backupAddr=new PilotAddress(backupRec); + } + PilotRecord*palmRec=fDatabase->readRecordById(id); + + if ( e.isEmpty() ) + { + DEBUGKPILOT << fname << ": no Addressee found for this id." << endl; + DEBUGKPILOT << fname << "\n" + << backupAddr->getTextRepresentation( + fAddressAppInfo,Qt::PlainText) << endl; + + if (palmRec) { + DEBUGKPILOT << fname << ": deleting from database on palm." << endl; + fDatabase->deleteRecord(id); + fCtrHH->deleted(); + } + DEBUGKPILOT << fname << ": deleting from backup database." << endl; + fLocalDatabase->deleteRecord(id); + + // because we just deleted a record, we need to go back one + pilotindex--; + } + + KPILOT_DELETE(palmRec); + KPILOT_DELETE(backupAddr); + KPILOT_DELETE(backupRec); + TQTimer::singleShot(0, this, TQT_SLOT(slotDeletedRecord())); +} + + + +void AbbrowserConduit::slotDeleteUnsyncedPCRecords() +{ + FUNCTIONSETUP; + if ( syncMode()==SyncMode::eCopyHHToPC ) + { + TQStringList uids; + RecordIDList::iterator it; + TQString uid; + for ( it = syncedIds.begin(); it != syncedIds.end(); ++it) + { + uid=addresseeMap[*it]; + if (!uid.isEmpty()) uids.append(uid); + } + // TODO: Does this speed up anything? + // qHeapSort( uids ); + AddressBook::Iterator abit; + for (abit = aBook->begin(); abit != aBook->end(); ++abit) + { + if (!uids.tqcontains((*abit).uid())) + { + DEBUGKPILOT<<"Deleting addressee "<<(*abit).realName()<<" from PC (is not on HH, and syncing with HH->PC direction)"<<endl; + abChanged = true; + // TODO: Can I really remove the current iterator??? + aBook->removeAddressee(*abit); + fCtrPC->deleted(); + } + } + } + TQTimer::singleShot(0, this, TQT_SLOT(slotDeleteUnsyncedHHRecords())); +} + + + +void AbbrowserConduit::slotDeleteUnsyncedHHRecords() +{ + FUNCTIONSETUP; + if ( syncMode()==SyncMode::eCopyPCToHH ) + { + RecordIDList ids=fDatabase->idList(); + RecordIDList::iterator it; + for ( it = ids.begin(); it != ids.end(); ++it ) + { + if (!syncedIds.tqcontains(*it)) + { + DEBUGKPILOT<<"Deleting record with ID "<<*it<<" from handheld (is not on PC, and syncing with PC->HH direction)"<<endl; + fDatabase->deleteRecord(*it); + fCtrHH->deleted(); + fLocalDatabase->deleteRecord(*it); + } + } + } + TQTimer::singleShot(0, this, TQT_SLOT(slotCleanup())); +} + + +void AbbrowserConduit::slotCleanup() +{ + FUNCTIONSETUP; + + // Set the appInfoBlock, just in case the category labels changed + _setAppInfo(); + if(fDatabase) + { + fDatabase->resetSyncFlags(); + fDatabase->cleanup(); + } + if(fLocalDatabase) + { + fLocalDatabase->resetSyncFlags(); + fLocalDatabase->cleanup(); + } + + // Write out the sync maps + TQString syncFile = fLocalDatabase->dbPathName() + CSL1(".sync"); + DEBUGKPILOT << fname << ": Writing sync map to " << syncFile << endl; + KSaveFile map( syncFile ); + if ( map.status() == 0 ) + { + DEBUGKPILOT << fname << ": Writing sync map ..." << endl; + (*map.dataStream()) << addresseeMap ; + map.close(); + } + // This also picks up errors from map.close() + if ( map.status() != 0 ) + { + WARNINGKPILOT << "Could not make backup of sync map." << endl; + } + + _saveAddressBook(); + delayDone(); +} + + + +/********************************************************************* + G E N E R A L S Y N C F U N C T I O N + These functions modify the Handheld and the addressbook + *********************************************************************/ + + + +bool AbbrowserConduit::syncAddressee(Addressee &pcAddr, PilotAddress*backupAddr, + PilotAddress*palmAddr) +{ + FUNCTIONSETUP; + showAddresses(pcAddr, backupAddr, palmAddr); + + if ( syncMode() == SyncMode::eCopyPCToHH ) + { + if (pcAddr.isEmpty()) + { + return _deleteAddressee(pcAddr, backupAddr, palmAddr); + } + else + { + return _copyToHH(pcAddr, backupAddr, palmAddr); + } + } + + if ( syncMode() == SyncMode::eCopyHHToPC ) + { + if (!palmAddr) + { + return _deleteAddressee(pcAddr, backupAddr, palmAddr); + } + else + { + return _copyToPC(pcAddr, backupAddr, palmAddr); + } + } + + if ( !backupAddr || isFirstSync() ) + { + DEBUGKPILOT<< fname << ": Special case: no backup." << endl; + /* + Resolution matrix (0..does not exist, E..exists, D..deleted flag set, A..archived): + HH PC | Resolution + ------------------------------------------------------------ + 0 A | - + 0 E | PC -> HH, reset ID if not set correctly + D 0 | delete (error, should never occur!!!) + D E | CR (ERROR) + E/A 0 | HH -> PC + E/A E/A| merge/CR + */ + if (!palmAddr && KABCSync::isArchived(pcAddr) ) + { + return true; + } + else if (!palmAddr && !pcAddr.isEmpty()) + { + DEBUGKPILOT << fname << ": case: 1a"<<endl; + // PC->HH + bool res=_copyToHH(pcAddr, 0L, 0L); + return res; + } + else if (!palmAddr && pcAddr.isEmpty()) + { + DEBUGKPILOT << fname << ": case: 1b"<<endl; + // everything's empty -> ERROR + return false; + } + else if ( (isDeleted(palmAddr) || isArchived(palmAddr)) && pcAddr.isEmpty()) + { + DEBUGKPILOT << fname << ": case: 1c"<<endl; + if (isArchived(palmAddr)) + return _copyToPC(pcAddr, 0L, palmAddr); + else + // this happens if you add a record on the handheld and delete it again before you do the next sync + return _deleteAddressee(pcAddr, 0L, palmAddr); + } + else if ((isDeleted(palmAddr)||isArchived(palmAddr)) && !pcAddr.isEmpty()) + { + DEBUGKPILOT << fname << ": case: 1d"<<endl; + // CR (ERROR) + return _smartMergeAddressee(pcAddr, 0L, palmAddr); + } + else if (pcAddr.isEmpty()) + { + DEBUGKPILOT << fname << ": case: 1e"<<endl; + // HH->PC + return _copyToPC(pcAddr, 0L, palmAddr); + } + else + { + DEBUGKPILOT << fname << ": case: 1f"<<endl; + // Conflict Resolution + return _smartMergeAddressee(pcAddr, 0L, palmAddr); + } + } // !backupAddr + else + { + DEBUGKPILOT << fname << ": case: 2"<<endl; + /* + Resolution matrix: + 1) if HH.(empty| (deleted &! archived) ) -> { if (PC==B) -> delete, else -> CR } + if HH.archied -> {if (PC==B) -> copyToPC, else -> CR } + if PC.empty -> { if (HH==B) -> delete, else -> CR } + if PC.archived -> {if (HH==B) -> delete on HH, else CR } + 2) if PC==HH -> { update B, update ID of PC if needed } + 3) if PC==B -> { HH!=PC, thus HH modified, so copy HH->PC } + if HH==B -> { PC!=HH, thus PC modified, so copy PC->HH } + 4) else: all three addressees are different -> CR + */ + + if (!palmAddr || isDeleted(palmAddr) ) + { + DEBUGKPILOT << fname << ": case: 2a"<<endl; + if (_equal(backupAddr, pcAddr) || pcAddr.isEmpty()) + { + return _deleteAddressee(pcAddr, backupAddr, 0L); + } + else + { + return _smartMergeAddressee(pcAddr, backupAddr, 0L); + } + } + else if (pcAddr.isEmpty()) + { + DEBUGKPILOT << fname << ": case: 2b"<<endl; + if (*palmAddr == *backupAddr) + { + return _deleteAddressee(pcAddr, backupAddr, palmAddr); + } + else + { + return _smartMergeAddressee(pcAddr, backupAddr, palmAddr); + } + } + else if (_equal(palmAddr, pcAddr)) + { + DEBUGKPILOT << fname << ": case: 2c"<<endl; + // update Backup, update ID of PC if neededd + return _writeBackup(palmAddr); + } + else if (_equal(backupAddr, pcAddr)) + { + DEBUGKPILOT << fname << ": case: 2d"<<endl; + DEBUGKPILOT << fname << ": Flags: "<<palmAddr->attributes()<<", isDeleted="<< + isDeleted(palmAddr)<<", isArchived="<<isArchived(palmAddr)<<endl; + if (isDeleted(palmAddr)) + return _deleteAddressee(pcAddr, backupAddr, palmAddr); + else + return _copyToPC(pcAddr, backupAddr, palmAddr); + } + else if (*palmAddr == *backupAddr) + { + DEBUGKPILOT << fname << ": case: 2e"<<endl; + return _copyToHH(pcAddr, backupAddr, palmAddr); + } + else + { + DEBUGKPILOT << fname << ": case: 2f"<<endl; + // CR, since all are different + return _smartMergeAddressee(pcAddr, backupAddr, palmAddr); + } + } // backupAddr + return false; +} + + + +bool AbbrowserConduit::_copyToHH(Addressee &pcAddr, PilotAddress*backupAddr, + PilotAddress*palmAddr) +{ + FUNCTIONSETUP; + + if (pcAddr.isEmpty()) return false; + PilotAddress*paddr=palmAddr; + bool paddrcreated=false; + if (!paddr) + { + paddr=new PilotAddress(); + paddrcreated=true; + fCtrHH->created(); + } + else + { + fCtrHH->updated(); + } + KABCSync::copy(*paddr, pcAddr, *fAddressAppInfo, fSyncSettings); + + DEBUGKPILOT << fname << "palmAddr->id=" << paddr->id() + << ", pcAddr.ID=" << pcAddr.custom(KABCSync::appString, KABCSync::idString) << endl; + + if(_savePalmAddr(paddr, pcAddr)) + { + _savePCAddr(pcAddr, backupAddr, paddr); + } + if (paddrcreated) KPILOT_DELETE(paddr); + return true; +} + + + +bool AbbrowserConduit::_copyToPC(Addressee &pcAddr, PilotAddress*backupAddr, + PilotAddress*palmAddr) +{ + FUNCTIONSETUP; + if (!palmAddr) + { + return false; + } + // keep track of CUD's... + if (pcAddr.isEmpty()) + { + fCtrPC->created(); + } + else + { + fCtrPC->updated(); + } + showPilotAddress(palmAddr); + + KABCSync::copy(pcAddr, *palmAddr, *fAddressAppInfo, fSyncSettings); + if (isArchived(palmAddr)) + { + KABCSync::makeArchived(pcAddr); + } + + _savePCAddr(pcAddr, backupAddr, palmAddr); + _writeBackup(palmAddr); + return true; +} + + + +bool AbbrowserConduit::_writeBackup(PilotAddress *backup) +{ + FUNCTIONSETUP; + if (!backup) return false; + + showPilotAddress(backup); + + PilotRecord *pilotRec = backup->pack(); + fLocalDatabase->writeRecord(pilotRec); + KPILOT_DELETE(pilotRec); + return true; +} + + + +bool AbbrowserConduit::_deleteAddressee(Addressee &pcAddr, PilotAddress*backupAddr, + PilotAddress*palmAddr) +{ + FUNCTIONSETUP; + + if (palmAddr) + { + if (!syncedIds.tqcontains(palmAddr->id())) { + DEBUGKPILOT << fname << ": adding id:["<< palmAddr->id() << "] to syncedIds." << endl; + syncedIds.append(palmAddr->id()); + } + fDatabase->deleteRecord(palmAddr->id()); + fCtrHH->deleted(); + fLocalDatabase->deleteRecord(palmAddr->id()); + } + else if (backupAddr) + { + if (!syncedIds.tqcontains(backupAddr->id())) { + DEBUGKPILOT << fname << ": adding id:["<< backupAddr->id() << "] to syncedIds." << endl; + syncedIds.append(backupAddr->id()); + } + fLocalDatabase->deleteRecord(backupAddr->id()); + } + if (!pcAddr.isEmpty()) + { + DEBUGKPILOT << fname << " removing " << pcAddr.formattedName() << endl; + abChanged = true; + aBook->removeAddressee(pcAddr); + fCtrPC->deleted(); + } + return true; +} + + + +/********************************************************************* + l o w - l e v e l f u n c t i o n s f o r + adding / removing palm/pc records + *********************************************************************/ + + + +bool AbbrowserConduit::_savePalmAddr(PilotAddress *palmAddr, Addressee &pcAddr) +{ + FUNCTIONSETUP; + + DEBUGKPILOT << fname << ": Saving to pilot " << palmAddr->id() + << " " << palmAddr->getField(entryFirstname) + << " " << palmAddr->getField(entryLastname)<< endl; + + PilotRecord *pilotRec = palmAddr->pack(); + DEBUGKPILOT << fname << ": record with id=" << pilotRec->id() + << " len=" << pilotRec->size() << endl; + recordid_t pilotId = fDatabase->writeRecord(pilotRec); + DEBUGKPILOT << fname << ": Wrote "<<pilotId<<": ID="<<pilotRec->id()<<endl; + fLocalDatabase->writeRecord(pilotRec); + KPILOT_DELETE(pilotRec); + + // pilotId == 0 if using local db, so don't overwrite the valid id + if(pilotId != 0) + { + palmAddr->setID(pilotId); + if (!syncedIds.tqcontains(pilotId)) { + DEBUGKPILOT << fname << ": adding id:["<< pilotId << "] to syncedIds." << endl; + syncedIds.append(pilotId); + } + } + + recordid_t abId = 0; + abId = pcAddr.custom(KABCSync::appString, KABCSync::idString).toUInt(); + if(abId != pilotId) + { + pcAddr.insertCustom(KABCSync::appString, KABCSync::idString, TQString::number(pilotId)); + return true; + } + + return false; +} + + + +bool AbbrowserConduit::_savePCAddr(Addressee &pcAddr, PilotAddress*, + PilotAddress*) +{ + FUNCTIONSETUP; + + DEBUGKPILOT<<"Before _savePCAddr, pcAddr.custom="<<pcAddr.custom(KABCSync::appString, KABCSync::idString)<<endl; + TQString pilotId = pcAddr.custom(KABCSync::appString, KABCSync::idString); + long pilotIdL = pilotId.toLong(); + if(!pilotId.isEmpty()) + { + // because we maintain a mapping between pilotId -> kabc uid, whenever we add + // a new relationship, we have to remove any old mapping that would tie a different + // pilot id -> this kabc uid + TQMap < recordid_t, TQString>::iterator it; + for ( it = addresseeMap.begin(); it != addresseeMap.end(); ++it ) { + TQString kabcUid = it.data(); + if (kabcUid == pcAddr.uid()) { + addresseeMap.remove(it); + break; + } + } + + // now put the new mapping in + addresseeMap.insert(pilotIdL, pcAddr.uid()); + } + + aBook->insertAddressee(pcAddr); + + abChanged = true; + return true; +} + + + + +/********************************************************************* + C O P Y R E C O R D S + *********************************************************************/ + + + +bool AbbrowserConduit::_equal(const PilotAddress *piAddress, const Addressee &abEntry, + enum eqFlagsType flags) const +{ + FUNCTIONSETUP; + + // empty records are never equal! + if (!piAddress) { + DEBUGKPILOT << fname << ": no pilot address passed" << endl; + return false; + } + if (abEntry.isEmpty()) { + DEBUGKPILOT << fname << ":abEntry.isEmpty()" << endl; + return false; + } + // Archived records match anything so they won't be copied to the HH again + if (flags & eqFlagsFlags) + if (isArchived(piAddress) && KABCSync::isArchived(abEntry) ) return true; + + if (flags & eqFlagsName) + { + if(!_equal(abEntry.familyName(), piAddress->getField(entryLastname))) + { + DEBUGKPILOT << fname << ": last name not equal" << endl; + return false; + } + if(!_equal(abEntry.givenName(), piAddress->getField(entryFirstname))) + { + DEBUGKPILOT << fname << ": first name not equal" << endl; + return false; + } + if(!_equal(abEntry.prefix(), piAddress->getField(entryTitle))) + { + DEBUGKPILOT << fname << ": title/prefix not equal" << endl; + return false; + } + if(!_equal(abEntry.organization(), piAddress->getField(entryCompany))) + { + DEBUGKPILOT << fname << ": company/organization not equal" << endl; + return false; + } + } + if (flags & eqFlagsNote) + if(!_equal(abEntry.note(), piAddress->getField(entryNote))) + { + DEBUGKPILOT << fname << ": note not equal" << endl; + return false; + } + + if (flags & eqFlagsCategory) + { + // Check that the name of the category of the HH record + // is one matching the PC record. + TQString addressCategoryLabel = fAddressAppInfo->categoryName(piAddress->category()); + TQString cat = KABCSync::bestMatchedCategoryName(abEntry.categories(), + *fAddressAppInfo, piAddress->category()); + if(!_equal(cat, addressCategoryLabel)) + { + DEBUGKPILOT << fname << ": category not equal" << endl; + return false; + } + } + + if (flags & eqFlagsPhones) + { + // first, look for missing e-mail addresses on either side + TQStringList abEmails(abEntry.emails()); + TQStringList piEmails(piAddress->getEmails()); + + if (abEmails.count() != piEmails.count()) + { + DEBUGKPILOT << fname << ": email count not equal" << endl; + return false; + } + for (TQStringList::Iterator it = abEmails.begin(); it != abEmails.end(); it++) { + if (!piEmails.tqcontains(*it)) + { + DEBUGKPILOT << fname << ": pilot e-mail missing" << endl; + return false; + } + } + for (TQStringList::Iterator it = piEmails.begin(); it != piEmails.end(); it++) { + if (!abEmails.tqcontains(*it)) + { + DEBUGKPILOT << fname << ": kabc e-mail missing" << endl; + return false; + } + } + + // now look for differences in phone numbers. Note: we can't just compare one + // of each kind of phone number, because there's no guarantee that if the user + // has more than one of a given type, we're comparing the correct two. + + PhoneNumber::List abPhones(abEntry.phoneNumbers()); + PhoneNumber::List piPhones = KABCSync::getPhoneNumbers(*piAddress); + // first make sure that all of the pilot phone numbers are in kabc + for (PhoneNumber::List::Iterator it = piPhones.begin(); it != piPhones.end(); it++) { + PhoneNumber piPhone = *it; + bool found=false; + for (PhoneNumber::List::Iterator it = abPhones.begin(); it != abPhones.end(); it++) { + PhoneNumber abPhone = *it; + // see if we have the same number here... + // * Note * We used to check for preferred number matching, but + // this seems to have broke in kdepim 3.5 and I don't have time to + // figure out why, so we won't check to see if preferred number match + if ( _equal(piPhone.number(), abPhone.number()) ) { + found = true; + break; + } + } + if (!found) { + DEBUGKPILOT << fname << ": not equal because kabc phone not found." << endl; + return false; + } + } + // now the other way. *cringe* kabc has the capacity to store way more addresses + // than the Pilot, so this might give false positives more than we'd want.... + for (PhoneNumber::List::Iterator it = abPhones.begin(); it != abPhones.end(); it++) { + PhoneNumber abPhone = *it; + bool found=false; + for (PhoneNumber::List::Iterator it = piPhones.begin(); it != piPhones.end(); it++) { + PhoneNumber piPhone = *it; + if ( _equal(piPhone.number(), abPhone.number()) ) { + found = true; + break; + } + } + if (!found) + { + DEBUGKPILOT << fname << ": not equal because pilot phone not found." << endl; + return false; + } + } + + if(!_equal(KABCSync::getFieldForHHOtherPhone(abEntry,fSyncSettings), + piAddress->getPhoneField(PilotAddressInfo::eOther))) + { + DEBUGKPILOT << fname << ": not equal because of other phone field." << endl; + return false; + } + } + + if (flags & eqFlagsAdress) + { + KABC::Address address = KABCSync::getAddress(abEntry,fSyncSettings); + if(!_equal(address.street(), piAddress->getField(entryAddress))) + { + DEBUGKPILOT << fname << ": address not equal" << endl; + return false; + } + if(!_equal(address.locality(), piAddress->getField(entryCity))) + { + DEBUGKPILOT << fname << ": city not equal" << endl; + return false; + } + if(!_equal(address.region(), piAddress->getField(entryState))) + { + DEBUGKPILOT << fname << ": state not equal" << endl; + return false; + } + if(!_equal(address.postalCode(), piAddress->getField(entryZip))) + { + DEBUGKPILOT << fname << ": zip not equal" << endl; + return false; + } + if(!_equal(address.country(), piAddress->getField(entryCountry))) + { + DEBUGKPILOT << fname << ": country not equal" << endl; + return false; + } + } + + if (flags & eqFlagsCustom) + { + unsigned int customIndex = 0; + unsigned int hhField = entryCustom1; + + for ( ; customIndex<4; ++customIndex,++hhField ) + { + if (!_equal(KABCSync::getFieldForHHCustom(customIndex, abEntry, fSyncSettings), + piAddress->getField(hhField))) + { + DEBUGKPILOT << fname << ": Custom field " << customIndex + << " (HH field " << hhField << ") differs." << endl; + return false; + } + } + } + + // if any side is marked archived, but the other is not, the two + // are not equal. + if ( (flags & eqFlagsFlags) && (isArchived(piAddress) || KABCSync::isArchived(abEntry) ) ) + { + DEBUGKPILOT << fname << ": archived flags don't match" << endl; + return false; + } + + return true; +} + + + + + + + + + + +/********************************************************************* + 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 + *********************************************************************/ + + + +/** smartly merge the given field for the given entry. use the backup record to determine which record has been modified + @pc, @backup, @palm ... entries of the according databases + @returns string of the merged entries. +*/ +TQString AbbrowserConduit::_smartMergeString(const TQString &pc, const TQString & backup, + const TQString & palm, ConflictResolution confRes) +{ + FUNCTIONSETUP; + + // if both entries are already the same, no need to do anything + if(pc == palm) return pc; + + // If this is a first sync, we don't have a backup record, so + if(isFirstSync() || backup.isEmpty()) { + if (pc.isEmpty() && palm.isEmpty() ) return TQString::null; + if(pc.isEmpty()) return palm; + if(palm.isEmpty()) return pc; + } else { + // only one side modified, so return that string, no conflict + if(palm == backup) return pc; + if(pc == backup) return palm; + } + + DEBUGKPILOT<<"pc="<<pc<<", backup="<<backup<<", palm="<< + palm<<", ConfRes="<<confRes<<endl; + DEBUGKPILOT<<"Use conflict resolution :"<<confRes<< + ", PC="<<SyncAction::ePCOverrides<<endl; + switch(confRes) { + case SyncAction::ePCOverrides: return pc; break; + case SyncAction::eHHOverrides: return palm; break; + case SyncAction::ePreviousSyncOverrides: return backup; break; + default: break; + } + return TQString::null; +} + + + +bool AbbrowserConduit::_buildResolutionTable(ResolutionTable*tab, const Addressee &pcAddr, + PilotAddress *backupAddr, PilotAddress *palmAddr) +{ + FUNCTIONSETUP; + if (!tab) return false; + tab->setAutoDelete( TRUE ); + tab->labels[0]=i18n("Item on PC"); + tab->labels[1]=i18n("Handheld"); + tab->labels[2]=i18n("Last sync"); + if (!pcAddr.isEmpty()) + tab->fExistItems=(eExistItems)(tab->fExistItems|eExistsPC); + if (backupAddr) + tab->fExistItems=(eExistItems)(tab->fExistItems|eExistsBackup); + if (palmAddr) + tab->fExistItems=(eExistItems)(tab->fExistItems|eExistsPalm); + +#define appendGen(desc, abfield, palmfield) \ + tab->append(new ResolutionItem(desc, tab->fExistItems, \ + (!pcAddr.isEmpty())?(abfield):(TQString::null), \ + (palmAddr)?(palmAddr->palmfield):(TQString::null), \ + (backupAddr)?(backupAddr->palmfield):(TQString::null) )) +#define appendAddr(desc, abfield, palmfield) \ + appendGen(desc, abfield, getField(palmfield)) +#define appendGenPhone(desc, abfield, palmfield) \ + appendGen(desc, abfield, getPhoneField(PilotAddressInfo::palmfield)) +#define appendPhone(desc, abfield, palmfield) \ + appendGenPhone(desc, pcAddr.phoneNumber(PhoneNumber::abfield).number(), palmfield) + + + appendAddr(i18n("Last name"), pcAddr.familyName(), entryLastname); + appendAddr(i18n("First name"), pcAddr.givenName(), entryFirstname); + appendAddr(i18n("Organization"), pcAddr.organization(), entryCompany); + appendAddr(i18n("Title"), pcAddr.prefix(), entryTitle); + appendAddr(i18n("Note"), pcAddr.note(), entryNote); + + appendAddr(i18n("Custom 1"), KABCSync::getFieldForHHCustom(0, pcAddr, fSyncSettings), entryCustom1); + appendAddr(i18n("Custom 2"), KABCSync::getFieldForHHCustom(1, pcAddr, fSyncSettings), entryCustom2); + appendAddr(i18n("Custom 3"), KABCSync::getFieldForHHCustom(2, pcAddr, fSyncSettings), entryCustom3); + appendAddr(i18n("Custom 4"), KABCSync::getFieldForHHCustom(3, pcAddr, fSyncSettings), entryCustom4); + + appendPhone(i18n("Work Phone"), Work, eWork); + appendPhone(i18n("Home Phone"), Home, eHome); + appendPhone(i18n("Mobile Phone"), Cell, eMobile); + appendGenPhone(i18n("Fax"), pcAddr.phoneNumber(faxTypeOnPC()).number(), eFax); + appendPhone(i18n("Pager"), Pager, ePager); + appendGenPhone(i18n("Other"), KABCSync::getFieldForHHOtherPhone(pcAddr,fSyncSettings), eOther); + appendGenPhone(i18n("Email"), pcAddr.preferredEmail(), eEmail); + + KABC::Address abAddress = KABCSync::getAddress(pcAddr,fSyncSettings); + appendAddr(i18n("Address"), abAddress.street(), entryAddress); + appendAddr(i18n("City"), abAddress.locality(), entryCity); + appendAddr(i18n("Region"), abAddress.region(), entryState); + appendAddr(i18n("Postal code"), abAddress.postalCode(), entryZip); + appendAddr(i18n("Country"), abAddress.country(), entryCountry); + + TQString palmAddrCategoryLabel; + if (palmAddr) + { + palmAddrCategoryLabel = fAddressAppInfo->categoryName(palmAddr->category()); + } + TQString backupAddrCategoryLabel; + if (backupAddr) + { + backupAddrCategoryLabel = fAddressAppInfo->categoryName(backupAddr->category()); + } + int category = palmAddr ? palmAddr->category() : 0; + tab->append(new ResolutionItem( + i18n("Category"), + tab->fExistItems, + !pcAddr.isEmpty() ? + KABCSync::bestMatchedCategoryName(pcAddr.categories(), *fAddressAppInfo, category) : + TQString::null, + palmAddrCategoryLabel, + backupAddrCategoryLabel)); +#undef appendGen +#undef appendAddr +#undef appendGenPhone +#undef appendPhone + + return true; +} + + +/// This function just sets the phone number of type "type" to "phone" +static inline void setPhoneNumber(Addressee &abEntry, int type, const TQString &nr) +{ + PhoneNumber phone = abEntry.phoneNumber(type); + phone.setNumber(nr); + abEntry.insertPhoneNumber(phone); +} + + +bool AbbrowserConduit::_applyResolutionTable(ResolutionTable*tab, Addressee &pcAddr, + PilotAddress *backupAddr, PilotAddress *palmAddr) +{ + FUNCTIONSETUP; + if (!tab) return false; + if (!palmAddr) { + WARNINGKPILOT << "Empty palmAddr after conflict resolution." << endl; + return false; + } + + ResolutionItem*item=tab->first(); +#define SETGENFIELD(abfield, palmfield) \ + if (item) {\ + abfield; \ + palmAddr->setField(palmfield, item->fResolved); \ + }\ + item=tab->next(); +#define SETFIELD(abfield, palmfield) \ + SETGENFIELD(pcAddr.set##abfield(item->fResolved), palmfield) +#define SETCUSTOMFIELD(abfield, palmfield) \ + SETGENFIELD(KABCSync::setFieldFromHHCustom(abfield, pcAddr, item->fResolved, fSyncSettings), palmfield) +#define SETGENPHONE(abfield, palmfield) \ + if (item) { \ + abfield; \ + palmAddr->setPhoneField(PilotAddressInfo::palmfield, item->fResolved, PilotAddress::Replace); \ + }\ + item=tab->next(); +#define SETPHONEFIELD(abfield, palmfield) \ + SETGENPHONE(setPhoneNumber(pcAddr, PhoneNumber::abfield, item->fResolved), palmfield) +#define SETADDRESSFIELD(abfield, palmfield) \ + SETGENFIELD(abAddress.abfield(item->fResolved), palmfield) + + SETFIELD(FamilyName, entryLastname); + SETFIELD(GivenName, entryFirstname); + SETFIELD(Organization, entryCompany); + SETFIELD(Prefix, entryTitle); + SETFIELD(Note, entryNote); + + SETCUSTOMFIELD(0, entryCustom1); + SETCUSTOMFIELD(1, entryCustom2); + SETCUSTOMFIELD(2, entryCustom3); + SETCUSTOMFIELD(3, entryCustom4); + + SETPHONEFIELD(Work, eWork); + SETPHONEFIELD(Home, eHome); + SETPHONEFIELD(Cell, eMobile); + SETGENPHONE(setPhoneNumber(pcAddr, faxTypeOnPC(), item->fResolved), eFax); + SETPHONEFIELD(Pager, ePager); + SETGENPHONE(KABCSync::setFieldFromHHOtherPhone(pcAddr, item->fResolved, fSyncSettings), eOther); + + // TODO: fix email + if (item) + { + palmAddr->setPhoneField(PilotAddressInfo::eEmail, item->fResolved, PilotAddress::Replace); + if (backupAddr) + { + pcAddr.removeEmail(backupAddr->getPhoneField(PilotAddressInfo::eEmail)); + } + pcAddr.removeEmail(palmAddr->getPhoneField(PilotAddressInfo::eEmail)); + pcAddr.insertEmail(item->fResolved, true); + } + item=tab->next(); + + KABC::Address abAddress = KABCSync::getAddress(pcAddr, fSyncSettings); + SETADDRESSFIELD(setStreet, entryAddress); + SETADDRESSFIELD(setLocality, entryCity); + SETADDRESSFIELD(setRegion, entryState); + SETADDRESSFIELD(setPostalCode, entryZip); + SETADDRESSFIELD(setCountry, entryCountry); + pcAddr.insertAddress(abAddress); + + // TODO: Is this correct? + if (item) + { + palmAddr->setCategory( fAddressAppInfo->findCategory(item->fResolved) ); + KABCSync::setCategory(pcAddr, item->fResolved); + } + + +#undef SETGENFIELD +#undef SETFIELD +#undef SETCUSTOMFIELD +#undef SETGENPHONE +#undef SETPHONEFIELD +#undef SETADDRESSFIELD + + return true; +} + + + +bool AbbrowserConduit::_smartMergeTable(ResolutionTable*tab) +{ + FUNCTIONSETUP; + if (!tab) return false; + bool noconflict=true; + ResolutionItem*item; + for ( item = tab->first(); item; item = tab->next() ) + { + // try to merge the three strings + item->fResolved=_smartMergeString(item->fEntries[0], + item->fEntries[2], item->fEntries[1], getConflictResolution()); + // if a conflict occurred, set the default to something sensitive: + if (item->fResolved.isNull() && !(item->fEntries[0].isEmpty() && + item->fEntries[1].isEmpty() && item->fEntries[2].isEmpty() ) ) + { + item->fResolved=item->fEntries[0]; + noconflict=false; + } + if (item->fResolved.isNull()) item->fResolved=item->fEntries[1]; + if (item->fResolved.isNull()) item->fResolved=item->fEntries[2]; + } + return noconflict; +} + + + +/** Merge the palm and the pc entries with the additional information of + * the backup. + * return value: no meaning yet + */ +bool AbbrowserConduit::_smartMergeAddressee(Addressee &pcAddr, + PilotAddress *backupAddr, PilotAddress *palmAddr) +{ + FUNCTIONSETUP; + + // Merge them, then look which records have to be written to device or abook + int res = SyncAction::eAskUser; + bool result=true; + ResolutionTable tab; + + result &= _buildResolutionTable(&tab, pcAddr, backupAddr, palmAddr); + // Now attempt a smart merge. If that fails, let conflict resolution do the job + bool mergeOk=_smartMergeTable(&tab); + + if (!mergeOk) + { + TQString dlgText; + if (!palmAddr) + { + dlgText=i18n("The following address entry was changed, but does no longer exist on the handheld. Please resolve this conflict:"); + } + else if (pcAddr.isEmpty()) + { + dlgText=i18n("The following address entry was changed, but does no longer exist on the PC. Please resolve this conflict:"); + } + else + { + dlgText=i18n("The following address entry was changed on the handheld as well as on the PC side. The changes could not be merged automatically, so please resolve the conflict yourself:"); + } + ResolutionDlg*resdlg=new ResolutionDlg(0L, fHandle, i18n("Address conflict"), dlgText, &tab); + resdlg->exec(); + KPILOT_DELETE(resdlg); + } + res=tab.fResolution; + + // Disallow some resolution under certain conditions, fix wrong values: + switch (res) { + case SyncAction::eHHOverrides: + if (!palmAddr) res=SyncAction::eDelete; + break; + case SyncAction::ePCOverrides: + if (pcAddr.isEmpty()) res=SyncAction::eDelete; + break; + case SyncAction::ePreviousSyncOverrides: + if (!backupAddr) res=SyncAction::eDoNothing; + break; + } + + PilotAddress*pAddr=palmAddr; + bool pAddrCreated=false; + // Now that we have done a possible conflict resolution, apply the changes + switch (res) { + case SyncAction::eDuplicate: + // Set the Palm ID to 0 so we don't overwrite the existing record. + pcAddr.removeCustom(KABCSync::appString, KABCSync::idString); + result &= _copyToHH(pcAddr, 0L, 0L); + { + Addressee pcadr; + result &= _copyToPC(pcadr, backupAddr, palmAddr); + } + break; + case SyncAction::eDoNothing: + break; + case SyncAction::eHHOverrides: + result &= _copyToPC(pcAddr, backupAddr, palmAddr); + break; + case SyncAction::ePCOverrides: + result &= _copyToHH(pcAddr, backupAddr, pAddr); + break; + case SyncAction::ePreviousSyncOverrides: + KABCSync::copy(pcAddr, *backupAddr, *fAddressAppInfo, fSyncSettings); + if (palmAddr && backupAddr) *palmAddr=*backupAddr; + result &= _savePalmAddr(backupAddr, pcAddr); + result &= _savePCAddr(pcAddr, backupAddr, backupAddr); + break; + case SyncAction::eDelete: + result &= _deleteAddressee(pcAddr, backupAddr, palmAddr); + break; + case SyncAction::eAskUser: + default: + if (!pAddr) + { + pAddr=new PilotAddress(); + pAddrCreated=true; + } + result &= _applyResolutionTable(&tab, pcAddr, backupAddr, pAddr); +showAddresses(pcAddr, backupAddr, pAddr); + // savePalmAddr sets the RecordID custom field already + result &= _savePalmAddr(pAddr, pcAddr); + result &= _savePCAddr(pcAddr, backupAddr, pAddr); + if (pAddrCreated) KPILOT_DELETE(pAddr); + break; + } + + return result; +} + + + +// TODO: right now entries are equal if both first/last name and organization are +// equal. This rules out two entries for the same person(e.g. real home and weekend home) +// or two persons with the same name where you don't know the organization.!!! +Addressee AbbrowserConduit::_findMatch(const PilotAddress & pilotAddress) const +{ + FUNCTIONSETUP; + // TODO: also search with the pilotID + // first, use the pilotID to UID map to find the appropriate record + if( !isFirstSync() && (pilotAddress.id() > 0) ) + { + TQString id(addresseeMap[pilotAddress.id()]); + DEBUGKPILOT << fname << ": PilotRecord has id " << pilotAddress.id() << ", mapped to " << id << endl; + if(!id.isEmpty()) + { + Addressee res(aBook->tqfindByUid(id)); + if(!res.isEmpty()) return res; + DEBUGKPILOT << fname << ": PilotRecord has id " << pilotAddress.id() << ", but could not be found in the addressbook" << endl; + } + } + + for(AddressBook::Iterator iter = aBook->begin(); iter != aBook->end(); ++iter) + { + Addressee abEntry = *iter; + TQString recID(abEntry.custom(KABCSync::appString, KABCSync::idString)); + bool ok; + if (!recID.isEmpty() ) + { + recordid_t rid = recID.toLong(&ok); + if (ok && rid) + { + if (rid==pilotAddress.id()) return abEntry;// yes, we found it + // skip this addressee, as it can an other corresponding address on the handheld + if (allIds.tqcontains(rid)) continue; + } + } + + if (_equal(&pilotAddress, abEntry, eqFlagsAlmostAll)) + { + return abEntry; + } + } + DEBUGKPILOT << fname << ": Could not find any addressbook enty matching " << pilotAddress.getField(entryLastname) << endl; + return Addressee(); +} + +void AbbrowserConduit::slotTestRecord() +{ + FUNCTIONSETUP; + + // Get a record and interpret it as an address. + PilotRecord *r = fDatabase->readRecordByIndex( pilotindex ); + if (!r) + { + delayDone(); + return; + } + PilotAddress a(r); + KPILOT_DELETE(r); + + // Process this record. + showPilotAddress(&a); + + // Schedule more work. + ++pilotindex; + TQTimer::singleShot(0, this, TQT_SLOT(slotTestRecord())); +} diff --git a/conduits/abbrowserconduit/abbrowser-conduit.h b/conduits/abbrowserconduit/abbrowser-conduit.h new file mode 100644 index 0000000..22e9446 --- /dev/null +++ b/conduits/abbrowserconduit/abbrowser-conduit.h @@ -0,0 +1,222 @@ +#ifndef _ABBROWSER_CONDUIT_H +#define _ABBROWSER_CONDUIT_H +/* abbrowser-conduit.h KPilot +** +** Copyright (C) 2000,2001 by Dan Pilone +** Copyright (C) 2000 Gregory Stern +** Copyright (C) 2002-2003 by Reinhold Kainhofer +** +*/ + +/* +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program in a file called COPYING; if not, write to +** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +** MA 02110-1301, USA. +*/ + +/* +** Bug reports and questions can be sent to [email protected] +*/ + + +#include <kabc/addressbook.h> + +#include <pilotAddress.h> +#include <plugin.h> + +#include "kabcRecord.h" + + +class ResolutionTable; +namespace KABC +{ +class Addressee; +class Address; +class PhoneNumber; +class Ticket; +} + +using namespace KABC; + +typedef TQValueList<recordid_t> RecordIDList; + +class AbbrowserConduit : public ConduitAction +{ +Q_OBJECT +public: + AbbrowserConduit(KPilotLink *o,const char *n = 0L, + const TQStringList &a = TQStringList() ); + virtual ~AbbrowserConduit(); + +/********************************************************************* + S Y N C S T R U C T U R E + *********************************************************************/ + virtual bool exec(); +protected slots: + void slotPalmRecToPC(); + void slotPCRecToPalm(); + void slotDeletedRecord(); + void slotDeleteUnsyncedPCRecords(); + void slotDeleteUnsyncedHHRecords(); + void slotCleanup(); + + void slotTestRecord(); + +private: + + /********************************************************/ + /* Handle the configuration */ + /********************************************************/ + + /* Read the global KPilot config file for settings + * particular to the AbbrowserConduit conduit. */ + void readConfig(); + + void showPilotAddress(const PilotAddress *pilotAddress); + void showAddresses( + const Addressee &pcAddr, + const PilotAddress *backupAddr, + const PilotAddress *palmAddr); + + + /********************************************************/ + /* Loading and saving the addressbook and database */ + /********************************************************/ + + + /* Given a list of contacts, creates the pilot id to contact key map + * and a list of new contacts in O(n) time (single pass) */ + void _mapContactsToPilot( TQMap < recordid_t, TQString> &idContactMap); + /* Do the preperations before doSync or doBackup. + * Load contacts, set the pilot */ + bool _prepare(); + /* Load the contacts from the addressbook. + * @return true if successful, false if not */ + bool _loadAddressBook(); + /* Save the contacts back to the addressbook. + * @return true if successful, false if not */ + bool _saveAddressBook(); + void _getAppInfo(); + void _setAppInfo(); + + void _cleanupAddressBookPointer(); + + + +/********************************************************************* + G E N E R A L S Y N C F U N C T I O N + These functions modify the Handheld and the addressbook + *********************************************************************/ + bool syncAddressee(Addressee &pcAddr, PilotAddress*backupAddr, + PilotAddress*palmAddr); + bool _copyToHH(Addressee &pcAddr, PilotAddress*backupAddr, + PilotAddress*palmAddr); + bool _copyToPC(Addressee &pcAddr, PilotAddress*backupAddr, + PilotAddress*palmAddr); + bool _writeBackup(PilotAddress *backup); + bool _deleteAddressee(Addressee &pcAddr, PilotAddress*backupAddr, + PilotAddress*palmAddr); + + +/********************************************************************* + l o w - l e v e l f u n c t i o n s f o r + adding / removing palm/pc records + *********************************************************************/ + bool _savePalmAddr(PilotAddress *palmAddr, Addressee &pcAddr); + bool _savePCAddr(Addressee &pcAddr, PilotAddress*backupAddr, + PilotAddress*palmAddr); + + +/********************************************************************* + C O P Y R E C O R D S + *********************************************************************/ + inline bool _equal(const TQString & str1, const TQString & str2) const + { + return (str1.isEmpty() && str2.isEmpty()) || (str1 == str2); + } ; + typedef enum eqFlagsType + { + eqFlagsName=0x1, + eqFlagsAdress=0x2, + eqFlagsPhones=0x4, + eqFlagsNote=0x8, + eqFlagsCategory=0x10, + eqFlagsFlags=0x20, + eqFlagsCustom=0x40, + eqFlagsAll=0xFFFF, + eqFlagsAlmostAll=eqFlagsName|eqFlagsAdress|eqFlagsPhones|eqFlagsNote|eqFlagsCustom + }; + bool _equal(const PilotAddress *piAddress, const Addressee &abEntry, + enum eqFlagsType flags=eqFlagsAll) const; + +/********************************************************************* + 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 + *********************************************************************/ + /** smartly merge the given field for the given entry. use the + * backup record to determine which record has been modified + * @pc, @backup, @palm ... entries of the according databases + * @returns string of the merged entries. + */ + TQString _smartMergeString(const TQString &pc, const TQString & backup, + const TQString & palm, ConflictResolution confRes); + bool _buildResolutionTable(ResolutionTable*tab, const Addressee &pcAddr, + PilotAddress *backupAddr, PilotAddress *palmAddr); + bool _applyResolutionTable(ResolutionTable*tab, Addressee &pcAddr, + PilotAddress *backupAddr, PilotAddress *palmAddr); + bool _smartMergeTable(ResolutionTable*tab); + /** Merge the palm and the pc entries with the additional + * information of the backup record. Calls _smartMerge + * which does the actual syncing of the data structures. + * According to the return value of _smartMerge, this function + * writes the data back to the palm/pc. + * return value: no meaning yet + */ + bool _smartMergeAddressee(Addressee &pcAddr, PilotAddress *backupAddr, + PilotAddress *palmAddr); + Addressee _findMatch(const PilotAddress & pilotAddress) const; + + +/********************************************************/ +/* D A T A M E M B E R S , S E T T I N G S */ +/********************************************************/ + + AddressBook* aBook; + + PilotAddressInfo *fAddressAppInfo; + + KABCSync::Settings fSyncSettings; + + int pilotindex; + bool abChanged; + /** addresseeMap maps record ids to IDs of Addressees. This is used to speed up searching the local addressbook */ + TQMap < recordid_t, TQString> addresseeMap; + RecordIDList syncedIds, allIds; + TQString fABookFile; + AddressBook::Iterator abiter; + /** For a local file resource, we need to obtain a saveTicket + * when opening the abook, just in case we want to modify it + * at all. + */ + Ticket *fTicket; + bool fCreatedBook; + + /** if we add a resource from the addressbook, track it to remove it + * later... + */ + KABC::Resource *fBookResource; + + +} ; + +#endif diff --git a/conduits/abbrowserconduit/abbrowser-factory.cc b/conduits/abbrowserconduit/abbrowser-factory.cc new file mode 100644 index 0000000..9a84508 --- /dev/null +++ b/conduits/abbrowserconduit/abbrowser-factory.cc @@ -0,0 +1,45 @@ +/* KPilot +** +** Copyright (C) 2001 by Dan Pilone +** Copyright (C) 2002-2003 Reinhold Kainhofer +** +** This file defines the factory for the abbrowser-conduit plugin. +*/ + +/* +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program in a file called COPYING; if not, write to +** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +** MA 02110-1301, USA. +*/ + +/* +** Bug reports and questions can be sent to [email protected] +*/ + +#include "options.h" + +#include "pluginfactory.h" + +#include "abbrowser-conduit.h" +#include "abbrowser-setup.h" + +extern "C" +{ + +void *init_conduit_address() +{ + return new ConduitFactory<AbbrowserWidgetSetup,AbbrowserConduit>(0,"abbrowserconduit"); +} + +} diff --git a/conduits/abbrowserconduit/abbrowser-factory.h b/conduits/abbrowserconduit/abbrowser-factory.h new file mode 100644 index 0000000..bbf573c --- /dev/null +++ b/conduits/abbrowserconduit/abbrowser-factory.h @@ -0,0 +1,40 @@ +#ifndef _ABBROWSER_FACTORY_H +#define _ABBROWSER_FACTORY_H +/* abbrowser-factory.h KPilot +** +** Copyright (C) 2001 by Dan Pilone +** Copyright (C) 2002-2003 Reinhold Kainhofer +** +** This file defines the factory for the abbrowser-conduit plugin. +*/ + +/* +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program in a file called COPYING; if not, write to +** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +** MA 02110-1301, USA. +*/ + +/* +** Bug reports and questions can be sent to [email protected] +*/ + +extern "C" +{ + +void *init_conduit_address(); + +} + +#endif + diff --git a/conduits/abbrowserconduit/abbrowser-setup.cc b/conduits/abbrowserconduit/abbrowser-setup.cc new file mode 100644 index 0000000..f4d80d2 --- /dev/null +++ b/conduits/abbrowserconduit/abbrowser-setup.cc @@ -0,0 +1,195 @@ +/* KPilot +** +** Copyright (C) 2001 by Dan Pilone +** Copyright (C) 2002-2003 Reinhold Kainhofer +** +** This file defines the setup dialog for the abbrowser-conduit plugin. +*/ + +/* +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program in a file called COPYING; if not, write to +** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +** MA 02110-1301, USA. +*/ + +/* +** Bug reports and questions can be sent to [email protected] +*/ + +#include "options.h" + +#include <tqcheckbox.h> +#include <tqcombobox.h> +#include <tqbuttongroup.h> + +#include <kurlrequester.h> +#include <kaboutdata.h> + +#include "kaddressbookConduit.h" +#include "abbrowser-setup.h" +#include "abbrowserSettings.h" + + + +static KAboutData *createAbout() +{ + KAboutData *fAbout = new KAboutData("abbrowserconduit", + I18N_NOOP("Abbrowser Conduit for KPilot"), + KPILOT_VERSION, + I18N_NOOP("Configures the Abbrowser Conduit for KPilot"), + KAboutData::License_GPL, + "(C) 2001, Dan Pilone\n(C) 2002-2003, Reinhold Kainhofer"); + fAbout->addAuthor("Greg Stern", + I18N_NOOP("Primary Author")); + fAbout->addAuthor("Adriaan de Groot", + I18N_NOOP("Maintainer"), + "[email protected]", + "http://www.cs.kun.nl/~adridg/kpilot"); + fAbout->addAuthor("Reinhold Kainhofer", I18N_NOOP("Maintainer"), + "[email protected]", "http://reinhold.kainhofer.com"); + fAbout->addCredit("David Bishop", I18N_NOOP("UI")); + return fAbout; +} + +AbbrowserWidgetSetup::AbbrowserWidgetSetup(TQWidget *w, const char *n) : + ConduitConfigBase(w,n), + fConfigWidget(new AbbrowserWidget(w)) +{ + FUNCTIONSETUP; + + fConduitName=i18n("Addressbook"); + fAbout = createAbout(); + ConduitConfigBase::addAboutPage(fConfigWidget->tabWidget,fAbout); + fWidget=fConfigWidget; + fConfigWidget->fAbookFile->setMode(KFile::File); +#define CM(a,b) connect(fConfigWidget->a,b,this,TQT_SLOT(modified())); + CM(fSyncDestination,TQT_SIGNAL(clicked(int))); + CM(fAbookFile,TQT_SIGNAL(textChanged(const TQString &))); + CM(fArchive,TQT_SIGNAL(toggled(bool))); + CM(fConflictResolution,TQT_SIGNAL(activated(int))); + CM(fOtherPhone,TQT_SIGNAL(activated(int))); + CM(fAddress,TQT_SIGNAL(activated(int))); + CM(fFax,TQT_SIGNAL(activated(int))); + CM(fCustom0,TQT_SIGNAL(activated(int))); + CM(fCustom1,TQT_SIGNAL(activated(int))); + CM(fCustom2,TQT_SIGNAL(activated(int))); + CM(fCustom3,TQT_SIGNAL(activated(int))); + CM(fCustomDate, TQT_SIGNAL(activated(int))); + CM(fCustomDate, TQT_SIGNAL(textChanged(const TQString&))); +#undef CM +} + +AbbrowserWidgetSetup::~AbbrowserWidgetSetup() +{ + FUNCTIONSETUP; +} + +/* virtual */ void AbbrowserWidgetSetup::commit() +{ + FUNCTIONSETUP; + + TQButtonGroup*grp=fConfigWidget->fSyncDestination; + AbbrowserSettings::setAddressbookType(grp->id(grp->selected())); + AbbrowserSettings::setFileName(fConfigWidget->fAbookFile->url()); + AbbrowserSettings::setArchiveDeleted(fConfigWidget->fArchive->isChecked()); + + // Conflicts page + AbbrowserSettings::setConflictResolution( + fConfigWidget->fConflictResolution->currentItem()+SyncAction::eCROffset); + + // Fields page + AbbrowserSettings::setPilotOther(fConfigWidget->fOtherPhone->currentItem()); + AbbrowserSettings::setPilotStreet(fConfigWidget->fAddress->currentItem()); + AbbrowserSettings::setPilotFax(fConfigWidget->fFax->currentItem()); + + // Custom fields page + AbbrowserSettings::setCustom0(fConfigWidget->fCustom0->currentItem()); + AbbrowserSettings::setCustom1(fConfigWidget->fCustom1->currentItem()); + AbbrowserSettings::setCustom2(fConfigWidget->fCustom2->currentItem()); + AbbrowserSettings::setCustom3(fConfigWidget->fCustom3->currentItem()); +#ifdef DEBUG + DEBUGKPILOT <<fname<< + "Custom0: "<<fConfigWidget->fCustom0->currentItem()<<" "<< + "Custom1: "<<fConfigWidget->fCustom1->currentItem()<<" "<< + "Custom2: "<<fConfigWidget->fCustom2->currentItem()<<" "<< + "Custom3: "<<fConfigWidget->fCustom3->currentItem()<<" " + << " eCustom[0]=" << AbbrowserSettings::custom0()<<" " + << " eCustom[1]=" << AbbrowserSettings::custom1()<<" " + << " eCustom[2]=" << AbbrowserSettings::custom2()<<" " + << " eCustom[3]=" << AbbrowserSettings::custom3()<<" "<< + endl; +#endif + int fmtindex=fConfigWidget->fCustomDate->currentItem(); + AbbrowserSettings::setCustomDateFormat( + (fmtindex==0)?(TQString::null):fConfigWidget->fCustomDate->currentText() ); + + AbbrowserSettings::self()->writeConfig(); + unmodified(); +} + +/* virtual */ void AbbrowserWidgetSetup::load() +{ + FUNCTIONSETUP; + AbbrowserSettings::self()->readConfig(); + +#ifdef DEBUG + DEBUGKPILOT << fname + << ": Settings " + << " fPilotStreetHome=" << AbbrowserSettings::pilotStreet() + << " fPilotFaxHome=" << AbbrowserSettings::pilotFax() + << " fArchive=" << AbbrowserSettings::archiveDeleted() + << " eCustom[0]=" << AbbrowserSettings::custom0() + << " eCustom[1]=" << AbbrowserSettings::custom1() + << " eCustom[2]=" << AbbrowserSettings::custom2() + << " eCustom[3]=" << AbbrowserSettings::custom3() + << endl; +#endif + + // General page + fConfigWidget->fSyncDestination->setButton(AbbrowserSettings::addressbookType()); + fConfigWidget->fAbookFile->setURL(AbbrowserSettings::fileName()); + fConfigWidget->fArchive->setChecked(AbbrowserSettings::archiveDeleted()); + + // Conflicts page + fConfigWidget->fConflictResolution->setCurrentItem( + AbbrowserSettings::conflictResolution() - SyncAction::eCROffset ); + + // Fields page + fConfigWidget->fOtherPhone->setCurrentItem(AbbrowserSettings::pilotOther()); + fConfigWidget->fAddress->setCurrentItem(AbbrowserSettings::pilotStreet()); + fConfigWidget->fFax->setCurrentItem(AbbrowserSettings::pilotFax()); + + // Custom fields page + fConfigWidget->fCustom0->setCurrentItem(AbbrowserSettings::custom0()); + fConfigWidget->fCustom1->setCurrentItem(AbbrowserSettings::custom1()); + fConfigWidget->fCustom2->setCurrentItem(AbbrowserSettings::custom2()); + fConfigWidget->fCustom3->setCurrentItem(AbbrowserSettings::custom3()); + TQString datefmt=AbbrowserSettings::customDateFormat(); + if (datefmt.isEmpty()) + { + fConfigWidget->fCustomDate->setCurrentItem(0); + } + else + { + fConfigWidget->fCustomDate->setCurrentText(datefmt); + } + + unmodified(); +} + +/* static */ ConduitConfigBase *AbbrowserWidgetSetup::create(TQWidget *w, const char *n) +{ + return new AbbrowserWidgetSetup(w,n); +} + diff --git a/conduits/abbrowserconduit/abbrowser-setup.h b/conduits/abbrowserconduit/abbrowser-setup.h new file mode 100644 index 0000000..47eff1c --- /dev/null +++ b/conduits/abbrowserconduit/abbrowser-setup.h @@ -0,0 +1,52 @@ +#ifndef _ABBROWSER_ABBROWSER_SETUP_H +#define _ABBROWSER_ABBROWSER_SETUP_H +/* knotes-setup.h KPilot +** +** Copyright (C) 2001 by Dan Pilone +** Copyright (C) 2002-2003 Reinhold Kainhofer +** +** This file defines the widget and behavior for the config dialog +** of the KNotes conduit. +*/ + +/* +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program in a file called COPYING; if not, write to +** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +** MA 02110-1301, USA. +*/ + +/* +** Bug reports and questions can be sent to [email protected] +*/ + +#include "plugin.h" + +class AbbrowserWidget; +class KAboutData; + +class AbbrowserWidgetSetup : public ConduitConfigBase +{ +public: + AbbrowserWidgetSetup(TQWidget *,const char *); + virtual ~AbbrowserWidgetSetup(); + virtual void load(); + virtual void commit(); + static ConduitConfigBase *create(TQWidget *,const char *); +private: + AbbrowserWidget *fConfigWidget; + KAboutData *fAbout; +} ; + +#endif + diff --git a/conduits/abbrowserconduit/abbrowserSettings.kcfgc b/conduits/abbrowserconduit/abbrowserSettings.kcfgc new file mode 100644 index 0000000..f6ea209 --- /dev/null +++ b/conduits/abbrowserconduit/abbrowserSettings.kcfgc @@ -0,0 +1,7 @@ +File=abbrowserconduit.kcfg +ClassName=AbbrowserSettings +Singleton=true +ItemAccessors=true +Mutators=true +GlobalEnums=true +SetUserTexts=true diff --git a/conduits/abbrowserconduit/abbrowser_conduit.desktop b/conduits/abbrowserconduit/abbrowser_conduit.desktop new file mode 100644 index 0000000..5c95db5 --- /dev/null +++ b/conduits/abbrowserconduit/abbrowser_conduit.desktop @@ -0,0 +1,113 @@ +[Desktop Entry] +Type=Service +Comment=This conduit syncs the handheld addressbook with KDE's addressbook. +Comment[af]=Hierdie pad sinkroniseer die draagbare toestel adresboek met KDE se adresboek. +Comment[bg]=Синхронизация на адресника на KDE с мобилни устройства +Comment[bs]=Ovaj conduit sinhronizuje adresar ručnog računara sa KDEovim adresarom. +Comment[ca]=Aquest conducte sincronitza la llibreta d'adreces de la vostra agenda electrònica amb la llibreta d'adreces de KDE. +Comment[cs]=Toto propojení synchronizuje vašeho Pilota Knihou adres +Comment[cy]=Mae'r cwndid yma yn cydamseru llyfr cyfeiriadau'r llawiadur efo llyfr cyfeiriadau KDE. +Comment[da]=Denne kanal synkroniserer din håndholdte med KDE's adressebog. +Comment[de]=Abgleich der Adressbücher von Taschencomputer und KDE. +Comment[el]=Αυτός ο σύνδεσμος συγχρονίζει το βιβλίο διευθύνσεων του υπολογιστή παλάμης με το βιβλίο διευθύνσεων του KDE. +Comment[eo]=Tiu kanalo sinkronigas vian poŝkomputil-adreslibron kun la KDE-aadreslibro. +Comment[es]=Este conducto sincroniza la libreta de direcciones de su agenda electrónica con la de KDE +Comment[et]=See kanal sünkroniseerib pihuarvuti ja KDE aadressiraamatu. +Comment[eu]=Kanal honek agenda-elektronikoaren helbide-liburua KDE-ren helbide-liburuarekin sinkronizatzen du. +Comment[fa]=این لوله، کتاب نشانی دستی را با کتاب نشانی KDE همگام میسازد. +Comment[fi]=Tämä yhdyskäytävä synkronoi taskutietokoneen KDE:n osoitekirjan kanssa +Comment[fr]=Ce canal synchronise le carnet d'adresses du périphérique avec celui de KDE. +Comment[fy]=Dit conduit syngronisearret jo handheld mei KDE's adresboek. +Comment[gl]=Este conducto sincroniza o caderno de enderezos do seu aparello portátil co caderno de enderezos de KDE. +Comment[hi]=यह कन्ड्यूइट हैंडहेल्ड पता-पुस्तिका को केडीई के पता-पुस्तिका से सिंक करती है. +Comment[hu]=Ezzel a csatolóval egy kéziszámítógép és a KDE címjegyzéke között lehet szinkronizálást végezni. +Comment[is]=Þessi rás samstillir póstfangaskrár KDE og lófatölvunnar +Comment[it]=Questo condotto sincronizza il tuo palmare con la rubrica indirizzi di KDE +Comment[ja]=このコンジットはハンドヘルドのアドレス帳をKDEのアドレス帳と同期させます。 +Comment[kk]=Қалта құрылғыдағы адрестік кітапшамен KDE-нің адрестік кітапшаларды қадамдастыру арнасы. +Comment[km]=បំពង់នេះអាចឲ្យសៀវភៅអាសយដ្ឋានរបស់ឧបករណ៍យួរដៃ ធ្វើសមកាលកម្មជាមួយនឹងសៀវភៅអាសយដ្ឋានរបស់ KDE +Comment[lt]=Šis kanalas sinchronizuoja nešiojamą adresų knygelę su KDE adresų knygele. +Comment[mk]=Овој канал ги синхронизира адресарите од рачниот уред и од KDE. +Comment[ms]=Saluran ini mensegerakkan buku alamat komputer telapak dengan buku alamat KDE. +Comment[nb]=Denne kanalen synkroniserer Pilotens adressebok med KDEs adressebok. +Comment[nds]=Synkroniseert de Adressböker vun Handreekners un KDE. +Comment[ne]=यो कन्ड्युटले केडीई को ठेगाना पुस्तकमा ह्यान्डल गरिएका ठेगाना पुस्तिका सिन्क गर्दछ । +Comment[nl]=Dit conduit synchroniseert uw handheld met KDE's adresboek. +Comment[nn]=Denne koplinga synkroniserer den handheldte adresseboka med med KDE-adresseboka. +Comment[pl]=Ten łącznik synchronizuje książkę adresową palmtopa z książką adresową KDE. +Comment[pt]=Esta conduta sincroniza o livro de endereços ou agenda do seu dispositivo com a agenda do KDE. +Comment[pt_BR]=Este conduíte sincroniza o livro de endereços do handheld com o livro de endereços do KDE. +Comment[ru]=Канал синхронизации адресных книг КПК и KDE. +Comment[sk]=Táto spojka synchronizuje adresár vášho prenosného zariadenia s adresárom KDE. +Comment[sl]=Ta veznik usklajuje adresar v ročnem računalniku z adresarjem v KDE. +Comment[sr]=Овај провод синхронизује адресар ручног рачунара са KDE-овим адресаром +Comment[sr@Latn]=Ovaj provod sinhronizuje adresar ručnog računara sa KDE-ovim adresarom +Comment[sv]=Den här kanalen synkroniserar handdatorns adressbok med KDE:s adressbok. +Comment[ta]=இந்த குழாய் கையில் உள்ள முகவரிப்புத்தகத்தை கேடிஇயின் முகவரிப்புத்தகத்தோடு ஒத்திசைக்கிறது +Comment[tg]=Канали синхронизатсияи китоби адресии Pilot ва KDE. +Comment[tr]=Bu bileşen el bilgisayarı adres defteri ile KDE'ninkini birleştirir. +Comment[uk]=Цей акведук синхронізує адресну книгу кишенькового пристрою з адресною книгою KDE. +Comment[zh_CN]=此管道会将您的手持设备与 KDE 的地址簿同步。 +Comment[zh_TW]=此軟體讓您把 KDE 通訊錄與手邊通訊錄同步。 +Name=Addressbook +Name[af]=Adresboek +Name[ar]=دفتر العناوين +Name[az]=Ünvan Dəftəri +Name[be]=Адрасная кніга +Name[bg]=Адресник +Name[br]=Karned chomlec'hioù +Name[bs]=Adresar +Name[ca]=Llibreta d'adreces +Name[cs]=Kniha adres +Name[cy]=Llyfr Cyfeiriadau +Name[da]=Adressebog +Name[de]=Adressbuch +Name[el]=Βιβλίο διευθύνσεων +Name[eo]=Adresaro +Name[es]=Libreta de direcciones +Name[et]=Aadressiraamat +Name[eu]=Helbide-liburua +Name[fa]=کتاب نشانی +Name[fi]=Osoitekirja +Name[fr]=Carnet d'adresses +Name[fy]=Adresboek +Name[ga]=Leabhar Seoltaí +Name[gl]=Libro de enderezos +Name[hi]=पता-पुस्तिका +Name[hr]=Adresar +Name[hu]=Címjegyzék +Name[id]=Buku alamat +Name[is]=Póstfangaskrá +Name[it]=Rubrica degli indirizzi +Name[ja]=アドレス帳 +Name[kk]=Адрестік кітапшасы +Name[km]=សៀវភៅអាសយដ្ឋាន +Name[lt]=Adresų knygelė +Name[mk]=Адресар +Name[ms]=Buku Alamat +Name[nb]=Addressebok +Name[nds]=Adressbook +Name[ne]=ठेगाना पुस्तिका +Name[nl]=Adresboek +Name[nn]=Adressebok +Name[pl]=Książka adresowa +Name[pt]=Livro de Endereços +Name[pt_BR]=Livro de Endereços +Name[ro]=Carte de adrese +Name[ru]=Адресная книга +Name[se]=Čujuhusgirji +Name[sk]=Adresár +Name[sl]=Adresar +Name[sr]=Адресар +Name[sr@Latn]=Adresar +Name[sv]=Adressbok +Name[ta]=முகவரிப்புத்தகம் +Name[tg]=Китоби адресӣ +Name[tr]=Adresdefteri +Name[uk]=Адресна книга +Name[uz]=Манзиллар дафтари +Name[zh_CN]=地址簿 +Name[zh_TW]=通訊錄 +Implemented=file +ServiceTypes=KPilotConduit +X-KDE-Library=conduit_address diff --git a/conduits/abbrowserconduit/abbrowserconduit.kcfg b/conduits/abbrowserconduit/abbrowserconduit.kcfg new file mode 100644 index 0000000..aed7706 --- /dev/null +++ b/conduits/abbrowserconduit/abbrowserconduit.kcfg @@ -0,0 +1,80 @@ +<?xml version="1.0" encoding="UTF-8"?> +<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0 + http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" > + <kcfgfile name="kpilot_addressconduitrc"/> + <group name="General"> + <entry name="AddressbookType" key="Addressbook type" type="Enum"> + <choices> + <choice name="eAbookResource"/> + <choice name="eAbookFile"/> + </choices> + <default>eAbookFile</default> + </entry> + <entry name="FileName" type="Path"> + <default>$HOME/.kde/share/apps/kabc/std.vcf</default> + </entry> + <entry name="ArchiveDeleted" type="Bool"> + <default>true</default> + </entry> + <entry name="ConflictResolution" key="ConflictResolve" type="Int"> + <default>-1</default> + </entry> + <entry name="PilotOther" type="Enum"> + <choices> + <choice name="eOtherPhone"/> + <choice name="eAssistant"/> + <choice name="eBusinessFax"/> + <choice name="eCarPhone"/> + <choice name="eEmail2"/> + <choice name="eHomeFax"/> + <choice name="eTelex"/> + <choice name="eTTYTTDPhone"/> + </choices> + <default>eOtherPhone</default> + </entry> + <entry name="PilotStreet" type="Enum"> + <choices> + <choice name="ePilotStreetHome"/> + <choice name="ePilotStreetWork"/> + </choices> + <default>ePilotStreetHome</default> + </entry> + <entry name="PilotFax" type="Enum"> + <choices> + <choice name="ePilotFaxHome"/> + <choice name="ePilotFaxWork"/> + </choices> + <default>ePilotFaxHome</default> + </entry> + <entry name="Custom0" key="Custom 0" type="Enum"> + <choices label="eCustomEnum"> + <choice name="eCustomField"/> + <choice name="eCustomBirthdate"/> + <choice name="eCustomURL"/> + <choice name="eCustomIM"/> + </choices> + <default>eCustomField</default> + </entry> + <entry name="Custom1" key="Custom 1" type="Enum"> + <choices label="eCustomEnum"> + </choices> + <default>eCustomField</default> + </entry> + <entry name="Custom2" key="Custom 2" type="Enum"> + <choices label="eCustomEnum"> + </choices> + <default>eCustomField</default> + </entry> + <entry name="Custom3" key="Custom 3" type="Enum"> + <choices label="eCustomEnum"> + </choices> + <default>eCustomField</default> + </entry> + <entry name="CustomDateFormat" type="String"> + <default></default> + </entry> + </group> + +</kcfg> diff --git a/conduits/abbrowserconduit/kabcRecord.cc b/conduits/abbrowserconduit/kabcRecord.cc new file mode 100644 index 0000000..b713b84 --- /dev/null +++ b/conduits/abbrowserconduit/kabcRecord.cc @@ -0,0 +1,710 @@ +/* KPilot +** +** Copyright (C) 2000,2001 by Dan Pilone +** Copyright (C) 2002-2003 by Reinhold Kainhofer +** Copyright (C) 2007 by Adriaan de Groot <[email protected]> +** +** The abbrowser conduit copies addresses from the Pilot's address book to +** the KDE addressbook maintained via the kabc library. This file +** deals with the actual copying of HH addresses to KABC addresses +** and back again. +*/ + +/* +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program in a file called COPYING; if not, write to +** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +** MA 02110-1301, USA. +*/ + +/* +** Bug reports and questions can be sent to [email protected]. +*/ + +#include "options.h" + +#include <tqregexp.h> + +#include <kglobal.h> +#include <kabc/addressee.h> + +#include "kabcRecord.h" + +/** + * Okay, this is so that we can map the Pilot phone types to Phone Number + * types. Email addresses are NOT included in this map, and are handled + * separately (not in PhoneNumber at all). The Pilot has 8 different kinds + * of phone numbers (which may be *labeled* however you like). These + * need to be mapped to the things that KABC::PhoneNumber handles. + * + * From KABC::PhoneNumber + * enum Types { Home = 1, Work = 2, Msg = 4, Pref = 8, Voice = 16, Fax = 32, + * Cell = 64, Video = 128, Bbs = 256, Modem = 512, Car = 1024, + * Isdn = 2048, Pcs = 4096, Pager = 8192 }; + * + * + * From PilotAddress: + * enum EPhoneType { + * eWork=0, eHome, eFax, eOther, eEmail, eMain, + * ePager, eMobile + * }; + * + * This array must have as many elements as PilotAddress::PhoneType + * and its elements must be KABC::PhoneNumber::Types. + */ + +static KABC::PhoneNumber::Types pilotToPhoneMap[8] = { + KABC::PhoneNumber::Work, // eWork + KABC::PhoneNumber::Home, // eHome, + KABC::PhoneNumber::Fax, // eFax, + (KABC::PhoneNumber::Types)0, // eOther -> wasn't mapped properly, + (KABC::PhoneNumber::Types)0, // eEmail -> shouldn't occur, + KABC::PhoneNumber::Home, // eMain + KABC::PhoneNumber::Pager, // ePager, + KABC::PhoneNumber::Cell // eMobile +} ; + +KABC::PhoneNumber::List KABCSync::getPhoneNumbers(const PilotAddress &a) +{ + FUNCTIONSETUP; + + KABC::PhoneNumber::List list; + TQString test; + + PhoneSlot shownPhone = a.getShownPhone(); + + DEBUGKPILOT << fname << ": preferred pilot index is: [" + << shownPhone << "], preferred phone number is: [" + << a.getField(shownPhone) << "]" << endl; + + for (PhoneSlot i = PhoneSlot::begin(); i.isValid(); ++i) + { + // skip email entries + if ( a.getPhoneType(i) == PilotAddressInfo::eEmail ) + { + continue; + } + + test = a.getField(i); + // only look at this if the field is populated + if (test.isEmpty()) + { + continue; + } + + int phoneType = pilotToPhoneMap[a.getPhoneType(i)]; + + // only populate a PhoneNumber if we have a corresponding type + if (phoneType >=0) + { + // if this is the preferred phone number, set it as such + if (shownPhone == i) + { + phoneType |= KABC::PhoneNumber::Pref; + DEBUGKPILOT << fname << ": found preferred pilot index: [" + << i << "], text: [" << test << "]" << endl; + } + KABC::PhoneNumber ph(test, phoneType); + list.append(ph); + } + else + { + DEBUGKPILOT << fname << ": whoopsie. pilot phone number: [" + << test << "], index: [" << i << "], type: [" + << phoneType << "], has no corresponding PhoneNumber type." << endl; + } + } + + DEBUGKPILOT << fname << ": returning: [" + << list.count() << "] phone numbers." << endl; + + return list; +} + +void KABCSync::setPhoneNumbers(const PilotAddressInfo &info, + PilotAddress &a, + const KABC::PhoneNumber::List &list) +{ + FUNCTIONSETUP; + TQString test; + + // clear all phone numbers (not e-mails) first + for ( PhoneSlot i = PhoneSlot::begin(); i.isValid() ; ++i ) + { + PilotAddressInfo::EPhoneType ind = a.getPhoneType( i ); + if (ind != PilotAddressInfo::eEmail) + { + a.setField(i, TQString()); + } + } + + // now iterate through the list and for each PhoneNumber in the list, + // iterate through our phone types using our map and set the first one + // we find as the type of address for the Pilot + for(KABC::PhoneNumber::List::ConstIterator listIter = list.begin(); + listIter != list.end(); ++listIter) + { + KABC::PhoneNumber phone = *listIter; + + PilotAddressInfo::EPhoneType phoneType = PilotAddressInfo::eHome; + + for ( int pilotPhoneType = PilotAddressInfo::eWork; + pilotPhoneType <= PilotAddressInfo::eMobile; + ++pilotPhoneType) + { + int phoneKey = pilotToPhoneMap[pilotPhoneType]; + if ( phone.type() & phoneKey) + { + DEBUGKPILOT << fname << ": found pilot type: [" + << pilotPhoneType << "] (" + << info.phoneLabel( (PilotAddressInfo::EPhoneType)pilotPhoneType) + << ") for PhoneNumber: [" + << phone.number() << "]" << endl; + + phoneType = (PilotAddressInfo::EPhoneType) pilotPhoneType; + break; + } + } + PhoneSlot fieldSlot = + a.setPhoneField(phoneType, phone.number(), PilotAddress::NoFlags); + + // if this is the preferred phone number, then set it as such + if (fieldSlot.isValid() && (phone.type() & KABC::PhoneNumber::Pref)) + { + DEBUGKPILOT << fname << ": found preferred PhoneNumber. " + << "setting showPhone to index: [" + << fieldSlot << "], PhoneNumber: [" + << phone.number() << "]" << endl; + a.setShownPhone( fieldSlot ); + } + + if (!fieldSlot.isValid()) + { + DEBUGKPILOT << fname << ": Phone listing overflowed." << endl; + } + } + + DEBUGKPILOT << fname << ": Pilot's showPhone now: [" + << a.getShownPhone() << "]." << endl; + + // after setting the numbers, make sure that something sensible is set as the + // shownPhone on the Pilot if nothing is yet... + TQString pref = a.getField(a.getShownPhone()); + if (!a.getShownPhone().isValid() || pref.isEmpty()) + { + DEBUGKPILOT << fname << ": Pilot's showPhone: [" + << a.getShownPhone() + << "] not properly set to a default." + << endl; + + for (PhoneSlot i = PhoneSlot::begin(); i.isValid(); ++i) + { + pref = a.getField(i); + if (!pref.isEmpty()) + { + a.setShownPhone( i ); + DEBUGKPILOT << fname << ": Pilot's showPhone now: [" + << a.getShownPhone() + << "], and that's final." << endl; + break; + } + } + } +} + +unsigned int KABCSync::bestMatchedCategory(const TQStringList &pccategories, + const PilotAddressInfo &info, + unsigned int hhcategory) +{ + FUNCTIONSETUP; + // No categories in list, must be unfiled + if (pccategories.size()<1) + { + return Pilot::Unfiled; + } + + // See if the suggested hhcategory is in the list, and if + // so that is the best match. + if (Pilot::validCategory(hhcategory) && + pccategories.tqcontains(info.categoryName(hhcategory))) + { + return hhcategory; + } + + // Look for the first category from the list which is available on + // the handheld as well. + for(TQStringList::ConstIterator it = pccategories.begin(); it != pccategories.end(); ++it) + { + // Do not map unknown to unfiled when looking for category + int c = info.findCategory( *it, false ); + if ( c >= 0) + { + Q_ASSERT(Pilot::validCategory(c)); + return c; + } + } + + // didn't find anything. return null + return Pilot::Unfiled; +} + +void KABCSync::setCategory(KABC::Addressee & abEntry, const TQString &cat) +{ + if ( (!cat.isEmpty())) + { + abEntry.insertCategory(cat); + } +} + + +TQString KABCSync::getFieldForHHCustom( + const unsigned int index, + const KABC::Addressee &abEntry, + const KABCSync::Settings &settings) +{ + FUNCTIONSETUPL(4); + + TQString retval; + + if (index>3) + { + WARNINGKPILOT << "Bad index number " << index << endl; + retval = TQString(); + } + if (settings.customMapping().count() != 4) + { + WARNINGKPILOT << "Mapping does not have 4 elements." << index << endl; + retval = TQString(); + } + + switch (settings.custom(index)) + { + case eCustomBirthdate: + if (settings.dateFormat().isEmpty()) + { + retval = KGlobal::locale()->formatDate(abEntry.birthday().date()); + } + else + { + TQString tmpfmt(KGlobal::locale()->dateFormat()); + KGlobal::locale()->setDateFormat(settings.dateFormat()); + TQString ret(KGlobal::locale()->formatDate(abEntry.birthday().date())); + KGlobal::locale()->setDateFormat(tmpfmt); + retval = ret; + } + break; + case eCustomURL: + retval = abEntry.url().url(); + break; + case eCustomIM: + retval = abEntry.custom(CSL1("KADDRESSBOOK"), CSL1("X-IMAddress")); + break; + case eCustomField: + default: + retval = abEntry.custom(appString, CSL1("CUSTOM")+TQString::number(index)); + break; + } + + return retval; +} + +void KABCSync::setFieldFromHHCustom( + const unsigned int index, + KABC::Addressee &abEntry, + const TQString &value, + const KABCSync::Settings &settings) +{ + FUNCTIONSETUPL(4); + + if (index>3) + { + WARNINGKPILOT << "Bad index number " << index << endl; + return; + } + if (settings.customMapping().count() != 4) + { + WARNINGKPILOT << "Mapping does not have 4 elements." << index << endl; + return; + } + + switch (settings.custom(index)) + { + case eCustomBirthdate: + { + TQDate bdate; + bool ok=false; + if (settings.dateFormat().isEmpty()) + { + // empty format means use locale setting + bdate=KGlobal::locale()->readDate(value, &ok); + } + else + { + // use given format + bdate=KGlobal::locale()->readDate(value, settings.dateFormat(), &ok); + } + + if (!ok) + { + TQString format = KGlobal::locale()->dateFormatShort(); + TQRegExp re(CSL1("%[yY][^%]*")); + format.remove(re); // Remove references to year and following punctuation + bdate = KGlobal::locale()->readDate(value, format, &ok); + } + DEBUGKPILOT << "Birthdate from " << index << "-th custom field: " + << bdate.toString() << endl; + DEBUGKPILOT << "Is Valid: " << bdate.isValid() << endl; + if (bdate.isValid()) + { + abEntry.setBirthday(bdate); + } + else + { + abEntry.insertCustom(CSL1("KADDRESSBOOK"), CSL1("X-Birthday"), value); + } + break; + } + case eCustomURL: + abEntry.setUrl(value); + break; + case eCustomIM: + abEntry.insertCustom(CSL1("KADDRESSBOOK"), CSL1("X-IMAddress"), value); + break; + case eCustomField: + default: + abEntry.insertCustom(appString, CSL1("CUSTOM")+TQString::number(index), value); + break; + } +} + + +/** First search for a preferred address. If we don't have one, search + * for home or work as specified in the config dialog. If we don't have + * such one, either, search for the other type. If we still have no luck, + * return an address with preferred + home/work flag (from config dlg). */ +KABC::Address KABCSync::getAddress(const KABC::Addressee &abEntry, const KABCSync::Settings &s) +{ + // preferhome == (AbbrowserSettings::pilotStreet==0) + + // Check for preferred address first + KABC::Address ad(abEntry.address(KABC::Address::Pref)); + if (!ad.isEmpty()) return ad; + + // Look for home or work, whichever is preferred + int type = s.preferHome() ? KABC::Address::Home : KABC::Address::Work; + ad=abEntry.address(type); + if (!ad.isEmpty()) return ad; + + // Switch preference if still none found + type = !s.preferHome() ? KABC::Address::Home : KABC::Address::Work; + ad=abEntry.address(type); + if (!ad.isEmpty()) return ad; + + // Last-ditch attempt; see if there is a preferred home or work address + type = s.preferHome() ? KABC::Address::Home : KABC::Address::Work; + return abEntry.address(type | KABC::Address::Pref); +} + + +TQString KABCSync::getFieldForHHOtherPhone(const KABC::Addressee & abEntry, const KABCSync::Settings &s) +{ + switch(s.fieldForOtherPhone()) + { + case eOtherPhone: + return abEntry.phoneNumber(0).number(); + case eAssistant: + return abEntry.custom(CSL1("KADDRESSBOOK"), CSL1("AssistantsName")); + case eBusinessFax: + return abEntry.phoneNumber(KABC::PhoneNumber::Fax | KABC::PhoneNumber::Work).number(); + case eCarPhone: + return abEntry.phoneNumber(KABC::PhoneNumber::Car).number(); + case eEmail2: + return abEntry.emails().first(); + case eHomeFax: + return abEntry.phoneNumber(KABC::PhoneNumber::Fax | KABC::PhoneNumber::Home).number(); + case eTelex: + return abEntry.phoneNumber(KABC::PhoneNumber::Bbs).number(); + case eTTYTTDPhone: + return abEntry.phoneNumber(KABC::PhoneNumber::Pcs).number(); + default: + return TQString::null; + } +} + +void KABCSync::setFieldFromHHOtherPhone(KABC::Addressee & abEntry, const TQString &nr, const KABCSync::Settings &s) +{ + int phoneType = 0; + switch (s.fieldForOtherPhone()) + { + // One very special case which doesn't even map to a real phone type in KABC + case eAssistant: + abEntry.insertCustom(CSL1("KADDRESSBOOK"), CSL1("AssistantsName"), nr); + return; + // Special case: map phone to email, needs different handling. + case eEmail2: + abEntry.insertEmail(nr); + return; + // Remaining cases all map to various phone types + case eOtherPhone: + phoneType = 0; + break; + case eBusinessFax: + phoneType = KABC::PhoneNumber::Fax | KABC::PhoneNumber::Work; + break; + case eHomeFax: + phoneType = KABC::PhoneNumber::Fax | KABC::PhoneNumber::Home; + break; + case eCarPhone: + phoneType = KABC::PhoneNumber::Car; + break; + case eTelex: + phoneType = KABC::PhoneNumber::Bbs; + break; + case eTTYTTDPhone: + phoneType = KABC::PhoneNumber::Pcs; + break; + default: + WARNINGKPILOT << "Unknown phone mapping " << s.fieldForOtherPhone() << endl; + phoneType = 0; + } + KABC::PhoneNumber phone = abEntry.phoneNumber(phoneType); + phone.setNumber(nr); + phone.setType(phoneType); // Double-check in case there was no phonenumber of given type + abEntry.insertPhoneNumber(phone); +} + +void KABCSync::setAddress(PilotAddress &toPilotAddr, + const KABC::Address & abAddress) +{ + toPilotAddr.setField(entryAddress, abAddress.street()); + toPilotAddr.setField(entryCity, abAddress.locality()); + toPilotAddr.setField(entryState, abAddress.region()); + toPilotAddr.setField(entryZip, abAddress.postalCode()); + toPilotAddr.setField(entryCountry, abAddress.country()); +} + + +bool KABCSync::isArchived(const KABC::Addressee &addr) +{ + return addr.custom(KABCSync::appString, KABCSync::flagString) == TQString::number(SYNCDEL); +} + +void KABCSync::makeArchived(KABC::Addressee &addr) +{ + FUNCTIONSETUP; + addr.insertCustom(KABCSync::appString, KABCSync::flagString, TQString::number(SYNCDEL)); + addr.removeCustom(KABCSync::appString, KABCSync::idString); +} + + + + +void KABCSync::copy(PilotAddress &toPilotAddr, + const KABC::Addressee &fromAbEntry, + const PilotAddressInfo &appInfo, + const KABCSync::Settings &syncSettings) +{ + FUNCTIONSETUP; + + toPilotAddr.setDeleted(false); + + // don't do a reset since this could wipe out non copied info + //toPilotAddr.reset(); + toPilotAddr.setField(entryLastname, fromAbEntry.familyName()); + toPilotAddr.setField(entryFirstname, fromAbEntry.givenName()); + toPilotAddr.setField(entryCompany, fromAbEntry.organization()); + toPilotAddr.setField(entryTitle, fromAbEntry.prefix()); + toPilotAddr.setField(entryNote, fromAbEntry.note()); + + // do email first, to ensure they get stored + toPilotAddr.setEmails(fromAbEntry.emails()); + + // now in one fell swoop, set all phone numbers from the Addressee. Note, + // we don't need to differentiate between Fax numbers here--all Fax numbers + // (Home Fax or Work Fax or just plain old Fax) will get synced to the Pilot + KABCSync::setPhoneNumbers(appInfo,toPilotAddr,fromAbEntry.phoneNumbers()); + + // Other field is an oddball and if the user has more than one field set + // as "Other" then only one will be carried over. + TQString oth = KABCSync::getFieldForHHOtherPhone(fromAbEntry,syncSettings); + DEBUGKPILOT << fname << ": putting: ["<<oth<<"] into Palm's other"<<endl; + toPilotAddr.setPhoneField(PilotAddressInfo::eOther, + oth, PilotAddress::Replace); + + KABC::Address homeAddress = KABCSync::getAddress(fromAbEntry, syncSettings); + KABCSync::setAddress(toPilotAddr, homeAddress); + + // Process the additional entries from the Palm(the palm database app block tells us the name of the fields) + unsigned int customIndex = 0; + unsigned int hhField = entryCustom1; + + for ( ; customIndex<4; ++customIndex,++hhField ) + { + toPilotAddr.setField(hhField,getFieldForHHCustom(customIndex,fromAbEntry,syncSettings)); + } + + int categoryForHH = KABCSync::bestMatchedCategory(fromAbEntry.categories(), + appInfo,toPilotAddr.category()); + toPilotAddr.setCategory(categoryForHH); + + if (isArchived(fromAbEntry)) + { + toPilotAddr.setArchived( true ); + } + else + { + toPilotAddr.setArchived( false ); + } +} + +void KABCSync::copy(KABC::Addressee &toAbEntry, + const PilotAddress &fromPiAddr, + const PilotAddressInfo &appInfo, + const KABCSync::Settings &syncSettings) +{ + FUNCTIONSETUP; + + // copy straight forward values + toAbEntry.setFamilyName(fromPiAddr.getField(entryLastname)); + toAbEntry.setGivenName(fromPiAddr.getField(entryFirstname)); + toAbEntry.setOrganization(fromPiAddr.getField(entryCompany)); + toAbEntry.setPrefix(fromPiAddr.getField(entryTitle)); + toAbEntry.setNote(fromPiAddr.getField(entryNote)); + + // set the formatted name + // TODO this is silly and should be removed soon. + toAbEntry.setFormattedName(toAbEntry.realName()); + + // copy the phone stuff + // first off, handle the e-mail addresses as a group and separate from + // the other phone number fields + toAbEntry.setEmails(fromPiAddr.getEmails()); + + // going from Pilot to kabc, we need to clear out all phone records in kabc + // so that they can be set from the Pilot. If we do not do this, then records + // will be left in kabc when they are removed from the Pilot and we'll look + // broken. + KABC::PhoneNumber::List old = toAbEntry.phoneNumbers(); + for (KABC::PhoneNumber::List::Iterator it = old.begin(); it != old.end(); ++it) { + KABC::PhoneNumber phone = *it; + toAbEntry.removePhoneNumber(phone); + } + + // now, get the phone numbers from the Pilot and set them one at a time in kabc + KABC::PhoneNumber::List phones = KABCSync::getPhoneNumbers(fromPiAddr); + for (KABC::PhoneNumber::List::Iterator it = phones.begin(); it != phones.end(); ++it) { + KABC::PhoneNumber phone = *it; + // check for fax number if it is one, set the type per the user's direction + if (phone.type() & KABC::PhoneNumber::Fax) + { + phone.setType(syncSettings.faxTypeOnPC()); + } + toAbEntry.insertPhoneNumber(phone); + } + + // Note: this is weird, and it may cause data to not be synced if there is + // more than one "Other" field being used on the Pilot, since only one will + // be synced in either direction. + KABCSync::setFieldFromHHOtherPhone(toAbEntry, + fromPiAddr.getPhoneField(PilotAddressInfo::eOther),syncSettings); + + // going from Pilot to kabc, we need to clear out all addresses in kabc + // so that they can be set from the Pilot. If we do not do this, then records + // will be left in kabc when they are removed from the Pilot and we'll look + // broken. + KABC::Address::List oAddr = toAbEntry.addresses(); + for (KABC::Address::List::Iterator it = oAddr.begin(); it != oAddr.end(); ++it) { + const KABC::Address addr = *it; + toAbEntry.removeAddress(addr); + } + KABC::Address homeAddress = KABCSync::getAddress(toAbEntry,syncSettings); + homeAddress.setStreet(fromPiAddr.getField(entryAddress)); + homeAddress.setLocality(fromPiAddr.getField(entryCity)); + homeAddress.setRegion(fromPiAddr.getField(entryState)); + homeAddress.setPostalCode(fromPiAddr.getField(entryZip)); + homeAddress.setCountry(fromPiAddr.getField(entryCountry)); + toAbEntry.insertAddress(homeAddress); + + unsigned int customIndex = 0; + unsigned int hhField = entryCustom1; + + for ( ; customIndex<4; ++customIndex,++hhField ) + { + KABCSync::setFieldFromHHCustom(customIndex, + toAbEntry, + fromPiAddr.getField(hhField), + syncSettings); + } + + // copy the fromPiAddr pilot id to the custom field KPilot_Id; + // pilot id may be zero(since it could be new) but couldn't hurt + // to even assign it to zero; let's us know what state the + // toAbEntry is in + toAbEntry.insertCustom(KABCSync::appString, KABCSync::idString, TQString::number(fromPiAddr.id())); + + KABCSync::setCategory(toAbEntry, appInfo.categoryName(fromPiAddr.category())); + + showAddressee(toAbEntry); +} + +void KABCSync::showAddressee(const KABC::Addressee & abAddress) +{ + FUNCTIONSETUP; +#ifdef DEBUG + DEBUGKPILOT << "\tAbbrowser Contact Entry" << endl; + if (abAddress.isEmpty()) + { + DEBUGKPILOT<< "\t\tEMPTY"<<endl; + return; + } + DEBUGKPILOT << "\t\tLast name = " << abAddress.familyName() << endl; + DEBUGKPILOT << "\t\tFirst name = " << abAddress.givenName() << endl; + DEBUGKPILOT << "\t\tCompany = " << abAddress.organization() << endl; + DEBUGKPILOT << "\t\tJob Title = " << abAddress.prefix() << endl; + DEBUGKPILOT << "\t\tNote = " << abAddress.note() << endl; + DEBUGKPILOT << "\t\tCategory = " << abAddress.categories().first() << endl; + DEBUGKPILOT << "\t\tEmail = " << abAddress.emails().join(",") << endl; + + KABC::PhoneNumber::List phs = abAddress.phoneNumbers(); + for (KABC::PhoneNumber::List::Iterator it = phs.begin(); it != phs.end(); ++it) { + KABC::PhoneNumber phone = *it; + DEBUGKPILOT << "\t\t" << phone.label() + << "= " << phone.number() << endl; + } + + KABC::Address::List ads = abAddress.addresses(); + for (KABC::Address::List::Iterator it = ads.begin(); it != ads.end(); ++it) { + const KABC::Address addr = *it; + DEBUGKPILOT << "\t\tAddress = " << addr.street() <<endl; + DEBUGKPILOT << "\t\tLocality = " << addr.locality() <<endl; + DEBUGKPILOT << "\t\tRegion = " << addr.region() <<endl; + DEBUGKPILOT << "\t\tPostal code = " << addr.postalCode() <<endl; + DEBUGKPILOT << "\t\tCountry = " << addr.country() <<endl << endl; + } +#else + Q_UNUSED( abAddress ); +#endif +} + + + + +KABCSync::Settings::Settings() : + fDateFormat(), + fCustomMapping(4), // Reserve space for 4 elements, value 0 == CustomField + fOtherPhone(eOtherPhone), + fPreferHome(true), + fFaxTypeOnPC(KABC::PhoneNumber::Fax | KABC::PhoneNumber::Home) +{ +} + diff --git a/conduits/abbrowserconduit/kabcRecord.h b/conduits/abbrowserconduit/kabcRecord.h new file mode 100644 index 0000000..f27f0b1 --- /dev/null +++ b/conduits/abbrowserconduit/kabcRecord.h @@ -0,0 +1,263 @@ +#ifndef _KPILOT_KABCRECORD_H +#define _KPILOT_KABCRECORD_H +/* KPilot +** +** Copyright (C) 2000,2001 by Dan Pilone +** Copyright (C) 2002-2003 by Reinhold Kainhofer +** Copyright (C) 2007 by Adriaan de Groot <[email protected]> +** +** The abbrowser conduit copies addresses from the Pilot's address book to +** the KDE addressbook maintained via the kabc library. This file +** deals with the actual copying of HH addresses to KABC addresses +** and back again. +*/ + +/* +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program in a file called COPYING; if not, write to +** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +** MA 02110-1301, USA. +*/ + +/* +** Bug reports and questions can be sent to [email protected]. +*/ + +#include <tqvaluevector.h> + +#include <kabc/phonenumber.h> + +#include "pilotAddress.h" + +#define SYNCNONE 0 +#define SYNCMOD 1 +#define SYNCDEL 3 + +namespace KABCSync +{ + // This duplicates values from the config settings, + // and must be kept in sync if new settings are added + // -- there are compile time checks for this in the + // abbrowser conduit code. + enum MappingForOtherPhone { + eOtherPhone=0, + eAssistant, + eBusinessFax, + eCarPhone, + eEmail2, + eHomeFax, + eTelex, + eTTYTTDPhone + } ; + + enum MappingForCustomField { + eCustomField=0, + eCustomBirthdate, + eCustomURL, + eCustomIM + } ; + + class Settings + { + public: + Settings(); + TQString dateFormat() const + { + return fDateFormat; + } + void setDateFormat(const TQString& s) + { + fDateFormat = s; + } + + const TQValueVector<int> &customMapping() const + { + return fCustomMapping; + } + void setCustomMapping(const TQValueVector<int> &v) + { + if (v.count()==4) + { + fCustomMapping = v; + } + } + int custom(int index) const + { + if ( (index<0) || (index>3) ) + { + return 0; + } + else + { + return fCustomMapping[index]; + } + } + + int fieldForOtherPhone() const + { + return fOtherPhone; + } + void setFieldForOtherPhone(int v) + { + fOtherPhone = v; + } + + bool preferHome() const + { + return fPreferHome; + } + void setPreferHome(bool v) + { + fPreferHome = v; + } + + int faxTypeOnPC() const + { + return fFaxTypeOnPC; + } + void setFaxTypeOnPC(int v) + { + fFaxTypeOnPC = v; + } + private: + TQString fDateFormat; + TQValueVector<int> fCustomMapping; + int fOtherPhone; + bool fPreferHome; + int fFaxTypeOnPC; + } ; + + + /** Return a list of all the phone numbers (max. 8) set in this + * handheld entry @p a . Email entries are ignored. + */ + KABC::PhoneNumber::List getPhoneNumbers(const PilotAddress &a); + + /** Set the phone numbers from @p list in the handheld entry + * @p a (with info block @p info providing the mapping of category + * names and some other fiddly stuff) as far as possible. + * @em No overflow handling is done at all. If the desktop has + * more than 5 phone entries, the remainder are dropped. + */ + void setPhoneNumbers(const PilotAddressInfo &info, + PilotAddress &a, + const KABC::PhoneNumber::List &list); + + /** Given a list of category names from the KDE side (e.g. attached + * to a PC-based Addressee) @p categorynames , look for the + * category @em best matching the category @p category + * in the appinfo block @p info . Here, best is defined as follows: + * - if the name of category @p category is in the list, use it + * - otherwise use the first category from the list that is a valid + * category on the handheld. + * - use Pilot::Unfiled if none match. + * + * @return Category index that best matches. + * @return Pilot::Unfiled if no best match. + */ + unsigned int bestMatchedCategory(const TQStringList &categorynames, + const PilotAddressInfo &info, + unsigned int category); + + /** As above, but return the name of the category. */ + inline TQString bestMatchedCategoryName(const TQStringList &categorynames, + const PilotAddressInfo &info, + unsigned int category) + { + return info.categoryName( + bestMatchedCategory(categorynames, info, category)); + } + + /** Give the addressee @p abEntry the category @p cat (leaving + * existing category assignments intact). + */ + void setCategory(KABC::Addressee &abEntry, const TQString &cat); + + /* These are string identifiers used for custom properties in the addressees, + * used to store KPilot-specific settings. + */ + const TQString appString=CSL1("KPILOT"); ///< Identifier for the application + const TQString flagString=CSL1("Flag"); ///< Flags: synced or not + const TQString idString=CSL1("RecordID"); ///< Record ID on HH for this addressee + + + /** Get the string value for HH custom field @p index (0..3) from the addressee + * @p abEntry . Which @em actual field this is depends on the mapping + * of custom HH fields to PC fields. This mapping is given by the @p customMapping + * which may be created from the conduit settings or by hand. Since one of the + * possible actual fields is "birthday," which needs formatting, use the date format + * string @p dateFormat. If this is empty, use the locale setting. + * + * @return String value for HH custom field @p index + * @return Null TQString on error (is also a valid return value) + */ + TQString getFieldForHHCustom( + unsigned int index, + const KABC::Addressee &abEntry, + const Settings &settings); + + /** Set a field of the PC @p abEntry address from the custom HH field. + * Use value @p value . The value comes from custom field @p index + * using the interpretation of custom fields @p customMapping . Because + * one of the interpretations includes the birthday, use the date format + * @p dateFormat ; if empty, use the local format when setting dates from the HH. + */ + void setFieldFromHHCustom( + const unsigned int index, + KABC::Addressee &abEntry, + const TQString &value, + const Settings &settings); + + /** The HH has a phone type "other" which may be mapped to any one of + * several PC side phone numbers. Return the right one depending in the mapping. + * + * @note @p mappingForOther should come from AbbrowserSettings::pilotOther() + */ + TQString getFieldForHHOtherPhone(const KABC::Addressee &abEntry, const Settings &s); + + /** The HH has a phone type "other" which may be mapped to any one + * of several PC side phone numbers. Store the number @p nr in the + * PC side phone field indicated by @p mappingForOther . + * + * @note @p mappingForOther should come from AbbrowserSettings::pilotOther() + */ + void setFieldFromHHOtherPhone(KABC::Addressee &abEntry, const TQString &nr, const Settings &s); + + /** Returns the address portion of an addressee. Since the HH can only store + * one address, we return the preferred address (if the Addressee @p abEntry + * has one) and then either home or business depending on @p preferHome + * and if that doesn't exist, the other one and if @em that doesn't exist, + * return the preferred home or work address if it exists. + */ + KABC::Address getAddress(const KABC::Addressee &abEntry, const Settings &); + + /** Set the address fields of the HH record from the @p abAddress . */ + void setAddress(PilotAddress &toPilotAddr, const KABC::Address &abAddress); + + bool isArchived(const KABC::Addressee &); + void makeArchived(KABC::Addressee &); + + void copy(PilotAddress &toPilotAddr, + const KABC::Addressee &fromAbEntry, + const PilotAddressInfo &appInfo, + const Settings &syncSettings); + void copy(KABC::Addressee &toAbEntry, + const PilotAddress &fromPiAddr, + const PilotAddressInfo &appInfo, + const Settings &syncSettings); + + void showAddressee(const KABC::Addressee &); +} + +#endif + diff --git a/conduits/abbrowserconduit/kaddressbookConduit.ui b/conduits/abbrowserconduit/kaddressbookConduit.ui new file mode 100644 index 0000000..6eb317b --- /dev/null +++ b/conduits/abbrowserconduit/kaddressbookConduit.ui @@ -0,0 +1,746 @@ +<!DOCTYPE UI><UI version="3.1" stdsetdef="1"> +<class>AbbrowserWidget</class> +<author>Adriaan de Groot and David Bishop</author> +<widget class="QWidget"> + <property name="name"> + <cstring>Form2</cstring> + </property> + <property name="tqgeometry"> + <rect> + <x>0</x> + <y>0</y> + <width>645</width> + <height>287</height> + </rect> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QTabWidget" row="0" column="0"> + <property name="name"> + <cstring>tabWidget</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <widget class="QWidget"> + <property name="name"> + <cstring>tab</cstring> + </property> + <attribute name="title"> + <string>General</string> + </attribute> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer row="3" column="0"> + <property name="name"> + <cstring>Spacer1</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="tqsizeHint"> + <size> + <width>20</width> + <height>50</height> + </size> + </property> + </spacer> + <widget class="QButtonGroup" row="0" column="0"> + <property name="name"> + <cstring>fSyncDestination</cstring> + </property> + <property name="title"> + <string>Sync Destination</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>fSyncStdAbook</cstring> + </property> + <property name="text"> + <string>&Standard addressbook</string> + </property> + <property name="whatsThis" stdset="0"> + <string><qt>Select this option to synchronize with KDE's standard addressbook (i.e. the addressbook that you edit in KAddressBook, and which you use in KMail)</qt></string> + </property> + </widget> + <widget class="QRadioButton" row="1" column="0"> + <property name="name"> + <cstring>fSyncFile</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>vCard &file:</string> + </property> + <property name="whatsThis" stdset="0"> + <string><qt>Select this option to use a specific address book file, instead of the standard KDE address book. This file must be in the vCard format (.vcf). Type the location of this file in the edit box or select it clicking the file picker button.</qt></string> + </property> + </widget> + <widget class="KURLRequester" row="1" column="1"> + <property name="name"> + <cstring>fAbookFile</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="whatsThis" stdset="0"> + <string><qt>Enter the vCard file name here or select it by clicking the file picker button. vCard is a standard format for exchanging contact information. </qt></string> + </property> + </widget> + </grid> + </widget> + <widget class="QCheckBox" row="2" column="0"> + <property name="name"> + <cstring>fArchive</cstring> + </property> + <property name="text"> + <string>Store &archived records in the KDE addressbook</string> + </property> + <property name="whatsThis" stdset="0"> + <string><qt>If you delete an address on your handheld, you can determine if it should be archived on the PC. If you check that and this checkbox, the address will be added to your addressbook, but no longer synchronized with the handheld.</qt></string> + </property> + </widget> + </grid> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>tab</cstring> + </property> + <attribute name="title"> + <string>Conflicts</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox4</cstring> + </property> + <property name="title"> + <string>Conflict Resolution</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel1_2_2</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>4</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Conflict &resolution:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>fConflictResolution</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string><qt>Select in this list how conflicting entries (entries which were edited both on your handheld and on the PC) are resolved. Possibly values are "Use KPilot's Global Setting" to use the settings defined in KPilot HotSync configuration, "Ask User" to let you decide case by case, "Do Nothing" to allow the entries to be different, "PC overrides", "Handheld overrides", "Use values from last sync" and "Use both entries" to create a new entry on both the PC and handheld.</qt></string> + </property> + </widget> + <widget class="QComboBox" row="1" column="1"> + <item> + <property name="text"> + <string>Use KPilot's Global Setting</string> + </property> + </item> + <item> + <property name="text"> + <string>Ask User</string> + </property> + </item> + <item> + <property name="text"> + <string>Do Nothing</string> + </property> + </item> + <item> + <property name="text"> + <string>Handheld Overrides</string> + </property> + </item> + <item> + <property name="text"> + <string>PC Overrides</string> + </property> + </item> + <item> + <property name="text"> + <string>Values From Last Sync (if possible)</string> + </property> + </item> + <item> + <property name="text"> + <string>Use Both Entries</string> + </property> + </item> + <property name="name"> + <cstring>fConflictResolution</cstring> + </property> + <property name="currentItem"> + <number>6</number> + </property> + <property name="whatsThis" stdset="0"> + <string><qt>Select in this list how conflicting entries (entries which were edited both on your handheld and on the PC) are resolved. Possibly values are "Use KPilot's Global Setting" to use the settings defined in KPilot HotSync configuration, "Ask User" to let you decide case by case, "Do Nothing" to allow the entries to be different, "PC overrides", "Handheld overrides", "Use values from last sync" and "Use both entries" to create a new entry on both the PC and handheld.</qt></string> + </property> + </widget> + <widget class="QLabel" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>textLabel1_7</cstring> + </property> + <property name="text"> + <string><p>Select the default action if an event was modified on both sides here. </p></string> + </property> + <property name="tqalignment"> + <set>WordBreak|AlignJustify|AlignVCenter</set> + </property> + </widget> + </grid> + </widget> + <spacer> + <property name="name"> + <cstring>spacer6</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="tqsizeHint"> + <size> + <width>20</width> + <height>41</height> + </size> + </property> + </spacer> + </vbox> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>tab</cstring> + </property> + <attribute name="title"> + <string>Fields</string> + </attribute> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>11</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>TextLabel2</cstring> + </property> + <property name="text"> + <string>Handheld other phone:</string> + </property> + <property name="whatsThis" stdset="0"> + <string><qt>Select which KAddressBook field should be used to store the Pilot's &quot;Other&quot; phone here.</qt></string> + </property> + </widget> + <widget class="QComboBox" row="0" column="1"> + <item> + <property name="text"> + <string>Other Phone</string> + </property> + </item> + <item> + <property name="text"> + <string>Assistant</string> + </property> + </item> + <item> + <property name="text"> + <string>Business Fax</string> + </property> + </item> + <item> + <property name="text"> + <string>Car Phone</string> + </property> + </item> + <item> + <property name="text"> + <string>Email 2</string> + </property> + </item> + <item> + <property name="text"> + <string>Home Fax</string> + </property> + </item> + <item> + <property name="text"> + <string>Telex</string> + </property> + </item> + <item> + <property name="text"> + <string>TTY/TTD Phone</string> + </property> + </item> + <property name="name"> + <cstring>fOtherPhone</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="whatsThis" stdset="0"> + <string><qt>Select which KAddressBook field should be used to store the Pilot's &quot;Other&quot; phone here.</qt></string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>TextLabel4</cstring> + </property> + <property name="text"> + <string>Handheld street address:</string> + </property> + <property name="whatsThis" stdset="0"> + <string><qt>Select which KAddressBook field should be used to store the Pilot's Street Address here.</qt></string> + </property> + </widget> + <widget class="QComboBox" row="1" column="1"> + <item> + <property name="text"> + <string>Preferred, then Home Address</string> + </property> + </item> + <item> + <property name="text"> + <string>Preferred, then Business Address</string> + </property> + </item> + <property name="name"> + <cstring>fAddress</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string><qt>Select which KAddressBook field should be used to store the Pilot's Street Address here.</qt></string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>TextLabel5</cstring> + </property> + <property name="text"> + <string>Handheld fax:</string> + </property> + <property name="whatsThis" stdset="0"> + <string><qt>Select which KAddressBook field should be used to store the Fax number from the Pilot here.</qt></string> + </property> + </widget> + <widget class="QComboBox" row="2" column="1"> + <item> + <property name="text"> + <string>Home Fax</string> + </property> + </item> + <item> + <property name="text"> + <string>Business Fax</string> + </property> + </item> + <property name="name"> + <cstring>fFax</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string><qt>Select which KAddressBook field should be used to store the Fax number from the Pilot here.</qt></string> + </property> + </widget> + <spacer row="3" column="0"> + <property name="name"> + <cstring>Spacer2</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="tqsizeHint"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </grid> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>tab</cstring> + </property> + <attribute name="title"> + <string>Custom Fields</string> + </attribute> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Handheld custom field 1:</string> + </property> + <property name="whatsThis" stdset="0"> + <string><qt>Select the field from this list that represents best the meaning given by your use of the first custom field on your handheld.</qt></string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel1_2</cstring> + </property> + <property name="text"> + <string>Handheld custom field 2:</string> + </property> + <property name="whatsThis" stdset="0"> + <string><qt>Select the field from this list that represents best the meaning given by your use of the second custom field on your handheld.</qt></string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel1_3</cstring> + </property> + <property name="text"> + <string>Handheld custom field 3:</string> + </property> + <property name="whatsThis" stdset="0"> + <string><qt>Select the field from this list that represents best the meaning given by your use of the third custom field on your handheld.</qt></string> + </property> + </widget> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>textLabel1_4</cstring> + </property> + <property name="text"> + <string>Handheld custom field 4:</string> + </property> + <property name="whatsThis" stdset="0"> + <string><qt>Select the field from this list that represents best the meaning given by your use of the fourth custom field on your handheld.</qt></string> + </property> + </widget> + <widget class="QComboBox" row="0" column="1"> + <item> + <property name="text"> + <string>Store as Custom Field</string> + </property> + </item> + <item> + <property name="text"> + <string>Birthdate</string> + </property> + </item> + <item> + <property name="text"> + <string>URL</string> + </property> + </item> + <item> + <property name="text"> + <string>IM Address (ICQ, MS, ...)</string> + </property> + </item> + <property name="name"> + <cstring>fCustom0</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="whatsThis" stdset="0"> + <string><qt>Select the field from this list that represents best the meaning given by your use of the first custom field on your handheld.</qt></string> + </property> + </widget> + <widget class="QComboBox" row="1" column="1"> + <item> + <property name="text"> + <string>Store as Custom Field</string> + </property> + </item> + <item> + <property name="text"> + <string>Birthdate</string> + </property> + </item> + <item> + <property name="text"> + <string>URL</string> + </property> + </item> + <item> + <property name="text"> + <string>IM Address (ICQ, MSN, ...)</string> + </property> + </item> + <property name="name"> + <cstring>fCustom1</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="whatsThis" stdset="0"> + <string><qt>Select the field from this list that represents best the meaning given by your use of the second custom field on your handheld.</qt></string> + </property> + </widget> + <widget class="QComboBox" row="2" column="1"> + <item> + <property name="text"> + <string>Store as Custom Field</string> + </property> + </item> + <item> + <property name="text"> + <string>Birthdate</string> + </property> + </item> + <item> + <property name="text"> + <string>URL</string> + </property> + </item> + <item> + <property name="text"> + <string>IM Address (ICQ, MSN, ...)</string> + </property> + </item> + <property name="name"> + <cstring>fCustom2</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="whatsThis" stdset="0"> + <string><qt>Select the field from this list that represents best the meaning given by your use of the third custom field on your handheld.</qt></string> + </property> + </widget> + <widget class="QComboBox" row="3" column="1"> + <item> + <property name="text"> + <string>Store as Custom Field</string> + </property> + </item> + <item> + <property name="text"> + <string>Birthdate</string> + </property> + </item> + <item> + <property name="text"> + <string>URL</string> + </property> + </item> + <item> + <property name="text"> + <string>IM Address (ICQ, MSN, ...)</string> + </property> + </item> + <property name="name"> + <cstring>fCustom3</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="whatsThis" stdset="0"> + <string><qt>Select the field from this list that represents best the meaning given by your use of the fourth custom field on your handheld.</qt></string> + </property> + </widget> + <widget class="Line" row="4" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>line1</cstring> + </property> + <property name="frameShape"> + <enum>HLine</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + </widget> + <widget class="QLabel" row="5" column="0" rowspan="2" colspan="1"> + <property name="name"> + <cstring>textLabel1_5</cstring> + </property> + <property name="text"> + <string>Date &format:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>fCustomDate</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string><qt>Select the birthdate format here, if you selected "birthdate" for any of the custom fields above. Possible placeholders are:<br> %d for the day, %m for the month, %y for the two-digit year, %Y for the four-digit year. For example, %d.%m.%Y would generate a date like 27.3.1952, while %m/%d/%y would write the same date as 03/27/52. </qt></string> + </property> + </widget> + <widget class="QComboBox" row="6" column="1"> + <item> + <property name="text"> + <string>Locale Settings</string> + </property> + </item> + <item> + <property name="text"> + <string>%d.%m.%Y</string> + </property> + </item> + <item> + <property name="text"> + <string>%d.%m.%y</string> + </property> + </item> + <item> + <property name="text"> + <string>%d/%m/%Y</string> + </property> + </item> + <item> + <property name="text"> + <string>%d/%m/%y</string> + </property> + </item> + <item> + <property name="text"> + <string>%m/%d/%Y</string> + </property> + </item> + <item> + <property name="text"> + <string>%m/%d/%y</string> + </property> + </item> + <property name="name"> + <cstring>fCustomDate</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="editable"> + <bool>true</bool> + </property> + <property name="whatsThis" stdset="0"> + <string><qt>Select the birthdate format here, if you selected "birthdate" for any of the custom fields above. Possible placeholders are:<br> %d for the day, %m for the month, %y for the two-digit year, %Y for the four-digit year. For example, %d.%m.%Y would generate a date like 27.3.1952, while %m/%d/%y would write the same date as 03/27/52. </qt></string> + </property> + </widget> + <spacer row="8" column="1"> + <property name="name"> + <cstring>spacer5</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="tqsizeHint"> + <size> + <width>31</width> + <height>30</height> + </size> + </property> + </spacer> + </grid> + </widget> + </widget> + </grid> +</widget> +<connections> + <connection> + <sender>fSyncFile</sender> + <signal>toggled(bool)</signal> + <receiver>fAbookFile</receiver> + <slot>setEnabled(bool)</slot> + </connection> +</connections> +<tabstops> + <tabstop>fArchive</tabstop> + <tabstop>tabWidget</tabstop> + <tabstop>fOtherPhone</tabstop> + <tabstop>fAddress</tabstop> + <tabstop>fFax</tabstop> +</tabstops> +<tqlayoutdefaults spacing="6" margin="11"/> +<includes> + <include location="global" impldecl="in implementation">kurlrequester.h</include> + <include location="global" impldecl="in implementation">klineedit.h</include> + <include location="global" impldecl="in implementation">kpushbutton.h</include> +</includes> +</UI> diff --git a/conduits/abbrowserconduit/resolutionDialog.cc b/conduits/abbrowserconduit/resolutionDialog.cc new file mode 100644 index 0000000..d9b7089 --- /dev/null +++ b/conduits/abbrowserconduit/resolutionDialog.cc @@ -0,0 +1,323 @@ +/* resolutionDialog.h KPilot +** +** Copyright (C) 2002-2003 by Reinhold Kainhofer +** +** See the .cc file for an explanation of what this file is for. +*/ + +/* +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program in a file called COPYING; if not, write to +** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +** MA 02110-1301, USA. +*/ + +/* +** Bug reports and questions can be sent to [email protected] +*/ + +#include "options.h" + +#include <tqtimer.h> +#include <tqlabel.h> +#include <tqpushbutton.h> +#include <tqlistview.h> +#include <tqregexp.h> + +#include "resolutionTable.h" +#include "resolutionDialog_base.h" + +#include "resolutionDialog.moc" + +/** This class describes the controllers of the conflict resolution ListView, + * as well as its child radio buttons. There are two different constructors + * for them. + * Each controller has three child radio buttons, and if any of them is + * activated (stateChange), it sets the text of its parent (which is the + * controller, which is an instance of ResolutionCheckListItem, too). + **/ +class ResolutionCheckListItem : TQCheckListItem { +public: + ResolutionCheckListItem(ResolutionItem*it, ResolutionTable*tb, + TQListView*parent); + ResolutionCheckListItem(TQString header, TQString text, + ResolutionCheckListItem*parent); + ~ResolutionCheckListItem() {}; + virtual void stateChange(bool newstate); + virtual void setValue(TQString text); + virtual void setCaption(TQString caption); + +protected: + void updateText(); + /* fResItem is only set for the controller */ + ResolutionItem*fResItem; + bool isController; + /* The description of the entry, e.g. Backup, PC, Palm for the radio buttons, + * of the field name for the controllers + */ + TQString fCaption; + /* The currrent value of the entry (for controllers this changes with the + * selected button */ + TQString fText; +}; + + +ResolutionCheckListItem::ResolutionCheckListItem(ResolutionItem*it, + ResolutionTable*tb, TQListView*parent) : + TQCheckListItem(parent, TQString::null, TQCheckListItem::Controller), + fResItem(it), + isController(true), + fCaption(it?(it->fName):(TQString::null)), + fText(it?(it->fResolved):(TQString::null)) +{ + FUNCTIONSETUP; + if (it && tb) + { + // If all three texts are identical, there is no need for + // resolution so don't show the radio items below + bool itemsEqual=true; + TQString testtext(TQString::null); + const enum eExistItems its[3]={eExistsPC, eExistsPalm, eExistsBackup}; + // get a valid text from a valid field, which will serve as the + // test text for the comparison + for (int i=0; i<3; i++) + { + if ((testtext.isNull()) && (it->fExistItems & its[i]) ) + testtext=it->fEntries[i]; + } + for (int i=0; i<3; i++) + { + if (it->fExistItems & its[i]) + itemsEqual&=(it->fEntries[i]==testtext); + } + if (!itemsEqual) + { + ResolutionCheckListItem*item; + for (int i=2; i>=0; i--) + { + // Add only existing items + if (it->fExistItems & its[i]) + { + item=new ResolutionCheckListItem(it->fEntries[i], tb->labels[i], this); + item->setOn(it->fEntries[i]==fText); + } + } + } + updateText(); + } + setOpen(true); +} + +ResolutionCheckListItem::ResolutionCheckListItem(TQString text, TQString header, + ResolutionCheckListItem*parent) : + TQCheckListItem(parent, TQString(), TQCheckListItem::RadioButton), + fResItem(0L), + isController(false), + fCaption(header), + fText(text) +{ + updateText(); +} + +void ResolutionCheckListItem::stateChange(bool newstate) +{ + if (newstate && !isController) + { + ResolutionCheckListItem*par=static_cast<ResolutionCheckListItem*>(parent()); + { + par->setValue(fText); + } + } +} + +void ResolutionCheckListItem::setValue(TQString text) +{ + FUNCTIONSETUP; + fText=text; + if (isController && fResItem) + { + fResItem->fResolved=text; + } + updateText(); +} + +void ResolutionCheckListItem::setCaption(TQString caption) +{ + fCaption=caption; + updateText(); +} + +void ResolutionCheckListItem::updateText() +{ + TQString newText(i18n("Entries in the resolution dialog. First the name of the field, then the entry from the Handheld or PC after the colon", "%1: %2").arg(fCaption).arg(fText)); + newText.tqreplace(TQRegExp(CSL1("\n")), + i18n("Denoting newlines in Address entries. No need to translate", " | ")); + setText(0, newText); +} + + + +/***************************************************************** + * + *****************************************************************/ + +ResolutionDlg::ResolutionDlg( TQWidget* parent, KPilotLink*fH, + const TQString &caption, const TQString &helpText, ResolutionTable*tab) : + KDialogBase( parent, "ResolutionDlg", false, caption, Apply|Cancel, Apply), + tickleTimer(0L), + fHandle(fH), + fTable(tab) +{ + fWidget = new ResolutionDialogBase( this ); + setMainWidget(fWidget); + fTable->fResolution=SyncAction::eDoNothing; + fWidget->fIntroText->setText(helpText); + + fillListView(); + adjustButtons(tab); + + adjustSize(); + resize(size()); + + if (fHandle) tickleTimer=new TQTimer(this, "TickleTimer"); + + if (tickleTimer) + { + connect( tickleTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(_tickle())); + // tickle the palm every 10 seconds to prevent a timeout until the + // sync is really finished. + tickleTimer->start( 10000 ); + } + + connect(fWidget->fKeepBoth, TQT_SIGNAL(clicked()), TQT_SLOT(slotKeepBoth())); + connect(fWidget->fBackupValues, TQT_SIGNAL(clicked()), TQT_SLOT(slotUseBackup())); + connect(fWidget->fPalmValues, TQT_SIGNAL(clicked()), TQT_SLOT(slotUsePalm())); + connect(fWidget->fPCValues, TQT_SIGNAL(clicked()), TQT_SLOT(slotUsePC())); +} + +void ResolutionDlg::adjustButtons(ResolutionTable*tab) +{ + FUNCTIONSETUP; + if (!tab) return; + if (!(tab->fExistItems & eExistsPC) ) + { + fWidget->fPCValues->setText(i18n("Delete entry")); + fWidget->fKeepBoth->setDisabled(TRUE); + fWidget->fKeepBoth->hide(); + } + if (!(tab->fExistItems & eExistsPalm) ) + { + fWidget->fPalmValues->setText(i18n("Delete entry")); + fWidget->fKeepBoth->setDisabled(TRUE); + fWidget->fKeepBoth->hide(); + } + if (!(tab->fExistItems & eExistsBackup) ) + { + fWidget->fBackupValues->setDisabled(TRUE); + } +} + +void ResolutionDlg::fillListView() +{ + FUNCTIONSETUP; + fWidget->fResolutionView->setSorting(-1, FALSE); + fWidget->fResolutionView->clear(); + for ( ResolutionItem* it = fTable->last(); it; it = fTable->prev() ) + { +#ifdef DEBUG + DEBUGKPILOT<<"Building table, items="<<it->fExistItems<<", PC="<< + it->fEntries[0]<<", Palm="<<it->fEntries[1]<<", Backup="<< + it->fEntries[2]<<endl; +#endif + bool hasValidValues=false; + if (it->fExistItems & eExistsPC) + hasValidValues = hasValidValues || !(it->fEntries[0].isEmpty()); + if (it->fExistItems & eExistsPalm) + hasValidValues = hasValidValues || !(it->fEntries[1].isEmpty()); + if (it->fExistItems & eExistsBackup) + hasValidValues = hasValidValues || !(it->fEntries[2].isEmpty()); + if (hasValidValues) + new ResolutionCheckListItem(it, fTable, fWidget->fResolutionView); + } +} + +void ResolutionDlg::slotKeepBoth() +{ + if ( (fTable->fExistItems & eExistsPC) && (fTable->fExistItems & eExistsPalm) ) + { + fTable->fResolution=SyncAction::eDuplicate; + } + else + { + fTable->fResolution=SyncAction::eDoNothing; + } + done(fTable->fResolution); +} + +void ResolutionDlg::slotUseBackup() +{ + if (fTable->fExistItems & eExistsBackup) + { + fTable->fResolution=SyncAction::ePreviousSyncOverrides; + } + else + { + fTable->fResolution=SyncAction::eDoNothing; + } + done(fTable->fResolution); +} + +void ResolutionDlg::slotUsePalm() +{ + if (fTable->fExistItems & eExistsPalm) + { + fTable->fResolution=SyncAction::eHHOverrides; + } + else + { + fTable->fResolution=SyncAction::eDelete; + } + done(fTable->fResolution); +} + +void ResolutionDlg::slotUsePC() +{ + if (fTable->fExistItems & eExistsPC) + { + fTable->fResolution=SyncAction::ePCOverrides; + } + else + { + fTable->fResolution=SyncAction::eDelete; + } + done(fTable->fResolution); +} + +void ResolutionDlg::slotApply() +{ + fTable->fResolution=SyncAction::eAskUser; + done(fTable->fResolution); +} + +void ResolutionDlg::_tickle() +{ + if (fHandle) fHandle->tickle(); +} + +/* + * Destroys the object and frees any allocated resources + */ +ResolutionDlg::~ResolutionDlg() +{ + // no need to delete child widgets, Qt does it all for us +} diff --git a/conduits/abbrowserconduit/resolutionDialog.h b/conduits/abbrowserconduit/resolutionDialog.h new file mode 100644 index 0000000..2c7a8b0 --- /dev/null +++ b/conduits/abbrowserconduit/resolutionDialog.h @@ -0,0 +1,70 @@ +#ifndef RESOLUTIONDIALOG_H +#define RESOLUTIONDIALOG_H +/* resolutionDialog.h KPilot +** +** Copyright (C) 2002-2003 by Reinhold Kainhofer +** +** See the .cc file for an explanation of what this file is for. +*/ + +/* +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program in a file called COPYING; if not, write to +** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +** MA 02110-1301, USA. +*/ + +/* +** Bug reports and questions can be sent to [email protected] +*/ + +#include <kdialogbase.h> +class KPilotLink; +class TQTimer; +class TQListView; +class ResolutionDialogBase; + + +class ResolutionTable; + +class ResolutionDlg : public KDialogBase +{ + Q_OBJECT + +public: + ResolutionDlg( TQWidget* parent=0, + KPilotLink*fH=0L, + const TQString &caption=TQString(), + const TQString &helpText=TQString(), + ResolutionTable *tab=0L ); + ~ResolutionDlg(); + +public slots: + void slotKeepBoth(); + void slotUseBackup(); + void slotUsePalm(); + void slotUsePC(); + void slotApply(); + void _tickle(); +protected: + void fillListView(); + void adjustButtons(ResolutionTable*tab); + + TQTimer* tickleTimer; + KPilotLink* fHandle; + ResolutionTable*fTable; + + ResolutionDialogBase*fWidget; +}; + +#endif // RESOLUTIONDIALOG_H diff --git a/conduits/abbrowserconduit/resolutionDialog_base.ui b/conduits/abbrowserconduit/resolutionDialog_base.ui new file mode 100644 index 0000000..0f4b7c0 --- /dev/null +++ b/conduits/abbrowserconduit/resolutionDialog_base.ui @@ -0,0 +1,129 @@ +<!DOCTYPE UI><UI version="3.2" stdsetdef="1"> +<class>ResolutionDialogBase</class> +<widget class="QWidget"> + <property name="name"> + <cstring>widget2</cstring> + </property> + <property name="tqgeometry"> + <rect> + <x>0</x> + <y>0</y> + <width>459</width> + <height>350</height> + </rect> + </property> + <property name="caption"> + <string>widget2</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>fIntroText</cstring> + </property> + <property name="text"> + <string>The following record was edited both on the handheld and on the PC. Please choose which values shall be synced:</string> + </property> + <property name="tqalignment"> + <set>WordBreak|AlignVCenter</set> + </property> + </widget> + <widget class="QListView" row="1" column="0"> + <column> + <property name="text"> + <string>Field</string> + </property> + <property name="clickable"> + <bool>false</bool> + </property> + <property name="resizable"> + <bool>false</bool> + </property> + </column> + <property name="name"> + <cstring>fResolutionView</cstring> + </property> + <property name="rootIsDecorated"> + <bool>true</bool> + </property> + <property name="resizeMode"> + <enum>AllColumns</enum> + </property> + <property name="whatsThis" stdset="0"> + <string><qt>Use this list to resolve, field by field, the conflicts created when a record was edited both on the handheld and on the PC. For each record, the different values from the last sync, the handheld and PC are displayed for each field, allowing you to choose the desired value.</qt></string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Line breaks in any of the entries are denoted by a " | " (without the quotes).</string> + </property> + <property name="tqalignment"> + <set>WordBreak|AlignVCenter</set> + </property> + </widget> + <widget class="QFrame" row="3" column="0"> + <property name="name"> + <cstring>frame3</cstring> + </property> + <property name="frameShape"> + <enum>GroupBoxPanel</enum> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QPushButton" row="0" column="1"> + <property name="name"> + <cstring>fKeepBoth</cstring> + </property> + <property name="text"> + <string>&Keep Both</string> + </property> + <property name="whatsThis" stdset="0"> + <string><qt>Click this button to use both values, resulting in the duplication of the record.</qt></string> + </property> + </widget> + <widget class="QPushButton" row="0" column="0"> + <property name="name"> + <cstring>fPCValues</cstring> + </property> + <property name="text"> + <string>&PC Values</string> + </property> + <property name="whatsThis" stdset="0"> + <string><qt>Click this button to use the PC values for synchronizing all conflicting fields in this record.</qt></string> + </property> + </widget> + <widget class="QPushButton" row="1" column="0"> + <property name="name"> + <cstring>fBackupValues</cstring> + </property> + <property name="text"> + <string>&Last Sync Values</string> + </property> + <property name="whatsThis" stdset="0"> + <string><qt>Click this button to use the last sync values (old values) for synchronizing all conflicting fields in this record.</qt></string> + </property> + </widget> + <widget class="QPushButton" row="1" column="1"> + <property name="name"> + <cstring>fPalmValues</cstring> + </property> + <property name="text"> + <string>&Handheld Values</string> + </property> + <property name="whatsThis" stdset="0"> + <string><qt>Click this button to use the handheld values for synchronizing all conflicting fields in this record.</qt></string> + </property> + </widget> + </grid> + </widget> + </grid> +</widget> +<tqlayoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/conduits/abbrowserconduit/resolutionTable.h b/conduits/abbrowserconduit/resolutionTable.h new file mode 100644 index 0000000..51c2519 --- /dev/null +++ b/conduits/abbrowserconduit/resolutionTable.h @@ -0,0 +1,70 @@ +#ifndef RESOLUTIONTABLE_H +#define RESOLUTIONTABLE_H +/* resolutionTable.h KPilot +** +** Copyright (C) 2003 by Reinhold Kainhofer +** +** See the .cc file for an explanation of what this file is for. +*/ + +/* +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program in a file called COPYING; if not, write to +** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +** MA 02110-1301, USA. +*/ + +/* +** Bug reports and questions can be sent to [email protected] +*/ + + +#include <tqvaluelist.h> +#include "syncAction.h" + +typedef enum eExistItems { + eExistsPC=0x1, eExistsPalm=0x2, eExistsBackup=0x4, + eExistsAll=eExistsPC|eExistsPalm|eExistsBackup +}; + +class ResolutionItem +{ +public: + enum eExistItems fExistItems; + TQString fEntries[3]; + TQString fResolved; + TQString fName; +public: + ResolutionItem() {} + ResolutionItem(TQString name, int ex, TQString pc, TQString palm, TQString backup):fExistItems((eExistItems)ex),fName(name) + {fEntries[0]=pc;fEntries[1]=palm; fEntries[2]=backup; /*fExistItems=(eExistItems)ex;*/ } + ~ResolutionItem() {} +}; + +/** +@author Reinhold Kainhofer +*/ +class ResolutionTable : public TQPtrList<ResolutionItem> +{ +public: + ResolutionTable():TQPtrList<ResolutionItem>() {fResolution=SyncAction::eAskUser;}; + + ~ResolutionTable() {}; + + SyncAction::ConflictResolution fResolution; + TQString labels[3]; + enum eExistItems fExistItems; +}; + +#endif + |