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