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 >
C/C++ Source or Header  |  2000-09-05  |  59KB  |  2,237 lines

  1. /*******************************************************************
  2.  Copyright ⌐ 1995 - 1998, 3Com Corporation or its subsidiaries ("3Com").  
  3.  All rights reserved.
  4.    
  5.  This software may be copied and used solely for developing products for 
  6.  the Palm Computing platform and for archival and backup purposes.  Except 
  7.  for the foregoing, no part of this software may be reproduced or transmitted 
  8.  in any form or by any means or used to make any derivative work (such as 
  9.  translation, transformation or adaptation) without express written consent 
  10.  from 3Com.
  11.  
  12.  3Com reserves the right to revise this software and to make changes in content 
  13.  from time to time without obligation on the part of 3Com to provide notification 
  14.  of such revision or changes.  
  15.  3COM MAKES NO REPRESENTATIONS OR WARRANTIES THAT THE SOFTWARE IS FREE OF ERRORS 
  16.  OR THAT THE SOFTWARE IS SUITABLE FOR YOUR USE.  THE SOFTWARE IS PROVIDED ON AN 
  17.  "AS IS" BASIS.  3COM MAKES NO WARRANTIES, TERMS OR CONDITIONS, EXPRESS OR IMPLIED, 
  18.  EITHER IN FACT OR BY OPERATION OF LAW, STATUTORY OR OTHERWISE, INCLUDING WARRANTIES, 
  19.  TERMS, OR CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND 
  20.  SATISFACTORY QUALITY.
  21.  
  22.  TO THE FULL EXTENT ALLOWED BY LAW, 3COM ALSO EXCLUDES FOR ITSELF AND ITS SUPPLIERS 
  23.  ANY LIABILITY, WHETHER BASED IN CONTRACT OR TORT (INCLUDING NEGLIGENCE), FOR 
  24.  DIRECT, INCIDENTAL, CONSEQUENTIAL, INDIRECT, SPECIAL, OR PUNITIVE DAMAGES OF 
  25.  ANY KIND, OR FOR LOSS OF REVENUE OR PROFITS, LOSS OF BUSINESS, LOSS OF INFORMATION 
  26.  OR DATA, OR OTHER FINANCIAL LOSS ARISING OUT OF OR IN CONNECTION WITH THIS SOFTWARE, 
  27.  EVEN IF 3COM HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
  28.  
  29.  3Com, HotSync, Palm Computing, and Graffiti are registered trademarks, and 
  30.  Palm III and Palm OS are trademarks of 3Com Corporation or its subsidiaries.
  31.  
  32.  IF THIS SOFTWARE IS PROVIDED ON A COMPACT DISK, THE OTHER SOFTWARE AND 
  33.  DOCUMENTATION ON THE COMPACT DISK ARE SUBJECT TO THE LICENSE AGREEMENT 
  34.  ACCOMPANYING THE COMPACT DISK.
  35.  
  36.  *****************************************************************************
  37.  * FileName:
  38.  *        DateDB.c
  39.  *
  40.  * Description:
  41.  *        Appointment Manager routines
  42.  *
  43.  * History:
  44.  *       1/25/95  rsf - Created
  45.  *      8/5/99   bgd - ifdef'd out most functions
  46.  *
  47.  *******************************************************************/
  48.  
  49. #include <Pilot.h>
  50.  
  51. #define    NON_PORTABLE
  52. #include <MemoryPrv.h>
  53.  
  54. // Set this to get to private database defines
  55. #define __APPTMGR_PRIVATE__
  56. #include "Datebook.h"
  57.  
  58. /***********************************************************************
  59.  *
  60.  *    Internal Structutes
  61.  *
  62.  ***********************************************************************/
  63.  
  64. // The following structure doesn't really exist.  The first field
  65. // varies depending on the data present.  However, it is convient
  66. // (and less error prone) to use when accessing the other information.
  67. typedef struct {
  68.     ApptDateTimeType     when;
  69.     ApptDBRecordFlags    flags;    // A flag set for each  datum present
  70.     char                firstField;
  71. } ApptPackedDBRecordType;
  72.  
  73. typedef ApptPackedDBRecordType * ApptPackedDBRecordPtr;
  74.  
  75. typedef Int comparF (const void *, const void *, Int other);
  76.  
  77.  
  78. #if 0
  79. /***********************************************************************
  80.  *
  81.  *    Internal Routines
  82.  *
  83.  ***********************************************************************/
  84. void ECApptDBValidate (DmOpenRef dbP);
  85.  
  86. static Int TimeCompare (TimeType t1, TimeType t2);
  87.  
  88. static Int DateCompare (DateType d1, DateType d2);
  89.  
  90.  
  91.  
  92. /************************************************************
  93.  *
  94.  *  FUNCTION: ECApptDBValidate
  95.  *
  96.  *  DESCRIPTION: This routine validates the integrity of a
  97.  *                      datebook datebase.
  98.  *
  99.  *  PARAMETERS: database pointer
  100.  *
  101.  *  RETURNS: nothing
  102.  *
  103.  * REVISION HISTORY:
  104.  *            Name    Date        Description
  105.  *            ----    ----        -----------
  106.  *            art    11/15/95    Initial Revision
  107.  *
  108.  *************************************************************/
  109. #if    EMULATION_LEVEL != EMULATION_NONE
  110.  
  111. #define maxDescLen    tableMaxTextItemSize
  112. #define maxNoteLen    noteViewMaxLength
  113.  
  114. void ECApptDBValidate (DmOpenRef dbP)
  115. {
  116.     UInt i;
  117.     UInt size;
  118.     UInt len;
  119.     UInt blockSize;
  120.     UInt numRecord;
  121.     VoidHand recH;
  122.     DateType date;
  123.     ApptDBRecordType rec;
  124.     
  125.     numRecord = DmNumRecords (dbP);
  126.     for (i = 0 ; i < numRecord; i++)
  127.         {
  128.         recH = DmQueryRecord (dbP, i);
  129.         if (! recH) continue;
  130.  
  131.         ApptGetRecord (dbP, i, &rec, &recH);
  132.         
  133.         // Is the event an untimed event?
  134.         if (TimeToInt(rec.when->startTime) == apptNoTime)
  135.             {
  136.             // There should not be and end time if there is no start time.
  137.             if (TimeToInt(rec.when->endTime) != apptNoTime)
  138.                 ErrDisplay ("DB integrity error");                
  139.             }
  140.  
  141.         // Validate the event date.
  142.         if ((rec.when->date.month == 0) ||
  143.              (rec.when->date.month > 12) ||
  144.              (rec.when->date.day == 0) ||
  145.              (rec.when->date.day > DaysInMonth (rec.when->date.month, 
  146.                  rec.when->date.year + firstYear)))
  147.             ErrDisplay ("DB integrity error");                
  148.              
  149.  
  150.         // The start time may not be greater than the end time.
  151.         else if (TimeCompare (rec.when->startTime, rec.when->endTime) > 0)
  152.             {
  153.             ErrDisplay ("DB integrity error");                
  154.             }
  155.  
  156.         // Validate the alarm info.
  157.         if (rec.alarm)
  158.             {
  159.             if (rec.alarm->advance > 99)
  160.                 ErrDisplay ("DB integrity error");                
  161.  
  162.             if (rec.alarm->advanceUnit > aauDays)
  163.                 ErrDisplay ("DB integrity error");                
  164.             }
  165.  
  166.  
  167.         // Validate the repeat info.
  168.         if (rec.repeat)
  169.             {
  170.             // Validate the repeat type.
  171.             if (rec.repeat->repeatType > repeatYearly)
  172.                 ErrDisplay ("DB integrity error");                
  173.  
  174.             // Validate the repeat end date.
  175.             date = rec.repeat->repeatEndDate;
  176.             if (DateToInt (date) != apptNoTime)
  177.                 {
  178.                 if (DateCompare (date, rec.when->date) < 0)
  179.                     ErrDisplay ("DB integrity error");                
  180.                 
  181.                 if ((date.month == 0) ||
  182.                      (date.month > 12) ||
  183.                      (date.day == 0) ||
  184.                      (date.day > DaysInMonth (date.month, date.year + firstYear)))
  185.                     ErrDisplay ("DB integrity error");
  186.                 }                
  187.             
  188.             // Validate the repeat frequency.
  189.             if (rec.repeat->repeatFrequency > 99)
  190.                 ErrDisplay ("DB integrity error");                
  191.  
  192.             // Validate the "repeatOn" info
  193.             if (rec.repeat->repeatType == repeatWeekly)
  194.                 {
  195.                 if (rec.repeat->repeatOn == 0)
  196.                     ErrDisplay ("DB integrity error");                
  197.                 }
  198.             else if (rec.repeat->repeatType == repeatMonthlyByDay)
  199.                 {
  200.                 if (rec.repeat->repeatOn > domLastSat)
  201.                     ErrDisplay ("DB integrity error");                
  202.                 }
  203.             else
  204.                 {
  205.                 if (rec.repeat->repeatOn != 0)
  206.                     ErrDisplay ("DB integrity error");                
  207.                 }
  208.  
  209.             // Validate the "repeatStartOfWeek" info,
  210.             if (rec.repeat->repeatType == repeatWeekly)
  211.                 {
  212.                 if (rec.repeat->repeatStartOfWeek > monday)
  213.                     ErrDisplay ("DB integrity error");
  214.                 }
  215.             else if (rec.repeat->repeatStartOfWeek)
  216.                 ErrDisplay ("DB integrity error");
  217.  
  218.             }
  219.  
  220.         // Validate the record size.
  221.         size = sizeof (ApptDateTimeType) + sizeof (ApptDBRecordFlags);
  222.         if (rec.alarm)
  223.             size += sizeof (AlarmInfoType);
  224.         if (rec.repeat)
  225.             size += sizeof (RepeatInfoType);
  226.         if (rec.exceptions)
  227.             size += sizeof (DateType) * rec.exceptions->numExceptions +
  228.                 sizeof (UInt);
  229.         if (rec.description)
  230.             {
  231.             len = StrLen (rec.description);
  232.             ErrFatalDisplayIf (len > maxDescLen, "DB integrity error");
  233.             size += len + 1;
  234.             }
  235.         if (rec.note)
  236.             {
  237.             len = StrLen (rec.note);
  238.             ErrFatalDisplayIf (len > maxNoteLen, "DB integrity error");
  239.             size += len + 1;
  240.             }
  241.  
  242.         blockSize = MemHandleSize (recH);
  243. //        ErrFatalDisplayIf ( (blockSize != size), "DB integrity error");
  244.  
  245.         MemHandleUnlock (recH);
  246.         }
  247. }
  248. #endif
  249.  
  250. #endif /* 0 */
  251. /***********************************************************************
  252.  *
  253.  * FUNCTION:    DateCompare
  254.  *
  255.  * DESCRIPTION: This routine compares two dates.
  256.  *
  257.  * PARAMETERS:  d1 - a date 
  258.  *              d2 - a date 
  259.  *
  260.  * RETURNED:    if d1 > d2  returns a positive int
  261.  *              if d1 < d2  returns a negative int
  262.  *              if d1 = d2  returns zero
  263.  *
  264.  * NOTE: This routine treats the DateType structure like an unsigned int,
  265.  *       it depends on the fact the the members of the structure are ordered
  266.  *       year, month, day form high bit to low low bit.
  267.  *
  268.  * REVISION HISTORY:
  269.  *            Name    Date        Description
  270.  *            ----    ----        -----------
  271.  *            art    6/12/95        Initial Revision
  272.  *
  273.  ***********************************************************************/
  274. static Int DateCompare (DateType d1, DateType d2)
  275. {
  276.     UInt int1, int2;
  277.     
  278.     int1 = DateToInt(d1);
  279.     int2 = DateToInt(d2);
  280.     
  281.     if (int1 > int2)
  282.         return (1);
  283.     else if (int1 < int2)
  284.         return (-1);
  285.     return 0;
  286. }
  287. #if 0
  288.  
  289. /***********************************************************************
  290.  *
  291.  * FUNCTION:    TimeCompare
  292.  *
  293.  * DESCRIPTION: This routine compares two times.  "No time" is represented
  294.  *              by minus one, and is considered less than all times.
  295.  *
  296.  * PARAMETERS:  nothing
  297.  *
  298.  * RETURNED:    if t1 > t2  returns a positive int
  299.  *              if t1 < t2  returns a negative int
  300.  *              if t1 = t2  returns zero
  301.  *
  302.  * REVISION HISTORY:
  303.  *            Name    Date        Description
  304.  *            ----    ----        -----------
  305.  *            art    6/12/95        Initial Revision
  306.  *
  307.  ***********************************************************************/
  308. static Int TimeCompare (TimeType t1, TimeType t2)
  309. {
  310.     Int int1, int2;
  311.     
  312.     int1 = TimeToInt(t1);
  313.     int2 = TimeToInt(t2);
  314.     
  315.     if (int1 > int2)
  316.         return (1);
  317.     else if (int1 < int2)
  318.         return (-1);
  319.     return 0;
  320.  
  321. }
  322.  
  323.  
  324. /************************************************************
  325.  *
  326.  *  FUNCTION: ApptAppInfoInit
  327.  *
  328.  *  DESCRIPTION: Create and initialize the app info chunk if missing.
  329.  *
  330.  *  PARAMETERS: database pointer
  331.  *
  332.  *  RETURNS: 0 if successful, errorcode if not
  333.  *
  334.  *  CREATED: 1/25/95 
  335.  *
  336.  *  BY: Roger Flores
  337.  *
  338.  *************************************************************/
  339. Err    ApptAppInfoInit(DmOpenRef dbP)
  340. {
  341.     UInt                 cardNo;
  342.     VoidHand         h;
  343.     LocalID             dbID;
  344.     LocalID             appInfoID;
  345.     ApptAppInfoPtr    appInfoP;
  346.     
  347.     if (DmOpenDatabaseInfo(dbP, &dbID, NULL, NULL, &cardNo, NULL))
  348.         return dmErrInvalidParam;
  349.         
  350.     if (DmDatabaseInfo(cardNo, dbID, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &appInfoID, NULL, NULL, NULL))
  351.         return dmErrInvalidParam;
  352.     
  353.     if (appInfoID == NULL) 
  354.         {
  355.         h = DmNewHandle(dbP, sizeof(ApptAppInfoType));
  356.         if (! h) return dmErrMemError;
  357.  
  358.         appInfoID = MemHandleToLocalID (h);
  359.         DmSetDatabaseInfo(cardNo, dbID, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &appInfoID, NULL, NULL, NULL);
  360.         }
  361.         
  362.     // Get pointer to app Info chunk
  363.     appInfoP = MemLocalIDToLockedPtr(appInfoID, cardNo);
  364.     
  365.     // Init it
  366.     DmSet(appInfoP, 0, sizeof(ApptAppInfoType), 0); 
  367.  
  368.     // Unlock it
  369.     MemPtrUnlock(appInfoP);
  370.     
  371.     return 0;
  372. }
  373.  
  374.  
  375.  
  376. /************************************************************
  377.  *
  378.  *  FUNCTION:    ApptComparePackedRecords
  379.  *
  380.  *  DESCRIPTION: Compare two packed records.
  381.  *
  382.  *  PARAMETERS:  r1    - database record 1
  383.  *                     r2    - database record 2
  384.  *               extra - extra data, not used in the function
  385.  *
  386.  *  RETURNS:    -1 if record one is less
  387.  *                   1 if record two is less
  388.  *
  389.  *  CREATED: 1/14/95 
  390.  *
  391.  *  BY: Roger Flores
  392.  *
  393.  *    COMMENTS:    Compare the two records key by key until
  394.  *    there is a difference.  Return -1 if r1 is less or 1 if r2
  395.  *    is less.  A zero is never returned because if two records
  396.  *    seem identical then their unique IDs are compared!
  397.  *
  398.  *************************************************************/ 
  399. static Int ApptComparePackedRecords (ApptPackedDBRecordPtr r1, 
  400.     ApptPackedDBRecordPtr r2, Int extra, SortRecordInfoPtr info1, 
  401.     SortRecordInfoPtr info2, VoidHand appInfoH)
  402. {
  403.     Int result;
  404.  
  405.     if ((r1->flags.repeat) || (r2->flags.repeat))
  406.         {
  407.         if ((r1->flags.repeat) && (r2->flags.repeat))
  408.             result = 0;
  409.         else if (r1->flags.repeat)
  410.             result = -1;
  411.         else
  412.             result = 1;
  413.         }
  414.  
  415.     else
  416.         {
  417.         result = DateCompare (r1->when.date, r2->when.date);
  418.         if (result == 0)
  419.             {
  420.             result = TimeCompare (r1->when.startTime, r2->when.startTime);
  421.             }
  422.         }
  423.     return result;
  424. }
  425.  
  426.  
  427. /************************************************************
  428.  *
  429.  *  FUNCTION: ApptPackedSize
  430.  *
  431.  *  DESCRIPTION: Return the packed size of an ApptDBRecordType 
  432.  *
  433.  *  PARAMETERS: database record
  434.  *
  435.  *  RETURNS: the size in bytes
  436.  *
  437.  *  CREATED: 1/25/95 
  438.  *
  439.  *  BY: Roger Flores
  440.  *
  441.  *************************************************************/
  442. static UInt ApptPackedSize (ApptDBRecordPtr r)
  443. {
  444.     UInt size;
  445.  
  446.     
  447.     size = sizeof (ApptDateTimeType) + sizeof (ApptDBRecordFlags);
  448.     
  449.     if (r->alarm != NULL)
  450.         size += sizeof (AlarmInfoType);
  451.     
  452.     if (r->repeat != NULL)
  453.         size += sizeof (RepeatInfoType);
  454.     
  455.     if (r->exceptions != NULL)
  456.         size += sizeof (UInt) + 
  457.             (r->exceptions->numExceptions * sizeof (DateType));
  458.     
  459.     if (r->description != NULL)
  460.         size += StrLen(r->description) + 1;
  461.     
  462.     if (r->note != NULL)
  463.         size += StrLen(r->note) + 1;
  464.     
  465.  
  466.     return size;
  467. }
  468.  
  469.  
  470. /************************************************************
  471.  *
  472.  *  FUNCTION: ApptPack
  473.  *
  474.  *  DESCRIPTION: Pack an ApptDBRecordType
  475.  *
  476.  *  PARAMETERS: database record
  477.  *
  478.  *  RETURNS: the ApptPackedDBRecord is packed
  479.  *
  480.  *  CREATED: 1/25/95 
  481.  *
  482.  *  BY: Roger Flores
  483.  *
  484.  *************************************************************/
  485. static void ApptPack(ApptDBRecordPtr s, ApptPackedDBRecordPtr d)
  486. {
  487.     ApptDBRecordFlags    flags;
  488.     UInt     size;
  489.     ULong    offset = 0;
  490.     
  491.     
  492.     *(unsigned char *)&flags = 0;            // clear the flags
  493.     
  494.     
  495.     // copy the ApptDateTimeType
  496.     //c = (char *) d;
  497.     offset = 0;
  498.     DmWrite(d, offset, s->when, sizeof(ApptDateTimeType));
  499.     offset += sizeof (ApptDateTimeType) + sizeof (ApptDBRecordFlags);
  500.     
  501.  
  502.     if (s->alarm != NULL)
  503.         {
  504.         DmWrite(d, offset, s->alarm, sizeof(AlarmInfoType));
  505.         offset += sizeof (AlarmInfoType);
  506.         flags.alarm = 1;
  507.         }
  508.     
  509.  
  510.     if (s->repeat != NULL)
  511.         {
  512.         DmWrite(d, offset, s->repeat, sizeof(RepeatInfoType));
  513.         offset += sizeof (RepeatInfoType);
  514.         flags.repeat = 1;
  515.         }
  516.     
  517.  
  518.     if (s->exceptions != NULL)
  519.         {
  520.         size = sizeof (UInt) + 
  521.             (s->exceptions->numExceptions * sizeof (DateType));
  522.         DmWrite(d, offset, s->exceptions, size);
  523.         offset += size;
  524.         flags.exceptions = 1;
  525.         }
  526.     
  527.     
  528.     if (s->description != NULL)
  529.         {
  530.         size = StrLen(s->description) + 1;
  531.         DmWrite(d, offset, s->description, size);
  532.         offset += size;
  533.         flags.description = 1;
  534.         }
  535.     
  536.  
  537.     
  538.     if (s->note != NULL)
  539.         {
  540.         size = StrLen(s->note) + 1;
  541.         DmWrite(d, offset, s->note, size);
  542.         offset += size;
  543.         flags.note = 1;
  544.         }
  545.     
  546.     DmWrite(d, sizeof(ApptDateTimeType), &flags, sizeof(flags));
  547. }
  548.  
  549. #endif /* 0 */
  550. /************************************************************
  551.  *
  552.  *  FUNCTION: ApptUnpack
  553.  *
  554.  *  DESCRIPTION: Fills in the ApptDBRecord structure
  555.  *
  556.  *  PARAMETERS: database record
  557.  *
  558.  *  RETURNS: the record unpacked
  559.  *
  560.  *  CREATED: 1/25/95 
  561.  *
  562.  *  BY: Roger Flores
  563.  *
  564.  *************************************************************/
  565. static void ApptUnpack(ApptPackedDBRecordPtr src, ApptDBRecordPtr dest)
  566. {
  567.     ApptDBRecordFlags    flags;
  568.     char *p;
  569.  
  570.     
  571.     flags = src->flags;
  572.     p = &src->firstField;
  573.  
  574.     dest->when = (ApptDateTimeType *) src;
  575.     
  576.     if (flags.alarm) 
  577.         {
  578.         dest->alarm = (AlarmInfoType *) p;
  579.         p += sizeof (AlarmInfoType);
  580.         }
  581.     else
  582.         dest->alarm = NULL;
  583.  
  584.     
  585.     if (flags.repeat)
  586.         {
  587.         dest->repeat = (RepeatInfoType *) p;
  588.         p += sizeof (RepeatInfoType);
  589.         }
  590.     else
  591.         dest->repeat = NULL;
  592.     
  593.     
  594.     if (flags.exceptions)
  595.         {
  596.         dest->exceptions = (ExceptionsListType *) p;
  597.         p += sizeof (UInt) + 
  598.             (((ExceptionsListType *) p)->numExceptions * sizeof (DateType));
  599.         }
  600.     else
  601.         dest->exceptions = NULL;
  602.         
  603.     
  604.     if (flags.description)
  605.         {
  606.         dest->description = p;
  607.         p += StrLen(p) + 1;
  608.         }
  609.     else
  610.         dest->description = NULL;
  611.         
  612.     
  613.     if (flags.note)
  614.         {
  615.         dest->note = p;
  616.         }
  617.     else
  618.         dest->note = NULL;
  619.     
  620. }
  621. #if 0
  622. /************************************************************
  623.  *
  624.  *  FUNCTION: ApptFindSortPosition
  625.  *
  626.  *  DESCRIPTION: Return where a record is or should be
  627.  *        Useful to find or find where to insert a record.
  628.  *
  629.  *  PARAMETERS: database record
  630.  *
  631.  *  RETURNS: position where a record should be
  632.  *
  633.  *  CREATED: 1/25/95 
  634.  *
  635.  *  BY: Roger Flores
  636.  *
  637.  *************************************************************/
  638. static UInt ApptFindSortPosition(DmOpenRef dbP, ApptPackedDBRecordPtr newRecord)
  639. {
  640.     return (DmFindSortPosition (dbP, newRecord, NULL, (DmComparF *)ApptComparePackedRecords, 0));
  641. }
  642.  
  643.  
  644. /************************************************************
  645.  *
  646.  *  FUNCTION: ApptSort
  647.  *
  648.  *  DESCRIPTION: Sort the appointment database.
  649.  *
  650.  *  PARAMETERS: database record
  651.  *
  652.  *  RETURNS: nothing
  653.  *
  654.  *  CREATED: 9/5/95 
  655.  *
  656.  *  BY: Art Lamb
  657.  *
  658.  *************************************************************/
  659. void ApptSort (DmOpenRef dbP)
  660. {
  661.     DmQuickSort(dbP, (DmComparF *)ApptComparePackedRecords, 0);
  662. }
  663.  
  664. #endif /* 0 */
  665. /***********************************************************************
  666.  *
  667.  * FUNCTION:    ApptFindFirst
  668.  *
  669.  * DESCRIPTION: This routine finds the first appointment on the specified
  670.  *              day.
  671.  *
  672.  * PARAMETERS:  dbP    - pointer to the database
  673.  *              date   - date to search for
  674.  *              indexP - pointer to the index of the first record on the 
  675.  *                       specified day (returned value)
  676.  *
  677.  * RETURNED:    true if a record has found
  678.  *
  679.  * REVISION HISTORY:
  680.  *            Name    Date        Description
  681.  *            ----    ----        -----------
  682.  *            art    6/15/95        Initial Revision
  683.  *
  684.  ***********************************************************************/
  685. Boolean ApptFindFirst (DmOpenRef dbP, DateType date, UIntPtr indexP)
  686. {
  687.     Err err;
  688.     Int numOfRecords;
  689.     Int kmin, probe, i;        // all positions in the database.
  690.     Int result = 0;            // result of comparing two records
  691.     UInt index;
  692.     VoidHand recordH;
  693.     Boolean found = false;
  694.     ApptPackedDBRecordPtr r;
  695.  
  696.  
  697.     kmin = probe = 0;
  698.     numOfRecords = DmNumRecords(dbP);
  699.     
  700.     
  701.     while (numOfRecords > 0)
  702.         {
  703.         i = numOfRecords >> 1;
  704.         probe = kmin + i;
  705.         
  706.         index = probe;
  707.         recordH = DmQueryNextInCategory (dbP, &index, dmAllCategories);
  708.         if (recordH)
  709.             {
  710.             r = (ApptPackedDBRecordPtr) MemHandleLock (recordH);
  711.             if (r->flags.repeat)
  712.                 result = 1;
  713.             else
  714.                 result = DateCompare (date, r->when.date);
  715.             MemHandleUnlock (recordH);
  716.             }
  717.  
  718.         // If no handle, assume the record is deleted, deleted records
  719.         // are greater.
  720.         else
  721.             result = -1;
  722.             
  723.  
  724.         // If the date passed is less than the probe's date, keep searching.
  725.         if (result < 0)
  726.             numOfRecords = i;
  727.  
  728.         // If the date passed is greater than the probe's date, keep searching.
  729.         else if (result > 0)
  730.             {
  731.             kmin = probe + 1;
  732.             numOfRecords = numOfRecords - i - 1;
  733.             }
  734.  
  735.         // If the records are equal find the first record on the day.
  736.         else
  737.             {
  738.             found = true;
  739.             *indexP = index;
  740.             while (true)
  741.                 {
  742.                 err = DmSeekRecordInCategory (dbP, &index, 1, dmSeekBackward, 
  743.                     dmAllCategories);
  744.                 if (err == dmErrSeekFailed) break;
  745.                 
  746.                 recordH = DmQueryRecord(dbP, index);
  747.                 r = (ApptPackedDBRecordPtr) MemHandleLock (recordH);
  748.                 if (r->flags.repeat)
  749.                     result = 1;
  750.                 else
  751.                     result = DateCompare (date, r->when.date);
  752.                 MemHandleUnlock (recordH);
  753.                 if (result != 0) break;
  754.                 *indexP = index;
  755.                 }
  756.  
  757.             break;
  758.             }
  759.         }
  760.  
  761.     
  762.     // If that were no appointments on the specified day, return the 
  763.     // index of the next appointment (on a future day).
  764.     if (! found)
  765.         {
  766.         if (result < 0) 
  767.             *indexP = probe;
  768.         else
  769.             *indexP = probe + 1;
  770.         }
  771.  
  772.     return (found);
  773. }
  774.  
  775.  
  776.  
  777. /************************************************************
  778.  *
  779.  *  FUNCTION: ApptGetRecord
  780.  *
  781.  *  DESCRIPTION: Get a record from a Appointment Database
  782.  *
  783.  *  PARAMETERS: database pointer
  784.  *                    database index
  785.  *                    database record
  786.  *
  787.  *  RETURNS: ##0 if successful, errorcode if not
  788.  *
  789.  *  CREATED: 1/25/95 
  790.  *
  791.  *  BY: Roger Flores
  792.  *
  793.  *************************************************************/
  794. Err ApptGetRecord (DmOpenRef dbP, UInt index, ApptDBRecordPtr r, 
  795.     VoidHand * handleP)
  796. {
  797.     VoidHand handle;
  798.     ApptPackedDBRecordPtr src;
  799.     
  800.  
  801.     handle = DmQueryRecord(dbP, index);
  802.     ErrFatalDisplayIf(DmGetLastErr(), "Error Querying record");
  803.     
  804.     src = (ApptPackedDBRecordPtr) MemHandleLock (handle);
  805.  
  806.     if (DmGetLastErr())
  807.         {
  808.         *handleP = 0;
  809.         return DmGetLastErr();
  810.         }
  811.     
  812.     ApptUnpack(src, r);
  813.     
  814.     *handleP = handle;
  815.     return 0;
  816. }
  817. #if 0
  818.  
  819. /************************************************************
  820.  *
  821.  *  FUNCTION: ApptChangeRecord
  822.  *
  823.  *  DESCRIPTION: Change a record in the Appointment Database
  824.  *
  825.  *  PARAMETERS: database pointer
  826.  *                     database index
  827.  *                     database record
  828.  *                     changed fields
  829.  *
  830.  *  RETURNS: ##0 if successful, errorcode if not
  831.  *
  832.  *  CREATED: 1/25/95 
  833.  *
  834.  *  BY: Roger Flores
  835.  *
  836.  *    COMMENTS:    Records are not stored with extra padding - they
  837.  *    are always resized to their exact storage space.  This avoids
  838.  *    a database compression issue.  The code works as follows:
  839.  *    
  840.  *    1)    get the size of the new record
  841.  *    2)    make the new record
  842.  *    3)    pack the packed record plus the changes into the new record
  843.  *    4)    if the sort position is changes move to the new position
  844.  *    5)    attach in position
  845.  *
  846.  *************************************************************/
  847. Err ApptChangeRecord(DmOpenRef dbP, UInt *index, ApptDBRecordPtr r, 
  848.     ApptDBRecordFlags changedFields)
  849. {
  850.     Err result;
  851.     Int newIndex;
  852.     UInt attributes;
  853.     Boolean dontMove;
  854.     VoidHand oldH;
  855.     VoidHand srcH;
  856.     VoidHand dstH;
  857.     ApptDBRecordType src;
  858.     ApptPackedDBRecordPtr dst;
  859.     ApptPackedDBRecordPtr cmp;
  860.     
  861.     // We do not assume that r is completely valid so we get a valid
  862.     // ApptDBRecordPtr...
  863.     if ((result = ApptGetRecord(dbP, *index, &src, &srcH)) != 0)
  864.         return result;
  865.     
  866.     // and we apply the changes to it.
  867.     if (changedFields.when) 
  868.         src.when = r->when;
  869.     
  870.     if (changedFields.alarm) 
  871.         src.alarm = r->alarm;
  872.     
  873.     if (changedFields.repeat)
  874.         src.repeat = r->repeat;
  875.     
  876.     if (changedFields.exceptions)
  877.         src.exceptions = r->exceptions;
  878.         
  879.     if (changedFields.description)
  880.         src.description = r->description;
  881.         
  882.     if (changedFields.note)
  883.         src.note = r->note;
  884.  
  885.  
  886.     // Allocate a new chunk with the correct size and pack the data from 
  887.     // the unpacked record into it.
  888.     dstH = DmNewHandle(dbP, (ULong) ApptPackedSize(&src));
  889.     if (dstH)
  890.         {
  891.         dst = MemHandleLock (dstH);
  892.         ApptPack (&src, dst);
  893.         }
  894.  
  895.     MemHandleUnlock (srcH);
  896.     if (dstH == NULL)
  897.         return dmErrMemError;
  898.  
  899.  
  900.     // If the sort position is changed move to the new position.
  901.     // Check if any of the key fields have changed. 
  902.     if ((!changedFields.when) && (! changedFields.repeat))
  903.         goto attachRecord;                // repeating events aren't in sorted order
  904.     
  905.         
  906.     // Make sure *index-1 < *index < *index+1, if so it's in sorted 
  907.     // order.  Leave it there.    
  908.     if (*index > 0)
  909.         {
  910.         // This record wasn't deleted and deleted records are at the end of the
  911.         // database so the prior record may not be deleted!
  912.         cmp = MemHandleLock (DmQueryRecord(dbP, *index-1));        
  913.         dontMove = (ApptComparePackedRecords (cmp, dst, 0, NULL, NULL, 0) <= 0);
  914.         MemPtrUnlock (cmp);
  915.         }
  916.     else 
  917.         dontMove = true;
  918.  
  919.  
  920.     if (dontMove && (*index+1 < DmNumRecords (dbP)))
  921.         {
  922.         DmRecordInfo(dbP, *index+1, &attributes, NULL, NULL);
  923.         if ( ! (attributes & dmRecAttrDelete) )
  924.             {
  925.             cmp = MemHandleLock (DmQueryRecord(dbP, *index+1));
  926.             dontMove &= (ApptComparePackedRecords (dst, cmp, 0, NULL, NULL, 0) <= 0);
  927.             MemPtrUnlock (cmp);
  928.             }
  929.         }
  930.         
  931.     if (dontMove)
  932.         goto attachRecord;
  933.     
  934.     
  935.     // The record isn't in the right position.  Move it.
  936.     newIndex = ApptFindSortPosition (dbP, dst);
  937.     DmMoveRecord (dbP, *index, newIndex);
  938.     if (newIndex > *index) newIndex--;
  939.     *index = newIndex;                        // return new position
  940.  
  941.  
  942. attachRecord:
  943.     // Attach the new record to the old index,  the preserves the 
  944.     // category and record id.
  945.     result = DmAttachRecord (dbP, index, dstH, &oldH);
  946.     
  947.     MemPtrUnlock(dst);
  948.  
  949.     if (result) return result;
  950.  
  951.     MemHandleFree(oldH);
  952.  
  953.     #if    EMULATION_LEVEL != EMULATION_NONE
  954.         ECApptDBValidate (dbP);
  955.     #endif
  956.  
  957.     return 0;
  958. }
  959.  
  960. /***********************************************************************
  961.  *
  962.  * FUNCTION:    ApptAddException
  963.  *
  964.  * DESCRIPTION: This routine adds an entry to the exceptions list of the 
  965.  *              specified record.
  966.  *
  967.  *  PARAMETERS: database pointer
  968.  *                     database index
  969.  *                     exception date
  970.  *
  971.  * RETURNED:     error code 
  972.  *
  973.  * REVISION HISTORY:
  974.  *            Name    Date        Description
  975.  *            ----    ----        -----------
  976.  *            art    7/26/95    Initial Revision
  977.  *
  978.  ***********************************************************************/
  979. Err ApptAddException (DmOpenRef dbP, UInt *index, DateType date)
  980. {
  981.     Err error;
  982.     UInt size;
  983.     VoidHand recordH;
  984.     ApptDBRecordType r;
  985.     ExceptionsListPtr exceptions;
  986.     ApptDBRecordFlags changedFields;
  987.     
  988.     error = ApptGetRecord(dbP, *index, &r, &recordH);
  989.     if (error) return error;
  990.     
  991.     // If the record already has an expections list, add an entry to 
  992.     // the list.
  993.     if (r.exceptions)
  994.         {
  995.         size = sizeof (ExceptionsListType) + 
  996.             (sizeof (DateType) * r.exceptions->numExceptions);
  997.         exceptions = MemPtrNew (size);
  998.         ErrFatalDisplayIf ((!exceptions), "Out of memory");
  999.         
  1000.         MemMove (exceptions, r.exceptions, size - sizeof (DateType));
  1001.         exceptions->numExceptions++;
  1002.         *(&exceptions->exception + r.exceptions->numExceptions) = date;
  1003.         }
  1004.  
  1005.     // Create an expections list.
  1006.     else
  1007.         {
  1008.         size = sizeof (ExceptionsListType);
  1009.         exceptions = MemPtrNew (size);
  1010.         ErrFatalDisplayIf ((!exceptions), "Out of memory");
  1011.  
  1012.         exceptions->numExceptions = 1;
  1013.         exceptions->exception = date;
  1014.         }
  1015.     
  1016.     MemHandleUnlock (recordH);
  1017.     
  1018.     // Update the record
  1019.     r.exceptions = exceptions;
  1020.     MemSet (&changedFields, sizeof (changedFields), 0);
  1021.     changedFields.exceptions = true;
  1022.     error = ApptChangeRecord (dbP, index, &r, changedFields);
  1023.     
  1024.     MemPtrFree (exceptions);
  1025.  
  1026.     return (error);
  1027. }
  1028.  
  1029.  
  1030. /***********************************************************************
  1031.  *
  1032.  * FUNCTION:    ApptRepeatsOnDate
  1033.  *
  1034.  * DESCRIPTION: This routine returns true if a repeating appointment
  1035.  *              occurrs on the specified date.
  1036.  *
  1037.  * PARAMETERS:  apptRec - a pointer to an appointment record
  1038.  *              date    - date to check              
  1039.  *
  1040.  * RETURNED:    true if the appointment occurs on the date specified
  1041.  *
  1042.  * REVISION HISTORY:
  1043.  *            Name    Date        Description
  1044.  *            ----    ----        -----------
  1045.  *            art    6/14/95        Initial Revision
  1046.  *
  1047.  ***********************************************************************/
  1048. Boolean ApptRepeatsOnDate (ApptDBRecordPtr apptRec, DateType date)
  1049. {
  1050.     Int  i;
  1051.     Word freq;
  1052.     Word weeksDiff;
  1053.     Word dayInMonth;
  1054.     Word dayOfWeek;
  1055.     Word dayOfMonth;
  1056.     Word firstDayOfWeek;
  1057.     long dateInDays;
  1058.     long startInDays;
  1059.     Boolean onDate;
  1060.     DatePtr exceptions;
  1061.     DateType startDate;
  1062.  
  1063.     // Is the date passed before the start date of the appointment?
  1064.     if (DateCompare (date, apptRec->when->date) < 0)
  1065.         return (false);
  1066.  
  1067.     // Is the date passed after the end date of the appointment?
  1068.     if (DateCompare (date, apptRec->repeat->repeatEndDate) > 0)
  1069.         return (false);
  1070.     
  1071.  
  1072.     // Get the frequency of occurrecne (ex: every 2nd day, every 3rd month, etc.).  
  1073.     freq = apptRec->repeat->repeatFrequency;
  1074.     
  1075.     // Get the date of the first occurrecne of the appointment.
  1076.     startDate = apptRec->when->date;
  1077.  
  1078.     switch (apptRec->repeat->repeatType)
  1079.         {
  1080.         // Daily repeating appointment.
  1081.         case repeatDaily:
  1082.             dateInDays = DateToDays (date);
  1083.             startInDays = DateToDays (startDate);
  1084.             onDate = ((dateInDays - startInDays) % freq) == 0;
  1085.             break;
  1086.             
  1087.  
  1088.         // Weekly repeating appointment (ex: every Monday and Friday). 
  1089.         // Yes, weekly repeating appointment can occur more then once a
  1090.         // week.
  1091.         case repeatWeekly:
  1092.             // Are we on a day of the week that the appointment repeats on.
  1093.             dayOfWeek = DayOfWeek (date.month, date.day, date.year+firstYear);
  1094.             onDate = ((1 << dayOfWeek) & apptRec->repeat->repeatOn);
  1095.             if (! onDate) break;
  1096.  
  1097.             // Are we in a week in which the appointment occurrs, if not 
  1098.             // move to that start of the next week in which the appointment
  1099.             // does occur.
  1100.             dateInDays = DateToDays (date);
  1101.             startInDays = DateToDays (startDate);
  1102.  
  1103.             firstDayOfWeek = (DayOfWeek (1, 1, firstYear) - 
  1104.                 apptRec->repeat->repeatStartOfWeek + daysInWeek) % daysInWeek;
  1105.  
  1106.             weeksDiff = (((dateInDays + firstDayOfWeek) / daysInWeek) - 
  1107.                              ((startInDays + firstDayOfWeek) / daysInWeek)) %freq;
  1108.             onDate = (weeksDiff == 0);
  1109.             break;
  1110.  
  1111.  
  1112. //            // Compute the first occurrence of the appointment that occurs
  1113. //            // on the same day of the week as the date passed.
  1114. //            startDayOfWeek = DayOfWeek (startDate.month, startDate.day, 
  1115. //                startDate.year+firstYear);
  1116. //            startInDays = DateToDays (startDate);
  1117. //            if (startDayOfWeek < dayOfWeek)
  1118. //                startInDays += dayOfWeek - startDayOfWeek;
  1119. //            else if (startDayOfWeek > dayOfWeek)
  1120. //                startInDays += dayOfWeek+ (daysInWeek *freq) - startDayOfWeek;
  1121. //            
  1122. //            // Are we in a week in which the appointment repeats.
  1123. //            dateInDays = DateToDays (date);
  1124. //            onDate = (((dateInDays - startInDays) / daysInWeek) % freq) == 0;
  1125. //            break;
  1126.  
  1127.  
  1128.         // Monthly-by-day repeating appointment (ex: the 3rd Friday of every
  1129.         // month).
  1130.         case repeatMonthlyByDay:
  1131.             // Are we in a month in which the appointment repeats.
  1132.             onDate = ((((date.year - startDate.year) * monthsInYear) + 
  1133.                            (date.month - startDate.month)) % freq) == 0;
  1134.             if (! onDate) break;
  1135.  
  1136.             // Do the days of the month match (ex: 3rd Friday)
  1137.             dayOfMonth = DayOfMonth (date.month, date.day, date.year+firstYear);
  1138.             onDate = (dayOfMonth == apptRec->repeat->repeatOn);
  1139.             if (onDate) break;
  1140.             
  1141.             // If the appointment repeats on one of the last days of the month,
  1142.             // check if the date passed is also one of the last days of the 
  1143.             // month.  By last days of the month we mean: last sunday, 
  1144.             // last monday, etc.
  1145.             if ((apptRec->repeat->repeatOn >= domLastSun) &&
  1146.                  (dayOfMonth >= dom4thSun))
  1147.                 {
  1148.                 dayOfWeek = DayOfWeek (date.month, date.day, date.year+firstYear);
  1149.                 dayInMonth = DaysInMonth (date.month, date.year+firstYear);
  1150.                 onDate = (((date.day + daysInWeek) > dayInMonth) &&
  1151.                              (dayOfWeek == (apptRec->repeat->repeatOn % daysInWeek)));
  1152.                 }
  1153.             break;                        
  1154.  
  1155.  
  1156.         // Monthly-by-date repeating appointment (ex: the 15th of every
  1157.         // month).
  1158.         case repeatMonthlyByDate:
  1159.             // Are we in a month in which the appointment repeats.
  1160.             onDate = ((((date.year - startDate.year) * monthsInYear) + 
  1161.                            (date.month - startDate.month)) % freq) == 0;
  1162.             if (! onDate) break;
  1163.             
  1164.             // Are we on the same day of the month as the start date.
  1165.             onDate = (date.day == startDate.day);
  1166.             if (onDate) break;
  1167.  
  1168.             // If the staring day of the appointment is greater then the 
  1169.             // number of day in the month passed, and the day passed is the 
  1170.             // last day of the month, then the appointment repeats on the day.
  1171.             dayInMonth = DaysInMonth (date.month, date.year+firstYear);
  1172.             onDate = ((startDate.day > dayInMonth) && (date.day == dayInMonth));
  1173.             break;
  1174.  
  1175.  
  1176.         // Yearly repeating appointment.
  1177.         case repeatYearly:
  1178.             // Are we in a year in which the appointment repeats.
  1179.             onDate = ((date.year - startDate.year) % freq) == 0;
  1180.             if (! onDate) break;
  1181.             
  1182.             // Are we on the month and day that the appointment repeats.
  1183.             onDate = (date.month == startDate.month) &&
  1184.                       (date.day == startDate.day);
  1185.             if (onDate) break;
  1186.             
  1187.             // Specal leap day processing.
  1188.             if ( (startDate.month == february) && 
  1189.                   (startDate.day == 29) &&
  1190.                   (date.month == february) && 
  1191.                   (date.day == DaysInMonth (date.month, date.year+firstYear)))
  1192.                 {
  1193.                 onDate = true;
  1194.                 }                      
  1195.             break;
  1196.         }
  1197.  
  1198.     // Check for an exception.
  1199.     if ((onDate) && (apptRec->exceptions))
  1200.         {
  1201.         exceptions = &apptRec->exceptions->exception;
  1202.         for (i = 0; i < apptRec->exceptions->numExceptions; i++)
  1203.             {
  1204.             if (DateCompare (date, exceptions[i]) == 0)
  1205.                 {
  1206.                 onDate = false;
  1207.                 break;
  1208.                 }
  1209.             }
  1210.         }
  1211.  
  1212.  
  1213.     return (onDate);
  1214. }
  1215.  
  1216.  
  1217.  
  1218. /***********************************************************************
  1219.  *
  1220.  * FUNCTION:    ApptNextRepeat
  1221.  *
  1222.  * DESCRIPTION: This routine computes the date of the next 
  1223.  *              occurrence of a repeating appointment.
  1224.  *
  1225.  * PARAMETERS:  apptRec - a pointer to an appointment record
  1226.  *              date    - passed:   date to start from
  1227.  *                        returned: date of next occurrence             
  1228.  *
  1229.  * RETURNED:    true if the appointment occurs again
  1230.  *
  1231.  * REVISION HISTORY:
  1232.  *            Name    Date        Description
  1233.  *            ----    ----        -----------
  1234.  *            art    6/14/95    Initial Revision
  1235.  *
  1236.  ***********************************************************************/
  1237. static Boolean NextRepeat (ApptDBRecordPtr apptRec, DatePtr dateP)
  1238. {
  1239.     Int  i;
  1240.     Word day;
  1241.     Word freq;
  1242.     Word year;
  1243.     Word adjust;
  1244.     Word weeksDiff;
  1245.     Word monthsDiff;
  1246.     Word daysInMonth;
  1247.     Word dayOfWeek;
  1248.     Word apptWeekDay;
  1249.     Word firstDayOfWeek;
  1250.     Word daysTilNext;
  1251.     Word monthsTilNext;
  1252.     ULong dateInDays;
  1253.     ULong startInDays;
  1254.     DateType start;
  1255.     DateType date;
  1256.     DateType next;
  1257.  
  1258.     date = *dateP;
  1259.  
  1260.     // Is the date passed after the end date of the appointment?
  1261.     if (DateCompare (date, apptRec->repeat->repeatEndDate) > 0)
  1262.         return (false);
  1263.     
  1264.     // Is the date passed before the start date of the appointment?
  1265.     if (DateCompare (date, apptRec->when->date) < 0)
  1266.         date = apptRec->when->date;
  1267.  
  1268.     // Get the frequency on occurrecne (ex: every 2nd day, every 3rd month, etc).  
  1269.     freq = apptRec->repeat->repeatFrequency;
  1270.     
  1271.     // Get the date of the first occurrecne of the appointment.
  1272.     start = apptRec->when->date;
  1273.     
  1274.  
  1275.     switch (apptRec->repeat->repeatType)
  1276.         {
  1277.         // Daily repeating appointment.
  1278.         case repeatDaily:
  1279.             dateInDays = DateToDays (date);
  1280.             startInDays = DateToDays (start);
  1281.             daysTilNext = (dateInDays - startInDays + freq - 1) / freq * freq;
  1282.             if (startInDays + daysTilNext > (ULong) maxDays)
  1283.                 return (false);
  1284.             DateDaysToDate (startInDays + daysTilNext, &next);
  1285.             break;
  1286.             
  1287.  
  1288.  
  1289.         // Weekly repeating appointment (ex: every Monday and Friday). 
  1290.         // Yes, weekly repeating appointment can occur more then once a
  1291.         // week.
  1292.         case repeatWeekly:
  1293.             dateInDays = DateToDays (date);
  1294.             startInDays = DateToDays (start);
  1295.  
  1296.             firstDayOfWeek = (DayOfWeek (1, 1, firstYear) - 
  1297.                 apptRec->repeat->repeatStartOfWeek + daysInWeek) % daysInWeek;
  1298.  
  1299.             dayOfWeek = DayOfWeek (date.month, date.day, date.year+firstYear);
  1300.             apptWeekDay = (dayOfWeek - apptRec->repeat->repeatStartOfWeek +
  1301.                 daysInWeek) % daysInWeek;
  1302.  
  1303.             // Are we in a week in which the appointment occurrs, if not 
  1304.             // move to that start of the next week in which the appointment
  1305.             // does occur.
  1306.             weeksDiff = (((dateInDays + firstDayOfWeek) / daysInWeek) - 
  1307.                              ((startInDays + firstDayOfWeek) / daysInWeek)) %freq;
  1308.             if (weeksDiff)
  1309.                 {
  1310.                 adjust = ((freq - weeksDiff) * daysInWeek)- apptWeekDay;
  1311.                 apptWeekDay = 0;
  1312.                 dayOfWeek = (dayOfWeek + adjust) % daysInWeek;
  1313.                 }
  1314.             else
  1315.                 adjust = 0;
  1316.             
  1317.             // Find the next day on which the appointment repeats.
  1318.             for (i = 0; i < daysInWeek; i++)
  1319.                 {
  1320.                 if (apptRec->repeat->repeatOn & (1 << dayOfWeek)) break;
  1321.                 adjust++;
  1322.                 if (++dayOfWeek == daysInWeek)
  1323.                     dayOfWeek = 0;
  1324.                 if (++apptWeekDay == daysInWeek)
  1325.                     adjust += (freq - 1) * daysInWeek;
  1326.                 }
  1327.  
  1328.             if (dateInDays + adjust > (ULong) maxDays)
  1329.                 return (false);
  1330.             DateDaysToDate (dateInDays + adjust, &next);
  1331. //            next = date;
  1332. //            DateAdjust (&next, adjust);
  1333.             break;
  1334.  
  1335.  
  1336.  
  1337.         // Monthly-by-day repeating appointment (ex: the 3rd Friday of every
  1338.         // month).
  1339.         case repeatMonthlyByDay:
  1340.             // Compute the number of month until the appointment repeats again.
  1341.             monthsTilNext = (date.month - start.month);
  1342.  
  1343.             monthsTilNext = ((((date.year - start.year) * monthsInYear) + 
  1344.                                   (date.month - start.month)) + freq - 1) /
  1345.                                freq * freq;
  1346.  
  1347.             while (true)
  1348.                 {
  1349.                 year = start.year + 
  1350.                                  (start.month - 1 + monthsTilNext) / monthsInYear;
  1351.                 if (year >= numberOfYears)
  1352.                     return (false);
  1353.  
  1354.                 next.year = year;
  1355.                 next.month = (start.month - 1 + monthsTilNext) % monthsInYear + 1;
  1356.     
  1357.                 dayOfWeek = DayOfWeek (next.month, 1, next.year+firstYear);
  1358.                 if ((apptRec->repeat->repeatOn % daysInWeek) >= dayOfWeek)
  1359.                     day = apptRec->repeat->repeatOn - dayOfWeek + 1;
  1360.                 else
  1361.                     day = apptRec->repeat->repeatOn + daysInWeek - dayOfWeek + 1;
  1362.     
  1363.                 // If repeat-on day is between the last sunday and the last
  1364.                 // saturday, make sure we're not passed the end of the month.
  1365.                 if ( (apptRec->repeat->repeatOn >= domLastSun) &&
  1366.                       (day > DaysInMonth (next.month, next.year+firstYear)))
  1367.                     {
  1368.                     day -= daysInWeek;
  1369.                     }
  1370.                 next.day = day;
  1371.  
  1372.                 // Its posible that "next date" calculated above is 
  1373.                 // before the date passed.  If so, move forward
  1374.                 // by the length of the repeat freguency and preform
  1375.                 // the calculation again.
  1376.                 if ( DateToInt(date) > DateToInt (next))
  1377.                     monthsTilNext += freq;
  1378.                 else
  1379.                     break;
  1380.                 }
  1381.             break;                        
  1382.  
  1383.  
  1384.  
  1385.         // Monthly-by-date repeating appointment (ex: the 15th of every
  1386.         // month).
  1387.         case repeatMonthlyByDate:
  1388.             // Compute the number of month until the appointment repeats again.
  1389.             monthsDiff = ((date.year - start.year) * monthsInYear) + 
  1390.                 (date.month - start.month);
  1391.             monthsTilNext = (monthsDiff + freq - 1) / freq * freq;
  1392.  
  1393.             if ((date.day > start.day) && (!(monthsDiff % freq)))
  1394.                 monthsTilNext += freq;
  1395.                 
  1396.             year = start.year + 
  1397.                              (start.month - 1 + monthsTilNext) / monthsInYear;
  1398.             if (year >= numberOfYears)
  1399.                 return (false);
  1400.  
  1401.             next.year = year;
  1402.             next.month = (start.month - 1 + monthsTilNext) % monthsInYear + 1;
  1403.             next.day = start.day;
  1404.  
  1405.             // Make sure we're not passed the last day of the month.
  1406.             daysInMonth = DaysInMonth (next.month, next.year+firstYear);
  1407.             if (next.day > daysInMonth)
  1408.                 next.day = daysInMonth;
  1409.             break;
  1410.  
  1411.  
  1412.  
  1413.         // Yearly repeating appointment.
  1414.         case repeatYearly:
  1415.             next.day = start.day;
  1416.             next.month = start.month;
  1417.  
  1418.             year = start.year + 
  1419.                 ((date.year - start.year + freq - 1) / freq * freq);
  1420.             
  1421.             if ((date.month > start.month) ||
  1422.                 ((date.month == start.month) && (date.day > start.day)))
  1423.                  year += freq;
  1424.  
  1425.             // Specal leap day processing.
  1426.             if ( (next.month == february) && (next.day == 29) &&
  1427.                   (next.day > DaysInMonth (next.month, year+firstYear)))
  1428.                 {
  1429.                 next.day = DaysInMonth (next.month, year+firstYear);
  1430.                 }                      
  1431.             if (year >= numberOfYears)
  1432.                 return (false);
  1433.  
  1434.             next.year = year;    
  1435.             break;
  1436.         }
  1437.  
  1438.     // Is the next occurrence after the end date of the appointment?
  1439.     if (DateCompare (next, apptRec->repeat->repeatEndDate) > 0)
  1440.         return (false);
  1441.  
  1442.     ErrFatalDisplayIf ((DateToInt (next) < DateToInt (*dateP)),
  1443.         "Calculation error");
  1444.  
  1445.     *dateP = next;
  1446.     return (true);
  1447. }
  1448.  
  1449.  
  1450. /***********************************************************************
  1451.  *
  1452.  * FUNCTION:    IsException
  1453.  *
  1454.  * DESCRIPTION: This routine returns true the date passed is in a 
  1455.  *              repeating appointment's exception list.
  1456.  *
  1457.  * PARAMETERS:  apptRec - a pointer to an appointment record
  1458.  *              date    - date to check              
  1459.  *
  1460.  * RETURNED:    true if the date is an exception date.
  1461.  *
  1462.  * REVISION HISTORY:
  1463.  *            Name    Date        Description
  1464.  *            ----    ----        -----------
  1465.  *            art    6/14/95        Initial Revision
  1466.  *
  1467.  ***********************************************************************/
  1468. static Boolean IsException (ApptDBRecordPtr apptRec, DateType date)
  1469. {
  1470.     int i;
  1471.     DatePtr exceptions;
  1472.  
  1473.     if (apptRec->exceptions)
  1474.         {
  1475.         exceptions = &apptRec->exceptions->exception;
  1476.         for (i = 0; i < apptRec->exceptions->numExceptions; i++)
  1477.             {
  1478.             if (DateCompare (date, exceptions[i]) == 0)
  1479.             return (true);
  1480.             }
  1481.         }
  1482.     return (false);
  1483. }
  1484.  
  1485.  
  1486. /***********************************************************************
  1487.  *
  1488.  * FUNCTION:    ApptNextRepeat
  1489.  *
  1490.  * DESCRIPTION: This routine computes the next occurrence of a 
  1491.  *              repeating appointment.
  1492.  *
  1493.  * PARAMETERS:  apptRec - a pointer to an appointment record
  1494.  *              dateP   - passed:   date to start from
  1495.  *                        returned: date of next occurrence            
  1496.  *
  1497.  * RETURNED:    true if there is an occurrence of the appointment
  1498.  *              between the date passed and the appointment's end date
  1499.  *
  1500.  * REVISION HISTORY:
  1501.  *            Name    Date        Description
  1502.  *            ----    ----        -----------
  1503.  *            art    6/20/95    Initial Revision
  1504.  *
  1505.  ***********************************************************************/
  1506. Boolean ApptNextRepeat (ApptDBRecordPtr apptRec, DatePtr dateP)
  1507. {
  1508.     DateType date;
  1509.     
  1510.     date = *dateP;
  1511.     
  1512.     while (true)
  1513.         {
  1514.         // Compute the next time the appointment repeats.
  1515.         if (! NextRepeat (apptRec, &date))
  1516.             return (false);
  1517.  
  1518.         // Check if the date computed is in the exceptions list.
  1519.         if (! IsException (apptRec, date))
  1520.             {
  1521.             *dateP = date;
  1522.             return (true);
  1523.             }
  1524.             
  1525.         DateAdjust (&date, 1);
  1526.         }        
  1527. }
  1528.  
  1529.  
  1530. /***********************************************************************
  1531.  *
  1532.  * FUNCTION:    ApptListCompare
  1533.  *
  1534.  * DESCRIPTION: This routine compares two entries in the appointment list, 
  1535.  *              it's called by ApptGetAppointments via the quick sort 
  1536.  *              routine.
  1537.  *
  1538.  * PARAMETERS:  a     - a pointer to an entry in the appointment list
  1539.  *                     b     - a pointer to an entry in the appointment list
  1540.  *              extra - extra data passed to quick sort - not used
  1541.  *
  1542.  * RETURNED:    if a1 > a2  returns a positive int
  1543.  *              if a1 < a2  returns a negative int
  1544.  *              if a1 = a2  returns zero
  1545.  *
  1546.  * REVISION HISTORY:
  1547.  *            Name    Date        Description
  1548.  *            ----    ----        -----------
  1549.  *            art    6/15/95        Initial Revision
  1550.  *
  1551.  ***********************************************************************/
  1552. static Int ApptListCompare (ApptInfoPtr a1, ApptInfoPtr  a2, Long extra)
  1553. {
  1554.     Int result;
  1555.     
  1556.     result = TimeCompare (a1->startTime, a2->startTime);
  1557.     if (result == 0)
  1558.         {
  1559.         result = TimeCompare (a1->endTime, a2->endTime);
  1560.         }
  1561.     return result;
  1562. }
  1563.  
  1564.  
  1565. /***********************************************************************
  1566.  *
  1567.  * FUNCTION:    ApptGetAppointments
  1568.  *
  1569.  * DESCRIPTION: This routine returns a list of appointments that are on 
  1570.  *              the date specified
  1571.  *
  1572.  * PARAMETERS:  dbP    - pointer to the database
  1573.  *              date   - date to search for
  1574.  *              countP - number of appointments on the specified 
  1575.  *                       day (returned value)
  1576.  *
  1577.  * RETURNED:    handle of the appointment list (ApptInfoType)
  1578.  *
  1579.  * REVISION HISTORY:
  1580.  *            Name    Date        Description
  1581.  *            ----    ----        -----------
  1582.  *            art    6/15/95        Initial Revision
  1583.  *
  1584.  ***********************************************************************/
  1585. #if 0
  1586. VoidHand ApptGetAppointments (DmOpenRef dbP, DateType date, UIntPtr countP)
  1587. {
  1588.     Err    error;
  1589.     Int    result;
  1590.     Int    count = 0;
  1591.     UInt    recordNum;
  1592.     Boolean repeats;
  1593.     VoidHand recordH;
  1594.     VoidHand apptListH;
  1595.     ApptInfoPtr apptList;
  1596.     ApptDBRecordType apptRec;
  1597.     ApptPackedDBRecordPtr r;
  1598.  
  1599.     // Allocated a block to hold the appointment list.
  1600.     apptListH = MemHandleNew (sizeof (ApptInfoType) * apptMaxPerDay);
  1601.     ErrFatalDisplayIf(!apptListH, "Out of memory");
  1602.     if (! apptListH) return (0);
  1603.  
  1604.     apptList = MemHandleLock (apptListH);
  1605.     
  1606.  
  1607.     // Find the first non-repeating appointment of the day.
  1608.     if (ApptFindFirst (dbP, date, &recordNum))
  1609.         {
  1610.         while (count < apptMaxPerDay)
  1611.             {
  1612.             // Check if the appointment is on the date passed, if it is 
  1613.             // add it to the appointment list.        
  1614.             recordH = DmQueryRecord (dbP, recordNum);
  1615.             r = MemHandleLock (recordH);
  1616.             result = DateCompare (r->when.date, date);
  1617.  
  1618.             if (result == 0)
  1619.                 {
  1620.                 // Add the record to the appoitment list.
  1621.                 apptList[count].startTime = r->when.startTime;                
  1622.                 apptList[count].endTime = r->when.endTime;                
  1623.                 apptList[count].recordNum = recordNum;    
  1624.                 count++;
  1625.                 }
  1626.             MemHandleUnlock (recordH);
  1627.             if (result != 0) break;
  1628.  
  1629.             // Get the next record.
  1630.             error = DmSeekRecordInCategory (dbP, &recordNum, 1, dmSeekForward, dmAllCategories);
  1631.             if (error == dmErrSeekFailed) break;
  1632.             }
  1633.         }
  1634.  
  1635.  
  1636.     // Add the repeating appointments to the list.  Repeating appointments
  1637.     // are stored at the beginning of the database.
  1638.     recordNum = 0;
  1639.     while (count < apptMaxPerDay)
  1640.         {
  1641.         recordH = DmQueryNextInCategory (dbP, &recordNum, dmAllCategories);
  1642.         if (! recordH) break;
  1643.         
  1644.         r = (ApptPackedDBRecordPtr) MemHandleLock (recordH);
  1645.         
  1646.         repeats = (r->flags.repeat != 0);
  1647.         if (repeats)
  1648.             {
  1649.             ApptUnpack (r, &apptRec);
  1650.             if (ApptRepeatsOnDate (&apptRec, date))
  1651.                 {
  1652.                 // Add the record to the appoitment list.
  1653.                 apptList[count].startTime = r->when.startTime;                
  1654.                 apptList[count].endTime = r->when.endTime;                
  1655.                 apptList[count].recordNum = recordNum;    
  1656.                 count++;
  1657.                 }
  1658.             }
  1659.         MemHandleUnlock (recordH);
  1660.  
  1661.         // If the record has no repeating info we've reached the end of the 
  1662.         // repeating appointments.
  1663.         if (! repeats) break;
  1664.         
  1665.          recordNum++;
  1666.         }
  1667.  
  1668.     
  1669.     // Sort the list by start time.
  1670.     SysInsertionSort (apptList, count, sizeof (ApptInfoType), ApptListCompare, 0L);
  1671.     
  1672.  
  1673.     // If there are no appointments on the specified day, free the appointment
  1674.     // list.
  1675.     if (count == 0)
  1676.         {
  1677.         MemPtrFree (apptList);
  1678.         apptListH = 0;
  1679.         }
  1680.  
  1681.     // Resize the appointment list block to release any unused space.
  1682.     else
  1683.         {
  1684.         MemHandleUnlock (apptListH);
  1685.         MemHandleResize (apptListH, count * sizeof (ApptInfoType));
  1686.         }
  1687.  
  1688.     *countP = count;
  1689.     return (apptListH);
  1690. }
  1691. #endif
  1692.  
  1693.  
  1694.  
  1695. /***********************************************************************
  1696.  *
  1697.  * FUNCTION:    ApptGetAlarmTime
  1698.  *
  1699.  * DESCRIPTION: This routine determines the date and time of the next alarm
  1700.  *              for the appointment passed.
  1701.  *
  1702.  * PARAMETERS:  apptRec     - pointer to an appointment record
  1703.  *              currentTime - current date and time in seconds
  1704.  *
  1705.  * RETURNED:    date and time of the alarm, in seconds, or zero if there
  1706.  *              is no alarm
  1707.  *
  1708.  * REVISION HISTORY:
  1709.  *            Name    Date        Description
  1710.  *            ----    ----        -----------
  1711.  *            art    6/20/95        Initial Revision
  1712.  *
  1713.  ***********************************************************************/
  1714. ULong ApptGetAlarmTime (ApptDBRecordPtr apptRec, ULong currentTime)
  1715. {
  1716.     ULong                    advance;
  1717.     ULong                    alarmTime;
  1718.     DateType                repeatDate;
  1719.     DateTimeType        curDateTime;
  1720.     DateTimeType        apptDateTime;
  1721.  
  1722.  
  1723.     // Non-repeating appointment?
  1724.     if (! apptRec->repeat)
  1725.         {
  1726.         // An alarm on an untimed event triggers at midnight.
  1727.         if (TimeToInt (apptRec->when->startTime) == apptNoTime)
  1728.             {
  1729.             apptDateTime.minute = 0;
  1730.             apptDateTime.hour = 0;
  1731.             }
  1732.         else
  1733.             {
  1734.             apptDateTime.minute = apptRec->when->startTime.minutes;
  1735.             apptDateTime.hour = apptRec->when->startTime.hours;
  1736.             }
  1737.         apptDateTime.second = 0;
  1738.         apptDateTime.day = apptRec->when->date.day;
  1739.         apptDateTime.month = apptRec->when->date.month;
  1740.         apptDateTime.year = apptRec->when->date.year + firstYear;
  1741.  
  1742.  
  1743.  
  1744.         // Compute the time of the alarm by adjusting the date and time 
  1745.         // of the appointment by the length of the advance notice.
  1746.         advance = apptRec->alarm->advance;
  1747.         switch (apptRec->alarm->advanceUnit)
  1748.             {
  1749.             case aauMinutes:
  1750.                 advance *= minutesInSeconds;
  1751.                 break;
  1752.             case aauHours:
  1753.                 advance *= hoursInSeconds;
  1754.                 break;
  1755.             case aauDays:
  1756.                 advance *= daysInSeconds;
  1757.                 break;
  1758.             }
  1759.  
  1760.         alarmTime = TimDateTimeToSeconds (&apptDateTime) - advance;
  1761.         if (alarmTime >= currentTime)
  1762.             return (alarmTime);
  1763.         else
  1764.             return (0);
  1765.         }
  1766.  
  1767.  
  1768.     // Repeating appointment.
  1769.     TimSecondsToDateTime (currentTime, &curDateTime);
  1770.     repeatDate.year = curDateTime.year - firstYear;
  1771.     repeatDate.month = curDateTime.month;
  1772.     repeatDate.day = curDateTime.day;
  1773.     
  1774.     while (ApptNextRepeat (apptRec, &repeatDate))
  1775.         {
  1776.         // An alarm on an untimed event triggers at midnight.
  1777.         if (TimeToInt (apptRec->when->startTime) == apptNoTime)
  1778.             {
  1779.             apptDateTime.minute = 0;
  1780.             apptDateTime.hour = 0;
  1781.             }
  1782.         else
  1783.             {
  1784.             apptDateTime.minute = apptRec->when->startTime.minutes;
  1785.             apptDateTime.hour = apptRec->when->startTime.hours;
  1786.             }
  1787.         apptDateTime.second = 0;
  1788.         apptDateTime.day = repeatDate.day;
  1789.         apptDateTime.month = repeatDate.month;
  1790.         apptDateTime.year = repeatDate.year + firstYear;
  1791.  
  1792.         // Compute the time of the alarm by adjusting the date and time 
  1793.         // of the appointment by the length of the advance notice.
  1794.         switch (apptRec->alarm->advanceUnit)
  1795.             {
  1796.             case aauMinutes:
  1797.                 advance = (ULong) apptRec->alarm->advance * minutesInSeconds;
  1798.                 break;
  1799.             case aauHours:
  1800.                 advance = (ULong) apptRec->alarm->advance * hoursInSeconds;
  1801.                 break;
  1802.             case aauDays:
  1803.                 advance = (ULong) apptRec->alarm->advance * daysInSeconds;
  1804.                 break;
  1805.             }
  1806.  
  1807.         alarmTime = TimDateTimeToSeconds (&apptDateTime) - advance;
  1808.         if (alarmTime >= currentTime)
  1809.             return (alarmTime);
  1810.  
  1811.         DateAdjust (&repeatDate, 1);
  1812.         } 
  1813.         
  1814.     return (0);
  1815. }
  1816.  
  1817.  
  1818. /***********************************************************************
  1819.  *
  1820.  * FUNCTION:    ApptGetAlarmsList
  1821.  *
  1822.  * DESCRIPTION: This routine returns a list of appointment that have
  1823.  *              alarm scheduled at the specified time.  The list contains
  1824.  *              the handles of the appointment record.
  1825.  *
  1826.  * PARAMETERS:  dbP       - pointer to the database
  1827.  *              alarmTime - time of the alarm expressed as seconds from
  1828.  *                          1/1/1904.
  1829.  *              countP    - returnd: number of entries in list
  1830.  *              quiteP    - returnd: true if alarm sound should not play.
  1831.  *
  1832.  * RETURNED:    the handle of a block containing the record handles of 
  1833.  *              appointments that have alarms scheduled at the passed time,
  1834.  *              or zero if there are not alarms scheduled.
  1835.  *              
  1836.  *
  1837.  * REVISION HISTORY:
  1838.  *            Name    Date        Description
  1839.  *            ----    ----        -----------
  1840.  *            art    8/30/95    Initial Revision
  1841.  *
  1842.  ***********************************************************************/
  1843. VoidHand ApptGetAlarmsList (DmOpenRef dbP, ULong alarmTime, UIntPtr countP,
  1844.         BooleanPtr quiteP)
  1845. {
  1846.     UInt count = 0;
  1847.     UInt numBytes;
  1848.     UInt recordNum;
  1849.     UInt    numRecords;
  1850.     ULong next;
  1851.     VoidHand recordH;
  1852.     WordPtr alarmList;
  1853.     VoidHand alarmListH;
  1854.     ApptDBRecordType apptRec;
  1855.     ApptPackedDBRecordPtr r;
  1856.     
  1857.     *quiteP = true;
  1858.  
  1859.     // Allocate a block to hold the a list of record handles.
  1860.     numBytes = sizeof (VoidHand) * apptMaxDisplayableAlarms;
  1861.     alarmListH = MemHandleNew (numBytes);
  1862.     ErrFatalDisplayIf(!alarmListH, "Out of memory");
  1863.  
  1864.     alarmList = MemHandleLock (alarmListH);
  1865.     MemSet (alarmList, numBytes, 0);
  1866.  
  1867.     // Searce the database for appointments with alarms at the passed time.
  1868.     numRecords = DmNumRecords(dbP);
  1869.     for (recordNum = 0; recordNum < numRecords; recordNum++)
  1870.         {
  1871.         recordH = DmQueryRecord(dbP, recordNum);
  1872.         if (! recordH) break;
  1873.         
  1874.         r = (ApptPackedDBRecordPtr) MemHandleLock (recordH);
  1875.         
  1876.         if (r->flags.alarm)
  1877.             {
  1878.             ApptUnpack (r, &apptRec);
  1879.             next = ApptGetAlarmTime (&apptRec, alarmTime);
  1880.             if (next == alarmTime)
  1881.                 {
  1882.                 // Add the record to the alarm list.
  1883.                 alarmList[count] = recordNum;                
  1884.                 count++;
  1885.                 
  1886.                 // If the alarm is on a timed event then we sould play
  1887.                 // the alarm sound.
  1888.                 if (TimeToInt (apptRec.when->startTime) != apptNoTime)
  1889.                     *quiteP = false;
  1890.                 }
  1891.             }
  1892.         MemHandleUnlock (recordH);
  1893.  
  1894.         if (count == apptMaxDisplayableAlarms) break;
  1895.         }
  1896.  
  1897.     MemPtrUnlock (alarmList);
  1898.     
  1899.     // Free the list if no alarms were found.
  1900.     if (count == 0) 
  1901.         {
  1902.         MemHandleFree (alarmListH);
  1903.         alarmListH = 0;
  1904.         }
  1905.     
  1906.     *countP = count;
  1907.     return (alarmListH);
  1908. }
  1909.  
  1910.  
  1911. /***********************************************************************
  1912.  *
  1913.  * FUNCTION:    ApptGetTimeOfNextAlarm
  1914.  *
  1915.  * DESCRIPTION: This routine determines the time of the next scheduled
  1916.  *              alarm.
  1917.  *
  1918.  * PARAMETERS:  dbP           - pointer to the database
  1919.  *              timeInSeconds - time to search forward from
  1920.  *
  1921.  * RETURNED:    time of the next alarm, in seconds from 1/1/1904, or
  1922.  *              zreo if there are no alarms scheduled.
  1923.  *
  1924.  * REVISION HISTORY:
  1925.  *            Name    Date        Description
  1926.  *            ----    ----        -----------
  1927.  *            art    6/20/95    Initial Revision
  1928.  *
  1929.  ***********************************************************************/
  1930. ULong ApptGetTimeOfNextAlarm (DmOpenRef dbP, ULong    timeInSeconds)
  1931. {
  1932.     UInt                    recordNum;
  1933.     UInt                    numRecords;
  1934.     ULong                    alarmTime;
  1935.     ULong                    nextAlarmTime = 0;
  1936.     VoidHand                nextAlarmH = 0;
  1937.     VoidHand             recordH;
  1938.     ApptDBRecordType     apptRec;
  1939.     ApptPackedDBRecordPtr r;
  1940.  
  1941.  
  1942.     numRecords = DmNumRecords(dbP);
  1943.     for (recordNum = 0; recordNum < numRecords; recordNum++)
  1944.         {
  1945.         recordH = DmQueryRecord(dbP, recordNum);
  1946.         if (! recordH) break;
  1947.         
  1948.         r = (ApptPackedDBRecordPtr) MemHandleLock (recordH);
  1949.         
  1950.         // Does the record have any alarm info.
  1951.         if (r->flags.alarm)
  1952.             {
  1953.             ApptUnpack (r, &apptRec);
  1954.             alarmTime = ApptGetAlarmTime (&apptRec, timeInSeconds);
  1955.             if (alarmTime && ((! nextAlarmTime) || (alarmTime < nextAlarmTime)))
  1956.                 nextAlarmTime = alarmTime;
  1957.             }
  1958.         MemHandleUnlock (recordH);
  1959.         }
  1960.  
  1961.     return (nextAlarmTime);
  1962. }
  1963.  
  1964.  
  1965. /************************************************************
  1966.  *
  1967.  *  FUNCTION: ApptNewRecord
  1968.  *
  1969.  *  DESCRIPTION: Create a new packed record in sorted position
  1970.  *
  1971.  *  PARAMETERS: database pointer
  1972.  *                database record
  1973.  *
  1974.  *  RETURNS: ##0 if successful, error code if not
  1975.  *
  1976.  *  CREATED: 1/25/95 
  1977.  *
  1978.  *  BY: Roger Flores
  1979.  *
  1980.  *************************************************************/
  1981. Err ApptNewRecord(DmOpenRef dbP, ApptDBRecordPtr r, UInt *index)
  1982. {
  1983.     VoidHand recordH;
  1984.     ApptPackedDBRecordPtr recordP;
  1985.     UInt                     newIndex;
  1986.     Err err;
  1987.  
  1988.     
  1989.     // Make a new chunk with the correct size.
  1990.     recordH = DmNewHandle (dbP, (ULong) ApptPackedSize(r));
  1991.     if (recordH == NULL)
  1992.         return dmErrMemError;
  1993.  
  1994.     recordP = MemHandleLock (recordH);
  1995.     
  1996.     // Copy the data from the unpacked record to the packed one.
  1997.     ApptPack (r, recordP);
  1998.  
  1999.     newIndex = ApptFindSortPosition(dbP, recordP);
  2000.  
  2001.     MemPtrUnlock (recordP);
  2002.  
  2003.     // 4) attach in place
  2004.     err = DmAttachRecord(dbP, &newIndex, recordH, 0);
  2005.     if (err) 
  2006.         MemHandleFree(recordH);
  2007.     else
  2008.         *index = newIndex;
  2009.     
  2010.     return err;
  2011. }
  2012.  
  2013.  
  2014. /***********************************************************************
  2015.  *
  2016.  * FUNCTION:    AddAppointmentToList
  2017.  *
  2018.  * DESCRIPTION: This routine adds an appointment to a list of appointments. 
  2019.  *
  2020.  * PARAMETERS:  dbP    - pointer to the database
  2021.  *              date   - date to search for
  2022.  *              countP - number of appointments on the specified 
  2023.  *                       day (returned value)
  2024.  *
  2025.  * RETURNED:    handle of the appointment list (ApptInfoType)
  2026.  *
  2027.  * REVISION HISTORY:
  2028.  *            Name    Date        Description
  2029.  *            ----    ----        -----------
  2030.  *            art    4/17/96    Initial Revision
  2031.  *
  2032.  ***********************************************************************/
  2033. static Boolean AddAppointmentToList (VoidHand * apptListH, UInt count,
  2034.     TimeType startTime, TimeType endTime, UInt recordNum)
  2035. {
  2036.     Err err;
  2037.     Word newSize;
  2038.     ApptInfoPtr apptList;
  2039.  
  2040.     if (count == 0)
  2041.         {
  2042.         // Allocated a block to hold the appointment list.
  2043.         *apptListH = MemHandleNew (sizeof (ApptInfoType) * (apptMaxPerDay / 10));
  2044.         ErrFatalDisplayIf(!apptListH, "Out of memory");
  2045.         if (! apptListH) return (false);
  2046.         apptList = MemHandleLock (*apptListH);
  2047.         }
  2048.         
  2049.     // Resize the list to hold more more appointments.
  2050.     else if ((count % (apptMaxPerDay / 10)) == 0)
  2051.         {
  2052.         if (count + (apptMaxPerDay / 10) > apptMaxPerDay)
  2053.             return (false);
  2054.         
  2055.         MemHandleUnlock (*apptListH);
  2056.         newSize = sizeof (ApptInfoType) * (count + (apptMaxPerDay / 10));
  2057.         err = MemHandleResize (*apptListH, newSize);
  2058.         apptList = MemHandleLock (*apptListH);
  2059.  
  2060.         ErrFatalDisplayIf(err, "Out of memory");
  2061.         if (err) return (false);
  2062.         }
  2063.  
  2064.     else
  2065.         apptList = MemDeref (*apptListH);
  2066.  
  2067.     apptList[count].startTime = startTime;                
  2068.     apptList[count].endTime = endTime;                        
  2069.     apptList[count].recordNum = recordNum;    
  2070.  
  2071.     return (true);
  2072. }
  2073.  
  2074.  
  2075. /***********************************************************************
  2076.  *
  2077.  * FUNCTION:    ApptGetAppointments
  2078.  *
  2079.  * DESCRIPTION: This routine returns a list of appointments that are in 
  2080.  *              the range of dates specified
  2081.  *
  2082.  * PARAMETERS:  dbP       - pointer to the database
  2083.  *              date         - start date to search from
  2084.  *              days         - number a days in search range
  2085.  *              apptLists - returned: array of handle of the 
  2086.  *                                     appointment list (ApptInfoType)
  2087.  *              counts    - returned: returned: array of counts of the 
  2088.  *                          number of appointments in each list.
  2089.  *
  2090.  * RETURNED:    nothing
  2091.  *
  2092.  * REVISION HISTORY:
  2093.  *            Name    Date        Description
  2094.  *            ----    ----        -----------
  2095.  *            art    4/7/96    Initial Revision
  2096.  *
  2097.  ***********************************************************************/
  2098. void ApptGetAppointments (DmOpenRef dbP, DateType date, Word days,
  2099.     VoidHand apptLists [], UInt counts [])
  2100. {
  2101.     Int    count = 0;
  2102.     UInt    startDate;
  2103.     UInt    endDate;
  2104.     UInt    index;
  2105.     UInt    recordNum;
  2106.     ULong    dateInDays;
  2107.     DateType    apptDate;
  2108.     TimeType startTime;
  2109.     TimeType endTime;
  2110.     DateType tempDate;
  2111.     DateType repeatDate;
  2112.     Boolean repeats;
  2113.     VoidHand recordH;
  2114.     ApptInfoPtr apptList;
  2115.     ApptDBRecordType apptRec;
  2116.     ApptPackedDBRecordPtr r;
  2117.  
  2118.     
  2119.     MemSet (apptLists, days * sizeof (VoidHand), 0);
  2120.     MemSet (counts, days * sizeof (UInt), 0);
  2121.  
  2122.  
  2123.     startDate = DateToInt (date);
  2124.     tempDate = date;
  2125.     DateAdjust (&tempDate, days-1);
  2126.     endDate = DateToInt(tempDate);
  2127.  
  2128.     // Find the first non-repeating appointment of the day.
  2129.     ApptFindFirst (dbP, date, &recordNum);
  2130.     while (true)
  2131.         {
  2132.         recordH = DmQueryNextInCategory (dbP, &recordNum, dmAllCategories);
  2133.         if (! recordH) break;
  2134.  
  2135.         // Check if the appointment is on the date passed, if it is 
  2136.         // add it to the appointment list.        
  2137.         r = MemHandleLock (recordH);
  2138.         startTime = r->when.startTime;
  2139.         endTime = r->when.endTime;
  2140.         apptDate = r->when.date;
  2141.         MemHandleUnlock (recordH);
  2142.         
  2143.         if ((DateToInt (apptDate) < startDate) || 
  2144.              (DateToInt (apptDate) > endDate))
  2145.             break;
  2146.         
  2147.         // Add the record to the appoitment list.
  2148.         index = DateToDays (apptDate) - DateToDays (date);
  2149.         
  2150.         if (AddAppointmentToList (&apptLists[index], counts[index], 
  2151.                 startTime, endTime, recordNum))
  2152.             counts[index]++;
  2153.         else
  2154.             break;
  2155.             
  2156.         recordNum++;
  2157.         }
  2158.  
  2159.  
  2160.     // Add the repeating appointments to the list.  Repeating appointments
  2161.     // are stored at the beginning of the database.
  2162.     recordNum = 0;
  2163.     dateInDays = DateToDays (date);
  2164.     while (true)
  2165.         {
  2166.         recordH = DmQueryNextInCategory (dbP, &recordNum, dmAllCategories);
  2167.         if (! recordH) break;
  2168.         
  2169.         r = (ApptPackedDBRecordPtr) MemHandleLock (recordH);
  2170.         repeats = (r->flags.repeat != 0);
  2171.         
  2172.         if (repeats)
  2173.             {
  2174.             ApptUnpack (r, &apptRec);
  2175.  
  2176.             if (days == 1)
  2177.                 {
  2178.                 if (ApptRepeatsOnDate (&apptRec, date))
  2179.                     {
  2180.                     if (AddAppointmentToList (apptLists, *counts, 
  2181.                             r->when.startTime, r->when.endTime, recordNum))
  2182.                         (*counts)++;
  2183.                     }
  2184.                 }
  2185.                 
  2186.             else
  2187.                 {
  2188.                 repeatDate = date;
  2189.                  while (ApptNextRepeat (&apptRec, &repeatDate))
  2190.                     {
  2191.                     if (DateToInt (repeatDate) > endDate)
  2192.                         break;
  2193.                     
  2194.                     // Add the record to the appoitment list.
  2195.                     index = DateToDays (repeatDate) - dateInDays;
  2196.     
  2197.                     if (AddAppointmentToList (&apptLists[index], counts[index], 
  2198.                             r->when.startTime, r->when.endTime, recordNum))
  2199.                         counts[index]++;
  2200.                     else
  2201.                         break;
  2202.     
  2203.                     if (DateToInt (repeatDate) == endDate)
  2204.                         break;
  2205.  
  2206.                     DateAdjust (&repeatDate, 1);
  2207.                     }
  2208.                 }
  2209.             }
  2210.         MemHandleUnlock (recordH);
  2211.  
  2212.         // If the record has no repeating info we've reached the end of the 
  2213.         // repeating appointments.
  2214.         if (! repeats) break;
  2215.         
  2216.          recordNum++;
  2217.         }
  2218.  
  2219.     
  2220.     // Sort the list by start time.
  2221.     for (index = 0; index < days; index ++)
  2222.         {
  2223.         if (apptLists[index])
  2224.             {
  2225.             apptList = MemDeref (apptLists[index]);
  2226.             SysInsertionSort (apptList, counts[index], sizeof (ApptInfoType), 
  2227.                 (_comparF *)ApptListCompare, 0L);
  2228.     
  2229.             MemHandleUnlock (apptLists[index]);
  2230.             MemHandleResize (apptLists[index], counts[index] *
  2231.                 sizeof (ApptInfoType));
  2232.             }
  2233.         }
  2234. }
  2235.  
  2236. #endif /* 0 */
  2237.