You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
637 lines
13 KiB
637 lines
13 KiB
/* KPilot
|
|
**
|
|
** Copyright (C) 1998-2001 by Dan Pilone
|
|
** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
|
|
** Copyright (C) 2007 by Adriaan de Groot <groot@kde.org>
|
|
**
|
|
** This is a C++ wrapper for the pilot's address database structures.
|
|
*/
|
|
|
|
/*
|
|
** This program is free software; you can redistribute it and/or modify
|
|
** it under the terms of the GNU Lesser General Public License as published by
|
|
** the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
|
|
**
|
|
** You should have received a copy of the GNU Lesser 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 kde-pim@kde.org
|
|
*/
|
|
|
|
|
|
#include "options.h"
|
|
|
|
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
|
|
#include <tqnamespace.h>
|
|
#include <tqstringlist.h>
|
|
|
|
#include "pilotAddress.h"
|
|
|
|
static const char *default_address_category_names[] = {
|
|
"Unfiled",
|
|
"Business",
|
|
"Personal",
|
|
"Quicklist",
|
|
0L
|
|
} ;
|
|
|
|
static const char *default_address_field_labels[] = {
|
|
"Last name",
|
|
"First name",
|
|
"Company",
|
|
"Work",
|
|
"Home",
|
|
"Fax",
|
|
"Other",
|
|
"E-mail",
|
|
"Addr(W)",
|
|
"City",
|
|
"State",
|
|
"Zip Code",
|
|
"Country",
|
|
"Title",
|
|
"Custom 1",
|
|
"Custom 2",
|
|
"Custom 3",
|
|
"Custom 4",
|
|
"Note",
|
|
0L
|
|
} ;
|
|
|
|
void PilotAddressInfo::resetToDefault()
|
|
{
|
|
FUNCTIONSETUP;
|
|
// Reset to all 0s
|
|
memset(&fInfo,0,sizeof(fInfo));
|
|
// Fill up default categories
|
|
for (unsigned int i=0; (i<4) && default_address_category_names[i]; ++i)
|
|
{
|
|
strncpy(fInfo.category.name[i],default_address_category_names[i],sizeof(fInfo.category.name[0]));
|
|
}
|
|
// Weird hack, looks like there's an extra copy of Unfiled
|
|
strncpy(fInfo.category.name[15],default_address_category_names[0],sizeof(fInfo.category.name[0]));
|
|
|
|
// And fill up the default labels.
|
|
for (unsigned int i=0; (i<19) && default_address_field_labels[i]; ++i)
|
|
{
|
|
strncpy(fInfo.labels[i],default_address_field_labels[i],sizeof(fInfo.labels[0]));
|
|
}
|
|
}
|
|
|
|
TQString PilotAddressInfo::phoneLabel(EPhoneType i) const
|
|
{
|
|
if (i<=eMobile)
|
|
{
|
|
return Pilot::fromPilot(info()->phoneLabels[i]);
|
|
}
|
|
else
|
|
{
|
|
return TQString();
|
|
}
|
|
}
|
|
|
|
PhoneSlot::PhoneSlot( const int v )
|
|
{
|
|
i = entryPhone1;
|
|
operator=(v);
|
|
}
|
|
|
|
const PhoneSlot &PhoneSlot::operator=( const int &v )
|
|
{
|
|
if ( (entryPhone1 <= v) && (v <= entryPhone5) )
|
|
{
|
|
i = v;
|
|
}
|
|
else
|
|
{
|
|
i = invalid;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
const PhoneSlot &PhoneSlot::operator++()
|
|
{
|
|
if ( (i!=invalid) && (i<entryPhone5) )
|
|
{
|
|
++i;
|
|
}
|
|
else
|
|
{
|
|
i = invalid;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
/* static */ const PhoneSlot PhoneSlot::begin()
|
|
{
|
|
return PhoneSlot( entryPhone1 );
|
|
}
|
|
|
|
/* static */ const PhoneSlot PhoneSlot::end()
|
|
{
|
|
return PhoneSlot( invalid );
|
|
}
|
|
|
|
unsigned int PhoneSlot::toOffset() const
|
|
{
|
|
if ( isValid() )
|
|
{
|
|
return i-entryPhone1;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
unsigned int PhoneSlot::toField() const
|
|
{
|
|
if ( isValid() )
|
|
{
|
|
return i;
|
|
}
|
|
else
|
|
{
|
|
return entryPhone1;
|
|
}
|
|
}
|
|
|
|
PhoneSlot::operator TQString() const
|
|
{
|
|
return TQString("%1,%2").arg(toOffset()).arg(toField());
|
|
}
|
|
|
|
#define MAXFIELDS 19
|
|
|
|
PilotAddress::PilotAddress(PilotRecord *rec) :
|
|
PilotRecordBase(rec),
|
|
fAddressInfo()
|
|
{
|
|
FUNCTIONSETUPL(4);
|
|
memset(&fAddressInfo,0,sizeof(fAddressInfo));
|
|
|
|
if (rec)
|
|
{
|
|
pi_buffer_t b;
|
|
b.data = (unsigned char *) rec->data();
|
|
b.allocated = b.used = rec->size();
|
|
unpack_Address(&fAddressInfo, &b, address_v1);
|
|
}
|
|
else
|
|
{
|
|
fAddressInfo.phoneLabel[0] = (int) PilotAddressInfo::eWork;
|
|
fAddressInfo.phoneLabel[1] = (int) PilotAddressInfo::eHome;
|
|
fAddressInfo.phoneLabel[2] = (int) PilotAddressInfo::eOther;
|
|
fAddressInfo.phoneLabel[3] = (int) PilotAddressInfo::eMobile;
|
|
fAddressInfo.phoneLabel[4] = (int) PilotAddressInfo::eEmail;
|
|
}
|
|
}
|
|
|
|
PilotAddress::PilotAddress(const PilotAddress & copyFrom) :
|
|
PilotRecordBase(copyFrom),
|
|
fAddressInfo()
|
|
{
|
|
FUNCTIONSETUPL(4);
|
|
_copyAddressInfo(copyFrom.fAddressInfo);
|
|
}
|
|
|
|
PilotAddress & PilotAddress::operator = (const PilotAddress & copyFrom)
|
|
{
|
|
FUNCTIONSETUPL(4);
|
|
PilotRecordBase::operator = (copyFrom);
|
|
_copyAddressInfo(copyFrom.fAddressInfo);
|
|
return *this;
|
|
}
|
|
|
|
bool PilotAddress::operator==(const PilotAddress &compareTo)
|
|
{
|
|
FUNCTIONSETUPL(4);
|
|
|
|
// now compare all the fields stored in the fAddressInfo.entry array of char*[19]
|
|
for (int i=0; i<MAXFIELDS; i++) {
|
|
// if one is NULL, and the other non-empty, they are not equal for sure
|
|
if ( !getFieldP(i) && compareTo.getFieldP(i))
|
|
{
|
|
return false;
|
|
}
|
|
if ( getFieldP(i) && !compareTo.getFieldP(i))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// test for getField(i)!=... to prevent strcmp or NULL strings! None or both can be zero, but not a single one.
|
|
if ( (getFieldP(i) != compareTo.getFieldP(i)) && ( strcmp(getFieldP(i), compareTo.getFieldP(i)) ) )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
void PilotAddress::_copyAddressInfo(const struct Address ©From)
|
|
{
|
|
FUNCTIONSETUPL(4);
|
|
fAddressInfo.showPhone = copyFrom.showPhone;
|
|
|
|
for (int labelLp = 0; labelLp < 5; labelLp++)
|
|
{
|
|
fAddressInfo.phoneLabel[labelLp] =
|
|
copyFrom.phoneLabel[labelLp];
|
|
}
|
|
|
|
for (unsigned int i = 0; i< MAXFIELDS; ++i)
|
|
{
|
|
if (copyFrom.entry[i])
|
|
{
|
|
fAddressInfo.entry[i] = tqstrdup(copyFrom.entry[i]);
|
|
}
|
|
else
|
|
{
|
|
fAddressInfo.entry[i] = 0L;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
PilotAddress::~PilotAddress()
|
|
{
|
|
FUNCTIONSETUPL(4);
|
|
free_Address(&fAddressInfo);
|
|
}
|
|
|
|
TQString PilotAddress::getTextRepresentation(const PilotAddressInfo *info, TQt::TextFormat richText) const
|
|
{
|
|
TQString text, tmp;
|
|
|
|
TQString par = (richText==TQt::RichText) ?CSL1("<p>"): TQString();
|
|
TQString ps = (richText==TQt::RichText) ?CSL1("</p>"):CSL1("\n");
|
|
TQString br = (richText==TQt::RichText) ?CSL1("<br/>"):CSL1("\n");
|
|
|
|
// title + name
|
|
text += par;
|
|
if (!getField(entryTitle).isEmpty())
|
|
{
|
|
text += rtExpand(getField(entryTitle), richText);
|
|
text += CSL1(" ");
|
|
}
|
|
|
|
tmp = richText ? CSL1("<b><big>%1 %2</big></b>") : CSL1("%1 %2");
|
|
TQString firstName = getField(entryFirstname);
|
|
if (firstName.isEmpty())
|
|
{
|
|
// So replace placeholder for first name (%1) with empty
|
|
tmp = tmp.arg(TQString());
|
|
}
|
|
else
|
|
{
|
|
tmp = tmp.arg(rtExpand(firstName,richText));
|
|
}
|
|
tmp=tmp.arg(rtExpand(getField(entryLastname), richText));
|
|
text += tmp;
|
|
text += ps;
|
|
|
|
// company
|
|
if (!getField(entryCompany).isEmpty())
|
|
{
|
|
text += par;
|
|
text += rtExpand(getField(entryCompany), richText);
|
|
text += ps;
|
|
}
|
|
|
|
// phone numbers (+ labels)
|
|
text += par;
|
|
for ( PhoneSlot i = PhoneSlot::begin(); i.isValid(); ++i )
|
|
{
|
|
if (!getField(i.toField()).isEmpty())
|
|
{
|
|
if (richText)
|
|
{
|
|
if (getShownPhone() == i)
|
|
{
|
|
tmp=CSL1("<small>%1: </small><b>%2</b>");
|
|
}
|
|
else
|
|
{
|
|
tmp=CSL1("<small>%1: </small>%2");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tmp=CSL1("%1: %2");
|
|
}
|
|
if (info)
|
|
{
|
|
tmp=tmp.arg(info->phoneLabel( getPhoneType( i ) ));
|
|
}
|
|
else
|
|
{
|
|
tmp=tmp.arg(CSL1("Contact: "));
|
|
}
|
|
tmp=tmp.arg(rtExpand(getField(i.toField()), richText));
|
|
text += tmp;
|
|
text += br;
|
|
}
|
|
}
|
|
text += ps;
|
|
|
|
// address, city, state, country
|
|
text += par;
|
|
if (!getField(entryAddress).isEmpty())
|
|
{
|
|
text += rtExpand(getField(entryAddress), richText);
|
|
text += br;
|
|
}
|
|
if (!getField(entryCity).isEmpty())
|
|
{
|
|
text += rtExpand(getField(entryCity), richText);
|
|
text += CSL1(" ");
|
|
}
|
|
if (!getField(entryState).isEmpty())
|
|
{
|
|
text += rtExpand(getField(entryState), richText);
|
|
text += CSL1(" ");
|
|
}
|
|
if (!getField(entryZip).isEmpty())
|
|
{
|
|
text += rtExpand(getField(entryZip), richText);
|
|
}
|
|
text += br;
|
|
if (!getField(entryCountry).isEmpty())
|
|
{
|
|
text += rtExpand(getField(entryCountry), richText);
|
|
text += br;
|
|
}
|
|
text += ps;
|
|
|
|
// custom fields
|
|
text += par;
|
|
for (int i = entryCustom1; i <= entryCustom4; i++)
|
|
{
|
|
if (!getField(i).isEmpty())
|
|
{
|
|
text += rtExpand(getField(i), richText);
|
|
text += br;
|
|
}
|
|
}
|
|
text += ps;
|
|
|
|
// category
|
|
if (info)
|
|
{
|
|
TQString categoryName = info->categoryName( category() );
|
|
if (!categoryName.isEmpty())
|
|
{
|
|
text += par;
|
|
text += rtExpand(categoryName, richText);
|
|
text += ps;
|
|
}
|
|
}
|
|
|
|
// note
|
|
if (!getField(entryNote).isEmpty())
|
|
{
|
|
text += richText?CSL1("<hr/>"):CSL1("-----------------------------\n");
|
|
text += par;
|
|
text += rtExpand(getField(entryNote), richText);
|
|
text += ps;
|
|
}
|
|
|
|
return text;
|
|
}
|
|
|
|
TQStringList PilotAddress::getEmails() const
|
|
{
|
|
TQStringList list;
|
|
|
|
for ( PhoneSlot i = PhoneSlot::begin(); i.isValid(); ++i)
|
|
{
|
|
PilotAddressInfo::EPhoneType t = getPhoneType( i );
|
|
if ( t == PilotAddressInfo::eEmail )
|
|
{
|
|
TQString s = getField(i.toField());
|
|
if (!s.isEmpty())
|
|
{
|
|
list.append(s);
|
|
}
|
|
}
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
void PilotAddress::setEmails(const TQStringList &list)
|
|
{
|
|
FUNCTIONSETUPL(4);
|
|
TQString test;
|
|
|
|
// clear all e-mails first
|
|
for ( PhoneSlot i = PhoneSlot::begin(); i.isValid(); ++i )
|
|
{
|
|
PilotAddressInfo::EPhoneType t = getPhoneType( i );
|
|
if (t == PilotAddressInfo::eEmail)
|
|
{
|
|
setField(i.toField(), TQString() );
|
|
}
|
|
}
|
|
|
|
for(TQStringList::ConstIterator listIter = list.begin();
|
|
listIter != list.end(); ++listIter)
|
|
{
|
|
TQString email = *listIter;
|
|
if (!setPhoneField(PilotAddressInfo::eEmail, email, NoFlags).isValid())
|
|
{
|
|
WARNINGKPILOT << "Email accounts overflowed, silently dropped." << endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
TQString PilotAddress::getField(int field) const
|
|
{
|
|
if ( (entryLastname <= field) && (field <= entryNote) )
|
|
{
|
|
return Pilot::fromPilot(fAddressInfo.entry[field]);
|
|
}
|
|
else
|
|
{
|
|
return TQString();
|
|
}
|
|
}
|
|
|
|
PhoneSlot PilotAddress::_getNextEmptyPhoneSlot() const
|
|
{
|
|
FUNCTIONSETUPL(4);
|
|
for (PhoneSlot i = PhoneSlot::begin(); i.isValid(); ++i)
|
|
{
|
|
const char *phoneField = getFieldP(i.toField());
|
|
|
|
if (!phoneField || !phoneField[0])
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
return PhoneSlot();
|
|
}
|
|
|
|
PhoneSlot PilotAddress::setPhoneField(PilotAddressInfo::EPhoneType type,
|
|
const TQString &field,
|
|
PhoneHandlingFlags flags)
|
|
{
|
|
FUNCTIONSETUPL(4);
|
|
|
|
const bool overwriteExisting = (flags == Replace);
|
|
PhoneSlot fieldSlot;
|
|
if (overwriteExisting)
|
|
{
|
|
fieldSlot = _findPhoneFieldSlot(type);
|
|
}
|
|
|
|
if ( !fieldSlot.isValid() )
|
|
{
|
|
fieldSlot = _getNextEmptyPhoneSlot();
|
|
}
|
|
|
|
// store the overflow phone
|
|
if ( !fieldSlot.isValid() )
|
|
{
|
|
DEBUGKPILOT << fname << ": Phone would overflow." << endl;
|
|
}
|
|
else // phone field 1 - 5; straight forward storage
|
|
{
|
|
setField(fieldSlot.toField(), field);
|
|
fAddressInfo.phoneLabel[fieldSlot.toOffset()] = (int) type;
|
|
}
|
|
return fieldSlot;
|
|
}
|
|
|
|
PhoneSlot PilotAddress::_findPhoneFieldSlot(PilotAddressInfo::EPhoneType t) const
|
|
{
|
|
FUNCTIONSETUPL(4);
|
|
for ( PhoneSlot i = PhoneSlot::begin(); i.isValid(); ++i )
|
|
{
|
|
if ( getPhoneType(i) == t )
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return PhoneSlot();
|
|
}
|
|
|
|
TQString PilotAddress::getPhoneField(PilotAddressInfo::EPhoneType type) const
|
|
{
|
|
FUNCTIONSETUPL(4);
|
|
PhoneSlot fieldSlot = _findPhoneFieldSlot(type);
|
|
|
|
if (fieldSlot.isValid())
|
|
{
|
|
return getField(fieldSlot.toField());
|
|
}
|
|
|
|
return TQString();
|
|
}
|
|
|
|
PhoneSlot PilotAddress::getShownPhone() const
|
|
{
|
|
// The slot is stored as an offset
|
|
return PhoneSlot(entryPhone1 + fAddressInfo.showPhone);
|
|
}
|
|
|
|
const PhoneSlot &PilotAddress::setShownPhone( const PhoneSlot &v )
|
|
{
|
|
FUNCTIONSETUPL(4);
|
|
if (v.isValid())
|
|
{
|
|
fAddressInfo.showPhone = v.toOffset();
|
|
}
|
|
return v;
|
|
}
|
|
|
|
PhoneSlot PilotAddress::setShownPhone(PilotAddressInfo::EPhoneType type)
|
|
{
|
|
FUNCTIONSETUPL(4);
|
|
PhoneSlot fieldSlot = _findPhoneFieldSlot(type);
|
|
|
|
// Did we find a slot with the requested type?
|
|
if (!fieldSlot.isValid())
|
|
{
|
|
// No, so look for first non-empty phone slot
|
|
for ( fieldSlot = PhoneSlot::begin(); fieldSlot.isValid(); ++fieldSlot )
|
|
{
|
|
const char *p = getFieldP(fieldSlot.toField());
|
|
if (p && p[0])
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
// If all of them are empty, then use first slot instead
|
|
if (!fieldSlot.isValid())
|
|
{
|
|
fieldSlot = PhoneSlot::begin();
|
|
}
|
|
}
|
|
setShownPhone(fieldSlot);
|
|
return fieldSlot;
|
|
}
|
|
|
|
PilotAddressInfo::EPhoneType PilotAddress::getPhoneType( const PhoneSlot &field ) const
|
|
{
|
|
if ( field.isValid() )
|
|
{
|
|
return (PilotAddressInfo::EPhoneType) fAddressInfo.phoneLabel[field.toOffset()];
|
|
}
|
|
else
|
|
{
|
|
return PilotAddressInfo::eNone;
|
|
}
|
|
}
|
|
|
|
void PilotAddress::setField(int field, const TQString &text)
|
|
{
|
|
FUNCTIONSETUPL(4);
|
|
// This will have either been created with unpack_Address, and/or will
|
|
// be released with free_Address, so use malloc/free here:
|
|
if (fAddressInfo.entry[field])
|
|
{
|
|
free(fAddressInfo.entry[field]);
|
|
fAddressInfo.entry[field]=0L;
|
|
}
|
|
if (!text.isEmpty())
|
|
{
|
|
fAddressInfo.entry[field] = (char *) malloc(text.length() + 1);
|
|
Pilot::toPilot(text, fAddressInfo.entry[field], text.length()+1);
|
|
}
|
|
else
|
|
{
|
|
fAddressInfo.entry[field] = 0L;
|
|
}
|
|
}
|
|
|
|
PilotRecord *PilotAddress::pack() const
|
|
{
|
|
FUNCTIONSETUPL(4);
|
|
int i;
|
|
|
|
pi_buffer_t *b = pi_buffer_new( sizeof(fAddressInfo) );
|
|
i = pack_Address(const_cast<Address_t *>(&fAddressInfo), b, address_v1);
|
|
if (i<0)
|
|
{
|
|
return 0L;
|
|
}
|
|
// pack_Address sets b->used
|
|
return new PilotRecord( b, this );
|
|
}
|