home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
linuxmafia.com 2016
/
linuxmafia.com.tar
/
linuxmafia.com
/
pub
/
palmos
/
linkmaster-src-1.0.4.tar.gz
/
linkmaster-src-1.0.4.tar
/
linkmaster-1.0.4
/
proxy
/
db
/
DateDB.c
< prev
next >
Wrap
C/C++ Source or Header
|
2000-09-05
|
59KB
|
2,237 lines
/*******************************************************************
Copyright ⌐ 1995 - 1998, 3Com Corporation or its subsidiaries ("3Com").
All rights reserved.
This software may be copied and used solely for developing products for
the Palm Computing platform and for archival and backup purposes. Except
for the foregoing, no part of this software may be reproduced or transmitted
in any form or by any means or used to make any derivative work (such as
translation, transformation or adaptation) without express written consent
from 3Com.
3Com reserves the right to revise this software and to make changes in content
from time to time without obligation on the part of 3Com to provide notification
of such revision or changes.
3COM MAKES NO REPRESENTATIONS OR WARRANTIES THAT THE SOFTWARE IS FREE OF ERRORS
OR THAT THE SOFTWARE IS SUITABLE FOR YOUR USE. THE SOFTWARE IS PROVIDED ON AN
"AS IS" BASIS. 3COM MAKES NO WARRANTIES, TERMS OR CONDITIONS, EXPRESS OR IMPLIED,
EITHER IN FACT OR BY OPERATION OF LAW, STATUTORY OR OTHERWISE, INCLUDING WARRANTIES,
TERMS, OR CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
SATISFACTORY QUALITY.
TO THE FULL EXTENT ALLOWED BY LAW, 3COM ALSO EXCLUDES FOR ITSELF AND ITS SUPPLIERS
ANY LIABILITY, WHETHER BASED IN CONTRACT OR TORT (INCLUDING NEGLIGENCE), FOR
DIRECT, INCIDENTAL, CONSEQUENTIAL, INDIRECT, SPECIAL, OR PUNITIVE DAMAGES OF
ANY KIND, OR FOR LOSS OF REVENUE OR PROFITS, LOSS OF BUSINESS, LOSS OF INFORMATION
OR DATA, OR OTHER FINANCIAL LOSS ARISING OUT OF OR IN CONNECTION WITH THIS SOFTWARE,
EVEN IF 3COM HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
3Com, HotSync, Palm Computing, and Graffiti are registered trademarks, and
Palm III and Palm OS are trademarks of 3Com Corporation or its subsidiaries.
IF THIS SOFTWARE IS PROVIDED ON A COMPACT DISK, THE OTHER SOFTWARE AND
DOCUMENTATION ON THE COMPACT DISK ARE SUBJECT TO THE LICENSE AGREEMENT
ACCOMPANYING THE COMPACT DISK.
*****************************************************************************
* FileName:
* DateDB.c
*
* Description:
* Appointment Manager routines
*
* History:
* 1/25/95 rsf - Created
* 8/5/99 bgd - ifdef'd out most functions
*
*******************************************************************/
#include <Pilot.h>
#define NON_PORTABLE
#include <MemoryPrv.h>
// Set this to get to private database defines
#define __APPTMGR_PRIVATE__
#include "Datebook.h"
/***********************************************************************
*
* Internal Structutes
*
***********************************************************************/
// 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 {
ApptDateTimeType when;
ApptDBRecordFlags flags; // A flag set for each datum present
char firstField;
} ApptPackedDBRecordType;
typedef ApptPackedDBRecordType * ApptPackedDBRecordPtr;
typedef Int comparF (const void *, const void *, Int other);
#if 0
/***********************************************************************
*
* Internal Routines
*
***********************************************************************/
void ECApptDBValidate (DmOpenRef dbP);
static Int TimeCompare (TimeType t1, TimeType t2);
static Int DateCompare (DateType d1, DateType d2);
/************************************************************
*
* FUNCTION: ECApptDBValidate
*
* DESCRIPTION: This routine validates the integrity of a
* datebook datebase.
*
* PARAMETERS: database pointer
*
* RETURNS: nothing
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 11/15/95 Initial Revision
*
*************************************************************/
#if EMULATION_LEVEL != EMULATION_NONE
#define maxDescLen tableMaxTextItemSize
#define maxNoteLen noteViewMaxLength
void ECApptDBValidate (DmOpenRef dbP)
{
UInt i;
UInt size;
UInt len;
UInt blockSize;
UInt numRecord;
VoidHand recH;
DateType date;
ApptDBRecordType rec;
numRecord = DmNumRecords (dbP);
for (i = 0 ; i < numRecord; i++)
{
recH = DmQueryRecord (dbP, i);
if (! recH) continue;
ApptGetRecord (dbP, i, &rec, &recH);
// Is the event an untimed event?
if (TimeToInt(rec.when->startTime) == apptNoTime)
{
// There should not be and end time if there is no start time.
if (TimeToInt(rec.when->endTime) != apptNoTime)
ErrDisplay ("DB integrity error");
}
// Validate the event date.
if ((rec.when->date.month == 0) ||
(rec.when->date.month > 12) ||
(rec.when->date.day == 0) ||
(rec.when->date.day > DaysInMonth (rec.when->date.month,
rec.when->date.year + firstYear)))
ErrDisplay ("DB integrity error");
// The start time may not be greater than the end time.
else if (TimeCompare (rec.when->startTime, rec.when->endTime) > 0)
{
ErrDisplay ("DB integrity error");
}
// Validate the alarm info.
if (rec.alarm)
{
if (rec.alarm->advance > 99)
ErrDisplay ("DB integrity error");
if (rec.alarm->advanceUnit > aauDays)
ErrDisplay ("DB integrity error");
}
// Validate the repeat info.
if (rec.repeat)
{
// Validate the repeat type.
if (rec.repeat->repeatType > repeatYearly)
ErrDisplay ("DB integrity error");
// Validate the repeat end date.
date = rec.repeat->repeatEndDate;
if (DateToInt (date) != apptNoTime)
{
if (DateCompare (date, rec.when->date) < 0)
ErrDisplay ("DB integrity error");
if ((date.month == 0) ||
(date.month > 12) ||
(date.day == 0) ||
(date.day > DaysInMonth (date.month, date.year + firstYear)))
ErrDisplay ("DB integrity error");
}
// Validate the repeat frequency.
if (rec.repeat->repeatFrequency > 99)
ErrDisplay ("DB integrity error");
// Validate the "repeatOn" info
if (rec.repeat->repeatType == repeatWeekly)
{
if (rec.repeat->repeatOn == 0)
ErrDisplay ("DB integrity error");
}
else if (rec.repeat->repeatType == repeatMonthlyByDay)
{
if (rec.repeat->repeatOn > domLastSat)
ErrDisplay ("DB integrity error");
}
else
{
if (rec.repeat->repeatOn != 0)
ErrDisplay ("DB integrity error");
}
// Validate the "repeatStartOfWeek" info,
if (rec.repeat->repeatType == repeatWeekly)
{
if (rec.repeat->repeatStartOfWeek > monday)
ErrDisplay ("DB integrity error");
}
else if (rec.repeat->repeatStartOfWeek)
ErrDisplay ("DB integrity error");
}
// Validate the record size.
size = sizeof (ApptDateTimeType) + sizeof (ApptDBRecordFlags);
if (rec.alarm)
size += sizeof (AlarmInfoType);
if (rec.repeat)
size += sizeof (RepeatInfoType);
if (rec.exceptions)
size += sizeof (DateType) * rec.exceptions->numExceptions +
sizeof (UInt);
if (rec.description)
{
len = StrLen (rec.description);
ErrFatalDisplayIf (len > maxDescLen, "DB integrity error");
size += len + 1;
}
if (rec.note)
{
len = StrLen (rec.note);
ErrFatalDisplayIf (len > maxNoteLen, "DB integrity error");
size += len + 1;
}
blockSize = MemHandleSize (recH);
// ErrFatalDisplayIf ( (blockSize != size), "DB integrity error");
MemHandleUnlock (recH);
}
}
#endif
#endif /* 0 */
/***********************************************************************
*
* FUNCTION: DateCompare
*
* DESCRIPTION: This routine compares two dates.
*
* PARAMETERS: d1 - a date
* d2 - a date
*
* RETURNED: if d1 > d2 returns a positive int
* if d1 < d2 returns a negative int
* if d1 = d2 returns zero
*
* NOTE: This routine treats the DateType structure like an unsigned int,
* it depends on the fact the the members of the structure are ordered
* year, month, day form high bit to low low bit.
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 6/12/95 Initial Revision
*
***********************************************************************/
static Int DateCompare (DateType d1, DateType d2)
{
UInt int1, int2;
int1 = DateToInt(d1);
int2 = DateToInt(d2);
if (int1 > int2)
return (1);
else if (int1 < int2)
return (-1);
return 0;
}
#if 0
/***********************************************************************
*
* FUNCTION: TimeCompare
*
* DESCRIPTION: This routine compares two times. "No time" is represented
* by minus one, and is considered less than all times.
*
* PARAMETERS: nothing
*
* RETURNED: if t1 > t2 returns a positive int
* if t1 < t2 returns a negative int
* if t1 = t2 returns zero
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 6/12/95 Initial Revision
*
***********************************************************************/
static Int TimeCompare (TimeType t1, TimeType t2)
{
Int int1, int2;
int1 = TimeToInt(t1);
int2 = TimeToInt(t2);
if (int1 > int2)
return (1);
else if (int1 < int2)
return (-1);
return 0;
}
/************************************************************
*
* FUNCTION: ApptAppInfoInit
*
* DESCRIPTION: Create and initialize the app info chunk if missing.
*
* PARAMETERS: database pointer
*
* RETURNS: 0 if successful, errorcode if not
*
* CREATED: 1/25/95
*
* BY: Roger Flores
*
*************************************************************/
Err ApptAppInfoInit(DmOpenRef dbP)
{
UInt cardNo;
VoidHand h;
LocalID dbID;
LocalID appInfoID;
ApptAppInfoPtr appInfoP;
if (DmOpenDatabaseInfo(dbP, &dbID, NULL, NULL, &cardNo, NULL))
return dmErrInvalidParam;
if (DmDatabaseInfo(cardNo, dbID, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &appInfoID, NULL, NULL, NULL))
return dmErrInvalidParam;
if (appInfoID == NULL)
{
h = DmNewHandle(dbP, sizeof(ApptAppInfoType));
if (! h) return dmErrMemError;
appInfoID = MemHandleToLocalID (h);
DmSetDatabaseInfo(cardNo, dbID, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &appInfoID, NULL, NULL, NULL);
}
// Get pointer to app Info chunk
appInfoP = MemLocalIDToLockedPtr(appInfoID, cardNo);
// Init it
DmSet(appInfoP, 0, sizeof(ApptAppInfoType), 0);
// Unlock it
MemPtrUnlock(appInfoP);
return 0;
}
/************************************************************
*
* FUNCTION: ApptComparePackedRecords
*
* DESCRIPTION: Compare two packed records.
*
* PARAMETERS: r1 - database record 1
* r2 - database record 2
* extra - extra data, not used in the function
*
* RETURNS: -1 if record one is less
* 1 if record two is less
*
* CREATED: 1/14/95
*
* BY: Roger Flores
*
* COMMENTS: Compare the two records key by key until
* there is a difference. Return -1 if r1 is less or 1 if r2
* is less. A zero is never returned because if two records
* seem identical then their unique IDs are compared!
*
*************************************************************/
static Int ApptComparePackedRecords (ApptPackedDBRecordPtr r1,
ApptPackedDBRecordPtr r2, Int extra, SortRecordInfoPtr info1,
SortRecordInfoPtr info2, VoidHand appInfoH)
{
Int result;
if ((r1->flags.repeat) || (r2->flags.repeat))
{
if ((r1->flags.repeat) && (r2->flags.repeat))
result = 0;
else if (r1->flags.repeat)
result = -1;
else
result = 1;
}
else
{
result = DateCompare (r1->when.date, r2->when.date);
if (result == 0)
{
result = TimeCompare (r1->when.startTime, r2->when.startTime);
}
}
return result;
}
/************************************************************
*
* FUNCTION: ApptPackedSize
*
* DESCRIPTION: Return the packed size of an ApptDBRecordType
*
* PARAMETERS: database record
*
* RETURNS: the size in bytes
*
* CREATED: 1/25/95
*
* BY: Roger Flores
*
*************************************************************/
static UInt ApptPackedSize (ApptDBRecordPtr r)
{
UInt size;
size = sizeof (ApptDateTimeType) + sizeof (ApptDBRecordFlags);
if (r->alarm != NULL)
size += sizeof (AlarmInfoType);
if (r->repeat != NULL)
size += sizeof (RepeatInfoType);
if (r->exceptions != NULL)
size += sizeof (UInt) +
(r->exceptions->numExceptions * sizeof (DateType));
if (r->description != NULL)
size += StrLen(r->description) + 1;
if (r->note != NULL)
size += StrLen(r->note) + 1;
return size;
}
/************************************************************
*
* FUNCTION: ApptPack
*
* DESCRIPTION: Pack an ApptDBRecordType
*
* PARAMETERS: database record
*
* RETURNS: the ApptPackedDBRecord is packed
*
* CREATED: 1/25/95
*
* BY: Roger Flores
*
*************************************************************/
static void ApptPack(ApptDBRecordPtr s, ApptPackedDBRecordPtr d)
{
ApptDBRecordFlags flags;
UInt size;
ULong offset = 0;
*(unsigned char *)&flags = 0; // clear the flags
// copy the ApptDateTimeType
//c = (char *) d;
offset = 0;
DmWrite(d, offset, s->when, sizeof(ApptDateTimeType));
offset += sizeof (ApptDateTimeType) + sizeof (ApptDBRecordFlags);
if (s->alarm != NULL)
{
DmWrite(d, offset, s->alarm, sizeof(AlarmInfoType));
offset += sizeof (AlarmInfoType);
flags.alarm = 1;
}
if (s->repeat != NULL)
{
DmWrite(d, offset, s->repeat, sizeof(RepeatInfoType));
offset += sizeof (RepeatInfoType);
flags.repeat = 1;
}
if (s->exceptions != NULL)
{
size = sizeof (UInt) +
(s->exceptions->numExceptions * sizeof (DateType));
DmWrite(d, offset, s->exceptions, size);
offset += size;
flags.exceptions = 1;
}
if (s->description != NULL)
{
size = StrLen(s->description) + 1;
DmWrite(d, offset, s->description, size);
offset += size;
flags.description = 1;
}
if (s->note != NULL)
{
size = StrLen(s->note) + 1;
DmWrite(d, offset, s->note, size);
offset += size;
flags.note = 1;
}
DmWrite(d, sizeof(ApptDateTimeType), &flags, sizeof(flags));
}
#endif /* 0 */
/************************************************************
*
* FUNCTION: ApptUnpack
*
* DESCRIPTION: Fills in the ApptDBRecord structure
*
* PARAMETERS: database record
*
* RETURNS: the record unpacked
*
* CREATED: 1/25/95
*
* BY: Roger Flores
*
*************************************************************/
static void ApptUnpack(ApptPackedDBRecordPtr src, ApptDBRecordPtr dest)
{
ApptDBRecordFlags flags;
char *p;
flags = src->flags;
p = &src->firstField;
dest->when = (ApptDateTimeType *) src;
if (flags.alarm)
{
dest->alarm = (AlarmInfoType *) p;
p += sizeof (AlarmInfoType);
}
else
dest->alarm = NULL;
if (flags.repeat)
{
dest->repeat = (RepeatInfoType *) p;
p += sizeof (RepeatInfoType);
}
else
dest->repeat = NULL;
if (flags.exceptions)
{
dest->exceptions = (ExceptionsListType *) p;
p += sizeof (UInt) +
(((ExceptionsListType *) p)->numExceptions * sizeof (DateType));
}
else
dest->exceptions = NULL;
if (flags.description)
{
dest->description = p;
p += StrLen(p) + 1;
}
else
dest->description = NULL;
if (flags.note)
{
dest->note = p;
}
else
dest->note = NULL;
}
#if 0
/************************************************************
*
* FUNCTION: ApptFindSortPosition
*
* DESCRIPTION: Return where a record is or should be
* Useful to find or find where to insert a record.
*
* PARAMETERS: database record
*
* RETURNS: position where a record should be
*
* CREATED: 1/25/95
*
* BY: Roger Flores
*
*************************************************************/
static UInt ApptFindSortPosition(DmOpenRef dbP, ApptPackedDBRecordPtr newRecord)
{
return (DmFindSortPosition (dbP, newRecord, NULL, (DmComparF *)ApptComparePackedRecords, 0));
}
/************************************************************
*
* FUNCTION: ApptSort
*
* DESCRIPTION: Sort the appointment database.
*
* PARAMETERS: database record
*
* RETURNS: nothing
*
* CREATED: 9/5/95
*
* BY: Art Lamb
*
*************************************************************/
void ApptSort (DmOpenRef dbP)
{
DmQuickSort(dbP, (DmComparF *)ApptComparePackedRecords, 0);
}
#endif /* 0 */
/***********************************************************************
*
* FUNCTION: ApptFindFirst
*
* DESCRIPTION: This routine finds the first appointment on the specified
* day.
*
* PARAMETERS: dbP - pointer to the database
* date - date to search for
* indexP - pointer to the index of the first record on the
* specified day (returned value)
*
* RETURNED: true if a record has found
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 6/15/95 Initial Revision
*
***********************************************************************/
Boolean ApptFindFirst (DmOpenRef dbP, DateType date, UIntPtr indexP)
{
Err err;
Int numOfRecords;
Int kmin, probe, i; // all positions in the database.
Int result = 0; // result of comparing two records
UInt index;
VoidHand recordH;
Boolean found = false;
ApptPackedDBRecordPtr r;
kmin = probe = 0;
numOfRecords = DmNumRecords(dbP);
while (numOfRecords > 0)
{
i = numOfRecords >> 1;
probe = kmin + i;
index = probe;
recordH = DmQueryNextInCategory (dbP, &index, dmAllCategories);
if (recordH)
{
r = (ApptPackedDBRecordPtr) MemHandleLock (recordH);
if (r->flags.repeat)
result = 1;
else
result = DateCompare (date, r->when.date);
MemHandleUnlock (recordH);
}
// If no handle, assume the record is deleted, deleted records
// are greater.
else
result = -1;
// If the date passed is less than the probe's date, keep searching.
if (result < 0)
numOfRecords = i;
// If the date passed is greater than the probe's date, keep searching.
else if (result > 0)
{
kmin = probe + 1;
numOfRecords = numOfRecords - i - 1;
}
// If the records are equal find the first record on the day.
else
{
found = true;
*indexP = index;
while (true)
{
err = DmSeekRecordInCategory (dbP, &index, 1, dmSeekBackward,
dmAllCategories);
if (err == dmErrSeekFailed) break;
recordH = DmQueryRecord(dbP, index);
r = (ApptPackedDBRecordPtr) MemHandleLock (recordH);
if (r->flags.repeat)
result = 1;
else
result = DateCompare (date, r->when.date);
MemHandleUnlock (recordH);
if (result != 0) break;
*indexP = index;
}
break;
}
}
// If that were no appointments on the specified day, return the
// index of the next appointment (on a future day).
if (! found)
{
if (result < 0)
*indexP = probe;
else
*indexP = probe + 1;
}
return (found);
}
/************************************************************
*
* FUNCTION: ApptGetRecord
*
* DESCRIPTION: Get a record from a Appointment Database
*
* PARAMETERS: database pointer
* database index
* database record
*
* RETURNS: ##0 if successful, errorcode if not
*
* CREATED: 1/25/95
*
* BY: Roger Flores
*
*************************************************************/
Err ApptGetRecord (DmOpenRef dbP, UInt index, ApptDBRecordPtr r,
VoidHand * handleP)
{
VoidHand handle;
ApptPackedDBRecordPtr src;
handle = DmQueryRecord(dbP, index);
ErrFatalDisplayIf(DmGetLastErr(), "Error Querying record");
src = (ApptPackedDBRecordPtr) MemHandleLock (handle);
if (DmGetLastErr())
{
*handleP = 0;
return DmGetLastErr();
}
ApptUnpack(src, r);
*handleP = handle;
return 0;
}
#if 0
/************************************************************
*
* FUNCTION: ApptChangeRecord
*
* DESCRIPTION: Change a record in the Appointment Database
*
* PARAMETERS: database pointer
* database index
* database record
* changed fields
*
* RETURNS: ##0 if successful, errorcode if not
*
* CREATED: 1/25/95
*
* BY: Roger Flores
*
* COMMENTS: Records are not stored with extra padding - they
* are always resized to their exact storage space. This avoids
* a database compression issue. The code works as follows:
*
* 1) get the size of the new record
* 2) make the new record
* 3) pack the packed record plus the changes into the new record
* 4) if the sort position is changes move to the new position
* 5) attach in position
*
*************************************************************/
Err ApptChangeRecord(DmOpenRef dbP, UInt *index, ApptDBRecordPtr r,
ApptDBRecordFlags changedFields)
{
Err result;
Int newIndex;
UInt attributes;
Boolean dontMove;
VoidHand oldH;
VoidHand srcH;
VoidHand dstH;
ApptDBRecordType src;
ApptPackedDBRecordPtr dst;
ApptPackedDBRecordPtr cmp;
// We do not assume that r is completely valid so we get a valid
// ApptDBRecordPtr...
if ((result = ApptGetRecord(dbP, *index, &src, &srcH)) != 0)
return result;
// and we apply the changes to it.
if (changedFields.when)
src.when = r->when;
if (changedFields.alarm)
src.alarm = r->alarm;
if (changedFields.repeat)
src.repeat = r->repeat;
if (changedFields.exceptions)
src.exceptions = r->exceptions;
if (changedFields.description)
src.description = r->description;
if (changedFields.note)
src.note = r->note;
// Allocate a new chunk with the correct size and pack the data from
// the unpacked record into it.
dstH = DmNewHandle(dbP, (ULong) ApptPackedSize(&src));
if (dstH)
{
dst = MemHandleLock (dstH);
ApptPack (&src, dst);
}
MemHandleUnlock (srcH);
if (dstH == NULL)
return dmErrMemError;
// If the sort position is changed move to the new position.
// Check if any of the key fields have changed.
if ((!changedFields.when) && (! changedFields.repeat))
goto attachRecord; // repeating events aren't in sorted order
// Make sure *index-1 < *index < *index+1, if so it's in sorted
// order. Leave it there.
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!
cmp = MemHandleLock (DmQueryRecord(dbP, *index-1));
dontMove = (ApptComparePackedRecords (cmp, dst, 0, NULL, NULL, 0) <= 0);
MemPtrUnlock (cmp);
}
else
dontMove = true;
if (dontMove && (*index+1 < DmNumRecords (dbP)))
{
DmRecordInfo(dbP, *index+1, &attributes, NULL, NULL);
if ( ! (attributes & dmRecAttrDelete) )
{
cmp = MemHandleLock (DmQueryRecord(dbP, *index+1));
dontMove &= (ApptComparePackedRecords (dst, cmp, 0, NULL, NULL, 0) <= 0);
MemPtrUnlock (cmp);
}
}
if (dontMove)
goto attachRecord;
// The record isn't in the right position. Move it.
newIndex = ApptFindSortPosition (dbP, dst);
DmMoveRecord (dbP, *index, newIndex);
if (newIndex > *index) newIndex--;
*index = newIndex; // return new position
attachRecord:
// Attach the new record to the old index, the preserves the
// category and record id.
result = DmAttachRecord (dbP, index, dstH, &oldH);
MemPtrUnlock(dst);
if (result) return result;
MemHandleFree(oldH);
#if EMULATION_LEVEL != EMULATION_NONE
ECApptDBValidate (dbP);
#endif
return 0;
}
/***********************************************************************
*
* FUNCTION: ApptAddException
*
* DESCRIPTION: This routine adds an entry to the exceptions list of the
* specified record.
*
* PARAMETERS: database pointer
* database index
* exception date
*
* RETURNED: error code
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 7/26/95 Initial Revision
*
***********************************************************************/
Err ApptAddException (DmOpenRef dbP, UInt *index, DateType date)
{
Err error;
UInt size;
VoidHand recordH;
ApptDBRecordType r;
ExceptionsListPtr exceptions;
ApptDBRecordFlags changedFields;
error = ApptGetRecord(dbP, *index, &r, &recordH);
if (error) return error;
// If the record already has an expections list, add an entry to
// the list.
if (r.exceptions)
{
size = sizeof (ExceptionsListType) +
(sizeof (DateType) * r.exceptions->numExceptions);
exceptions = MemPtrNew (size);
ErrFatalDisplayIf ((!exceptions), "Out of memory");
MemMove (exceptions, r.exceptions, size - sizeof (DateType));
exceptions->numExceptions++;
*(&exceptions->exception + r.exceptions->numExceptions) = date;
}
// Create an expections list.
else
{
size = sizeof (ExceptionsListType);
exceptions = MemPtrNew (size);
ErrFatalDisplayIf ((!exceptions), "Out of memory");
exceptions->numExceptions = 1;
exceptions->exception = date;
}
MemHandleUnlock (recordH);
// Update the record
r.exceptions = exceptions;
MemSet (&changedFields, sizeof (changedFields), 0);
changedFields.exceptions = true;
error = ApptChangeRecord (dbP, index, &r, changedFields);
MemPtrFree (exceptions);
return (error);
}
/***********************************************************************
*
* FUNCTION: ApptRepeatsOnDate
*
* DESCRIPTION: This routine returns true if a repeating appointment
* occurrs on the specified date.
*
* PARAMETERS: apptRec - a pointer to an appointment record
* date - date to check
*
* RETURNED: true if the appointment occurs on the date specified
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 6/14/95 Initial Revision
*
***********************************************************************/
Boolean ApptRepeatsOnDate (ApptDBRecordPtr apptRec, DateType date)
{
Int i;
Word freq;
Word weeksDiff;
Word dayInMonth;
Word dayOfWeek;
Word dayOfMonth;
Word firstDayOfWeek;
long dateInDays;
long startInDays;
Boolean onDate;
DatePtr exceptions;
DateType startDate;
// Is the date passed before the start date of the appointment?
if (DateCompare (date, apptRec->when->date) < 0)
return (false);
// Is the date passed after the end date of the appointment?
if (DateCompare (date, apptRec->repeat->repeatEndDate) > 0)
return (false);
// Get the frequency of occurrecne (ex: every 2nd day, every 3rd month, etc.).
freq = apptRec->repeat->repeatFrequency;
// Get the date of the first occurrecne of the appointment.
startDate = apptRec->when->date;
switch (apptRec->repeat->repeatType)
{
// Daily repeating appointment.
case repeatDaily:
dateInDays = DateToDays (date);
startInDays = DateToDays (startDate);
onDate = ((dateInDays - startInDays) % freq) == 0;
break;
// Weekly repeating appointment (ex: every Monday and Friday).
// Yes, weekly repeating appointment can occur more then once a
// week.
case repeatWeekly:
// Are we on a day of the week that the appointment repeats on.
dayOfWeek = DayOfWeek (date.month, date.day, date.year+firstYear);
onDate = ((1 << dayOfWeek) & apptRec->repeat->repeatOn);
if (! onDate) break;
// Are we in a week in which the appointment occurrs, if not
// move to that start of the next week in which the appointment
// does occur.
dateInDays = DateToDays (date);
startInDays = DateToDays (startDate);
firstDayOfWeek = (DayOfWeek (1, 1, firstYear) -
apptRec->repeat->repeatStartOfWeek + daysInWeek) % daysInWeek;
weeksDiff = (((dateInDays + firstDayOfWeek) / daysInWeek) -
((startInDays + firstDayOfWeek) / daysInWeek)) %freq;
onDate = (weeksDiff == 0);
break;
// // Compute the first occurrence of the appointment that occurs
// // on the same day of the week as the date passed.
// startDayOfWeek = DayOfWeek (startDate.month, startDate.day,
// startDate.year+firstYear);
// startInDays = DateToDays (startDate);
// if (startDayOfWeek < dayOfWeek)
// startInDays += dayOfWeek - startDayOfWeek;
// else if (startDayOfWeek > dayOfWeek)
// startInDays += dayOfWeek+ (daysInWeek *freq) - startDayOfWeek;
//
// // Are we in a week in which the appointment repeats.
// dateInDays = DateToDays (date);
// onDate = (((dateInDays - startInDays) / daysInWeek) % freq) == 0;
// break;
// Monthly-by-day repeating appointment (ex: the 3rd Friday of every
// month).
case repeatMonthlyByDay:
// Are we in a month in which the appointment repeats.
onDate = ((((date.year - startDate.year) * monthsInYear) +
(date.month - startDate.month)) % freq) == 0;
if (! onDate) break;
// Do the days of the month match (ex: 3rd Friday)
dayOfMonth = DayOfMonth (date.month, date.day, date.year+firstYear);
onDate = (dayOfMonth == apptRec->repeat->repeatOn);
if (onDate) break;
// If the appointment repeats on one of the last days of the month,
// check if the date passed is also one of the last days of the
// month. By last days of the month we mean: last sunday,
// last monday, etc.
if ((apptRec->repeat->repeatOn >= domLastSun) &&
(dayOfMonth >= dom4thSun))
{
dayOfWeek = DayOfWeek (date.month, date.day, date.year+firstYear);
dayInMonth = DaysInMonth (date.month, date.year+firstYear);
onDate = (((date.day + daysInWeek) > dayInMonth) &&
(dayOfWeek == (apptRec->repeat->repeatOn % daysInWeek)));
}
break;
// Monthly-by-date repeating appointment (ex: the 15th of every
// month).
case repeatMonthlyByDate:
// Are we in a month in which the appointment repeats.
onDate = ((((date.year - startDate.year) * monthsInYear) +
(date.month - startDate.month)) % freq) == 0;
if (! onDate) break;
// Are we on the same day of the month as the start date.
onDate = (date.day == startDate.day);
if (onDate) break;
// If the staring day of the appointment is greater then the
// number of day in the month passed, and the day passed is the
// last day of the month, then the appointment repeats on the day.
dayInMonth = DaysInMonth (date.month, date.year+firstYear);
onDate = ((startDate.day > dayInMonth) && (date.day == dayInMonth));
break;
// Yearly repeating appointment.
case repeatYearly:
// Are we in a year in which the appointment repeats.
onDate = ((date.year - startDate.year) % freq) == 0;
if (! onDate) break;
// Are we on the month and day that the appointment repeats.
onDate = (date.month == startDate.month) &&
(date.day == startDate.day);
if (onDate) break;
// Specal leap day processing.
if ( (startDate.month == february) &&
(startDate.day == 29) &&
(date.month == february) &&
(date.day == DaysInMonth (date.month, date.year+firstYear)))
{
onDate = true;
}
break;
}
// Check for an exception.
if ((onDate) && (apptRec->exceptions))
{
exceptions = &apptRec->exceptions->exception;
for (i = 0; i < apptRec->exceptions->numExceptions; i++)
{
if (DateCompare (date, exceptions[i]) == 0)
{
onDate = false;
break;
}
}
}
return (onDate);
}
/***********************************************************************
*
* FUNCTION: ApptNextRepeat
*
* DESCRIPTION: This routine computes the date of the next
* occurrence of a repeating appointment.
*
* PARAMETERS: apptRec - a pointer to an appointment record
* date - passed: date to start from
* returned: date of next occurrence
*
* RETURNED: true if the appointment occurs again
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 6/14/95 Initial Revision
*
***********************************************************************/
static Boolean NextRepeat (ApptDBRecordPtr apptRec, DatePtr dateP)
{
Int i;
Word day;
Word freq;
Word year;
Word adjust;
Word weeksDiff;
Word monthsDiff;
Word daysInMonth;
Word dayOfWeek;
Word apptWeekDay;
Word firstDayOfWeek;
Word daysTilNext;
Word monthsTilNext;
ULong dateInDays;
ULong startInDays;
DateType start;
DateType date;
DateType next;
date = *dateP;
// Is the date passed after the end date of the appointment?
if (DateCompare (date, apptRec->repeat->repeatEndDate) > 0)
return (false);
// Is the date passed before the start date of the appointment?
if (DateCompare (date, apptRec->when->date) < 0)
date = apptRec->when->date;
// Get the frequency on occurrecne (ex: every 2nd day, every 3rd month, etc).
freq = apptRec->repeat->repeatFrequency;
// Get the date of the first occurrecne of the appointment.
start = apptRec->when->date;
switch (apptRec->repeat->repeatType)
{
// Daily repeating appointment.
case repeatDaily:
dateInDays = DateToDays (date);
startInDays = DateToDays (start);
daysTilNext = (dateInDays - startInDays + freq - 1) / freq * freq;
if (startInDays + daysTilNext > (ULong) maxDays)
return (false);
DateDaysToDate (startInDays + daysTilNext, &next);
break;
// Weekly repeating appointment (ex: every Monday and Friday).
// Yes, weekly repeating appointment can occur more then once a
// week.
case repeatWeekly:
dateInDays = DateToDays (date);
startInDays = DateToDays (start);
firstDayOfWeek = (DayOfWeek (1, 1, firstYear) -
apptRec->repeat->repeatStartOfWeek + daysInWeek) % daysInWeek;
dayOfWeek = DayOfWeek (date.month, date.day, date.year+firstYear);
apptWeekDay = (dayOfWeek - apptRec->repeat->repeatStartOfWeek +
daysInWeek) % daysInWeek;
// Are we in a week in which the appointment occurrs, if not
// move to that start of the next week in which the appointment
// does occur.
weeksDiff = (((dateInDays + firstDayOfWeek) / daysInWeek) -
((startInDays + firstDayOfWeek) / daysInWeek)) %freq;
if (weeksDiff)
{
adjust = ((freq - weeksDiff) * daysInWeek)- apptWeekDay;
apptWeekDay = 0;
dayOfWeek = (dayOfWeek + adjust) % daysInWeek;
}
else
adjust = 0;
// Find the next day on which the appointment repeats.
for (i = 0; i < daysInWeek; i++)
{
if (apptRec->repeat->repeatOn & (1 << dayOfWeek)) break;
adjust++;
if (++dayOfWeek == daysInWeek)
dayOfWeek = 0;
if (++apptWeekDay == daysInWeek)
adjust += (freq - 1) * daysInWeek;
}
if (dateInDays + adjust > (ULong) maxDays)
return (false);
DateDaysToDate (dateInDays + adjust, &next);
// next = date;
// DateAdjust (&next, adjust);
break;
// Monthly-by-day repeating appointment (ex: the 3rd Friday of every
// month).
case repeatMonthlyByDay:
// Compute the number of month until the appointment repeats again.
monthsTilNext = (date.month - start.month);
monthsTilNext = ((((date.year - start.year) * monthsInYear) +
(date.month - start.month)) + freq - 1) /
freq * freq;
while (true)
{
year = start.year +
(start.month - 1 + monthsTilNext) / monthsInYear;
if (year >= numberOfYears)
return (false);
next.year = year;
next.month = (start.month - 1 + monthsTilNext) % monthsInYear + 1;
dayOfWeek = DayOfWeek (next.month, 1, next.year+firstYear);
if ((apptRec->repeat->repeatOn % daysInWeek) >= dayOfWeek)
day = apptRec->repeat->repeatOn - dayOfWeek + 1;
else
day = apptRec->repeat->repeatOn + daysInWeek - dayOfWeek + 1;
// If repeat-on day is between the last sunday and the last
// saturday, make sure we're not passed the end of the month.
if ( (apptRec->repeat->repeatOn >= domLastSun) &&
(day > DaysInMonth (next.month, next.year+firstYear)))
{
day -= daysInWeek;
}
next.day = day;
// Its posible that "next date" calculated above is
// before the date passed. If so, move forward
// by the length of the repeat freguency and preform
// the calculation again.
if ( DateToInt(date) > DateToInt (next))
monthsTilNext += freq;
else
break;
}
break;
// Monthly-by-date repeating appointment (ex: the 15th of every
// month).
case repeatMonthlyByDate:
// Compute the number of month until the appointment repeats again.
monthsDiff = ((date.year - start.year) * monthsInYear) +
(date.month - start.month);
monthsTilNext = (monthsDiff + freq - 1) / freq * freq;
if ((date.day > start.day) && (!(monthsDiff % freq)))
monthsTilNext += freq;
year = start.year +
(start.month - 1 + monthsTilNext) / monthsInYear;
if (year >= numberOfYears)
return (false);
next.year = year;
next.month = (start.month - 1 + monthsTilNext) % monthsInYear + 1;
next.day = start.day;
// Make sure we're not passed the last day of the month.
daysInMonth = DaysInMonth (next.month, next.year+firstYear);
if (next.day > daysInMonth)
next.day = daysInMonth;
break;
// Yearly repeating appointment.
case repeatYearly:
next.day = start.day;
next.month = start.month;
year = start.year +
((date.year - start.year + freq - 1) / freq * freq);
if ((date.month > start.month) ||
((date.month == start.month) && (date.day > start.day)))
year += freq;
// Specal leap day processing.
if ( (next.month == february) && (next.day == 29) &&
(next.day > DaysInMonth (next.month, year+firstYear)))
{
next.day = DaysInMonth (next.month, year+firstYear);
}
if (year >= numberOfYears)
return (false);
next.year = year;
break;
}
// Is the next occurrence after the end date of the appointment?
if (DateCompare (next, apptRec->repeat->repeatEndDate) > 0)
return (false);
ErrFatalDisplayIf ((DateToInt (next) < DateToInt (*dateP)),
"Calculation error");
*dateP = next;
return (true);
}
/***********************************************************************
*
* FUNCTION: IsException
*
* DESCRIPTION: This routine returns true the date passed is in a
* repeating appointment's exception list.
*
* PARAMETERS: apptRec - a pointer to an appointment record
* date - date to check
*
* RETURNED: true if the date is an exception date.
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 6/14/95 Initial Revision
*
***********************************************************************/
static Boolean IsException (ApptDBRecordPtr apptRec, DateType date)
{
int i;
DatePtr exceptions;
if (apptRec->exceptions)
{
exceptions = &apptRec->exceptions->exception;
for (i = 0; i < apptRec->exceptions->numExceptions; i++)
{
if (DateCompare (date, exceptions[i]) == 0)
return (true);
}
}
return (false);
}
/***********************************************************************
*
* FUNCTION: ApptNextRepeat
*
* DESCRIPTION: This routine computes the next occurrence of a
* repeating appointment.
*
* PARAMETERS: apptRec - a pointer to an appointment record
* dateP - passed: date to start from
* returned: date of next occurrence
*
* RETURNED: true if there is an occurrence of the appointment
* between the date passed and the appointment's end date
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 6/20/95 Initial Revision
*
***********************************************************************/
Boolean ApptNextRepeat (ApptDBRecordPtr apptRec, DatePtr dateP)
{
DateType date;
date = *dateP;
while (true)
{
// Compute the next time the appointment repeats.
if (! NextRepeat (apptRec, &date))
return (false);
// Check if the date computed is in the exceptions list.
if (! IsException (apptRec, date))
{
*dateP = date;
return (true);
}
DateAdjust (&date, 1);
}
}
/***********************************************************************
*
* FUNCTION: ApptListCompare
*
* DESCRIPTION: This routine compares two entries in the appointment list,
* it's called by ApptGetAppointments via the quick sort
* routine.
*
* PARAMETERS: a - a pointer to an entry in the appointment list
* b - a pointer to an entry in the appointment list
* extra - extra data passed to quick sort - not used
*
* RETURNED: if a1 > a2 returns a positive int
* if a1 < a2 returns a negative int
* if a1 = a2 returns zero
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 6/15/95 Initial Revision
*
***********************************************************************/
static Int ApptListCompare (ApptInfoPtr a1, ApptInfoPtr a2, Long extra)
{
Int result;
result = TimeCompare (a1->startTime, a2->startTime);
if (result == 0)
{
result = TimeCompare (a1->endTime, a2->endTime);
}
return result;
}
/***********************************************************************
*
* FUNCTION: ApptGetAppointments
*
* DESCRIPTION: This routine returns a list of appointments that are on
* the date specified
*
* PARAMETERS: dbP - pointer to the database
* date - date to search for
* countP - number of appointments on the specified
* day (returned value)
*
* RETURNED: handle of the appointment list (ApptInfoType)
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 6/15/95 Initial Revision
*
***********************************************************************/
#if 0
VoidHand ApptGetAppointments (DmOpenRef dbP, DateType date, UIntPtr countP)
{
Err error;
Int result;
Int count = 0;
UInt recordNum;
Boolean repeats;
VoidHand recordH;
VoidHand apptListH;
ApptInfoPtr apptList;
ApptDBRecordType apptRec;
ApptPackedDBRecordPtr r;
// Allocated a block to hold the appointment list.
apptListH = MemHandleNew (sizeof (ApptInfoType) * apptMaxPerDay);
ErrFatalDisplayIf(!apptListH, "Out of memory");
if (! apptListH) return (0);
apptList = MemHandleLock (apptListH);
// Find the first non-repeating appointment of the day.
if (ApptFindFirst (dbP, date, &recordNum))
{
while (count < apptMaxPerDay)
{
// Check if the appointment is on the date passed, if it is
// add it to the appointment list.
recordH = DmQueryRecord (dbP, recordNum);
r = MemHandleLock (recordH);
result = DateCompare (r->when.date, date);
if (result == 0)
{
// Add the record to the appoitment list.
apptList[count].startTime = r->when.startTime;
apptList[count].endTime = r->when.endTime;
apptList[count].recordNum = recordNum;
count++;
}
MemHandleUnlock (recordH);
if (result != 0) break;
// Get the next record.
error = DmSeekRecordInCategory (dbP, &recordNum, 1, dmSeekForward, dmAllCategories);
if (error == dmErrSeekFailed) break;
}
}
// Add the repeating appointments to the list. Repeating appointments
// are stored at the beginning of the database.
recordNum = 0;
while (count < apptMaxPerDay)
{
recordH = DmQueryNextInCategory (dbP, &recordNum, dmAllCategories);
if (! recordH) break;
r = (ApptPackedDBRecordPtr) MemHandleLock (recordH);
repeats = (r->flags.repeat != 0);
if (repeats)
{
ApptUnpack (r, &apptRec);
if (ApptRepeatsOnDate (&apptRec, date))
{
// Add the record to the appoitment list.
apptList[count].startTime = r->when.startTime;
apptList[count].endTime = r->when.endTime;
apptList[count].recordNum = recordNum;
count++;
}
}
MemHandleUnlock (recordH);
// If the record has no repeating info we've reached the end of the
// repeating appointments.
if (! repeats) break;
recordNum++;
}
// Sort the list by start time.
SysInsertionSort (apptList, count, sizeof (ApptInfoType), ApptListCompare, 0L);
// If there are no appointments on the specified day, free the appointment
// list.
if (count == 0)
{
MemPtrFree (apptList);
apptListH = 0;
}
// Resize the appointment list block to release any unused space.
else
{
MemHandleUnlock (apptListH);
MemHandleResize (apptListH, count * sizeof (ApptInfoType));
}
*countP = count;
return (apptListH);
}
#endif
/***********************************************************************
*
* FUNCTION: ApptGetAlarmTime
*
* DESCRIPTION: This routine determines the date and time of the next alarm
* for the appointment passed.
*
* PARAMETERS: apptRec - pointer to an appointment record
* currentTime - current date and time in seconds
*
* RETURNED: date and time of the alarm, in seconds, or zero if there
* is no alarm
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 6/20/95 Initial Revision
*
***********************************************************************/
ULong ApptGetAlarmTime (ApptDBRecordPtr apptRec, ULong currentTime)
{
ULong advance;
ULong alarmTime;
DateType repeatDate;
DateTimeType curDateTime;
DateTimeType apptDateTime;
// Non-repeating appointment?
if (! apptRec->repeat)
{
// An alarm on an untimed event triggers at midnight.
if (TimeToInt (apptRec->when->startTime) == apptNoTime)
{
apptDateTime.minute = 0;
apptDateTime.hour = 0;
}
else
{
apptDateTime.minute = apptRec->when->startTime.minutes;
apptDateTime.hour = apptRec->when->startTime.hours;
}
apptDateTime.second = 0;
apptDateTime.day = apptRec->when->date.day;
apptDateTime.month = apptRec->when->date.month;
apptDateTime.year = apptRec->when->date.year + firstYear;
// Compute the time of the alarm by adjusting the date and time
// of the appointment by the length of the advance notice.
advance = apptRec->alarm->advance;
switch (apptRec->alarm->advanceUnit)
{
case aauMinutes:
advance *= minutesInSeconds;
break;
case aauHours:
advance *= hoursInSeconds;
break;
case aauDays:
advance *= daysInSeconds;
break;
}
alarmTime = TimDateTimeToSeconds (&apptDateTime) - advance;
if (alarmTime >= currentTime)
return (alarmTime);
else
return (0);
}
// Repeating appointment.
TimSecondsToDateTime (currentTime, &curDateTime);
repeatDate.year = curDateTime.year - firstYear;
repeatDate.month = curDateTime.month;
repeatDate.day = curDateTime.day;
while (ApptNextRepeat (apptRec, &repeatDate))
{
// An alarm on an untimed event triggers at midnight.
if (TimeToInt (apptRec->when->startTime) == apptNoTime)
{
apptDateTime.minute = 0;
apptDateTime.hour = 0;
}
else
{
apptDateTime.minute = apptRec->when->startTime.minutes;
apptDateTime.hour = apptRec->when->startTime.hours;
}
apptDateTime.second = 0;
apptDateTime.day = repeatDate.day;
apptDateTime.month = repeatDate.month;
apptDateTime.year = repeatDate.year + firstYear;
// Compute the time of the alarm by adjusting the date and time
// of the appointment by the length of the advance notice.
switch (apptRec->alarm->advanceUnit)
{
case aauMinutes:
advance = (ULong) apptRec->alarm->advance * minutesInSeconds;
break;
case aauHours:
advance = (ULong) apptRec->alarm->advance * hoursInSeconds;
break;
case aauDays:
advance = (ULong) apptRec->alarm->advance * daysInSeconds;
break;
}
alarmTime = TimDateTimeToSeconds (&apptDateTime) - advance;
if (alarmTime >= currentTime)
return (alarmTime);
DateAdjust (&repeatDate, 1);
}
return (0);
}
/***********************************************************************
*
* FUNCTION: ApptGetAlarmsList
*
* DESCRIPTION: This routine returns a list of appointment that have
* alarm scheduled at the specified time. The list contains
* the handles of the appointment record.
*
* PARAMETERS: dbP - pointer to the database
* alarmTime - time of the alarm expressed as seconds from
* 1/1/1904.
* countP - returnd: number of entries in list
* quiteP - returnd: true if alarm sound should not play.
*
* RETURNED: the handle of a block containing the record handles of
* appointments that have alarms scheduled at the passed time,
* or zero if there are not alarms scheduled.
*
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 8/30/95 Initial Revision
*
***********************************************************************/
VoidHand ApptGetAlarmsList (DmOpenRef dbP, ULong alarmTime, UIntPtr countP,
BooleanPtr quiteP)
{
UInt count = 0;
UInt numBytes;
UInt recordNum;
UInt numRecords;
ULong next;
VoidHand recordH;
WordPtr alarmList;
VoidHand alarmListH;
ApptDBRecordType apptRec;
ApptPackedDBRecordPtr r;
*quiteP = true;
// Allocate a block to hold the a list of record handles.
numBytes = sizeof (VoidHand) * apptMaxDisplayableAlarms;
alarmListH = MemHandleNew (numBytes);
ErrFatalDisplayIf(!alarmListH, "Out of memory");
alarmList = MemHandleLock (alarmListH);
MemSet (alarmList, numBytes, 0);
// Searce the database for appointments with alarms at the passed time.
numRecords = DmNumRecords(dbP);
for (recordNum = 0; recordNum < numRecords; recordNum++)
{
recordH = DmQueryRecord(dbP, recordNum);
if (! recordH) break;
r = (ApptPackedDBRecordPtr) MemHandleLock (recordH);
if (r->flags.alarm)
{
ApptUnpack (r, &apptRec);
next = ApptGetAlarmTime (&apptRec, alarmTime);
if (next == alarmTime)
{
// Add the record to the alarm list.
alarmList[count] = recordNum;
count++;
// If the alarm is on a timed event then we sould play
// the alarm sound.
if (TimeToInt (apptRec.when->startTime) != apptNoTime)
*quiteP = false;
}
}
MemHandleUnlock (recordH);
if (count == apptMaxDisplayableAlarms) break;
}
MemPtrUnlock (alarmList);
// Free the list if no alarms were found.
if (count == 0)
{
MemHandleFree (alarmListH);
alarmListH = 0;
}
*countP = count;
return (alarmListH);
}
/***********************************************************************
*
* FUNCTION: ApptGetTimeOfNextAlarm
*
* DESCRIPTION: This routine determines the time of the next scheduled
* alarm.
*
* PARAMETERS: dbP - pointer to the database
* timeInSeconds - time to search forward from
*
* RETURNED: time of the next alarm, in seconds from 1/1/1904, or
* zreo if there are no alarms scheduled.
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 6/20/95 Initial Revision
*
***********************************************************************/
ULong ApptGetTimeOfNextAlarm (DmOpenRef dbP, ULong timeInSeconds)
{
UInt recordNum;
UInt numRecords;
ULong alarmTime;
ULong nextAlarmTime = 0;
VoidHand nextAlarmH = 0;
VoidHand recordH;
ApptDBRecordType apptRec;
ApptPackedDBRecordPtr r;
numRecords = DmNumRecords(dbP);
for (recordNum = 0; recordNum < numRecords; recordNum++)
{
recordH = DmQueryRecord(dbP, recordNum);
if (! recordH) break;
r = (ApptPackedDBRecordPtr) MemHandleLock (recordH);
// Does the record have any alarm info.
if (r->flags.alarm)
{
ApptUnpack (r, &apptRec);
alarmTime = ApptGetAlarmTime (&apptRec, timeInSeconds);
if (alarmTime && ((! nextAlarmTime) || (alarmTime < nextAlarmTime)))
nextAlarmTime = alarmTime;
}
MemHandleUnlock (recordH);
}
return (nextAlarmTime);
}
/************************************************************
*
* FUNCTION: ApptNewRecord
*
* DESCRIPTION: Create a new packed record in sorted position
*
* PARAMETERS: database pointer
* database record
*
* RETURNS: ##0 if successful, error code if not
*
* CREATED: 1/25/95
*
* BY: Roger Flores
*
*************************************************************/
Err ApptNewRecord(DmOpenRef dbP, ApptDBRecordPtr r, UInt *index)
{
VoidHand recordH;
ApptPackedDBRecordPtr recordP;
UInt newIndex;
Err err;
// Make a new chunk with the correct size.
recordH = DmNewHandle (dbP, (ULong) ApptPackedSize(r));
if (recordH == NULL)
return dmErrMemError;
recordP = MemHandleLock (recordH);
// Copy the data from the unpacked record to the packed one.
ApptPack (r, recordP);
newIndex = ApptFindSortPosition(dbP, recordP);
MemPtrUnlock (recordP);
// 4) attach in place
err = DmAttachRecord(dbP, &newIndex, recordH, 0);
if (err)
MemHandleFree(recordH);
else
*index = newIndex;
return err;
}
/***********************************************************************
*
* FUNCTION: AddAppointmentToList
*
* DESCRIPTION: This routine adds an appointment to a list of appointments.
*
* PARAMETERS: dbP - pointer to the database
* date - date to search for
* countP - number of appointments on the specified
* day (returned value)
*
* RETURNED: handle of the appointment list (ApptInfoType)
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 4/17/96 Initial Revision
*
***********************************************************************/
static Boolean AddAppointmentToList (VoidHand * apptListH, UInt count,
TimeType startTime, TimeType endTime, UInt recordNum)
{
Err err;
Word newSize;
ApptInfoPtr apptList;
if (count == 0)
{
// Allocated a block to hold the appointment list.
*apptListH = MemHandleNew (sizeof (ApptInfoType) * (apptMaxPerDay / 10));
ErrFatalDisplayIf(!apptListH, "Out of memory");
if (! apptListH) return (false);
apptList = MemHandleLock (*apptListH);
}
// Resize the list to hold more more appointments.
else if ((count % (apptMaxPerDay / 10)) == 0)
{
if (count + (apptMaxPerDay / 10) > apptMaxPerDay)
return (false);
MemHandleUnlock (*apptListH);
newSize = sizeof (ApptInfoType) * (count + (apptMaxPerDay / 10));
err = MemHandleResize (*apptListH, newSize);
apptList = MemHandleLock (*apptListH);
ErrFatalDisplayIf(err, "Out of memory");
if (err) return (false);
}
else
apptList = MemDeref (*apptListH);
apptList[count].startTime = startTime;
apptList[count].endTime = endTime;
apptList[count].recordNum = recordNum;
return (true);
}
/***********************************************************************
*
* FUNCTION: ApptGetAppointments
*
* DESCRIPTION: This routine returns a list of appointments that are in
* the range of dates specified
*
* PARAMETERS: dbP - pointer to the database
* date - start date to search from
* days - number a days in search range
* apptLists - returned: array of handle of the
* appointment list (ApptInfoType)
* counts - returned: returned: array of counts of the
* number of appointments in each list.
*
* RETURNED: nothing
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 4/7/96 Initial Revision
*
***********************************************************************/
void ApptGetAppointments (DmOpenRef dbP, DateType date, Word days,
VoidHand apptLists [], UInt counts [])
{
Int count = 0;
UInt startDate;
UInt endDate;
UInt index;
UInt recordNum;
ULong dateInDays;
DateType apptDate;
TimeType startTime;
TimeType endTime;
DateType tempDate;
DateType repeatDate;
Boolean repeats;
VoidHand recordH;
ApptInfoPtr apptList;
ApptDBRecordType apptRec;
ApptPackedDBRecordPtr r;
MemSet (apptLists, days * sizeof (VoidHand), 0);
MemSet (counts, days * sizeof (UInt), 0);
startDate = DateToInt (date);
tempDate = date;
DateAdjust (&tempDate, days-1);
endDate = DateToInt(tempDate);
// Find the first non-repeating appointment of the day.
ApptFindFirst (dbP, date, &recordNum);
while (true)
{
recordH = DmQueryNextInCategory (dbP, &recordNum, dmAllCategories);
if (! recordH) break;
// Check if the appointment is on the date passed, if it is
// add it to the appointment list.
r = MemHandleLock (recordH);
startTime = r->when.startTime;
endTime = r->when.endTime;
apptDate = r->when.date;
MemHandleUnlock (recordH);
if ((DateToInt (apptDate) < startDate) ||
(DateToInt (apptDate) > endDate))
break;
// Add the record to the appoitment list.
index = DateToDays (apptDate) - DateToDays (date);
if (AddAppointmentToList (&apptLists[index], counts[index],
startTime, endTime, recordNum))
counts[index]++;
else
break;
recordNum++;
}
// Add the repeating appointments to the list. Repeating appointments
// are stored at the beginning of the database.
recordNum = 0;
dateInDays = DateToDays (date);
while (true)
{
recordH = DmQueryNextInCategory (dbP, &recordNum, dmAllCategories);
if (! recordH) break;
r = (ApptPackedDBRecordPtr) MemHandleLock (recordH);
repeats = (r->flags.repeat != 0);
if (repeats)
{
ApptUnpack (r, &apptRec);
if (days == 1)
{
if (ApptRepeatsOnDate (&apptRec, date))
{
if (AddAppointmentToList (apptLists, *counts,
r->when.startTime, r->when.endTime, recordNum))
(*counts)++;
}
}
else
{
repeatDate = date;
while (ApptNextRepeat (&apptRec, &repeatDate))
{
if (DateToInt (repeatDate) > endDate)
break;
// Add the record to the appoitment list.
index = DateToDays (repeatDate) - dateInDays;
if (AddAppointmentToList (&apptLists[index], counts[index],
r->when.startTime, r->when.endTime, recordNum))
counts[index]++;
else
break;
if (DateToInt (repeatDate) == endDate)
break;
DateAdjust (&repeatDate, 1);
}
}
}
MemHandleUnlock (recordH);
// If the record has no repeating info we've reached the end of the
// repeating appointments.
if (! repeats) break;
recordNum++;
}
// Sort the list by start time.
for (index = 0; index < days; index ++)
{
if (apptLists[index])
{
apptList = MemDeref (apptLists[index]);
SysInsertionSort (apptList, counts[index], sizeof (ApptInfoType),
(_comparF *)ApptListCompare, 0L);
MemHandleUnlock (apptLists[index]);
MemHandleResize (apptLists[index], counts[index] *
sizeof (ApptInfoType));
}
}
}
#endif /* 0 */