palm-os-sdk/PalmOne/PIM/Samples/PIMExp/Src/AddressDB.c

771 lines
21 KiB
C

/******************************************************************************
*
* Copyright (c) 1995-2002 PalmSource, Inc. All rights reserved.
*
* File: AddressDB.c
*
* Release: Palm OS 5 SDK (68K) R3.
*
* Description:
* Address Manager routines
*
*****************************************************************************/
#include "AddressDB.h"
#include "AddrDefines.h"
#include <UIResources.h>
#include <SysUtils.h>
#include <ErrorMgr.h>
#include <StringMgr.h>
#include <TextMgr.h>
#include <PalmUtils.h>
#include <FeatureMgr.h>
#include <AddressSortLib.h>
#define sortKeyFieldBits (BitAtPosition(name) | BitAtPosition(firstName) | BitAtPosition(company))
// The following structure doesn't really exist. The first field
// varies depending on the data present. However, it is convient
// (and less error prone) to use when accessing the other information.
typedef struct {
AddrOptionsType options; // Display by company or by name
AddrDBRecordFlags flags;
UInt8 companyFieldOffset; // Offset from firstField
char firstField;
} PrvAddrPackedDBRecord;
/***********************************************************************
*
* Internal Functions
*
***********************************************************************/
static void PrvAddrDBFindKey(PrvAddrPackedDBRecord *r, char **key, UInt16 *whichKey, Int16 sortByCompany);
static Int16 PrvAddrDBComparePackedRecords(PrvAddrPackedDBRecord *r1, PrvAddrPackedDBRecord *r2, Int16 sortByCompany, SortRecordInfoPtr /*info1*/, SortRecordInfoPtr /*info2*/, MemHandle /*appInfoH*/);
static Int16 PrvAddrDBUnpackedSize(AddrDBRecordPtr r);
static void PrvAddrDBPack(AddrDBRecordPtr s, void * recordP);
static void PrvAddrDBUnpack(PrvAddrPackedDBRecord *src, AddrDBRecordPtr dest);
static UInt16 PrvAddrDBFindSortPosition(DmOpenRef dbP, PrvAddrPackedDBRecord *newRecord);
AddrAppInfoPtr AddrDBAppInfoGetPtr(DmOpenRef dbP)
{
UInt16 cardNo;
LocalID dbID;
LocalID appInfoID;
if (DmOpenDatabaseInfo(dbP, &dbID, NULL, NULL, &cardNo, NULL))
return NULL;
if (DmDatabaseInfo(cardNo, dbID, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &appInfoID, NULL, NULL, NULL))
return NULL;
if (appInfoID == 0)
return NULL;
else
return ((AddrAppInfoType*) MemLocalIDToLockedPtr(appInfoID, cardNo));
}
Err AddrDBNewRecord(DmOpenRef dbP, AddrDBRecordPtr r, UInt16 *index)
{
MemHandle recordH;
Err err;
PrvAddrPackedDBRecord* recordP;
UInt16 newIndex;
// 1) and 2) (make a new chunk with the correct size)
recordH = DmNewHandle(dbP, (Int32) PrvAddrDBUnpackedSize(r));
if (recordH == NULL)
return dmErrMemError;
// 3) Copy the data from the unpacked record to the packed one.
recordP = (PrvAddrPackedDBRecord*) MemHandleLock(recordH);
PrvAddrDBPack(r, recordP);
// Get the index
newIndex = PrvAddrDBFindSortPosition(dbP, recordP);
MemPtrUnlock(recordP);
// 4) attach in place
err = DmAttachRecord(dbP, &newIndex, recordH, 0);
if (err)
MemHandleFree(recordH);
else
*index = newIndex;
return err;
}
Err AddrDBChangeRecord(DmOpenRef dbP, UInt16 *index, AddrDBRecordPtr r, AddrDBRecordFlags changedFields, UInt16 blobDirty)
{
AddrDBRecordType src;
MemHandle srcH;
Err result;
MemHandle recordH=0;
MemHandle oldH;
Int16 i;
AddrDBRecordFlags changes;
Int16 sortByCompany;
AddrAppInfoPtr appInfoPtr;
Boolean dontMove;
UInt16 attributes; // to contain the deleted flag
PrvAddrPackedDBRecord* cmpP;
PrvAddrPackedDBRecord* recordP;
UInt16 Counter = 0;
MemHandle packedH;
MemMove(&changes, &changedFields, sizeof(changedFields));
// We do not assume that r is completely valid so we get a valid
// AddrDBRecordPtr...
if ((result = AddrDBGetRecord(dbP, *index, &src, &srcH)) != 0)
return result;
// Get handle to packed record, we need to figure out if there is blob data
packedH = DmQueryRecord(dbP, *index);
// And we apply the changes to it.
MemMove(&(src.options), (&r->options), sizeof(AddrOptionsType));
for (i = firstAddressField; i < addrNumStringFields; i++)
{
// If the flag is set, point to the string else NULL
if (GetBitMacro(changes, i) != 0)
{
src.fields[i] = r->fields[i];
RemoveBitMacro(changes, i);
}
if ((changes.allBits == 0) && (changes.allBits2 == 0))
break; // no more changes
}
// Fill in birthday info only if the birth date is present.
if(GetBitMacro(changes, birthdayDate))
{
src.birthdayInfo.birthdayDate = r->birthdayInfo.birthdayDate;
RemoveBitMacro(changes, birthdayDate);
}
if(GetBitMacro(changes, birthdayMask))
{
src.birthdayInfo.birthdayMask = r->birthdayInfo.birthdayMask;
RemoveBitMacro(changes, birthdayMask);
}
if(GetBitMacro(changes, birthdayPreset))
{
src.birthdayInfo.birthdayPreset = r->birthdayInfo.birthdayPreset;
RemoveBitMacro(changes, birthdayPreset);
}
// change 2.0
if(r->rel2blobInfo.dirty)
{
MemMove(&(src.rel2blobInfo.anniversaryInfo), &(r->rel2blobInfo.anniversaryInfo), sizeof(BirthdayInfo) + sizeof(ToneIdentifier));
r->rel2blobInfo.dirty = false;
}
//
if(r->pictureInfo.pictureDirty)
{
src.pictureInfo.pictureDirty = 1;
src.pictureInfo.pictureSize = r->pictureInfo.pictureSize;
src.pictureInfo.pictureData = r->pictureInfo.pictureData;
}
if(blobDirty)
{
for(Counter = 0;Counter< r->numBlobs ;Counter++)
{
src.blobs[Counter] = r->blobs[Counter];
}
src.numBlobs = r->numBlobs;
}
// 1) and 2) (make a new chunk with the correct size)
recordH = DmNewHandle(dbP, PrvAddrDBUnpackedSize(&src));
if (recordH == NULL)
{
MemHandleUnlock(srcH); // undo lock from AddrGetRecord above
return dmErrMemError;
}
recordP = (PrvAddrPackedDBRecord*) MemHandleLock(recordH);
// 3) Copy the data from the unpacked record to the packed one.
PrvAddrDBPack(&src, recordP);
// The original record is copied and no longer needed.
MemHandleUnlock(srcH);
// 4) If the sort position changes...
// Check if any of the key fields have changed
if ((changedFields.allBits & sortKeyFieldBits) == 0)
goto attachRecord;
// Make sure *index-1 < *index < *index+1, if so it's in sorted
// order. Leave it there.
appInfoPtr = (AddrAppInfoPtr) AddrDBAppInfoGetPtr(dbP);
sortByCompany = appInfoPtr->misc.sortByCompany;
MemPtrUnlock(appInfoPtr);
if (*index > 0)
{
// This record wasn't deleted and deleted records are at the end of the
// database so the prior record may not be deleted!
cmpP = (PrvAddrPackedDBRecord*) MemHandleLock(DmQueryRecord(dbP, *index-1));
dontMove = (PrvAddrDBComparePackedRecords (cmpP, recordP, sortByCompany,
NULL, NULL, 0) == -1);
MemPtrUnlock(cmpP);
}
else
dontMove = true;
if (*index+1 < DmNumRecords (dbP))
{
DmRecordInfo(dbP, *index+1, &attributes, NULL, NULL);
if (attributes & dmRecAttrDelete)
; // don't move it after the deleted record!
else {
cmpP = (PrvAddrPackedDBRecord*) MemHandleLock(DmQueryRecord(dbP, *index+1));
dontMove = dontMove && (PrvAddrDBComparePackedRecords (recordP, cmpP, sortByCompany,
NULL, NULL, 0) == -1);
MemPtrUnlock(cmpP);
}
}
if (dontMove)
goto attachRecord;
// The record isn't in the right position. Move it.
i = PrvAddrDBFindSortPosition(dbP, recordP);
DmMoveRecord(dbP, *index, i);
if (i > *index) i--;
*index = i; // return new position
// Attach the new record to the old index, the preserves the
// category and record id.
attachRecord:
result = DmAttachRecord(dbP, index, recordH, &oldH);
MemPtrUnlock(recordP);
if (result) return result;
MemHandleFree(oldH);
return 0;
}
Err AddrDBGetRecord(DmOpenRef dbP, UInt16 index, AddrDBRecordPtr recordP,
MemHandle *recordH)
{
PrvAddrPackedDBRecord *src;
*recordH = DmQueryRecord(dbP, index);
if (*recordH == NULL)
return dmErrIndexOutOfRange;
src = (PrvAddrPackedDBRecord *) MemHandleLock(*recordH);
if (src == NULL)
return dmErrIndexOutOfRange;
PrvAddrDBUnpack(src, recordP);
return 0;
}
Int16 PrvAddrDBUnpackedSize(AddrDBRecordPtr r)
{
Int16 size;
Int16 index;
size = sizeof (PrvAddrPackedDBRecord) - sizeof (char); // correct
for (index = firstAddressField; index < addrNumStringFields; index++)
{
if (r->fields[index] != NULL)
size += StrLen(r->fields[index]) + 1;
}
//calculate length for birthday info
if(r->birthdayInfo.birthdayDate.month && r->birthdayInfo.birthdayDate.day)
{
size += sizeof(DateType);
size += sizeof(AddressDBBirthdayFlags);
if(r->birthdayInfo.birthdayMask.alarm)
{
size += sizeof(UInt8); //preset
}
}
if(r->pictureInfo.pictureSize && r->pictureInfo.pictureData)
{
size += sizeof(UInt32); //blob id
size+=sizeof(r->pictureInfo.pictureSize); //blob size, same as sizeof(picture size)
size+= sizeof(r->pictureInfo.pictureDirty);
size+= r->pictureInfo.pictureSize;
}
// change 2.0
// SDS - if either an anniversary OR a custom ringtone, add the blob.
if(r->rel2blobInfo.anniversaryInfo.birthdayDate.month && r->rel2blobInfo.anniversaryInfo.birthdayDate.day || r->rel2blobInfo.ringtoneInfo.id)
{
size += sizeof(UInt32); //blob id
size +=sizeof(UInt16); //blob size
size += sizeof(ToneIdentifier) + sizeof(BirthdayInfo);
}
//
for (index = 0; index < r->numBlobs; index++)
{
size += sizeof(r->blobs[index].creatorID);
size += sizeof(r->blobs[index].size);
size += r->blobs[index].size;
}
return size;
}
void PrvAddrDBPack(AddrDBRecordPtr s, void * recordP)
{
Int32 offset;
AddrDBRecordFlags flags;
Int16 index;
PrvAddrPackedDBRecord* d=0;
UInt16 len;
void * srcP;
UInt8 companyFieldOffset;
UInt16 blobCount = 0;
UInt16 size = 0;
flags.allBits = 0;
flags.allBits2 = 0;
DmWrite(recordP, (Int32)&d->options, &s->options, sizeof(s->options));
offset = (Int32)&d->firstField;
for (index = firstAddressField; index < addrNumStringFields; index++) {
if (s->fields[index] != NULL)
/* if (s->fields[index][0] == '\0')
{
// so set the companyFieldOffset or clear it code doesn't fail
s->fields[index] = NULL;
}
else
*/
{
//ErrFatalDisplayIf(s->fields[index][0] == '\0' && index != P1ctnote,
// "Empty field being added");
// Skip fields with empty strings
if ((s->fields[index][0] != '\0') || (index == note))
{
srcP = s->fields[index];
len = StrLen((Char*)srcP) + 1;
DmWrite(recordP, offset, srcP, len);
offset += len;
SetBitMacro(flags, index);
}
}
}
//write birthday info
len = sizeof(DateType);
//fill in birthday info only if the birth date is present
if(s->birthdayInfo.birthdayDate.month && s->birthdayInfo.birthdayDate.day)
{
DmWrite(recordP, offset, &(s->birthdayInfo.birthdayDate), len);
offset += len;
SetBitMacro(flags, birthdayDate);
//write other birthday info only if birthdate is valid
//write birthday mask to determine if year is set.
len = sizeof(AddressDBBirthdayFlags);
DmWrite(recordP, offset, &(s->birthdayInfo.birthdayMask), len);
offset += len;
SetBitMacro(flags, birthdayMask);
//set preset and text only if Date Book reminder option is set
if(s->birthdayInfo.birthdayMask.alarm)
{
len = sizeof(UInt8);
DmWrite(recordP, offset, &(s->birthdayInfo.birthdayPreset), len);
offset += len;
SetBitMacro(flags, birthdayPreset);
}
}
//set picture blob information
if(s->pictureInfo.pictureSize && s->pictureInfo.pictureData)
{
UInt32 blobId = addrPictureBlobId;
UInt16 blobSize = 0;
//set 4 byte id
len = sizeof(blobId);
DmWrite(recordP, offset, &blobId, len);
offset += len;
//set 2 bytes len: blob size = 2 bytes picture dirty flag + picture size
len = sizeof(blobSize);
blobSize = s->pictureInfo.pictureSize + sizeof(s->pictureInfo.pictureDirty);
DmWrite(recordP, offset, &blobSize, len);
offset += len;
//set dirty flag and picture
len = sizeof(s->pictureInfo.pictureDirty);
DmWrite(recordP, offset, &(s->pictureInfo.pictureDirty), len);
offset += len;
DmWrite(recordP, offset, (s->pictureInfo.pictureData), s->pictureInfo.pictureSize);
offset += s->pictureInfo.pictureSize;
blobCount++;
}
// Write the Release 2 blob information
if( (s->rel2blobInfo.anniversaryInfo.birthdayDate.month && s->rel2blobInfo.anniversaryInfo.birthdayDate.day) || s->rel2blobInfo.ringtoneInfo.id )
{
UInt32 blobId = addrRelease2BlobId;
UInt16 blobSize = 0;
//set 4 byte id
len = sizeof(blobId);
DmWrite(recordP, offset, &blobId, len);
offset += len;
//set 2 bytes len: blob size = 2 bytes
len = sizeof(blobSize);
blobSize = sizeof(ToneIdentifier) + sizeof(BirthdayInfo);
DmWrite(recordP, offset, &blobSize, len);
offset += len;
// write out anniversary info and ringtone info
DmWrite(recordP, offset, &(s->rel2blobInfo.anniversaryInfo), blobSize);
offset += len;
blobCount++;
}
// Include any other blobs we don't understand.
ErrNonFatalDisplayIf(blobCount + s->numBlobs > apptMaxBlobs, "Too many blobs");
// Include any other blobs we don't understand.
for (index = 0; index < s->numBlobs; index++)
{
size = sizeof(s->blobs[index].creatorID);
DmWrite(recordP, offset, &(s->blobs[index].creatorID), size);
offset += size;
size = sizeof(s->blobs[index].size);
DmWrite(recordP, offset, &(s->blobs[index].size), size);
offset += size;
DmWrite(recordP, offset, s->blobs[index].content, s->blobs[index].size);
offset += s->blobs[index].size;
}
ErrNonFatalDisplayIf(offset > MemHandleSize(MemPtrRecoverHandle(recordP)), "Not enough room for packed record");
// Set the flags indicating which fields are used
DmWrite(recordP, (Int32)&d->flags.allBits, &flags.allBits, sizeof(flags.allBits));
DmWrite(recordP, (Int32)&d->flags.allBits2, &flags.allBits2, sizeof(flags.allBits2));
// Set the companyFieldOffset or clear it
if (s->fields[company] == NULL)
companyFieldOffset = 0;
else {
index = 1;
if (s->fields[name] != NULL)
index += StrLen(s->fields[name]) + 1;
if (s->fields[firstName] != NULL)
index += StrLen(s->fields[firstName]) + 1;
companyFieldOffset = (UInt8) index;
}
DmWrite(recordP, (Int32)(&d->companyFieldOffset), &companyFieldOffset, sizeof(companyFieldOffset));
}
void PrvAddrDBUnpack(PrvAddrPackedDBRecord *src, AddrDBRecordPtr dest)
{
Int16 index;
AddrDBRecordFlags flags;
char *p;
UInt16 recordSize = 0;
Char *blobStart;
Char *blobEnd;
UInt32 blobId;
UInt16 blobSize;
MemMove(&(dest->options), &(src->options), sizeof(AddrOptionsType));
MemMove(&flags, &(src->flags), sizeof(AddrDBRecordFlags));
p = &src->firstField;
for (index = firstAddressField; index < addrNumStringFields; index++)
{
// If the flag is set point to the string else NULL
if (GetBitMacro(flags, index) != 0)
{
dest->fields[index] = p;
p += StrLen(p) + 1;
}
else
dest->fields[index] = NULL;
}
//unpack birthday info
MemSet(&(dest->birthdayInfo), sizeof(BirthdayInfo), 0 );
if(GetBitMacro(flags, birthdayDate))
{
MemMove(&(dest->birthdayInfo.birthdayDate), p, sizeof(DateType));
p+= sizeof(DateType);
}
if(GetBitMacro(flags, birthdayMask) )
{
MemMove(&(dest->birthdayInfo.birthdayMask), p, sizeof(AddressDBBirthdayFlags));
//dest->birthdayInfo.birthdayMask = *((AddressDBBirthdayFlags*)p);
p+= sizeof(AddressDBBirthdayFlags);
}
if(GetBitMacro(flags, birthdayPreset) )
{
dest->birthdayInfo.birthdayPreset = *((UInt8*)p);
p+= sizeof(UInt8);
}
//get picture info
dest->pictureInfo.pictureDirty = 0;
dest->pictureInfo.pictureSize = 0;
dest->pictureInfo.pictureData = NULL;
// SDS - init all blob2 fields to zero.
MemSet(&(dest->rel2blobInfo), sizeof(Release2BlobType), 0 );
dest->numBlobs = 0;
// Then iterate through the blobs, ignoring any we don't understand.
blobStart = p; // First blob starts where last non-blob data ends.
recordSize = MemPtrSize(src);
while (blobStart < (Char *)src + recordSize)
{
p = blobStart;
ErrNonFatalDisplayIf((Char *)src + recordSize - blobStart <= sizeof (blobId) + sizeof (blobSize),"Invalid blob encountered");
MemMove(&blobId, p, sizeof (blobId));
p += sizeof (blobId);
MemMove(&blobSize, p, sizeof (blobSize));
p += sizeof (blobSize);
blobEnd = p + blobSize; // Blob size excludes space to store ID and size of blob.
switch (blobId)
{
case addrPictureBlobId:
{
UInt16 pictureDirtySize;
pictureDirtySize = sizeof(dest->pictureInfo.pictureDirty);
dest->pictureInfo.pictureSize = blobSize - pictureDirtySize;
MemMove(&(dest->pictureInfo.pictureDirty), p, pictureDirtySize);
p+= pictureDirtySize;
if(dest->pictureInfo.pictureSize)
dest->pictureInfo.pictureData = p;
p+= dest->pictureInfo.pictureSize;
break;
}
case addrRelease2BlobId:
{
MemMove(&dest->rel2blobInfo.anniversaryInfo, p, sizeof(BirthdayInfo));
p += sizeof(BirthdayInfo);
MemMove(&dest->rel2blobInfo.ringtoneInfo, p, sizeof(ToneIdentifier));
p += sizeof(ToneIdentifier);
break;
}
default:
{
ErrNonFatalDisplayIf (dest->numBlobs >= apptMaxBlobs, "Too many blobs");
dest->blobs[dest->numBlobs].creatorID = blobId;
dest->blobs[dest->numBlobs].size = blobSize;
dest->blobs[dest->numBlobs].content = p;
dest->numBlobs++;
p = blobEnd;
break;
}
}
ErrNonFatalDisplayIf(p != blobEnd, "Blob size does not agree with contents");
blobStart = blobEnd; // Next blob starts where last blob ends.
}
ErrNonFatalDisplayIf(blobStart != (Char *)src + recordSize,
"Last blob not aligned with end of record - don't let fields edit records directly!");
}
Int16 PrvAddrDBComparePackedRecords(PrvAddrPackedDBRecord *r1,
PrvAddrPackedDBRecord *r2,
Int16 sortByCompany,
SortRecordInfoPtr UNUSED_PARAM(info1),
SortRecordInfoPtr UNUSED_PARAM(info2),
MemHandle UNUSED_PARAM(appInfoH))
{
UInt16 whichKey1, whichKey2;
char *key1, *key2;
Int16 result;
whichKey1 = 1;
whichKey2 = 1;
do {
PrvAddrDBFindKey(r1, &key1, &whichKey1, sortByCompany);
PrvAddrDBFindKey(r2, &key2, &whichKey2, sortByCompany);
// A key with NULL loses the StrCompare.
if (key1 == NULL)
{
// If both are NULL then return them as equal
if (key2 == NULL)
{
result = 0;
return result;
}
else
result = -1;
}
else
if (key2 == NULL)
result = 1;
else
{
// With Palm OS 4.0, StrCompare will try a caseless
// comparison first, then a case-sensitive, so we
// only need to call StrCompare. Also, we can call
// TxtCompare to avoid one extra trap dispatch.
// result = StrCaselessCompare(key1, key2);
// if (result == 0)
// result = StrCompare(key1, key2);
result = TxtCompare( key1, // const Char *s1,
0xFFFF, // UInt16 s1Len,
NULL, // UInt16 *s1MatchLen,
key2, // const Char *s2,
0xFFFF, // UInt16 s2Len,
NULL); // UInt16 *s2MatchLen
}
} while (!result);
return result;
}
UInt16 PrvAddrDBFindSortPosition(DmOpenRef dbP, PrvAddrPackedDBRecord *newRecord)
{
Int16 sortByCompany;
AddrAppInfoPtr appInfoPtr;
appInfoPtr = (AddrAppInfoPtr) AddrDBAppInfoGetPtr(dbP);
sortByCompany = appInfoPtr->misc.sortByCompany;
MemPtrUnlock(appInfoPtr);
return DmFindSortPosition(dbP, (void *) newRecord, NULL, (DmComparF *)
PrvAddrDBComparePackedRecords, (Int16) sortByCompany);
}
void PrvAddrDBFindKey(PrvAddrPackedDBRecord *r, char **key, UInt16 *whichKey, Int16 sortByCompany)
{
AddrDBRecordFlags fieldFlags;
fieldFlags.allBits = r->flags.allBits;
ErrFatalDisplayIf(*whichKey == 0 || *whichKey == 5, "Bad addr key");
if (sortByCompany)
{
if (*whichKey == 1 && fieldFlags.bits.company)
{
*whichKey = 2;
goto returnCompanyKey;
}
if (*whichKey <= 2 && fieldFlags.bits.name)
{
*whichKey = 3;
goto returnNameKey;
}
if (*whichKey <= 3 && fieldFlags.bits.firstName)
{
*whichKey = 4;
goto returnFirstNameKey;
}
}
else
{
if (*whichKey == 1 && fieldFlags.bits.name)
{
*whichKey = 2;
goto returnNameKey;
}
if (*whichKey <= 2 && fieldFlags.bits.firstName)
{
*whichKey = 3;
goto returnFirstNameKey;
}
// For now don't consider company name when sorting by person name
// unless there isn't a name or firstName
if (*whichKey <= 3 && fieldFlags.bits.company &&
!(fieldFlags.bits.name || fieldFlags.bits.firstName))
{
*whichKey = 4;
goto returnCompanyKey;
}
}
// All possible fields have been tried so return NULL so that
// the uniq ID is compared.
*whichKey = 5;
*key = NULL;
return;
returnCompanyKey:
*key = (char *) &r->companyFieldOffset + r->companyFieldOffset;
return;
returnNameKey:
*key = &r->firstField;
return;
returnFirstNameKey:
*key = &r->firstField;
if (r->flags.bits.name)
{
*key += StrLen(*key) + 1;
}
return;
}