kpilot/lib

pilotAddress.cc

00001 /* KPilot
00002 **
00003 ** Copyright (C) 1998-2001 by Dan Pilone
00004 ** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
00005 **
00006 ** This is a C++ wrapper for the pilot's address database structures.
00007 */
00008 
00009 /*
00010 ** This program is free software; you can redistribute it and/or modify
00011 ** it under the terms of the GNU Lesser General Public License as published by
00012 ** the Free Software Foundation; either version 2.1 of the License, or
00013 ** (at your option) any later version.
00014 **
00015 ** This program is distributed in the hope that it will be useful,
00016 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00018 ** GNU Lesser General Public License for more details.
00019 **
00020 ** You should have received a copy of the GNU Lesser General Public License
00021 ** along with this program in a file called COPYING; if not, write to
00022 ** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
00023 ** MA 02110-1301, USA.
00024 */
00025 
00026 /*
00027 ** Bug reports and questions can be sent to kde-pim@kde.org
00028 */
00029 
00030 
00031 #include <config.h>
00032 #include "options.h"
00033 #include "fakes.h"
00034 
00035 
00036 #include <stdlib.h>
00037 #include <assert.h>
00038 
00039 #include <qstringlist.h>
00040 
00041 #include "pilotAddress.h"
00042 
00043 
00044 #define MAXFIELDS 19
00045 
00046 PilotAddress::PilotAddress(PilotAddressInfo *info, PilotRecord *rec) :
00047     PilotRecordBase(rec),
00048     fAppInfo(*(info->info())),
00049     fAddressInfo()
00050 {
00051     FUNCTIONSETUPL(4);
00052     reset();
00053 
00054     if (rec)
00055     {
00056         pi_buffer_t b;
00057         b.data = (unsigned char *) rec->data();
00058         b.allocated = b.used = rec->size();
00059         unpack_Address(&fAddressInfo, &b, address_v1);
00060     }
00061     else
00062     {
00063         fAddressInfo.phoneLabel[0] = (int) eWork;
00064         fAddressInfo.phoneLabel[1] = (int) eHome;
00065         fAddressInfo.phoneLabel[2] = (int) eOther;
00066         fAddressInfo.phoneLabel[3] = (int) eMobile;
00067         fAddressInfo.phoneLabel[4] = (int) eEmail;
00068     }
00069 
00070     _loadMaps();
00071 }
00072 
00073 PilotAddress::PilotAddress(const PilotAddress & copyFrom) :
00074     PilotRecordBase(copyFrom),
00075     fAppInfo(copyFrom.fAppInfo),
00076     fAddressInfo()
00077 {
00078     FUNCTIONSETUPL(4);
00079     _copyAddressInfo(copyFrom.fAddressInfo);
00080 
00081     _loadMaps();
00082 }
00083 
00084 PilotAddress & PilotAddress::operator = (const PilotAddress & copyFrom)
00085 {
00086     FUNCTIONSETUPL(4);
00087     PilotRecordBase::operator = (copyFrom);
00088     _copyAddressInfo(copyFrom.fAddressInfo);
00089     return *this;
00090 }
00091 
00092 bool PilotAddress::operator==(const PilotAddress &compareTo)
00093 {
00094     FUNCTIONSETUPL(4);
00095     // TODO: call == of PilotAppCategory. I don't think this is necessary, but I'm not so sure...
00096 //  if (!(PilotAppCategory)(this)->operator==(compareTo) ) return false;
00097 
00098     // now compare all the fields stored in the fAddressInfo.entry array of char*[19]
00099     for (int i=0; i<MAXFIELDS; i++) {
00100         // if one is NULL, and the other non-empty, they are not equal for sure
00101         if ( !getFieldP(i) && compareTo.getFieldP(i)) return false;
00102         if ( getFieldP(i) && !compareTo.getFieldP(i)) return false;
00103         // test for getField(i)!=... to prevent strcmp or NULL strings!  None or both can be zero, but not a single one.
00104         if ( (getFieldP(i) != compareTo.getFieldP(i)) && ( strcmp(getFieldP(i), compareTo.getFieldP(i)) ) )  return false;
00105     }
00106     return true;
00107 }
00108 
00109 
00110 void PilotAddress::_copyAddressInfo(const struct Address &copyFrom)
00111 {
00112     FUNCTIONSETUPL(4);
00113     fAddressInfo.showPhone = copyFrom.showPhone;
00114 
00115     for (int labelLp = 0; labelLp < 5; labelLp++)
00116     {
00117         fAddressInfo.phoneLabel[labelLp] =
00118             copyFrom.phoneLabel[labelLp];
00119     }
00120 
00121     for (int entryLp = 0; entryLp < MAXFIELDS; entryLp++)
00122     {
00123         if (copyFrom.entry[entryLp])
00124             fAddressInfo.entry[entryLp] =
00125                 qstrdup(copyFrom.entry[entryLp]);
00126         else
00127             fAddressInfo.entry[entryLp] = 0L;
00128     }
00129 }
00130 
00131 
00132 PilotAddress::~PilotAddress()
00133 {
00134     FUNCTIONSETUPL(4);
00135     free_Address(&fAddressInfo);
00136 }
00137 
00138 QString PilotAddress::getTextRepresentation(bool richText)
00139 {
00140     QString text, tmp;
00141 
00142     QString par = richText?CSL1("<p>"):CSL1("");
00143     QString ps = richText?CSL1("</p>"):CSL1("\n");
00144     QString br = richText?CSL1("<br/>"):CSL1("\n");
00145 
00146     // title + name
00147     text += par;
00148     if (!getField(entryTitle).isEmpty())
00149     {
00150         text += rtExpand(getField(entryTitle), richText);
00151         text += CSL1(" ");
00152     }
00153 
00154     tmp = richText?CSL1("<b><big>%1%2%3</big></b>"):CSL1("%1%2%3");
00155     if (!getField(entryFirstname).isEmpty())
00156         tmp=rtExpand(tmp.arg(getField(entryFirstname)), richText).arg(CSL1(" "));
00157     else
00158         tmp=tmp.arg(CSL1(" ")).arg(CSL1(" "));
00159     tmp=tmp.arg(rtExpand(getField(entryLastname), richText));
00160     text += tmp;
00161     text += ps;
00162 
00163     // company
00164     if (!getField(entryCompany).isEmpty())
00165     {
00166         text += par;
00167         text += rtExpand(getField(entryCompany), richText);
00168         text += ps;
00169     }
00170 
00171     // phone numbers (+ labels)
00172     text += par;
00173     for (int i = entryPhone1; i <= entryPhone5; i++)
00174         if (!getField(i).isEmpty())
00175         {
00176             if (richText)
00177             {
00178                 if (getShownPhone() == i - entryPhone1)
00179                     tmp=CSL1("<small>%1: </small><b>%2</b>");
00180                 else
00181                     tmp=CSL1("<small>%1: </small>%2");
00182             }
00183             else
00184                 tmp=CSL1("%1: %2");
00185             tmp=tmp.arg(Pilot::fromPilot(
00186                 fAppInfo.phoneLabels[getPhoneLabelIndex(i-entryPhone1)]));
00187             tmp=tmp.arg(rtExpand(getField(i), richText));
00188             text += tmp;
00189             text += br;
00190         }
00191     text += ps;
00192 
00193     // address, city, state, country
00194     text += par;
00195     if (!getField(entryAddress).isEmpty())
00196     {
00197         text += rtExpand(getField(entryAddress), richText);
00198         text += br;
00199     }
00200     if (!getField(entryCity).isEmpty())
00201     {
00202         text += rtExpand(getField(entryCity), richText);
00203         text += CSL1(" ");
00204     }
00205     if (!getField(entryState).isEmpty())
00206     {
00207         text += rtExpand(getField(entryState), richText);
00208         text += CSL1(" ");
00209     }
00210     if (!getField(entryZip).isEmpty())
00211     {
00212         text += rtExpand(getField(entryZip), richText);
00213     }
00214     text += br;
00215     if (!getField(entryCountry).isEmpty())
00216     {
00217         text += rtExpand(getField(entryCountry), richText);
00218         text += br;
00219     }
00220     text += ps;
00221 
00222     // custom fields
00223     text += par;
00224     for (int i = entryCustom1; i <= entryCustom4; i++)
00225         if (!getField(i).isEmpty())
00226         {
00227             text += rtExpand(getField(i), richText);
00228             text += br;
00229         }
00230     text += ps;
00231 
00232     // category
00233     if (!getCategoryLabel().isEmpty())
00234     {
00235         text += par;
00236         text += rtExpand(getCategoryLabel(), richText);
00237         text += ps;
00238     }
00239 
00240     // note
00241     if (!getField(entryNote).isEmpty())
00242     {
00243         text += richText?CSL1("<hr/>"):CSL1("-----------------------------\n");
00244         text += par;
00245         text += rtExpand(getField(entryNote), richText);
00246         text += ps;
00247     }
00248 
00249     return text;
00250 }
00251 
00252 QString PilotAddress::getCategoryLabel() const
00253 {
00254     int cat(category());
00255     if (cat>0) return Pilot::fromPilot(fAppInfo.category.name[cat]);
00256     else return QString::null;
00257 }
00258 
00259 QStringList PilotAddress::getEmails() const
00260 {
00261     FUNCTIONSETUP;
00262     QStringList list;
00263     QString test;
00264 
00265     for (int i = entryPhone1; i <= entryPhone5; i++)
00266     {
00267         test = getField(i);
00268         if (!test.isEmpty())
00269         {
00270             int ind = getPhoneLabelIndex(i-entryPhone1);
00271             if (ind == eEmail)
00272             {
00273                 list.append(test);
00274             }
00275         }
00276     }
00277 
00278 #ifdef DEBUG
00279     DEBUGLIBRARY << fname << ": returning: ["
00280                 << list.size() << "] e-mail addresses." << endl;
00281 #endif
00282     return list;
00283 }
00284 
00285 KABC::PhoneNumber::List PilotAddress::getPhoneNumbers() const
00286 {
00287     FUNCTIONSETUP;
00288 
00289     KABC::PhoneNumber::List list;
00290     QString test;
00291 
00292     int shownPhone = getShownPhone() + entryPhone1;
00293 #ifdef DEBUG
00294     DEBUGLIBRARY << fname << ": preferred pilot index is: ["
00295                 << shownPhone << "], preferred phone number is: ["
00296                 << getField(shownPhone) << "]" << endl;
00297 #endif
00298 
00299     for (int i = entryPhone1; i <= entryPhone5; i++)
00300     {
00301         test = getField(i);
00302         // only look at this if the field is populated
00303         if (!test.isEmpty())
00304         {
00305             int ind = getPhoneLabelIndex(i-entryPhone1);
00306             // we only care about non-email types
00307             if (ind != eEmail)
00308             {
00309                 int phoneType = pilotToPhoneMap[ind];
00310 
00311                 // only populate a PhoneNumber if we have a corresponding type
00312                 if (phoneType >=0)
00313                 {
00314                     // if this is the preferred phone number, set it as such
00315                     if (shownPhone == i) {
00316                         phoneType |= KABC::PhoneNumber::Pref;
00317 #ifdef DEBUG
00318     DEBUGLIBRARY << fname << ": found preferred pilot index: ["
00319                 << i << "], text: [" << test << "]" << endl;
00320 #endif
00321                     }
00322                     KABC::PhoneNumber ph(test, phoneType);
00323                     list.append(ph);
00324                 } else {
00325 #ifdef DEBUG
00326     DEBUGLIBRARY << fname << ": whoopsie.  pilot phone number: ["
00327                 << test << "], index: [" << i << "], type: ["
00328                 << ind << "], has no corresponding PhoneNumber type." << endl;
00329 #endif
00330                 }
00331             }
00332         }
00333     }
00334 #ifdef DEBUG
00335     DEBUGLIBRARY << fname << ": returning: ["
00336                 << list.size() << "] phone numbers" << endl;
00337 #endif
00338     return list;
00339 }
00340 
00341 void PilotAddress::setPhoneNumbers(KABC::PhoneNumber::List list)
00342 {
00343     FUNCTIONSETUP;
00344     QString test;
00345 
00346     // clear all phone numbers (not e-mails) first
00347     for (int i = entryPhone1; i <= entryPhone5; i++)
00348     {
00349         test = getField(i);
00350         if (!test.isEmpty())
00351         {
00352             int ind = getPhoneLabelIndex(i-entryPhone1);
00353             if (ind != eEmail)
00354             {
00355                 setField(i, "");
00356             }
00357         }
00358     }
00359 
00360     // now iterate through the list and for each PhoneNumber in the list,
00361     // iterate through our phone types using our map and set the first one
00362     // we find as the type of address for the Pilot
00363     QMap<int, int>::ConstIterator it;
00364 
00365     for(KABC::PhoneNumber::List::Iterator listIter = list.begin();
00366            listIter != list.end(); ++listIter)
00367     {
00368         KABC::PhoneNumber phone = *listIter;
00369 
00370         int category = eHome;
00371 
00372         for ( it = pilotToPhoneMap.begin(); it != pilotToPhoneMap.end(); ++it )
00373         {
00374             int pilotKey = it.key();
00375             int phoneKey = it.data();
00376             if ( phone.type() & phoneKey)
00377             {
00378 #ifdef DEBUG
00379     DEBUGLIBRARY << fname << ": found pilot type: ["
00380                 << pilotKey << "] ("
00381                 << fAppInfo.phoneLabels[pilotKey]
00382                 << ") for PhoneNumber: ["
00383                 << phone.number() << "]" << endl;
00384 #endif
00385                 category = pilotKey;
00386                 break;
00387             }
00388         }
00389         int fieldSlot = setPhoneField(static_cast<PilotAddress::EPhoneType>(category),
00390                       phone.number(), true, false);
00391 
00392         // if this is the preferred phone number, then set it as such
00393         if (phone.type() & KABC::PhoneNumber::Pref) {
00394 #ifdef DEBUG
00395     DEBUGLIBRARY << fname << ": found preferred PhoneNumber. "
00396                 << "setting showPhone to index: ["
00397                 << fieldSlot << "], PhoneNumber: ["
00398                 << phone.number() << "]" << endl;
00399 #endif
00400             fAddressInfo.showPhone = fieldSlot - entryPhone1;
00401         }
00402     }
00403 
00404 #ifdef DEBUG
00405     DEBUGLIBRARY << fname << ": Pilot's showPhone now: ["
00406                 << fAddressInfo.showPhone << "]." << endl;
00407 #endif
00408 
00409     // after setting the numbers, make sure that something sensible is set as the
00410     // shownPhone on the Pilot if nothing is yet...
00411     QString pref = getField(fAddressInfo.showPhone + entryPhone1);
00412     if (fAddressInfo.showPhone < 0 || fAddressInfo.showPhone > 4 || pref.isEmpty()) {
00413 #ifdef DEBUG
00414     DEBUGLIBRARY << fname << ": Pilot's showPhone: ["
00415                 << fAddressInfo.showPhone
00416                 << "] not properly set to a default. trying to set a sensible one."
00417                 << endl;
00418 #endif
00419         for (int i = entryPhone1; i <= entryPhone5; i++)
00420         {
00421             pref = getField(i);
00422             if (!pref.isEmpty())
00423             {
00424                 fAddressInfo.showPhone = i - entryPhone1;
00425                 break;
00426             }
00427         }
00428     }
00429 #ifdef DEBUG
00430     DEBUGLIBRARY << fname << ": Pilot's showPhone now: ["
00431                 << fAddressInfo.showPhone << "], and that's final." << endl;
00432 #endif
00433 }
00434 
00435 void PilotAddress::setEmails(QStringList list)
00436 {
00437     QString test;
00438 
00439     // clear all e-mails first
00440     for (int i = entryPhone1; i <= entryPhone5; i++)
00441     {
00442         test = getField(i);
00443         if (!test.isEmpty())
00444         {
00445             int ind = getPhoneLabelIndex(i-entryPhone1);
00446             if (ind == eEmail)
00447             {
00448                 setField(i, "");
00449             }
00450         }
00451     }
00452 
00453     for(QStringList::Iterator listIter = list.begin();
00454            listIter != list.end(); ++listIter)
00455     {
00456         QString email = *listIter;
00457         setPhoneField(eEmail, email, true, false);
00458     }
00459 }
00460 
00467 void PilotAddress::_loadMaps()
00468 {
00480     pilotToPhoneMap.clear();
00481     // do this one first, since it's an oddball (PhoneNumber has Fax | Home and
00482     // Fax | Work, so either way, we want to find Fax before we find Home.  =;)
00483     pilotToPhoneMap.insert(eFax, KABC::PhoneNumber::Fax);
00484 
00485     pilotToPhoneMap.insert(eWork, KABC::PhoneNumber::Work);
00486     pilotToPhoneMap.insert(eHome, KABC::PhoneNumber::Home);
00487     pilotToPhoneMap.insert(ePager, KABC::PhoneNumber::Pager);
00488     pilotToPhoneMap.insert(eMobile, KABC::PhoneNumber::Cell);
00489 
00490     // eMain doesn't cleanly map to anything in PhoneNumber, so we'll
00491     // pretend that Palm really meant to say "Home"
00492     pilotToPhoneMap.insert(eMain, KABC::PhoneNumber::Home);
00493 
00494     // okay, more ugliness.  Addressee maps Other separately, so it will be set
00495     // individually coming in and going out.  We're not counting this as a PhoneNumber.
00496     // pilotToPhoneMap.insert(eOther, KABC::PhoneNumber::Home);
00497 
00498 
00499 }
00500 
00501 QString PilotAddress::getField(int field) const
00502 {
00503     return Pilot::fromPilot(fAddressInfo.entry[field]);
00504 }
00505 
00506 int PilotAddress::_getNextEmptyPhoneSlot() const
00507 {
00508     FUNCTIONSETUPL(4);
00509     for (int phoneSlot = entryPhone1; phoneSlot <= entryPhone5;
00510         phoneSlot++)
00511     {
00512         QString phoneField = getField(phoneSlot);
00513 
00514         if (phoneField.isEmpty())
00515             return phoneSlot;
00516     }
00517     return entryCustom4;
00518 }
00519 
00520 int PilotAddress::setPhoneField(EPhoneType type, const QString &field,
00521     bool overflowCustom, bool overwriteExisting)
00522 {
00523     FUNCTIONSETUPL(4);
00524     // first look to see if the type is already assigned to a fieldSlot
00525     //QString typeStr(_typeToStr(type));
00526     //int appPhoneLabelNum = _getAppPhoneLabelNum(typeStr);
00527     int appPhoneLabelNum = (int) type;
00528     QString fieldStr(field);
00529     int fieldSlot = (overwriteExisting) ? _findPhoneFieldSlot(appPhoneLabelNum) : -1;
00530 
00531     if (fieldSlot == -1)
00532         fieldSlot = _getNextEmptyPhoneSlot();
00533 
00534     // store the overflow phone
00535     if (fieldSlot == entryCustom4)
00536     {
00537         if (!fieldStr.isEmpty() && overflowCustom)
00538         {
00539             QString custom4Field = getField(entryCustom4);
00540             QString typeStr(
00541                 Pilot::fromPilot(fAppInfo.phoneLabels[appPhoneLabelNum]));
00542 
00543             custom4Field += typeStr + CSL1(" ") + fieldStr;
00544             setField(entryCustom4, custom4Field);
00545         }
00546     }
00547     else            // phone field 1 - 5; straight forward storage
00548     {
00549         setField(fieldSlot, field);
00550         int labelIndex = fieldSlot - entryPhone1;
00551 
00552         fAddressInfo.phoneLabel[labelIndex] = appPhoneLabelNum;
00553     }
00554     return fieldSlot;
00555 }
00556 
00557 int PilotAddress::_findPhoneFieldSlot(int appTypeNum) const
00558 {
00559     FUNCTIONSETUPL(4);
00560     for (int index = 0; index < 5; index++)
00561     {
00562         if (fAddressInfo.phoneLabel[index] == appTypeNum)
00563             return index + entryPhone1;
00564     }
00565 
00566     return -1;
00567 }
00568 
00569 QString PilotAddress::getPhoneField(EPhoneType type, bool checkCustom4) const
00570 {
00571     FUNCTIONSETUPL(4);
00572     // given the type, need to find which slot is associated with it
00573     //QString typeToStr(_typeToStr(type));
00574     //int appTypeNum = _getAppPhoneLabelNum(typeToStr);
00575     int appTypeNum = (int) type;
00576 
00577     int fieldSlot = _findPhoneFieldSlot(appTypeNum);
00578 
00579     if (fieldSlot != -1)
00580         return getField(fieldSlot);
00581 
00582     // look through custom 4 for the field
00583     if (!checkCustom4)
00584         return QString::null;
00585 
00586     // look for the phone type str
00587     QString typeToStr(Pilot::fromPilot(fAppInfo.phoneLabels[appTypeNum]));
00588     QString customField(getField(entryCustom4));
00589     int foundField = customField.find(typeToStr);
00590 
00591     if (foundField == -1)
00592         return QString::null;
00593 
00594     // parse out the next token
00595     int startPos = foundField + typeToStr.length() + 1;
00596     int endPos = customField.find(' ', startPos);
00597 
00598     if (endPos == -1)
00599         endPos = customField.length();
00600     QString field = customField.mid(startPos, endPos);
00601 
00602     field = field.simplifyWhiteSpace();
00603 
00604     // return the token
00605     return field;
00606 }
00607 
00608 
00609 int PilotAddress::_getAppPhoneLabelNum(const QString & phoneType) const
00610 {
00611     FUNCTIONSETUPL(4);
00612     for (int index = 0; index < 8; index++)
00613     {
00614         if (phoneType == Pilot::fromPilot(fAppInfo.phoneLabels[index]))
00615             return index;
00616     }
00617 
00618     return -1;
00619 }
00620 
00621 void PilotAddress::setShownPhone(EPhoneType type)
00622 {
00623     FUNCTIONSETUPL(4);
00624     int appPhoneLabelNum = (int) type;
00625     int fieldSlot = _findPhoneFieldSlot(appPhoneLabelNum);
00626 
00627     if (fieldSlot == -1)
00628     {
00629         if (type != eHome)
00630         {
00631             setShownPhone(eHome);
00632             return;
00633         }
00634         fieldSlot = entryPhone1;
00635     }
00636     fAddressInfo.showPhone = fieldSlot - entryPhone1;
00637 }
00638 
00639 void PilotAddress::setField(int field, const QString &text)
00640 {
00641     FUNCTIONSETUPL(4);
00642     // This will have either been created with unpack_Address, and/or will
00643     // be released with free_Address, so use malloc/free here:
00644     if (fAddressInfo.entry[field])
00645     {
00646         free(fAddressInfo.entry[field]);
00647         fAddressInfo.entry[field]=0L;
00648     }
00649     if (!text.isEmpty())
00650     {
00651         fAddressInfo.entry[field] = (char *) malloc(text.length() + 1);
00652         strlcpy(fAddressInfo.entry[field], Pilot::toPilot(text), text.length() + 1);
00653     }
00654     else
00655     {
00656         fAddressInfo.entry[field] = 0L;
00657     }
00658 }
00659 
00660 PilotRecord *PilotAddress::pack() const
00661 {
00662     FUNCTIONSETUPL(4);
00663     int i;
00664 
00665     pi_buffer_t *b = pi_buffer_new( sizeof(fAddressInfo) );
00666     i = pack_Address(const_cast<Address_t *>(&fAddressInfo), b, address_v1);
00667     if (i<0)
00668     {
00669         return 0L;
00670     }
00671     // pack_Address sets b->used
00672     return new PilotRecord( b, this );
00673 }
KDE Home | KDE Accessibility Home | Description of Access Keys