home *** CD-ROM | disk | FTP | other *** search
/ linuxmafia.com 2016 / linuxmafia.com.tar / linuxmafia.com / pub / palmos / happydays-src-1.37.tar.gz / happydays-src-1.37.tar / happydays-1.37 / datebook.c < prev    next >
C/C++ Source or Header  |  2000-07-27  |  30KB  |  987 lines

  1. /*
  2. HappyDays - A Birthdate displayer for the PalmPilot
  3. Copyright (C) 1999-2000 JaeMok Jeong
  4.  
  5. This program is free software; you can redistribute it and/or
  6. modify it under the terms of the GNU General Public License
  7. as published by the Free Software Foundation; either version 2
  8. of the License, or (at your option) any later version.
  9.  
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. GNU General Public License for more details.
  14.  
  15. You should have received a copy of the GNU General Public License
  16. along with this program; if not, write to the Free Software
  17. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  18. */
  19.  
  20. /*
  21.  * Just so you know these routines are pulled from the Datebook source code
  22.  * as provided by Palm.  At least that is what I started from, I may have
  23.  * touched a few lines to get it to compile with gcc without warnings.
  24.  */
  25.  
  26. #include <PalmOS.h>
  27. #include "address.h"
  28. #include "datebook.h"
  29.  
  30. #include "happydays.h"
  31. #include "happydaysRsc.h"
  32.  
  33. /***********************************************************************
  34.  *  
  35.  *  Internal Structutes
  36.  *  
  37.  ***********************************************************************/
  38.     
  39. // The following structure doesn't really exist.  The first field
  40. // varies depending on the data present.  However, it is convient
  41. // (and less error prone) to use when accessing the other information.
  42. typedef struct {
  43.     ApptDateTimeType    when;
  44.     ApptDBRecordFlags   flags;  // A flag set for each  datum present
  45.     char                firstField;
  46. } ApptPackedDBRecordType;
  47.  
  48. typedef ApptPackedDBRecordType * ApptPackedDBRecordPtr;
  49.  
  50. typedef Int16 comparF (const void *, const void *, Int16 other);
  51.  
  52. /***********************************************************************
  53.  *
  54.  *  Internal Routines
  55.  *
  56.  ***********************************************************************/
  57.  
  58. static Int16 TimeCompare (TimeType t1, TimeType t2);
  59.  
  60. /***********************************************************************
  61.  *
  62.  * FUNCTION:    DateCompare
  63.  *
  64.  * DESCRIPTION: This routine compares two dates.
  65.  *
  66.  * PARAMETERS:  d1 - a date 
  67.  *              d2 - a date 
  68.  *
  69.  * RETURNED:    if d1 > d2  returns a positive int
  70.  *              if d1 < d2  returns a negative int
  71.  *              if d1 = d2  returns zero
  72.  *
  73.  * NOTE: This routine treats the DateType structure like an unsigned int,
  74.  *       it depends on the fact the the members of the structure are ordered
  75.  *       year, month, day form high bit to low low bit.
  76.  *
  77.  ***********************************************************************/
  78. Int16 DateCompare(DateType d1, DateType d2)
  79. {
  80.     UInt16 int1, int2;
  81.     
  82.     int1 = DateToInt(d1);
  83.     int2 = DateToInt(d2);
  84.     
  85.     if (int1 > int2) return (1);
  86.     else if (int1 < int2) return (-1);
  87.     return 0;
  88. }
  89.  
  90. /***********************************************************************
  91.  *
  92.  * FUNCTION:    TimeCompare
  93.  *
  94.  * DESCRIPTION: This routine compares two times.  "No time" is represented
  95.  *              by minus one, and is considered less than all times.
  96.  *
  97.  * PARAMETERS:  nothing
  98.  *
  99.  * RETURNED:    if t1 > t2  returns a positive int
  100.  *              if t1 < t2  returns a negative int
  101.  *              if t1 = t2  returns zero
  102.  *
  103.  * REVISION HISTORY:
  104.  *          Name    Date        Description
  105.  *          ----    ----        -----------
  106.  *          art 6/12/95     Initial Revision
  107.  *
  108.  ***********************************************************************/
  109. static Int16 TimeCompare (TimeType t1, TimeType t2) {
  110.     Int16 int1, int2;
  111.  
  112.     int1 = TimeToInt(t1);
  113.     int2 = TimeToInt(t2);
  114.  
  115.     if (int1 > int2)
  116.         return (1);
  117.     else if (int1 < int2)
  118.         return (-1);
  119.     return 0;
  120.  
  121. }
  122.  
  123. /************************************************************
  124.  *
  125.  *  FUNCTION:    ApptComparePackedRecords
  126.  *
  127.  *  DESCRIPTION: Compare two packed records.
  128.  *
  129.  *  PARAMETERS:  r1    - database record 1
  130.  *               r2    - database record 2
  131.  *               extra - extra data, not used in the function
  132.  *
  133.  *  RETURNS:    -1 if record one is less
  134.  *                 1 if record two is less
  135.  *
  136.  *  CREATED: 1/14/95 
  137.  *
  138.  *  BY: Roger Flores
  139.  *
  140.  *  COMMENTS:   Compare the two records key by key until
  141.  *  there is a difference.  Return -1 if r1 is less or 1 if r2
  142.  *  is less.  A zero is never returned because if two records
  143.  *  seem identical then their unique IDs are compared!
  144.  *
  145.  *************************************************************/
  146. static Int16
  147. ApptComparePackedRecords (ApptPackedDBRecordPtr r1,
  148.                           ApptPackedDBRecordPtr r2, Int16 extra,
  149.                           SortRecordInfoPtr info1,
  150.                           SortRecordInfoPtr info2, MemHandle appInfoH)
  151. {
  152.     Int16 result;
  153.  
  154.     if ((r1->flags.repeat) || (r2->flags.repeat)) {
  155.         if ((r1->flags.repeat) && (r2->flags.repeat))
  156.             result = 0;
  157.         else if (r1->flags.repeat)
  158.             result = -1;
  159.         else
  160.             result = 1;
  161.     }
  162.     else {
  163.         result = DateCompare (r1->when.date, r2->when.date);
  164.         if (result == 0) {
  165.             result = TimeCompare (r1->when.startTime, r2->when.startTime);
  166.         }
  167.     }
  168.     return result;
  169. }
  170.  
  171. /************************************************************
  172.  *
  173.  *  FUNCTION: ApptPackedSize
  174.  *
  175.  *  DESCRIPTION: Return the packed size of an ApptDBRecordType 
  176.  *
  177.  *  PARAMETERS: database record
  178.  *
  179.  *  RETURNS: the size in bytes
  180.  *
  181.  *  CREATED: 1/25/95 
  182.  *
  183.  *  BY: Roger Flores
  184.  *
  185.  *************************************************************/
  186. static UInt16 ApptPackedSize (ApptDBRecordPtr r) {
  187.     UInt16 size;
  188.  
  189.  
  190.     size = sizeof (ApptDateTimeType) + sizeof (ApptDBRecordFlags);
  191.  
  192.     if (r->alarm != NULL)
  193.         size += sizeof (AlarmInfoType);
  194.  
  195.     if (r->repeat != NULL)
  196.         size += sizeof (RepeatInfoType);
  197.  
  198.     if (r->exceptions != NULL)
  199.         size += sizeof (UInt16) +
  200.             (r->exceptions->numExceptions * sizeof (DateType));
  201.  
  202.     if (r->description != NULL)
  203.         size += StrLen(r->description) + 1;
  204.  
  205.     if (r->note != NULL)
  206.         size += StrLen(r->note) + 1;
  207.  
  208.  
  209.     return size;
  210. }
  211.  
  212.  
  213. /************************************************************
  214.  *
  215.  *  FUNCTION: ApptPack
  216.  *
  217.  *  DESCRIPTION: Pack an ApptDBRecordType
  218.  *
  219.  *  PARAMETERS: database record
  220.  *
  221.  *  RETURNS: the ApptPackedDBRecord is packed
  222.  *
  223.  *  CREATED: 1/25/95 
  224.  *
  225.  *  BY: Roger Flores
  226.  *
  227.  *************************************************************/
  228. static void ApptPack(ApptDBRecordPtr s, ApptPackedDBRecordPtr d) {
  229.     ApptDBRecordFlags   flags;
  230.     UInt16    size;
  231.     UInt32    offset = 0;
  232.  
  233.  
  234.     *(unsigned char *)&flags = 0;           // clear the flags
  235.  
  236.  
  237.     // copy the ApptDateTimeType
  238.     //c = (char *) d;
  239.     offset = 0;
  240.     DmWrite(d, offset, s->when, sizeof(ApptDateTimeType));
  241.     offset += sizeof (ApptDateTimeType) + sizeof (ApptDBRecordFlags);
  242.  
  243.  
  244.     if (s->alarm != NULL) {
  245.         DmWrite(d, offset, s->alarm, sizeof(AlarmInfoType));
  246.         offset += sizeof (AlarmInfoType);
  247.         flags.alarm = 1;
  248.     }
  249.  
  250.  
  251.     if (s->repeat != NULL) {
  252.         DmWrite(d, offset, s->repeat, sizeof(RepeatInfoType));
  253.         offset += sizeof (RepeatInfoType);
  254.         flags.repeat = 1;
  255.     }
  256.  
  257.  
  258.     if (s->exceptions != NULL) {
  259.         size = sizeof (UInt16) +
  260.             (s->exceptions->numExceptions * sizeof (DateType));
  261.         DmWrite(d, offset, s->exceptions, size);
  262.         offset += size;
  263.         flags.exceptions = 1;
  264.     }
  265.  
  266.  
  267.     if (s->description != NULL) {
  268.         size = StrLen(s->description) + 1;
  269.         DmWrite(d, offset, s->description, size);
  270.         offset += size;
  271.         flags.description = 1;
  272.     }
  273.  
  274.  
  275.  
  276.     if (s->note != NULL) {
  277.         size = StrLen(s->note) + 1;
  278.         DmWrite(d, offset, s->note, size);
  279.         offset += size;
  280.         flags.note = 1;
  281.     }
  282.  
  283.     DmWrite(d, sizeof(ApptDateTimeType), &flags, sizeof(flags));
  284. }
  285.  
  286. /************************************************************
  287.  *
  288.  *  FUNCTION: ApptUnpack
  289.  *
  290.  *  DESCRIPTION: Fills in the ApptDBRecord structure
  291.  *
  292.  *  PARAMETERS: database record
  293.  *
  294.  *  RETURNS: the record unpacked
  295.  *
  296.  *  CREATED: 1/25/95 
  297.  *
  298.  *  BY: Roger Flores
  299.  *
  300.  *************************************************************/
  301. static void ApptUnpack(ApptPackedDBRecordPtr src, ApptDBRecordPtr dest) {
  302.     ApptDBRecordFlags   flags;
  303.     char *p;
  304.  
  305.  
  306.     flags = src->flags;
  307.     p = &src->firstField;
  308.     dest->when = (ApptDateTimeType *) src;
  309.  
  310.     if (flags.alarm) {
  311.         dest->alarm = (AlarmInfoType *) p;
  312.         p += sizeof (AlarmInfoType);
  313.     } else
  314.         dest->alarm = NULL;
  315.  
  316.  
  317.     if (flags.repeat) {
  318.         dest->repeat = (RepeatInfoType *) p;
  319.         p += sizeof (RepeatInfoType);
  320.     } else
  321.         dest->repeat = NULL;
  322.  
  323.  
  324.     if (flags.exceptions) {
  325.         dest->exceptions = (ExceptionsListType *) p;
  326.         p += sizeof (UInt16) +
  327.             (((ExceptionsListType *) p)->numExceptions * sizeof (DateType));
  328.     } else
  329.         dest->exceptions = NULL;
  330.  
  331.  
  332.     if (flags.description) {
  333.         dest->description = p;
  334.         p += StrLen(p) + 1;
  335.     } else
  336.         dest->description = NULL;
  337.  
  338.  
  339.     if (flags.note) {
  340.         dest->note = p;
  341.     } else
  342.         dest->note = NULL;
  343.  
  344. }
  345.  
  346. /************************************************************
  347.  *
  348.  *  FUNCTION: ApptFindSortPosition
  349.  *
  350.  *  DESCRIPTION: Return where a record is or should be
  351.  *      Useful to find or find where to insert a record.
  352.  *
  353.  *  PARAMETERS: database record
  354.  *
  355.  *  RETURNS: position where a record should be
  356.  *
  357.  *  CREATED: 1/25/95 
  358.  *
  359.  *  BY: Roger Flores
  360.  *
  361.  *************************************************************/
  362. static UInt16 ApptFindSortPosition(DmOpenRef dbP, ApptPackedDBRecordPtr
  363.                                  newRecord)
  364. {
  365.     return (DmFindSortPosition (dbP, newRecord, NULL,
  366.                                 (DmComparF *)ApptComparePackedRecords, 0));
  367. }
  368.  
  369. /***********************************************************************
  370.  *
  371.  * FUNCTION:    ApptFindFirst
  372.  *
  373.  * DESCRIPTION: This routine finds the first appointment on the specified
  374.  *              day.
  375.  *
  376.  * PARAMETERS:  dbP    - pointer to the database
  377.  *              date   - date to search for
  378.  *              indexP - pointer to the index of the first record on the 
  379.  *                       specified day (returned value)
  380.  *
  381.  * RETURNED:    true if a record has found
  382.  *
  383.  * REVISION HISTORY:
  384.  *          Name    Date        Description
  385.  *          ----    ----        -----------
  386.  *          art 6/15/95     Initial Revision
  387.  *
  388.  ***********************************************************************/
  389. Boolean ApptFindFirst (DmOpenRef dbP, DateType date, UInt16 * indexP) {
  390.     Err err;
  391.     Int16 numOfRecords;
  392.     Int16 kmin, probe, i;     // all positions in the database.
  393.     Int16 result = 0;         // result of comparing two records
  394.     UInt16 index;
  395.     MemHandle recordH;
  396.     Boolean found = false;
  397.     ApptPackedDBRecordPtr r;
  398.  
  399.     kmin = probe = 0;
  400.     numOfRecords = DmNumRecords(dbP);
  401.  
  402.  
  403.     while (numOfRecords > 0) {
  404.         i = numOfRecords >> 1;
  405.         probe = kmin + i;
  406.  
  407.         index = probe;
  408.         recordH = DmQueryNextInCategory (dbP, &index, dmAllCategories);
  409.         if (recordH) {
  410.             r = (ApptPackedDBRecordPtr) MemHandleLock (recordH);
  411.             if (r->flags.repeat)
  412.                 result = 1;
  413.             else
  414.                 result = DateCompare (date, r->when.date);
  415.             MemHandleUnlock (recordH);
  416.         }
  417.  
  418.         // If no handle, assume the record is deleted, deleted records
  419.         // are greater.
  420.         else
  421.             result = -1;
  422.  
  423.  
  424.         // If the date passed is less than the probe's date, keep searching.
  425.         if (result < 0)
  426.             numOfRecords = i;
  427.  
  428.         // If the date passed is greater than the probe's date, keep searching.
  429.         else if (result > 0) {
  430.             kmin = probe + 1;
  431.             numOfRecords = numOfRecords - i - 1;
  432.         }
  433.  
  434.         // If the records are equal find the first record on the day.
  435.         else {
  436.             found = true;
  437.             *indexP = index;
  438.             while (true) {
  439.                 err = DmSeekRecordInCategory (dbP, &index, 1, dmSeekBackward,
  440.                                               dmAllCategories);
  441.                 if (err == dmErrSeekFailed) break;
  442.  
  443.                 recordH = DmQueryRecord(dbP, index);
  444.                 r = (ApptPackedDBRecordPtr) MemHandleLock (recordH);
  445.                 if (r->flags.repeat)
  446.                     result = 1;
  447.                 else
  448.                     result = DateCompare (date, r->when.date);
  449.                 MemHandleUnlock (recordH);
  450.                 if (result != 0) break;
  451.                 *indexP = index;
  452.             }
  453.  
  454.             break;
  455.         }
  456.     }
  457.  
  458.  
  459.     // If that were no appointments on the specified day, return the
  460.     // index of the next appointment (on a future day).
  461.     if (! found) {
  462.         if (result < 0)
  463.             *indexP = probe;
  464.         else
  465.             *indexP = probe + 1;
  466.     }
  467.  
  468.     return (found);
  469. }
  470.  
  471. /************************************************************
  472.  *
  473.  *  FUNCTION: ApptGetRecord
  474.  *
  475.  *  DESCRIPTION: Get a record from a Appointment Database
  476.  *
  477.  *  PARAMETERS: database pointer
  478.  *                  database index
  479.  *                  database record
  480.  *
  481.  *  RETURNS: ##0 if successful, errorcode if not
  482.  *
  483.  *  CREATED: 1/25/95 
  484.  *
  485.  *  BY: Roger Flores
  486.  *
  487.  *************************************************************/
  488. Err ApptGetRecord (DmOpenRef dbP, UInt16 index, ApptDBRecordPtr r,
  489.                    MemHandle * handleP) {
  490.     MemHandle handle;
  491.     ApptPackedDBRecordPtr src;
  492.  
  493.     handle = DmQueryRecord(dbP, index);
  494.     ErrFatalDisplayIf(DmGetLastErr(), "Error Querying record");
  495.  
  496.     src = (ApptPackedDBRecordPtr) MemHandleLock (handle);
  497.  
  498.     if (DmGetLastErr()) {
  499.         *handleP = 0;
  500.         return DmGetLastErr();
  501.     }
  502.  
  503.     ApptUnpack(src, r);
  504.  
  505.     *handleP = handle;
  506.     return 0;
  507. }
  508.  
  509. /************************************************************
  510.  *
  511.  *  FUNCTION: ApptChangeRecord
  512.  *
  513.  *  DESCRIPTION: Change a record in the Appointment Database
  514.  *
  515.  *  PARAMETERS: database pointer
  516.  *                   database index
  517.  *                   database record
  518.  *                   changed fields
  519.  *
  520.  *  RETURNS: ##0 if successful, errorcode if not
  521.  *
  522.  *  CREATED: 1/25/95 
  523.  *
  524.  *  BY: Roger Flores
  525.  *
  526.  *  COMMENTS:   Records are not stored with extra padding - they
  527.  *  are always resized to their exact storage space.  This avoids
  528.  *  a database compression issue.  The code works as follows:
  529.  *  
  530.  *  1)  get the size of the new record
  531.  *  2)  make the new record
  532.  *  3)  pack the packed record plus the changes into the new record
  533.  *  4)  if the sort position is changes move to the new position
  534.  *  5)  attach in position
  535.  *
  536.  *************************************************************/
  537. Err ApptChangeRecord(DmOpenRef dbP, UInt16 *index, ApptDBRecordPtr r,
  538.                      ApptDBRecordFlags changedFields) {
  539.     Err result;
  540.     Int16 newIndex;
  541.     UInt16 attributes;
  542.     Boolean dontMove;
  543.     MemHandle oldH;
  544.     MemHandle srcH;
  545.     MemHandle dstH;
  546.     ApptDBRecordType src;
  547.     ApptPackedDBRecordPtr dst = 0;
  548.     ApptPackedDBRecordPtr cmp;
  549.  
  550.     // We do not assume that r is completely valid so we get a valid
  551.     // ApptDBRecordPtr...
  552.     if ((result = ApptGetRecord(dbP, *index, &src, &srcH)) != 0)
  553.         return result;
  554.  
  555.     // and we apply the changes to it.
  556.     if (changedFields.when)
  557.         src.when = r->when;
  558.  
  559.     if (changedFields.alarm)
  560.         src.alarm = r->alarm;
  561.  
  562.     if (changedFields.repeat)
  563.         src.repeat = r->repeat;
  564.  
  565.     if (changedFields.exceptions)
  566.         src.exceptions = r->exceptions;
  567.  
  568.     if (changedFields.description)
  569.         src.description = r->description;
  570.  
  571.     if (changedFields.note)
  572.         src.note = r->note;
  573.  
  574.     // Allocate a new chunk with the correct size and pack the data from
  575.     // the unpacked record into it.
  576.     dstH = DmNewHandle(dbP, (UInt32) ApptPackedSize(&src));
  577.     if (dstH) {
  578.         dst = MemHandleLock (dstH);
  579.         ApptPack (&src, dst);
  580.     }
  581.  
  582.     MemHandleUnlock (srcH);
  583.     if (dstH == NULL)
  584.         return dmErrMemError;
  585.  
  586.     // If the sort position is changed move to the new position.
  587.     // Check if any of the key fields have changed.
  588.     if ((!changedFields.when) && (! changedFields.repeat))
  589.         goto attachRecord;      // repeating events aren't in sorted order
  590.  
  591.     // Make sure *index-1 < *index < *index+1, if so it's in sorted
  592.     // order.  Leave it there.
  593.     if (*index > 0) {
  594.         // This record wasn't deleted and deleted records are at the end of the
  595.         // database so the prior record may not be deleted!
  596.         cmp = MemHandleLock (DmQueryRecord(dbP, *index-1));
  597.         dontMove = (ApptComparePackedRecords (cmp, dst, 0, NULL, NULL, 0)
  598.                     <= 0);
  599.         MemPtrUnlock (cmp);
  600.     } else
  601.         dontMove = true;
  602.  
  603.  
  604.     if (dontMove && (*index+1 < DmNumRecords (dbP))) {
  605.         DmRecordInfo(dbP, *index+1, &attributes, NULL, NULL);
  606.         if ( ! (attributes & dmRecAttrDelete) ) {
  607.             cmp = MemHandleLock (DmQueryRecord(dbP, *index+1));
  608.             dontMove &= (ApptComparePackedRecords (dst, cmp, 0, NULL, NULL,
  609.                                                    0) <= 0);
  610.             MemPtrUnlock (cmp);
  611.         }
  612.     }
  613.  
  614.     if (dontMove)
  615.         goto attachRecord;
  616.  
  617.     // The record isn't in the right position.  Move it.
  618.     newIndex = ApptFindSortPosition (dbP, dst);
  619.     DmMoveRecord (dbP, *index, newIndex);
  620.     if (newIndex > *index) newIndex--;
  621.     *index = newIndex;                      // return new position
  622.  
  623.  attachRecord:
  624.     // Attach the new record to the old index,  the preserves the
  625.     // category and record id.
  626.     result = DmAttachRecord (dbP, index, dstH, &oldH);
  627.  
  628.     MemPtrUnlock(dst);
  629.  
  630.     if (result) return result;
  631.  
  632.     MemHandleFree(oldH);
  633.  
  634.  
  635.     return 0;
  636. }
  637. /***********************************************************************
  638.  *
  639.  * FUNCTION:    ApptRepeatsOnDate
  640.  *
  641.  * DESCRIPTION: This routine returns true if a repeating appointment
  642.  *              occurrs on the specified date.
  643.  *
  644.  * PARAMETERS:  apptRec - a pointer to an appointment record
  645.  *              date    - date to check              
  646.  *
  647.  * RETURNED:    true if the appointment occurs on the date specified
  648.  *
  649.  * REVISION HISTORY:
  650.  *            Name    Date        Description
  651.  *            ----    ----        -----------
  652.  *            art    6/14/95        Initial Revision
  653.  *
  654.  ***********************************************************************/
  655. Boolean ApptRepeatsOnDate (ApptDBRecordPtr apptRec, DateType date)
  656. {
  657.     Int16  i;
  658.     UInt16 freq;
  659.     UInt16 weeksDiff;
  660.     UInt16 dayInMonth;
  661.     UInt16 dayOfWeek;
  662.     UInt16 dayOfMonth;
  663.     UInt16 firstDayOfWeek;
  664.     long dateInDays;
  665.     long startInDays;
  666.     Boolean onDate = false;
  667.     DatePtr exceptions;
  668.     DateType startDate;
  669.  
  670.     // Is the date passed before the start date of the appointment?
  671.     if (DateCompare (date, apptRec->when->date) < 0)
  672.         return (false);
  673.  
  674.     // Is the date passed after the end date of the appointment?
  675.     if (DateCompare (date, apptRec->repeat->repeatEndDate) > 0)
  676.         return (false);
  677.     
  678.  
  679.     // Get the frequency of occurrecne
  680.     //    (ex: every 2nd day, every 3rd month, etc.).  
  681.     freq = apptRec->repeat->repeatFrequency;
  682.     
  683.     // Get the date of the first occurrecne of the appointment.
  684.     startDate = apptRec->when->date;
  685.  
  686.     switch (apptRec->repeat->repeatType)
  687.     {
  688.         // Daily repeating appointment.
  689.     case repeatDaily:
  690.         dateInDays = DateToDays (date);
  691.         startInDays = DateToDays (startDate);
  692.         onDate = ((dateInDays - startInDays) % freq) == 0;
  693.         break;
  694.             
  695.  
  696.         // Weekly repeating appointment (ex: every Monday and Friday). 
  697.         // Yes, weekly repeating appointment can occur more then once a
  698.         // week.
  699.     case repeatWeekly:
  700.         // Are we on a day of the week that the appointment repeats on.
  701.         dayOfWeek = DayOfWeek (date.month, date.day, date.year+firstYear);
  702.         onDate = ((1 << dayOfWeek) & apptRec->repeat->repeatOn);
  703.         if (! onDate) break;
  704.  
  705.         // Are we in a week in which the appointment occurrs, if not 
  706.         // move to that start of the next week in which the appointment
  707.         // does occur.
  708.         dateInDays = DateToDays (date);
  709.         startInDays = DateToDays (startDate);
  710.  
  711.         firstDayOfWeek = (DayOfWeek (1, 1, firstYear) - 
  712.                           apptRec->repeat->repeatStartOfWeek + daysInWeek) % daysInWeek;
  713.  
  714.         weeksDiff = (((dateInDays + firstDayOfWeek) / daysInWeek) - 
  715.                      ((startInDays + firstDayOfWeek) / daysInWeek)) %freq;
  716.         onDate = (weeksDiff == 0);
  717.         break;
  718.  
  719.  
  720. //            // Compute the first occurrence of the appointment that occurs
  721. //            // on the same day of the week as the date passed.
  722. //            startDayOfWeek = DayOfWeek (startDate.month, startDate.day, 
  723. //                startDate.year+firstYear);
  724. //            startInDays = DateToDays (startDate);
  725. //            if (startDayOfWeek < dayOfWeek)
  726. //                startInDays += dayOfWeek - startDayOfWeek;
  727. //            else if (startDayOfWeek > dayOfWeek)
  728. //                startInDays += dayOfWeek+ (daysInWeek *freq) - startDayOfWeek;
  729. //            
  730. //            // Are we in a week in which the appointment repeats.
  731. //            dateInDays = DateToDays (date);
  732. //            onDate = (((dateInDays - startInDays) / daysInWeek) % freq) == 0;
  733. //            break;
  734.  
  735.  
  736.         // Monthly-by-day repeating appointment (ex: the 3rd Friday of every
  737.         // month).
  738.     case repeatMonthlyByDay:
  739.         // Are we in a month in which the appointment repeats.
  740.         onDate = ((((date.year - startDate.year) * monthsInYear) + 
  741.                    (date.month - startDate.month)) % freq) == 0;
  742.         if (! onDate) break;
  743.  
  744.         // Do the days of the month match (ex: 3rd Friday)
  745.         dayOfMonth = DayOfMonth (date.month, date.day, date.year+firstYear);
  746.         onDate = (dayOfMonth == apptRec->repeat->repeatOn);
  747.         if (onDate) break;
  748.             
  749.         // If the appointment repeats on one of the last days of the month,
  750.         // check if the date passed is also one of the last days of the 
  751.         // month.  By last days of the month we mean: last sunday, 
  752.         // last monday, etc.
  753.         if ((apptRec->repeat->repeatOn >= domLastSun) &&
  754.             (dayOfMonth >= dom4thSun))
  755.         {
  756.             dayOfWeek = DayOfWeek (date.month, date.day, date.year+firstYear);
  757.             dayInMonth = DaysInMonth (date.month, date.year+firstYear);
  758.             onDate = (((date.day + daysInWeek) > dayInMonth) &&
  759.                       (dayOfWeek == (apptRec->repeat->repeatOn % daysInWeek)));
  760.         }
  761.         break;                        
  762.  
  763.  
  764.         // Monthly-by-date repeating appointment (ex: the 15th of every
  765.         // month).
  766.     case repeatMonthlyByDate:
  767.         // Are we in a month in which the appointment repeats.
  768.         onDate = ((((date.year - startDate.year) * monthsInYear) + 
  769.                    (date.month - startDate.month)) % freq) == 0;
  770.         if (! onDate) break;
  771.             
  772.         // Are we on the same day of the month as the start date.
  773.         onDate = (date.day == startDate.day);
  774.         if (onDate) break;
  775.  
  776.         // If the staring day of the appointment is greater then the 
  777.         // number of day in the month passed, and the day passed is the 
  778.         // last day of the month, then the appointment repeats on the day.
  779.         dayInMonth = DaysInMonth (date.month, date.year+firstYear);
  780.         onDate = ((startDate.day > dayInMonth) && (date.day == dayInMonth));
  781.         break;
  782.  
  783.  
  784.         // Yearly repeating appointment.
  785.     case repeatYearly:
  786.         // Are we in a year in which the appointment repeats.
  787.         onDate = ((date.year - startDate.year) % freq) == 0;
  788.         if (! onDate) break;
  789.             
  790.         // Are we on the month and day that the appointment repeats.
  791.         onDate = (date.month == startDate.month) &&
  792.             (date.day == startDate.day);
  793.         if (onDate) break;
  794.             
  795.         // Specal leap day processing.
  796.         if ( (startDate.month == february) && 
  797.              (startDate.day == 29) &&
  798.              (date.month == february) && 
  799.              (date.day == DaysInMonth (date.month, date.year+firstYear)))
  800.         {
  801.             onDate = true;
  802.         }                      
  803.         break;
  804.     default:
  805.         break;
  806.     }
  807.  
  808.     // Check for an exception.
  809.     if ((onDate) && (apptRec->exceptions))
  810.     {
  811.         exceptions = &apptRec->exceptions->exception;
  812.         for (i = 0; i < apptRec->exceptions->numExceptions; i++)
  813.         {
  814.             if (DateCompare (date, exceptions[i]) == 0)
  815.             {
  816.                 onDate = false;
  817.                 break;
  818.             }
  819.         }
  820.     }
  821.  
  822.     return (onDate);
  823. }
  824.  
  825. /***********************************************************************
  826.  *
  827.  * FUNCTION:    ApptGetAppointments
  828.  *
  829.  * DESCRIPTION: This routine returns a list of appointments that are on 
  830.  *              the date specified
  831.  *
  832.  * PARAMETERS:  dbP    - pointer to the database
  833.  *              date   - date to search for
  834.  *              countP - number of appointments on the specified 
  835.  *                       day (returned value)
  836.  *
  837.  * RETURNED:    handle of the appointment list (ApptInfoType)
  838.  *
  839.  * REVISION HISTORY:
  840.  *          Name    Date        Description
  841.  *          ----    ----        -----------
  842.  *          art     6/15/95     Initial Revision
  843.  *
  844.  ***********************************************************************/
  845. MemHandle ApptGetAppointments (DmOpenRef dbP, DateType date, UInt16 * countP) {
  846.     Err error;
  847.     Int16 result;
  848.     Int16 count = 0;
  849.     UInt16    recordNum;
  850.     Boolean repeats;
  851.     MemHandle recordH;
  852.     MemHandle apptListH;
  853.     ApptInfoPtr apptList;
  854.     ApptDBRecordType apptRec;
  855.     ApptPackedDBRecordPtr r;
  856.  
  857.     // Allocated a block to hold the appointment list.
  858.     apptListH = MemHandleNew (sizeof (ApptInfoType) * apptMaxPerDay);
  859.     ErrFatalDisplayIf(!apptListH, "Out of memory");
  860.     if (! apptListH) return (0);
  861.  
  862.     apptList = MemHandleLock (apptListH);
  863.  
  864.     // Find the first non-repeating appointment of the day.
  865.     if (ApptFindFirst (dbP, date, &recordNum)) {
  866.         while (count < apptMaxPerDay) {
  867.             // Check if the appointment is on the date passed, if it is
  868.             // add it to the appointment list.
  869.             recordH = DmQueryRecord (dbP, recordNum);
  870.             r = MemHandleLock (recordH);
  871.             result = DateCompare (r->when.date, date);
  872.  
  873.             if (result == 0) {
  874.                 // Add the record to the appoitment list.
  875.                 apptList[count].startTime = r->when.startTime;
  876.                 apptList[count].endTime = r->when.endTime;
  877.                 apptList[count].recordNum = recordNum;
  878.                 count++;
  879.             }
  880.             MemHandleUnlock (recordH);
  881.             if (result != 0) break;
  882.  
  883.             // Get the next record.
  884.             error = DmSeekRecordInCategory (dbP, &recordNum, 1,
  885.                                             dmSeekForward,
  886.                                             dmAllCategories);
  887.             if (error == dmErrSeekFailed) break;
  888.         }
  889.     }
  890.  
  891.     // Add the repeating appointments to the list.  Repeating appointments
  892.     // are stored at the beginning of the database.
  893.     recordNum = 0;
  894.     while (count < apptMaxPerDay) {
  895.         recordH = DmQueryNextInCategory (dbP, &recordNum, dmAllCategories);
  896.         if (! recordH) break;
  897.         
  898.         r = (ApptPackedDBRecordPtr) MemHandleLock (recordH);
  899.         
  900.         repeats = (r->flags.repeat != 0);
  901.         if (repeats) {
  902.             ApptUnpack (r, &apptRec);
  903.             if (ApptRepeatsOnDate (&apptRec, date)) {
  904.                 // Add the record to the appoitment list.
  905.                 apptList[count].startTime = r->when.startTime;              
  906.                 apptList[count].endTime = r->when.endTime;              
  907.                 apptList[count].recordNum = recordNum;  
  908.                 count++;
  909.             }
  910.         }
  911.         MemHandleUnlock (recordH);
  912.  
  913.         // If the record has no repeating info we've reached the end of the 
  914.         // repeating appointments.
  915.         if (! repeats) break;
  916.         
  917.         recordNum++;
  918.     }
  919.  
  920.     // Sort the list by start time.
  921.     // SysInsertionSort (apptList, count, sizeof (ApptInfoType),
  922.     //          ApptListCompare, 0L);
  923.  
  924.     // If there are no appointments on the specified day, free the appointment
  925.     // list.
  926.     if (count == 0) {
  927.         MemPtrFree (apptList);
  928.         apptListH = 0;
  929.     }
  930.     // Resize the appointment list block to release any unused space.
  931.     else {
  932.         MemHandleUnlock (apptListH);
  933.         MemHandleResize (apptListH, count * sizeof (ApptInfoType));
  934.     }
  935.  
  936.     *countP = count;
  937.     return (apptListH);
  938. }
  939.  
  940. /************************************************************
  941.  *
  942.  *  FUNCTION: ApptNewRecord
  943.  *
  944.  *  DESCRIPTION: Create a new packed record in sorted position
  945.  *
  946.  *  PARAMETERS: database pointer
  947.  *              database record
  948.  *
  949.  *  RETURNS: ##0 if successful, error code if not
  950.  *
  951.  *  CREATED: 1/25/95 
  952.  *
  953.  *  BY: Roger Flores
  954.  *
  955.  *************************************************************/
  956. Err ApptNewRecord(DmOpenRef dbP, ApptDBRecordPtr r, UInt16 *index) {
  957.     MemHandle recordH;
  958.     ApptPackedDBRecordPtr recordP;
  959.     UInt16                    newIndex;
  960.     Err err;
  961.  
  962.  
  963.     // Make a new chunk with the correct size.
  964.     recordH = DmNewHandle (dbP, (UInt32) ApptPackedSize(r));
  965.     if (recordH == NULL)
  966.         return dmErrMemError;
  967.  
  968.     recordP = MemHandleLock (recordH);
  969.  
  970.     // Copy the data from the unpacked record to the packed one.
  971.     ApptPack (r, recordP);
  972.  
  973.     newIndex = ApptFindSortPosition(dbP, recordP);
  974.  
  975.     MemPtrUnlock (recordP);
  976.  
  977.     // 4) attach in place
  978.     err = DmAttachRecord(dbP, &newIndex, recordH, 0);
  979.     if (err)
  980.         MemHandleFree(recordH);
  981.     else
  982.         *index = newIndex;
  983.  
  984.     return err;
  985. }
  986.  
  987.