kpilot

pilotAddress.cc

Go to the documentation of this file.
00001 /* KPilot
00002 **
00003 ** Copyright (C) 1998-2001 by Dan Pilone
00004 ** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
00005 ** Copyright (C) 2007 by Adriaan de Groot <groot@kde.org>
00006 **
00007 ** This is a C++ wrapper for the pilot's address database structures.
00008 */
00009 
00010 /*
00011 ** This program is free software; you can redistribute it and/or modify
00012 ** it under the terms of the GNU Lesser General Public License as published by
00013 ** the Free Software Foundation; either version 2.1 of the License, or
00014 ** (at your option) any later version.
00015 **
00016 ** This program is distributed in the hope that it will be useful,
00017 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
00018 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00019 ** GNU Lesser General Public License for more details.
00020 **
00021 ** You should have received a copy of the GNU Lesser General Public License
00022 ** along with this program in a file called COPYING; if not, write to
00023 ** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
00024 ** MA 02110-1301, USA.
00025 */
00026 
00027 /*
00028 ** Bug reports and questions can be sent to kde-pim@kde.org
00029 */
00030 
00031 
00032 #include "options.h"
00033 
00034 
00035 #include <stdlib.h>
00036 #include <assert.h>
00037 
00038 #include <qnamespace.h>
00039 #include <qstringlist.h>
00040 
00041 #include "pilotAddress.h"
00042 
00043 static const char *default_address_category_names[] = {
00044     "Unfiled",
00045     "Business",
00046     "Personal",
00047     "Quicklist",
00048     0L
00049 } ;
00050 
00051 static const char *default_address_field_labels[] = {
00052     "Last name",
00053     "First name",
00054     "Company",
00055     "Work",
00056     "Home",
00057     "Fax",
00058     "Other",
00059     "E-mail",
00060     "Addr(W)",
00061     "City",
00062     "State",
00063     "Zip Code",
00064     "Country",
00065     "Title",
00066     "Custom 1",
00067     "Custom 2",
00068     "Custom 3",
00069     "Custom 4",
00070     "Note",
00071     0L
00072 } ;
00073 
00074 void PilotAddressInfo::resetToDefault()
00075 {
00076     FUNCTIONSETUP;
00077     // Reset to all 0s
00078     memset(&fInfo,0,sizeof(fInfo));
00079     // Fill up default categories
00080     for (unsigned int i=0; (i<4) && default_address_category_names[i]; ++i)
00081     {
00082         strncpy(fInfo.category.name[i],default_address_category_names[i],sizeof(fInfo.category.name[0]));
00083     }
00084     // Weird hack, looks like there's an extra copy of Unfiled
00085     strncpy(fInfo.category.name[15],default_address_category_names[0],sizeof(fInfo.category.name[0]));
00086 
00087     // And fill up the default labels.
00088     for (unsigned int i=0; (i<19) && default_address_field_labels[i]; ++i)
00089     {
00090         strncpy(fInfo.labels[i],default_address_field_labels[i],sizeof(fInfo.labels[0]));
00091     }
00092 }
00093 
00094 QString PilotAddressInfo::phoneLabel(EPhoneType i) const
00095 {
00096     if (i<=eMobile)
00097     {
00098         return Pilot::fromPilot(info()->phoneLabels[i]);
00099     }
00100     else
00101     {
00102         return QString();
00103     }
00104 }
00105 
00106 PhoneSlot::PhoneSlot( const int v )
00107 {
00108     i = entryPhone1;
00109     operator=(v);
00110 }
00111 
00112 const PhoneSlot &PhoneSlot::operator=( const int &v )
00113 {
00114     if ( (entryPhone1 <= v) && (v <= entryPhone5) )
00115     {
00116         i = v;
00117     }
00118     else
00119     {
00120         i = invalid;
00121     }
00122     return *this;
00123 }
00124 
00125 const PhoneSlot &PhoneSlot::operator++()
00126 {
00127     if ( (i!=invalid) && (i<entryPhone5) )
00128     {
00129         ++i;
00130     }
00131     else
00132     {
00133         i = invalid;
00134     }
00135     return *this;
00136 }
00137 
00138 /* static */ const PhoneSlot PhoneSlot::begin()
00139 {
00140     return PhoneSlot( entryPhone1 );
00141 }
00142 
00143 /* static */ const PhoneSlot PhoneSlot::end()
00144 {
00145     return PhoneSlot( invalid );
00146 }
00147 
00148 unsigned int PhoneSlot::toOffset() const
00149 {
00150     if ( isValid() )
00151     {
00152         return i-entryPhone1;
00153     }
00154     else
00155     {
00156         return 0;
00157     }
00158 }
00159 
00160 unsigned int PhoneSlot::toField() const
00161 {
00162     if ( isValid() )
00163     {
00164         return i;
00165     }
00166     else
00167     {
00168         return entryPhone1;
00169     }
00170 }
00171 
00172 PhoneSlot::operator QString() const
00173 {
00174     return QString("%1,%2").arg(toOffset()).arg(toField());
00175 }
00176 
00177 #define MAXFIELDS 19
00178 
00179 PilotAddress::PilotAddress(PilotRecord *rec) :
00180     PilotRecordBase(rec),
00181     fAddressInfo()
00182 {
00183     FUNCTIONSETUPL(4);
00184     memset(&fAddressInfo,0,sizeof(fAddressInfo));
00185 
00186     if (rec)
00187     {
00188         pi_buffer_t b;
00189         b.data = (unsigned char *) rec->data();
00190         b.allocated = b.used = rec->size();
00191         unpack_Address(&fAddressInfo, &b, address_v1);
00192     }
00193     else
00194     {
00195         fAddressInfo.phoneLabel[0] = (int) PilotAddressInfo::eWork;
00196         fAddressInfo.phoneLabel[1] = (int) PilotAddressInfo::eHome;
00197         fAddressInfo.phoneLabel[2] = (int) PilotAddressInfo::eOther;
00198         fAddressInfo.phoneLabel[3] = (int) PilotAddressInfo::eMobile;
00199         fAddressInfo.phoneLabel[4] = (int) PilotAddressInfo::eEmail;
00200     }
00201 }
00202 
00203 PilotAddress::PilotAddress(const PilotAddress & copyFrom) :
00204     PilotRecordBase(copyFrom),
00205     fAddressInfo()
00206 {
00207     FUNCTIONSETUPL(4);
00208     _copyAddressInfo(copyFrom.fAddressInfo);
00209 }
00210 
00211 PilotAddress & PilotAddress::operator = (const PilotAddress & copyFrom)
00212 {
00213     FUNCTIONSETUPL(4);
00214     PilotRecordBase::operator = (copyFrom);
00215     _copyAddressInfo(copyFrom.fAddressInfo);
00216     return *this;
00217 }
00218 
00219 bool PilotAddress::operator==(const PilotAddress &compareTo)
00220 {
00221     FUNCTIONSETUPL(4);
00222 
00223     // now compare all the fields stored in the fAddressInfo.entry array of char*[19]
00224     for (int i=0; i<MAXFIELDS; i++) {
00225         // if one is NULL, and the other non-empty, they are not equal for sure
00226         if ( !getFieldP(i) && compareTo.getFieldP(i))
00227         {
00228             return false;
00229         }
00230         if ( getFieldP(i) && !compareTo.getFieldP(i))
00231         {
00232             return false;
00233         }
00234 
00235         // test for getField(i)!=... to prevent strcmp or NULL strings!  None or both can be zero, but not a single one.
00236         if ( (getFieldP(i) != compareTo.getFieldP(i)) && ( strcmp(getFieldP(i), compareTo.getFieldP(i)) ) )
00237         {
00238             return false;
00239         }
00240     }
00241     return true;
00242 }
00243 
00244 
00245 void PilotAddress::_copyAddressInfo(const struct Address &copyFrom)
00246 {
00247     FUNCTIONSETUPL(4);
00248     fAddressInfo.showPhone = copyFrom.showPhone;
00249 
00250     for (int labelLp = 0; labelLp < 5; labelLp++)
00251     {
00252         fAddressInfo.phoneLabel[labelLp] =
00253             copyFrom.phoneLabel[labelLp];
00254     }
00255 
00256     for (unsigned int i = 0; i< MAXFIELDS; ++i)
00257     {
00258         if (copyFrom.entry[i])
00259         {
00260             fAddressInfo.entry[i] = qstrdup(copyFrom.entry[i]);
00261         }
00262         else
00263         {
00264             fAddressInfo.entry[i] = 0L;
00265         }
00266     }
00267 }
00268 
00269 
00270 PilotAddress::~PilotAddress()
00271 {
00272     FUNCTIONSETUPL(4);
00273     free_Address(&fAddressInfo);
00274 }
00275 
00276 QString PilotAddress::getTextRepresentation(const PilotAddressInfo *info, Qt::TextFormat richText) const
00277 {
00278     QString text, tmp;
00279 
00280     QString par = (richText==Qt::RichText) ?CSL1("<p>"): QString();
00281     QString ps = (richText==Qt::RichText) ?CSL1("</p>"):CSL1("\n");
00282     QString br = (richText==Qt::RichText) ?CSL1("<br/>"):CSL1("\n");
00283 
00284     // title + name
00285     text += par;
00286     if (!getField(entryTitle).isEmpty())
00287     {
00288         text += rtExpand(getField(entryTitle), richText);
00289         text += CSL1(" ");
00290     }
00291 
00292     tmp = richText ? CSL1("<b><big>%1 %2</big></b>") : CSL1("%1 %2");
00293     QString firstName = getField(entryFirstname);
00294     if (firstName.isEmpty())
00295     {
00296         // So replace placeholder for first name (%1) with empty
00297         tmp = tmp.arg(QString());
00298     }
00299     else
00300     {
00301         tmp = tmp.arg(rtExpand(firstName,richText));
00302     }
00303     tmp=tmp.arg(rtExpand(getField(entryLastname), richText));
00304     text += tmp;
00305     text += ps;
00306 
00307     // company
00308     if (!getField(entryCompany).isEmpty())
00309     {
00310         text += par;
00311         text += rtExpand(getField(entryCompany), richText);
00312         text += ps;
00313     }
00314 
00315     // phone numbers (+ labels)
00316     text += par;
00317     for ( PhoneSlot i = PhoneSlot::begin(); i.isValid(); ++i )
00318     {
00319         if (!getField(i.toField()).isEmpty())
00320         {
00321             if (richText)
00322             {
00323                 if (getShownPhone() == i)
00324                 {
00325                     tmp=CSL1("<small>%1: </small><b>%2</b>");
00326                 }
00327                 else
00328                 {
00329                     tmp=CSL1("<small>%1: </small>%2");
00330                 }
00331             }
00332             else
00333             {
00334                 tmp=CSL1("%1: %2");
00335             }
00336             if (info)
00337             {
00338                 tmp=tmp.arg(info->phoneLabel( getPhoneType( i ) ));
00339             }
00340             else
00341             {
00342                 tmp=tmp.arg(CSL1("Contact: "));
00343             }
00344             tmp=tmp.arg(rtExpand(getField(i.toField()), richText));
00345             text += tmp;
00346             text += br;
00347         }
00348     }
00349     text += ps;
00350 
00351     // address, city, state, country
00352     text += par;
00353     if (!getField(entryAddress).isEmpty())
00354     {
00355         text += rtExpand(getField(entryAddress), richText);
00356         text += br;
00357     }
00358     if (!getField(entryCity).isEmpty())
00359     {
00360         text += rtExpand(getField(entryCity), richText);
00361         text += CSL1(" ");
00362     }
00363     if (!getField(entryState).isEmpty())
00364     {
00365         text += rtExpand(getField(entryState), richText);
00366         text += CSL1(" ");
00367     }
00368     if (!getField(entryZip).isEmpty())
00369     {
00370         text += rtExpand(getField(entryZip), richText);
00371     }
00372     text += br;
00373     if (!getField(entryCountry).isEmpty())
00374     {
00375         text += rtExpand(getField(entryCountry), richText);
00376         text += br;
00377     }
00378     text += ps;
00379 
00380     // custom fields
00381     text += par;
00382     for (int i = entryCustom1; i <= entryCustom4; i++)
00383     {
00384         if (!getField(i).isEmpty())
00385         {
00386             text += rtExpand(getField(i), richText);
00387             text += br;
00388         }
00389     }
00390     text += ps;
00391 
00392     // category
00393     if (info)
00394     {
00395         QString categoryName = info->categoryName( category() );
00396         if (!categoryName.isEmpty())
00397         {
00398             text += par;
00399             text += rtExpand(categoryName, richText);
00400             text += ps;
00401         }
00402     }
00403 
00404     // note
00405     if (!getField(entryNote).isEmpty())
00406     {
00407         text += richText?CSL1("<hr/>"):CSL1("-----------------------------\n");
00408         text += par;
00409         text += rtExpand(getField(entryNote), richText);
00410         text += ps;
00411     }
00412 
00413     return text;
00414 }
00415 
00416 QStringList PilotAddress::getEmails() const
00417 {
00418     QStringList list;
00419 
00420     for ( PhoneSlot i = PhoneSlot::begin(); i.isValid(); ++i)
00421     {
00422         PilotAddressInfo::EPhoneType t = getPhoneType( i );
00423         if ( t == PilotAddressInfo::eEmail )
00424         {
00425             QString s = getField(i.toField());
00426             if (!s.isEmpty())
00427             {
00428                 list.append(s);
00429             }
00430         }
00431     }
00432 
00433     return list;
00434 }
00435 
00436 void PilotAddress::setEmails(const QStringList &list)
00437 {
00438     FUNCTIONSETUPL(4);
00439     QString test;
00440 
00441     // clear all e-mails first
00442     for ( PhoneSlot i = PhoneSlot::begin(); i.isValid(); ++i )
00443     {
00444         PilotAddressInfo::EPhoneType t = getPhoneType( i );
00445         if (t == PilotAddressInfo::eEmail)
00446         {
00447             setField(i.toField(), QString() );
00448         }
00449     }
00450 
00451     for(QStringList::ConstIterator listIter = list.begin();
00452            listIter != list.end(); ++listIter)
00453     {
00454         QString email = *listIter;
00455         if (!setPhoneField(PilotAddressInfo::eEmail, email, NoFlags).isValid())
00456         {
00457             WARNINGKPILOT << "Email accounts overflowed, silently dropped." << endl;
00458         }
00459     }
00460 }
00461 
00462 QString PilotAddress::getField(int field) const
00463 {
00464     if ( (entryLastname <= field) && (field <= entryNote) )
00465     {
00466         return Pilot::fromPilot(fAddressInfo.entry[field]);
00467     }
00468     else
00469     {
00470         return QString();
00471     }
00472 }
00473 
00474 PhoneSlot PilotAddress::_getNextEmptyPhoneSlot() const
00475 {
00476     FUNCTIONSETUPL(4);
00477     for (PhoneSlot i = PhoneSlot::begin(); i.isValid(); ++i)
00478     {
00479         const char *phoneField = getFieldP(i.toField());
00480 
00481         if (!phoneField || !phoneField[0])
00482         {
00483             return i;
00484         }
00485     }
00486     return PhoneSlot();
00487 }
00488 
00489 PhoneSlot PilotAddress::setPhoneField(PilotAddressInfo::EPhoneType type,
00490     const QString &field,
00491     PhoneHandlingFlags flags)
00492 {
00493     FUNCTIONSETUPL(4);
00494 
00495     const bool overwriteExisting = (flags == Replace);
00496     PhoneSlot fieldSlot;
00497     if (overwriteExisting)
00498     {
00499         fieldSlot = _findPhoneFieldSlot(type);
00500     }
00501 
00502     if ( !fieldSlot.isValid() )
00503     {
00504         fieldSlot = _getNextEmptyPhoneSlot();
00505     }
00506 
00507     // store the overflow phone
00508     if ( !fieldSlot.isValid() )
00509     {
00510         DEBUGKPILOT << fname << ": Phone would overflow." << endl;
00511     }
00512     else            // phone field 1 - 5; straight forward storage
00513     {
00514         setField(fieldSlot.toField(), field);
00515         fAddressInfo.phoneLabel[fieldSlot.toOffset()] = (int) type;
00516     }
00517     return fieldSlot;
00518 }
00519 
00520 PhoneSlot PilotAddress::_findPhoneFieldSlot(PilotAddressInfo::EPhoneType t) const
00521 {
00522     FUNCTIONSETUPL(4);
00523     for ( PhoneSlot i = PhoneSlot::begin(); i.isValid(); ++i )
00524     {
00525         if ( getPhoneType(i) == t )
00526         {
00527             return i;
00528         }
00529     }
00530 
00531     return PhoneSlot();
00532 }
00533 
00534 QString PilotAddress::getPhoneField(PilotAddressInfo::EPhoneType type) const
00535 {
00536     FUNCTIONSETUPL(4);
00537     PhoneSlot fieldSlot = _findPhoneFieldSlot(type);
00538 
00539     if (fieldSlot.isValid())
00540     {
00541         return getField(fieldSlot.toField());
00542     }
00543 
00544     return QString();
00545 }
00546 
00547 PhoneSlot PilotAddress::getShownPhone() const
00548 {
00549     // The slot is stored as an offset
00550     return PhoneSlot(entryPhone1 + fAddressInfo.showPhone);
00551 }
00552 
00553 const PhoneSlot &PilotAddress::setShownPhone( const PhoneSlot &v )
00554 {
00555     FUNCTIONSETUPL(4);
00556     if (v.isValid())
00557     {
00558         fAddressInfo.showPhone = v.toOffset();
00559     }
00560     return v;
00561 }
00562 
00563 PhoneSlot PilotAddress::setShownPhone(PilotAddressInfo::EPhoneType type)
00564 {
00565     FUNCTIONSETUPL(4);
00566     PhoneSlot fieldSlot = _findPhoneFieldSlot(type);
00567 
00568     // Did we find a slot with the requested type?
00569     if (!fieldSlot.isValid())
00570     {
00571         // No, so look for first non-empty phone slot
00572         for ( fieldSlot = PhoneSlot::begin(); fieldSlot.isValid(); ++fieldSlot )
00573         {
00574             const char *p = getFieldP(fieldSlot.toField());
00575             if (p && p[0])
00576             {
00577                 break;
00578             }
00579         }
00580         // If all of them are empty, then use first slot instead
00581         if (!fieldSlot.isValid())
00582         {
00583             fieldSlot = PhoneSlot::begin();
00584         }
00585     }
00586     setShownPhone(fieldSlot);
00587     return fieldSlot;
00588 }
00589 
00590 PilotAddressInfo::EPhoneType PilotAddress::getPhoneType( const PhoneSlot &field ) const
00591 {
00592     if ( field.isValid() )
00593     {
00594         return (PilotAddressInfo::EPhoneType) fAddressInfo.phoneLabel[field.toOffset()];
00595     }
00596     else
00597     {
00598         return PilotAddressInfo::eNone;
00599     }
00600 }
00601 
00602 void PilotAddress::setField(int field, const QString &text)
00603 {
00604     FUNCTIONSETUPL(4);
00605     // This will have either been created with unpack_Address, and/or will
00606     // be released with free_Address, so use malloc/free here:
00607     if (fAddressInfo.entry[field])
00608     {
00609         free(fAddressInfo.entry[field]);
00610         fAddressInfo.entry[field]=0L;
00611     }
00612     if (!text.isEmpty())
00613     {
00614         fAddressInfo.entry[field] = (char *) malloc(text.length() + 1);
00615         Pilot::toPilot(text, fAddressInfo.entry[field], text.length()+1);
00616     }
00617     else
00618     {
00619         fAddressInfo.entry[field] = 0L;
00620     }
00621 }
00622 
00623 PilotRecord *PilotAddress::pack() const
00624 {
00625     FUNCTIONSETUPL(4);
00626     int i;
00627 
00628     pi_buffer_t *b = pi_buffer_new( sizeof(fAddressInfo) );
00629     i = pack_Address(const_cast<Address_t *>(&fAddressInfo), b, address_v1);
00630     if (i<0)
00631     {
00632         return 0L;
00633     }
00634     // pack_Address sets b->used
00635     return new PilotRecord( b, this );
00636 }