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 / happydays.c < prev    next >
C/C++ Source or Header  |  2000-11-03  |  92KB  |  3,168 lines

  1. /*
  2. HappyDays - A Birthday 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. #include <PalmOS.h>
  21. #include "address.h"
  22. #include "datebook.h"
  23. #include "memodb.h"
  24. #include "todo.h"
  25.  
  26. #include "happydays.h"
  27. #include "happydaysRsc.h"
  28. #include "calendar.h"
  29. #include "s2lconvert.h"
  30. #include "util.h"
  31.  
  32. #define frmRescanUpdateCode (frmRedrawUpdateCode + 1)
  33.  
  34. // global variable
  35. //
  36. MemHandle gTableRowHandle;
  37.  
  38. Int16 gBirthDateField;
  39. Char gAppErrStr[AppErrStrLen];
  40. Char gDateBk3Icon[52][8];      // dateBk3 icon string
  41. Boolean gDateBk3IconLoaded = false;     // datebk3 icon loaded
  42. Boolean gPrefsRdirty, gPrefsWeredirty;
  43. UInt32 gAdcdate, gAdmdate;      // AddressBook create/modify time
  44. UInt32 gMmcdate, gMmmdate;      // Memo create/modify time
  45. Boolean gSortByCompany=true;    // sort by company is set in AddressBook?
  46. UInt16 gAddrCategory;           // address book category
  47. UInt16 gToDoCategory;           // todo category
  48. Int16 gMainTableStart = 0;      // Main Form table start point
  49. Int16 gMainTableRows;         // Main Form table num of rows(default 11) constant
  50. Int16 gMainTableTotals;       // Main Form table total rows;
  51. Int16 gMainTableHandleRow;    // The row of birthdate view to display
  52. DateFormatType gPrefdfmts;  // global date format for Birthday field
  53. DateFormatType gSystemdfmts;  // global system date format 
  54. TimeFormatType gPreftfmts;  // global time format
  55. DateType gStartDate;        // staring date of birthday listing
  56.  
  57. Boolean gProgramExit = false;    // Program exit control(set by Startform)
  58.  
  59. // for scroll bar characters
  60. static char *pageupchr="\001", *pagedownchr="\002";
  61. static char *pageupgreychr="\003", *pagedowngreychr="\004";
  62.  
  63. // Prefs stuff 
  64. MemHandle PrefsRecHandle, PrefsRHandle;
  65. UInt16 PrefsRecIndex;
  66.  
  67. struct sPrefsR *gPrefsR;
  68. struct sPrefsR DefaultPrefsR = {
  69.     PREFSVERSION,
  70.     0, 0, 0,              // all/selected, keep/modified, private
  71.     {   1, 0, 9, 3, 1, {-1, -1}, "145" },
  72. #ifdef GERMAN
  73.     {  1, "Alle" },
  74.     {  "Geburtstag", "*HD:", 1, 1, 0, 0, 0, dfDMYWithDots },
  75. #else
  76.     {  1, "All" },
  77.     {  "Birthday", "*HD:", 1, 1, 0, 0, 0, dfMDYWithSlashes },
  78. #endif
  79.     0,
  80. #ifdef GERMAN
  81.     "Alle", 
  82. #else
  83.     "All", 
  84. #endif
  85.     "\0\0\0", "\0\0\0",         /* addr create/modify date */
  86.     "\0\0\0", "\0\0\0"          /* memo create/modify date */
  87. };
  88.  
  89. // function declaration 
  90. static void WritePrefsRec(void);
  91. static Boolean SelectCategoryPopup(DmOpenRef dbP, UInt16* selected,
  92.                                    UInt32 list, UInt32 trigger, Char *string);
  93. static void MainFormLoadTable(FormPtr frm, Boolean redraw);
  94.  
  95. /* private database */
  96. DmOpenRef MainDB;
  97. DmOpenRef PrefsDB;
  98.  
  99.  
  100. //  accessing the built-in DatebookDB
  101. //
  102. DmOpenRef DatebookDB;
  103.  
  104. //  accessing the built-in ToDoDB
  105. //
  106. DmOpenRef ToDoDB;
  107.  
  108. // These are for accessing the built-in AddressDB
  109. //
  110. DmOpenRef AddressDB;
  111.  
  112. // These are for accessing the built-in MemoDB
  113. //
  114. DmOpenRef MemoDB;
  115.  
  116. static void DisplayCategory(UInt32 trigger, Char* string, Boolean redraw)
  117. {
  118.     ControlPtr ctl;
  119.     FontID currFont;
  120.     UInt16 categoryLen;
  121.     FormPtr frm = FrmGetActiveForm();
  122.  
  123.     currFont = FntSetFont(stdFont);
  124.  
  125.     categoryLen = StrLen(string);
  126.     
  127.     ctl = GetObjectPointer(frm, trigger);
  128.     if ( redraw ) CtlEraseControl(ctl);
  129.  
  130.     CtlSetLabel(ctl, string);
  131.  
  132.     if (redraw) CtlDrawControl(ctl);
  133.     
  134.     FntSetFont (currFont);
  135. }
  136.  
  137. static void RereadBirthdateDB(DateType start)
  138. {
  139.     gMainTableTotals = AddrGetBirthdate(MainDB, gAddrCategory, start);
  140. }
  141.  
  142. static void HighlightAction(int selected, Boolean sound)
  143. {
  144.     FormPtr frm = FrmGetActiveForm();
  145.     TablePtr tableP = GetObjectPointer(frm, MainFormTable);
  146.  
  147.     if (sound) SndPlaySystemSound(sndClick);
  148.  
  149.     if (gMainTableStart > selected
  150.         || selected >= (gMainTableStart + gMainTableRows) ) {
  151.         // if not exist in table view, redraw table
  152.         gMainTableStart = MAX(0, selected-5);
  153.         MainFormLoadTable(frm, true);
  154.     }
  155.     // highlight the selection
  156.     TblUnhighlightSelection(tableP);
  157.     TblSelectItem(tableP, (selected-gMainTableStart), 0);
  158. }
  159.  
  160. static void HighlightMatchRowDate(DateTimeType inputDate)
  161. {
  162.     LineItemPtr ptr;
  163.     DateType dt;
  164.     Int16 selected = -1;
  165.     UInt32 maxDiff = 365*2;  // date is displayed within 1 year(upper bound)
  166.     Int16 i;
  167.  
  168.     if (gMainTableTotals <= 0) return;
  169.  
  170.     dt.year = inputDate.year - 1904;
  171.     dt.month = inputDate.month;
  172.     dt.day = inputDate.day;
  173.  
  174.     if ((ptr = MemHandleLock(gTableRowHandle))) {
  175.         for (i = 0; i < gMainTableTotals; i++) {
  176.             UInt32 diff = DateToDays(ptr[i].date) - DateToDays(dt);
  177.             if (diff >= 0 && diff < maxDiff) {
  178.                 maxDiff = diff;
  179.                 selected = i;
  180.             }
  181.         }
  182.         MemPtrUnlock(ptr);
  183.     }
  184.     if (selected >= 0) {        // matched one exists
  185.         HighlightAction(selected, true);
  186.     }
  187. }
  188.  
  189. static void HighlightMatchRowName(Char first)
  190. {
  191.     LineItemPtr ptr;
  192.     Int16 selected = -1;
  193.     Int16 i;
  194.     MemHandle recordH = 0;
  195.     PackedBirthDate* rp;
  196.     BirthDate r;
  197.     Char p;
  198.  
  199.     if (gMainTableTotals <= 0) return;
  200.  
  201.     if ((ptr = MemHandleLock(gTableRowHandle))) {
  202.         for (i = 0; i < gMainTableTotals; i++) {
  203.             if ((recordH = DmQueryRecord(MainDB, ptr[i].birthRecordNum))) {
  204.                 rp = (PackedBirthDate*) MemHandleLock(recordH);
  205.                 UnpackBirthdate(&r,rp);
  206.  
  207.                 p = (r.name1[0]) ? r.name1[0] : r.name2[0];
  208.                 // make capital letter
  209.                 //
  210.                 if (p >= 'a' && p <= 'z') {
  211.                     p -= 'a' - 'A';
  212.                 }
  213.  
  214.                 if (p == first ) {
  215.                     selected = i;
  216.  
  217.                     // trick.. exit the loop
  218.                     i = gMainTableTotals;
  219.                 }
  220.                 MemHandleUnlock(recordH);
  221.             }
  222.         }
  223.         MemPtrUnlock(ptr);
  224.     }
  225.     if (selected >= 0) {        // matched one exists
  226.         HighlightAction(selected, true);
  227.     }
  228. }
  229.  
  230. static void DoDateSelect()
  231. {
  232.     Char titleStr[32];
  233.     DateTimeType dt;
  234.     Boolean selected = false;
  235.  
  236.     dt.year = gStartDate.year + 1904;
  237.     dt.month = gStartDate.month;
  238.     dt.day = gStartDate.day;
  239.  
  240.     /* Pop up the form and get the date */
  241.     SysCopyStringResource(titleStr, SelectDateString);
  242.     selected = SelectDay(selectDayByDay, &(dt.month), &(dt.day),
  243.                          &(dt.year), titleStr);
  244.  
  245.     if (selected) {
  246.         // make the starting date to user selected date
  247.         gStartDate.year = dt.year - 1904; 
  248.         gStartDate.month = dt.month;
  249.         gStartDate.day = dt.day;
  250.  
  251.         gMainTableStart = 0;
  252.         FrmUpdateForm(MainForm, frmRedrawUpdateCode);
  253.     }
  254. }
  255.  
  256. static Boolean IsHappyDaysRecord(Char* notefield)
  257. {
  258.     if (notefield && StrStr(notefield, gPrefsR->BirthPrefs.notifywith)) {
  259.         return true; 
  260.     }
  261.     return false;
  262. }
  263.  
  264. // check if description has the information about name1 and name2
  265. //
  266. static Boolean IsSameRecord(Char* notefield, BirthDate birth)
  267. {
  268.     Char *p;
  269.     
  270.     if (notefield && (p = StrStr(notefield,gPrefsR->BirthPrefs.notifywith))) {
  271.         p += StrLen(gPrefsR->BirthPrefs.notifywith);
  272.  
  273.         StrPrintF(gAppErrStr, "%ld", Hash(birth.name1,birth.name2));
  274.         
  275.         if (StrCompare(gAppErrStr, p) ==0) return true;
  276.     }
  277.     return false;
  278. }
  279.  
  280. static int CleanupFromDB(DmOpenRef db)
  281. {
  282.     UInt16 currIndex = 0;
  283.     MemHandle recordH;
  284.     ApptDBRecordType apptRecord;
  285.     int ret = 0;
  286.     
  287.     while (1) {
  288.         recordH = DmQueryNextInCategory(db, &currIndex,
  289.                                         dmAllCategories);
  290.         if (!recordH) break;
  291.         ApptGetRecord(db, currIndex, &apptRecord, &recordH);
  292.         if (IsHappyDaysRecord(apptRecord.note)) {
  293.             // if it is happydays record?
  294.             //
  295.             ret++;
  296.             // remove the record
  297.             DmDeleteRecord(db, currIndex);
  298.             // deleted records are stored at the end of the database
  299.             //
  300.             DmMoveRecord(db, currIndex, DmNumRecords(DatebookDB));
  301.         }
  302.         else {
  303.             MemHandleUnlock(recordH);
  304.             currIndex++;
  305.         }
  306.     }
  307.     return ret;
  308. }
  309.  
  310. static int CleanupFromTD(DmOpenRef db)
  311. {
  312.     UInt16 currIndex = 0;
  313.     MemHandle recordH;
  314.     ToDoDBRecordPtr toDoRec;
  315.     Char *note;
  316.     int ret = 0;
  317.     
  318.     while (1) {
  319.         recordH = DmQueryNextInCategory(db, &currIndex, dmAllCategories);
  320.         if (!recordH) break;
  321.  
  322.         toDoRec = MemHandleLock(recordH);
  323.         note = GetToDoNotePtr(toDoRec);
  324.         
  325.         if (IsHappyDaysRecord(note)) {
  326.             // if it is happydays record?
  327.             //
  328.             ret++;
  329.             // remove the record
  330.             DmDeleteRecord(db, currIndex);
  331.             // deleted records are stored at the end of the database
  332.             //
  333.             DmMoveRecord(db, currIndex, DmNumRecords(ToDoDB));
  334.         }
  335.         else {
  336.             MemHandleUnlock(recordH);
  337.             currIndex++;
  338.         }
  339.     }
  340.     return ret;
  341. }
  342.  
  343. static void ShowScrollArrows(FormPtr frm, Int16 start, Int16 total)
  344. {
  345.     ControlPtr ptrup, ptrdown;
  346.     Boolean enable;
  347.  
  348.     ptrup = GetObjectPointer(frm, MainFormPageUp);
  349.     ptrdown = GetObjectPointer(frm, MainFormPageDown);
  350.  
  351.     if ((total > gMainTableRows) || start) {
  352.         if (start) {
  353.             CtlSetLabel(ptrup, pageupchr);
  354.             enable = true;
  355.         }
  356.         else {
  357.             CtlSetLabel(ptrup, pageupgreychr);
  358.             enable = false;
  359.         }
  360.         CtlShowControl(ptrup);
  361.         CtlSetEnabled(ptrup, enable);
  362.         if ((start+gMainTableRows) < total) {
  363.             CtlSetLabel(ptrdown, pagedownchr);
  364.             enable = true;
  365.         }
  366.         else {
  367.             CtlSetLabel(ptrdown, pagedowngreychr);
  368.             enable = false;
  369.         }
  370.         CtlShowControl(ptrdown);
  371.         CtlSetEnabled(ptrdown, enable);
  372.     }
  373.     else {
  374.         CtlHideControl(ptrup);
  375.         CtlHideControl(ptrdown);
  376.     }
  377. }
  378.  
  379. // temporary return value;
  380. static Char* EventTypeString(BirthDate r)
  381. {
  382.     if (!r.custom[0]) {
  383.         gAppErrStr[0] = gPrefsR->BirthPrefs.custom[0];
  384.     }
  385.     else gAppErrStr[0] = r.custom[0];
  386.     gAppErrStr[1] = 0;
  387.  
  388.     StrToLower(&gAppErrStr[3], gAppErrStr);     // use temporary
  389.     gAppErrStr[3] = gAppErrStr[3] - 'a' + 'A';  // make upper
  390.  
  391.     return &gAppErrStr[3];
  392. }
  393.  
  394. static void PerformExport(Char * memo, int mainDBIndex, DateType when)
  395. {
  396.     MemHandle recordH = 0;
  397.     PackedBirthDate* rp;
  398.     BirthDate r;
  399.     
  400.     if ((recordH = DmQueryRecord(MainDB, mainDBIndex))) {
  401.         rp = (PackedBirthDate *) MemHandleLock(recordH);
  402.         /*
  403.          * Build the unpacked structure for an AddressDB record.  It
  404.          * is just a bunch of pointers into the rp structure.
  405.          */
  406.         UnpackBirthdate(&r, rp);
  407.  
  408.         StrNCat(memo, "\"", 4096);
  409.         StrNCat(memo, r.name1, 4096);
  410.         if (r.name2 && r.name2[0]) {
  411.             if (r.name1 && r.name1[0]) StrNCat(memo, ", ", 4096);
  412.             StrNCat(memo, r.name2, 4096);
  413.         }
  414.         StrNCat(memo, "\",", 4096);
  415.  
  416.         if (r.flag.bits.lunar) {
  417.             StrNCat(memo, "-)", 4096);
  418.         }
  419.         else if (r.flag.bits.lunar_leap) {
  420.             StrNCat(memo, "#)", 4096);
  421.         }
  422.  
  423.         if (r.flag.bits.year) {
  424.             StrNCat(memo, DateToAsciiLong(r.date.month, r.date.day,
  425.                                           r.date.year + 1904, gPrefdfmts,
  426.                                           gAppErrStr), 4096);
  427.         }
  428.         else {
  429.             StrNCat(memo, DateToAsciiLong(r.date.month, r.date.day,
  430.                                           -1, gPrefdfmts,
  431.                                           gAppErrStr), 4096);
  432.         }
  433.  
  434.         StrNCat(memo, ",", 4096);
  435.         StrNCat(memo, EventTypeString(r), 4096);
  436.         
  437.         MemHandleUnlock(recordH);
  438.     }
  439. }
  440.  
  441. static Boolean MenuHandler(FormPtr frm, EventPtr e)
  442. {
  443.     Boolean handled = false;
  444.     static Char tmpString[25];
  445.  
  446.     MenuEraseStatus(NULL);
  447.  
  448.     if (FrmGetActiveFormID() == MainForm) {
  449.         // unhighlight table row selection
  450.         //      and delete the main form field
  451.         TablePtr tableP = GetObjectPointer(frm, MainFormTable);
  452.  
  453.         TblUnhighlightSelection(tableP);
  454.     }
  455.     
  456.     switch(e->data.menu.itemID) {
  457.     case MainFormMenuAbout: 
  458.         frm = FrmInitForm(AboutForm);
  459.  
  460.         switch (FrmDoDialog(frm)) {
  461.         case AboutFormOk:
  462.             break;
  463.         case AboutFormHelp:
  464.             FrmHelp(AboutHelpString);
  465.             break;
  466.         }
  467.  
  468.         FrmDeleteForm(frm);
  469.             
  470.         handled = true;
  471.         break;
  472.     case MainFormMenuPref:
  473.         FrmPopupForm(PrefForm);
  474.             
  475.         handled = true;
  476.         break;
  477.  
  478.     case MainFormMenuNotifyDatebook:
  479.  
  480.         // Select Records to all and select is invalid
  481.         //
  482.         gPrefsR->records = 3;
  483.         
  484.         FrmPopupForm(DateBookNotifyForm);
  485.  
  486.         handled = true;
  487.         break;
  488.  
  489.     case MainFormMenuCleanupDatebook: 
  490.     {
  491.         SysCopyStringResource(tmpString, DateBookString);
  492.  
  493.         SysCopyStringResource(gAppErrStr, RemoveConfirmString);
  494.         if (FrmCustomAlert(CleanupAlert, tmpString, gAppErrStr, " ") == 0) {
  495.             int ret;
  496.             
  497.             // do clean up processing
  498.             ret = CleanupFromDB(DatebookDB);
  499.             StrPrintF(gAppErrStr, "%d", ret);
  500.  
  501.             FrmCustomAlert(CleanupDone, tmpString, gAppErrStr, " ");
  502.         }
  503.         handled = true;
  504.         break;
  505.     }
  506.     case MainFormMenuNotifyTodo:
  507.         // Select Records to all and select is invalid
  508.         //
  509.         gPrefsR->records = 3;
  510.         
  511.         FrmPopupForm(ToDoNotifyForm);
  512.  
  513.         handled = true;
  514.         break;
  515.         
  516.     case MainFormMenuCleanupTodo: 
  517.     {
  518.         SysCopyStringResource(tmpString, ToDoString);
  519.  
  520.         SysCopyStringResource(gAppErrStr, RemoveConfirmString);
  521.         if (FrmCustomAlert(CleanupAlert, tmpString, gAppErrStr, " ") == 0) {
  522.             int ret;
  523.             
  524.             // do clean up processing
  525.             ret = CleanupFromTD(ToDoDB);
  526.             StrPrintF(gAppErrStr, "%d", ret);
  527.  
  528.             FrmCustomAlert(CleanupDone, tmpString, gAppErrStr, " ");
  529.         }
  530.         
  531.         handled = true;
  532.         break;
  533.     }
  534.     
  535.     case MainFormMenuExport:
  536.     {
  537.         LineItemPtr ptr;
  538.         Int16 i;
  539.         UInt16 index;
  540.         MemoItemType memo;
  541.  
  542.         if (gMainTableTotals >0
  543.             && (FrmCustomAlert(ExportAlert, " ", " ", " ") == 0)) {
  544.             memo.note = MemPtrNew(4096);
  545.             SysCopyStringResource(gAppErrStr, NotEnoughMemoryString);
  546.             ErrFatalDisplayIf(!memo.note, gAppErrStr);
  547.             ptr = MemHandleLock(gTableRowHandle);
  548.  
  549.             SysCopyStringResource(gAppErrStr, ExportHeaderString);
  550.             StrCopy(memo.note, gAppErrStr);
  551.  
  552.             for (i=0; i < gMainTableTotals; i++) {
  553.                 if (ptr[i].date.year != INVALID_CONV_DATE) {
  554.                     StrNCat(memo.note, "\n", 4096);
  555.                     PerformExport(memo.note, ptr[i].birthRecordNum,
  556.                                   ptr[i].date);
  557.                 }
  558.             }
  559.             MemPtrUnlock(ptr);
  560.             
  561.             MemoNewRecord(MemoDB, &memo, &index);
  562.             if (memo.note) MemPtrFree(memo.note);
  563.  
  564.             SysCopyStringResource(gAppErrStr, ExportDoneString);
  565.             FrmCustomAlert(ErrorAlert, gAppErrStr, " ", " ");
  566.         }
  567.         
  568.         handled = true;
  569.         break;
  570.     }
  571.     
  572.     case MainFormMenuSl2Ln:
  573.         FrmPopupForm(Sl2LnForm);
  574.                 
  575.         handled = true;
  576.         break;
  577.     case MainFormMenuLn2Sl:
  578.         FrmPopupForm(Ln2SlForm);   
  579.  
  580.         handled = true;
  581.         break;
  582.     }
  583.  
  584.     return handled;
  585. }
  586.  
  587. static Boolean MainFormHandleEvent (EventPtr e)
  588. {
  589.     Boolean handled = false;
  590.  
  591.     FormPtr frm = FrmGetFormPtr(MainForm);
  592.     TablePtr tableP = GetObjectPointer(frm, MainFormTable);
  593.  
  594.     switch (e->eType) {
  595.     case frmUpdateEvent:
  596.         if (e->data.frmUpdate.updateCode == frmRescanUpdateCode)
  597.         {
  598.             MemSet(&gPrefsR->adrmdate, 4, 0);  // force a re-scan
  599.             FrmGotoForm(StartForm);
  600.             handled = 1;
  601.             break;
  602.         }
  603.         // frmRedrawUpdateCode invoked by SpecialKeyDown
  604.         //
  605.         // else execute frmOpenEvent
  606.  
  607.     case frmOpenEvent:
  608.         // Main Form table row settting
  609.         //
  610.         gMainTableRows = TblGetNumberOfRows(tableP);
  611.  
  612.         DisplayCategory(MainFormPopupTrigger, gPrefsR->addrCategory, false);
  613.  
  614.         // category information of MainDB is same as AddressDB
  615.         //
  616.         if ((gAddrCategory = CategoryFind(AddressDB, gPrefsR->addrCategory)) ==
  617.             dmAllCategories) {
  618.             MemSet(gPrefsR->addrCategory, sizeof(gPrefsR->addrCategory), 0);
  619.             CategoryGetName(AddressDB, gAddrCategory, gPrefsR->addrCategory);
  620.         }
  621.  
  622.         RereadBirthdateDB(gStartDate);
  623.         if (e->eType == frmOpenEvent) {
  624.             MainFormLoadTable(frm, false);
  625.             FrmDrawForm(frm);
  626.             ShowScrollArrows(frm, gMainTableStart, gMainTableTotals);
  627.         }
  628.         else {
  629.             MainFormLoadTable(frm, true);
  630.         }
  631.         // display starting date
  632.         DateToAsciiLong(gStartDate.month, gStartDate.day, 
  633.                         gStartDate.year+1904, gPrefdfmts, gAppErrStr);
  634.         CtlSetLabel(GetObjectPointer(frm, MainFormStart), gAppErrStr);
  635.  
  636.         handled = true;
  637.         break;
  638.  
  639.     case menuEvent: 
  640.         handled = MenuHandler(frm, e);
  641.         break;
  642.             
  643.     case ctlSelectEvent:
  644.         switch(e->data.ctlSelect.controlID) {
  645.         case MainFormStart:
  646.             // display starting date
  647.             DateToAsciiLong(gStartDate.month, gStartDate.day, 
  648.                             gStartDate.year+1904, gPrefdfmts, gAppErrStr);
  649.             CtlSetLabel(GetObjectPointer(frm, MainFormStart), gAppErrStr);
  650.  
  651.             // popup select day window
  652.             DoDateSelect();
  653.             handled = true;
  654.             break;
  655.  
  656.         case MainFormPopupTrigger: 
  657.         {
  658.             if (SelectCategoryPopup(AddressDB, &gAddrCategory,
  659.                                     MainFormAddrCategories, MainFormPopupTrigger,
  660.                                     gPrefsR->addrCategory) ) {
  661.                 // changed
  662.                 gMainTableStart = 0;
  663.                 RereadBirthdateDB(gStartDate);
  664.                 MainFormLoadTable(frm, true);
  665.             }
  666.         
  667.  
  668.             handled = true;
  669.             
  670.             break;
  671.         }
  672.  
  673.         case MainFormPageUp:
  674.             if (gMainTableStart) {
  675.                 gMainTableStart -= MIN(gMainTableStart, gMainTableRows-1);
  676.                 MainFormLoadTable(frm, true);
  677.             }
  678.             
  679.             break;
  680.         case MainFormPageDown:
  681.             if ((gMainTableStart + gMainTableRows) < gMainTableTotals) {
  682.                 gMainTableStart += gMainTableRows-1;
  683.                 MainFormLoadTable(frm, true);
  684.             }
  685.             break;
  686.         default:
  687.             break;
  688.                 
  689.         }
  690.         break;
  691.  
  692.     case keyDownEvent: 
  693.     {
  694.         ControlPtr ptr;
  695.  
  696.         if (e->data.keyDown.chr == pageUpChr) {
  697.             ptr = GetObjectPointer(frm, MainFormPageUp);
  698.             CtlHitControl(ptr);
  699.         }
  700.         else if (e->data.keyDown.chr == pageDownChr) {
  701.             ptr = GetObjectPointer(frm, MainFormPageDown);
  702.             CtlHitControl(ptr);
  703.         }
  704.         break;
  705.     }
  706.  
  707.     case tblSelectEvent: 
  708.     {
  709.         gMainTableHandleRow = gMainTableStart + e->data.tblEnter.row;
  710.  
  711.         FrmPopupForm(BirthdateForm);
  712.         handled = true;
  713.         break;
  714.     }
  715.     
  716.     default:
  717.         break;
  718.     }
  719.  
  720.  
  721.     return handled;
  722. }
  723.  
  724.  
  725. static void LoadPrefsFields()
  726. {
  727.     FormPtr frm;
  728.     ListPtr lstdate, lstnotify;
  729.  
  730.     if ((frm = FrmGetFormPtr(PrefForm)) == 0) return;
  731.     
  732.     CtlSetValue(GetObjectPointer(frm, PrefFormOverrideSystemDate),
  733.                 gPrefsR->BirthPrefs.sysdateover);
  734.     lstdate = GetObjectPointer(frm, PrefFormDateFmts);
  735.     LstSetSelection(lstdate, gPrefsR->BirthPrefs.dateformat);
  736.     CtlSetLabel(GetObjectPointer(frm, PrefFormDateTrigger),
  737.                 LstGetSelectionText(lstdate, gPrefsR->BirthPrefs.dateformat));
  738.  
  739.     lstnotify = GetObjectPointer(frm, PrefFormNotifyFmts);
  740.     LstSetSelection(lstnotify, gPrefsR->BirthPrefs.notifyformat);
  741.     CtlSetLabel(GetObjectPointer(frm, PrefFormNotifyTrigger),
  742.                 LstGetSelectionText(lstnotify, gPrefsR->BirthPrefs.notifyformat));
  743.                     
  744.     SetFieldTextFromStr(PrefFormCustomField, gPrefsR->BirthPrefs.custom);
  745.     SetFieldTextFromStr(PrefFormNotifyWith, gPrefsR->BirthPrefs.notifywith);
  746.  
  747.     if (gPrefsR->BirthPrefs.emphasize == 1) {
  748.         CtlSetValue(GetObjectPointer(frm, PrefFormEmphasize), 1);
  749.     }
  750.     else {
  751.         CtlSetValue(GetObjectPointer(frm, PrefFormEmphasize), 0);
  752.     }
  753.     if (gPrefsR->BirthPrefs.scannote == 1) {
  754.         CtlSetValue(GetObjectPointer(frm, PrefFormScanNote), 1);
  755.     }
  756.     else {
  757.         CtlSetValue(GetObjectPointer(frm, PrefFormScanNote), 0);
  758.     }
  759.  
  760. }
  761.  
  762. static Boolean UnloadPrefsFields()
  763. {
  764.     FormPtr frm;
  765.     ControlPtr ptr;
  766.     ListPtr lstdate, lstnotify;
  767.     Boolean newoverride;
  768.     DateFormatType newdateformat;
  769.     Boolean needrescan = 0;
  770.     
  771.     if ((frm = FrmGetFormPtr(PrefForm)) == 0) return needrescan;
  772.     if (FldDirty(GetObjectPointer(frm, PrefFormCustomField))) {
  773.         needrescan = 1;
  774.         if (FldGetTextPtr(GetObjectPointer(frm, PrefFormCustomField))) {
  775.             StrNCopy(gPrefsR->BirthPrefs.custom,
  776.                      FldGetTextPtr(GetObjectPointer(frm,
  777.                                                     PrefFormCustomField)), 12);
  778.         }
  779.     }
  780.     if (FldDirty(GetObjectPointer(frm, PrefFormNotifyWith))) {
  781.         if (FldGetTextPtr(GetObjectPointer(frm, PrefFormNotifyWith))) {
  782.             StrNCopy(gPrefsR->BirthPrefs.notifywith,
  783.                      FldGetTextPtr(GetObjectPointer(frm,
  784.                                                     PrefFormNotifyWith)), 5);
  785.         }
  786.     }
  787.  
  788.     // If effective date format has changed, we need to signal for a re-scan
  789.     // of the address book database. This occurs if
  790.     ptr = GetObjectPointer(frm, PrefFormOverrideSystemDate);
  791.     newoverride = CtlGetValue(ptr);
  792.     lstdate = GetObjectPointer(frm, PrefFormDateFmts);
  793.     newdateformat = LstGetSelection(lstdate);
  794.     if (newoverride)
  795.     {
  796.         if (newdateformat != gPrefdfmts)
  797.         {
  798.             needrescan = 1;
  799.             gPrefdfmts = newdateformat;
  800.         }
  801.     }
  802.     else
  803.     {
  804.         if (gPrefsR->BirthPrefs.sysdateover && gPrefdfmts != gSystemdfmts)
  805.         {
  806.             needrescan = 1;
  807.             gPrefdfmts = gSystemdfmts;    // revert to system date format
  808.         }
  809.     }
  810.     gPrefsR->BirthPrefs.sysdateover = newoverride;
  811.     gPrefsR->BirthPrefs.dateformat = newdateformat;
  812.  
  813.     // notify string
  814.     lstnotify = GetObjectPointer(frm, PrefFormNotifyFmts);
  815.     gPrefsR->BirthPrefs.notifyformat = LstGetSelection(lstnotify);
  816.         
  817.     ptr = GetObjectPointer(frm, PrefFormEmphasize);
  818.     gPrefsR->BirthPrefs.emphasize = CtlGetValue(ptr);
  819.  
  820.     ptr = GetObjectPointer(frm, PrefFormScanNote);
  821.     if (CtlGetValue(ptr) != gPrefsR->BirthPrefs.scannote ) {
  822.         needrescan = 1;
  823.     }
  824.     gPrefsR->BirthPrefs.scannote = CtlGetValue(ptr);
  825.  
  826.     return needrescan;
  827. }
  828.  
  829. static Boolean PrefFormHandleEvent(EventPtr e)
  830. {
  831.     Boolean rescan;
  832.     Boolean handled = false;
  833.     FormPtr frm;
  834.     
  835.     switch (e->eType) {
  836.     case frmOpenEvent:
  837.         frm = FrmGetFormPtr(PrefForm);
  838.  
  839.         LoadPrefsFields();
  840.         FrmDrawForm(frm);
  841.         handled = true;
  842.         break;
  843.  
  844.         
  845.     case ctlSelectEvent:
  846.         switch(e->data.ctlSelect.controlID) {
  847.         case PrefFormOk:
  848.             rescan = UnloadPrefsFields();
  849.             WritePrefsRec();
  850.             
  851.             FrmReturnToForm(0);
  852.             if (rescan)
  853.                 FrmUpdateForm(MainForm, frmRescanUpdateCode);
  854.             else 
  855.                 FrmUpdateForm(MainForm, frmRedrawUpdateCode);
  856.  
  857.             handled = true;
  858.             break;
  859.         case PrefFormCancel:
  860.  
  861.             FrmReturnToForm(0);
  862.             // need for 'preference-cancel' work
  863.             FrmUpdateForm(StartForm, frmRedrawUpdateCode);
  864.  
  865.             handled = true;
  866.             break;
  867.  
  868.         default:
  869.             break;
  870.         }
  871.         break;
  872.  
  873.     case menuEvent:
  874.         MenuEraseStatus(NULL);
  875.         break;
  876.         
  877.     default:
  878.         break;
  879.     }
  880.  
  881.  
  882.     return handled;
  883. }
  884.  
  885. Boolean SelectCategoryPopup(DmOpenRef dbP, UInt16* selected,
  886.                             UInt32 list, UInt32 trigger, Char *string)
  887. {
  888.     FormPtr frm;
  889.     ListPtr lst;
  890.     Int16 curSelection, newSelection;
  891.     Char * name;
  892.     Boolean ret = false;
  893.  
  894.     frm = FrmGetActiveForm();
  895.  
  896.     lst = GetObjectPointer(frm, list);
  897.  
  898.     // create a list of categories 
  899.     CategoryCreateList(dbP, lst, *selected, true, true, 1, 0, true);
  900.  
  901.     // display the category list
  902.     curSelection = LstGetSelection(lst);
  903.     newSelection = LstPopupList(lst);
  904.  
  905.     // was a new category selected? 
  906.     if ((newSelection != curSelection) && (newSelection != -1)) {
  907.         if ((name = LstGetSelectionText(lst, newSelection))) {
  908.             *selected = CategoryFind(dbP, name);
  909.  
  910.             MemSet(string, dmCategoryLength, 0);
  911.             MemMove(string, name, StrLen(name));
  912.  
  913.             gPrefsRdirty = true;
  914.             DisplayCategory(trigger, string, true);
  915.             ret = true;
  916.         }
  917.     }
  918.     CategoryFreeList(dbP, lst, false, false);
  919.     return ret;
  920. }
  921.  
  922. static Int16 CalculateAge(DateType converted, DateType birthdate,
  923.                           BirthdateFlag flag)
  924. {
  925.     DateTimeType rtVal;
  926.     Int16 age;
  927.     int dummy = 0;
  928.     int ret;
  929.     
  930.     if (flag.bits.lunar_leap || flag.bits.lunar) {
  931.         // lunar birthdate
  932.         //          change to lunar date
  933.         //
  934.         // converted solar is again converted into lunar day for calculation
  935.         ret = sol2lun(converted.year + 1904, converted.month, converted.day,
  936.                       &rtVal, &dummy);
  937.         if (ret) return -1;     // error
  938.  
  939.         converted.year = rtVal.year - 1904;
  940.         converted.month = rtVal.month;
  941.         converted.day = rtVal.day;
  942.     }
  943.     
  944.     return converted.year - birthdate.year;
  945.     return age;
  946. }
  947.  
  948.  
  949. static void MainFormDrawRecord(MemPtr tableP, Int16 row, Int16 column, 
  950.                                RectanglePtr bounds)
  951. {
  952.     FontID currFont;
  953.     MemHandle recordH = 0;
  954.     Int16 x, y;
  955.     UInt16 nameExtent;
  956.     PackedBirthDate* rp;
  957.     BirthDate r;
  958.     LineItemPtr ptr;
  959.     LineItemType drawRecord;
  960.     Boolean ignored = true;
  961.     
  962.     /*
  963.      * Set the standard font.  Save the current font.
  964.      * It is a Pilot convention to not destroy the current font
  965.      * but to save and restore it.
  966.      */
  967.     currFont = FntSetFont(stdFont);
  968.  
  969.     x = bounds->topLeft.x;
  970.     y = bounds->topLeft.y;
  971.  
  972.     ptr = MemHandleLock(gTableRowHandle);
  973.     drawRecord = ptr[row+gMainTableStart];
  974.     
  975.     if ((recordH =
  976.          DmQueryRecord(MainDB, drawRecord.birthRecordNum))) {
  977.         short categoryWidth;
  978.         Int16 width, length;
  979.         Int16 age;
  980.         Char* eventType;
  981.  
  982.         rp = (PackedBirthDate *) MemHandleLock(recordH);
  983.         /*
  984.          * Build the unpacked structure for an AddressDB record.  It
  985.          * is just a bunch of pointers into the rp structure.
  986.          */
  987.         UnpackBirthdate(&r, rp);
  988.  
  989.         // category display
  990.         //
  991.         categoryWidth = FntCharWidth('W');
  992.         width = bounds->extent.x - MAINTABLEAGEFIELD;
  993.  
  994.         if (drawRecord.date.year != INVALID_CONV_DATE) {
  995.             DateToAscii(drawRecord.date.month, drawRecord.date.day,
  996.                         drawRecord.date.year+ 1904, gPrefdfmts, gAppErrStr);
  997.         }
  998.         else {
  999.             StrCopy(gAppErrStr, "-");
  1000.         }
  1001.  
  1002.         length = StrLen(gAppErrStr);
  1003.         FntCharsInWidth(gAppErrStr, &width,
  1004.                         &length, &ignored);
  1005.  
  1006.         nameExtent = bounds->extent.x - width - MAINTABLEAGEFIELD - 3; 
  1007.  
  1008.         DrawRecordName(r.name1, r.name2, nameExtent, &x, y,
  1009.                        false,
  1010.                        r.flag.bits.priority_name1 || !gSortByCompany);
  1011.  
  1012.         WinDrawChars(gAppErrStr, length,
  1013.                      bounds->topLeft.x + bounds->extent.x -
  1014.                      MAINTABLEAGEFIELD - width, y);
  1015.  
  1016.         if (gPrefsR->BirthPrefs.emphasize 
  1017.             && drawRecord.date.year != INVALID_CONV_DATE
  1018.             && (r.flag.bits.lunar || r.flag.bits.lunar_leap)) {
  1019.             // draw gray line
  1020.             WinDrawGrayLine(bounds->topLeft.x,
  1021.                             bounds->topLeft.y + bounds->extent.y-1,
  1022.                             bounds->topLeft.x + bounds->extent.x -
  1023.                             MAINTABLEAGEFIELD,
  1024.                             bounds->topLeft.y + bounds->extent.y-1);
  1025.         }
  1026.         
  1027.         if (drawRecord.date.year != INVALID_CONV_DATE 
  1028.             && r.flag.bits.year 
  1029.             && (age = CalculateAge(drawRecord.date, r.date, r.flag)) >= 0) {
  1030.             // calculate age if year exists
  1031.             //
  1032.             StrPrintF(gAppErrStr, "%d", age);
  1033.  
  1034.         }
  1035.         else {
  1036.             StrCopy(gAppErrStr, "-");
  1037.         }
  1038.         width = MAINTABLEAGEFIELD - categoryWidth - 1;
  1039.         length = StrLen(gAppErrStr);
  1040.  
  1041.         FntCharsInWidth(gAppErrStr, &width, &length, &ignored);
  1042.  
  1043.         WinDrawChars(gAppErrStr, length,
  1044.                      bounds->topLeft.x + bounds->extent.x - categoryWidth
  1045.                      - width - 1, y);
  1046.  
  1047.         // EventTypeString use  'gAppErrStr'
  1048.         //
  1049.         eventType = EventTypeString(r);
  1050.         
  1051.         width = categoryWidth;
  1052.         length = 1;
  1053.         FntCharsInWidth(eventType, &width, &length, &ignored);
  1054.         WinDrawChars(eventType, 1, bounds->topLeft.x +
  1055.                      bounds->extent.x - width - (categoryWidth - width)/2, y);
  1056.  
  1057.         MemHandleUnlock(recordH);
  1058.     }
  1059.  
  1060.     MemPtrUnlock(ptr);
  1061.  
  1062.     /* Restore the font */
  1063.     FntSetFont (currFont);
  1064. }
  1065.  
  1066. // IN : index - mainDB index
  1067. //
  1068. static void SetBirthdateViewForm(Int16 index, DateType converted)
  1069. {
  1070.     FontID currFont;
  1071.     MemHandle recordH = 0;
  1072.     PackedBirthDate* rp;
  1073.     BirthDate r;
  1074.     FormPtr frm;
  1075.     Char displayStr[255];
  1076.     UInt16 addrattr;
  1077.     Int16 dateDiff;
  1078.     Int16 age = 0;
  1079.     DateType current;
  1080.             
  1081.     if ((frm = FrmGetFormPtr(BirthdateForm)) == 0) return;
  1082.     /*
  1083.      * Set the standard font.  Save the current font.
  1084.      * It is a Pilot convention to not destroy the current font
  1085.      * but to save and restore it.
  1086.      */
  1087.     currFont = FntSetFont(stdFont);
  1088.  
  1089.     if ((recordH = DmQueryRecord(MainDB, index))) {
  1090.         rp = (PackedBirthDate *) MemHandleLock(recordH);
  1091.         /*
  1092.          * Build the unpacked structure for an AddressDB record.  It
  1093.          * is just a bunch of pointers into the rp structure.
  1094.          */
  1095.         UnpackBirthdate(&r, rp);
  1096.  
  1097.         if (r.name2 && r.name2[0]) {
  1098.             SetFieldTextFromStr(BirthdateFirst, r.name2);
  1099.             SetFieldTextFromStr(BirthdateSecond, r.name1);
  1100.         }
  1101.         else {
  1102.             SetFieldTextFromStr(BirthdateFirst, r.name1);
  1103.             ClearFieldText(BirthdateSecond);
  1104.         }
  1105.  
  1106.         if (!r.custom[0]) {         // custom does not exist
  1107.             SetFieldTextFromStr(BirthdateCustom,
  1108.                                 gPrefsR->BirthPrefs.custom);
  1109.         }
  1110.         else {
  1111.             SetFieldTextFromStr(BirthdateCustom, r.custom);
  1112.         }
  1113.  
  1114.         if (converted.year != INVALID_CONV_DATE) {
  1115.             DateToAsciiLong(converted.month, converted.day,
  1116.                             converted.year+ 1904, gPrefdfmts,
  1117.                             displayStr);
  1118.  
  1119.             if (r.flag.bits.year            // year exist 
  1120.                 && (age = CalculateAge(converted, r.date, r.flag)) >= 0) {
  1121.                 // calculate age if year exists
  1122.                 //
  1123.                 StrPrintF(gAppErrStr, "   [%d -> %d]", age-1, age);
  1124.                 StrCat(displayStr, gAppErrStr);
  1125.             }
  1126.         }
  1127.         else {
  1128.             SysCopyStringResource(gAppErrStr, ViewNotExistString);
  1129.             StrNCopy(displayStr, gAppErrStr, 255);
  1130.         }
  1131.         SetFieldTextFromStr(BirthdateDate, displayStr);
  1132.  
  1133.         DateSecondsToDate(TimGetSeconds(), ¤t);
  1134.         StrCopy(displayStr, " <==  ");
  1135.  
  1136.         if (r.flag.bits.lunar) {
  1137.             StrCat(displayStr, "-)");
  1138.         }
  1139.         else if (r.flag.bits.lunar_leap) {
  1140.             StrCat(displayStr, "#)");
  1141.         }
  1142.  
  1143.         if (r.flag.bits.year) {
  1144.             DateToAsciiLong(r.date.month, r.date.day, r.date.year + 1904,
  1145.                             gPrefdfmts, gAppErrStr);
  1146.         }
  1147.         else {
  1148.             DateToAsciiLong(r.date.month, r.date.day, -1, gPrefdfmts,
  1149.                             gAppErrStr);
  1150.         }
  1151.             
  1152.         StrCat(displayStr, gAppErrStr);
  1153.  
  1154.         if (r.flag.bits.year) {
  1155.             DateType solBirth;
  1156.             DateTimeType rtVal;
  1157.             UInt8 ret;
  1158.             
  1159.             if (r.flag.bits.lunar || r.flag.bits.lunar_leap) {
  1160.                 ret = !lun2sol(r.date.year+1904,
  1161.                                r.date.month,
  1162.                                r.date.day,
  1163.                                r.flag.bits.lunar_leap, &rtVal);
  1164.                 if (ret) {
  1165.                     solBirth.year = rtVal.year - 1904;
  1166.                     solBirth.month = rtVal.month;
  1167.                     solBirth.day = rtVal.day;
  1168.                 }
  1169.             }
  1170.             else solBirth = r.date;
  1171.             
  1172.             dateDiff = (Int16)(DateToDays(current) - DateToDays(solBirth));
  1173.             if (dateDiff > (Int16)0) {
  1174.                 StrPrintF(gAppErrStr, " (%d)", dateDiff);
  1175.                 StrCat(displayStr,gAppErrStr);
  1176.             }
  1177.         }
  1178.         SetFieldTextFromStr(BirthdateOrigin, displayStr);
  1179.  
  1180.         DateSecondsToDate(TimGetSeconds(), ¤t);
  1181.         dateDiff = DateToDays(converted) - DateToDays(current);
  1182.  
  1183.         if (dateDiff >= (Int16)0) {
  1184.             SysCopyStringResource(gAppErrStr, BirthLeftString);
  1185.             StrPrintF(displayStr, gAppErrStr, dateDiff);
  1186.         }
  1187.         else {
  1188.             SysCopyStringResource(gAppErrStr, BirthPassedString);
  1189.             StrPrintF(displayStr, gAppErrStr, -1 * dateDiff);
  1190.         }
  1191.         SetFieldTextFromStr(BirthdateLeft, displayStr);
  1192.  
  1193.         // read category information
  1194.         //
  1195.         DmRecordInfo(MainDB, index, &addrattr, NULL, NULL);
  1196.         addrattr &= dmRecAttrCategoryMask;      // get category info
  1197.         // Category information is the same as AddressDB
  1198.         //  and MainDB doesn't have the category information
  1199.         //  so read the AddressDB information
  1200.         //
  1201.         //  INFO: gAppErrStr's length is 255(must be larger than
  1202.         //  dmCategoryLength)
  1203.         //
  1204.         //
  1205.  
  1206.         
  1207.         // FrmSetCategoryLabel(frm, FrmGetObjectIndex(frm, BirthdateCategory),
  1208.         //                    gAppErrStr);
  1209.         // error? (suggested by Mike McCollister)
  1210.  
  1211.         CategoryGetName(AddressDB, addrattr, gAppErrStr);
  1212.         SetFieldTextFromStr(BirthdateCategory, gAppErrStr);
  1213.         
  1214.         MemHandleUnlock(recordH);
  1215.     }
  1216.     
  1217.     /* Restore the font */
  1218.     FntSetFont (currFont);
  1219. }
  1220.  
  1221. static void MainFormLoadTable(FormPtr frm, Boolean redraw)
  1222. {
  1223.     TablePtr tableP;
  1224.     int row;
  1225.  
  1226.     tableP = GetObjectPointer(frm, MainFormTable);
  1227.  
  1228.     if (redraw) {
  1229.         TblEraseTable(tableP);
  1230.     }
  1231.         
  1232.     for (row=0; row < gMainTableRows; row++) {
  1233.         if ((gMainTableStart + row) < gMainTableTotals) { 
  1234.             TblSetItemStyle(tableP, row, 0, customTableItem);
  1235.             TblSetItemInt(tableP, row, 0, row); 
  1236.  
  1237.             TblSetRowHeight(tableP, row, FntCharHeight());
  1238.             TblSetRowUsable(tableP, row, true);
  1239.             TblSetRowSelectable(tableP, row, true);
  1240.         }
  1241.         else {
  1242.             TblSetRowUsable(tableP, row, false);
  1243.         }
  1244.     }
  1245.     TblSetColumnUsable(tableP, 0, true);
  1246.  
  1247.     TblSetCustomDrawProcedure(tableP, 0, MainFormDrawRecord);
  1248.  
  1249.     if (redraw) {
  1250.         TblDrawTable(tableP);
  1251.         ShowScrollArrows(frm, gMainTableStart, gMainTableTotals);
  1252.     }
  1253. }
  1254.  
  1255. static void TimeToAsciiLocal(TimeType when, TimeFormatType timeFormat,
  1256.                              Char * pString)
  1257. {
  1258.     if (TimeToInt(when) != noTime) {
  1259.         TimeToAscii(when.hours, when.minutes, timeFormat, pString);
  1260.     }
  1261.     else {
  1262.         SysCopyStringResource(gAppErrStr, NoTimeString);
  1263.     }
  1264. }
  1265.  
  1266. static void LoadCommonPrefsFields(FormPtr frm)
  1267. {
  1268.     if (gPrefsR->records == 0) {
  1269.         CtlSetValue(GetObjectPointer(frm, NotifyFormRecordAll), 1);
  1270.     }
  1271.     else if (gPrefsR->records == 1) {
  1272.         CtlSetValue(GetObjectPointer(frm, NotifyFormRecordSlct), 1);
  1273.     }
  1274.     else {          //  '3' means that All is selected and slct is unusable
  1275.         CtlSetValue(GetObjectPointer(frm, NotifyFormRecordAll), 1);
  1276.         CtlSetUsable(GetObjectPointer(frm, NotifyFormRecordSlct), false);
  1277.     }
  1278.     if (gPrefsR->existing == 0) {
  1279.         CtlSetValue(GetObjectPointer(frm, NotifyFormEntryKeep), 1);
  1280.     }
  1281.     else {
  1282.         CtlSetValue(GetObjectPointer(frm, NotifyFormEntryModify), 1);
  1283.     }
  1284.  
  1285.     if (gPrefsR->private == 1) {
  1286.         CtlSetValue(GetObjectPointer(frm, NotifyFormPrivate), 1);
  1287.     }
  1288.     else {
  1289.         CtlSetValue(GetObjectPointer(frm, NotifyFormPrivate), 0);
  1290.     }
  1291.  
  1292. }
  1293.  
  1294. static void UnloadCommonNotifyPrefs(FormPtr frm)
  1295. {
  1296.     ControlPtr ptr;
  1297.     
  1298.     ptr = GetObjectPointer(frm, NotifyFormRecordAll);
  1299.     gPrefsR->records = !CtlGetValue(ptr);
  1300.  
  1301.     ptr = GetObjectPointer(frm, NotifyFormEntryKeep);
  1302.     gPrefsR->existing = !CtlGetValue(ptr);
  1303.  
  1304.     ptr = GetObjectPointer(frm, NotifyFormPrivate);
  1305.     gPrefsR->private = CtlGetValue(ptr);
  1306.  
  1307.     return;
  1308. }
  1309.  
  1310. static void LoadDBNotifyPrefsFields(void)
  1311. {
  1312.     FormPtr frm;
  1313.  
  1314.     if ((frm = FrmGetFormPtr(DateBookNotifyForm)) == 0) return;
  1315.  
  1316.     LoadCommonPrefsFields(frm);  // all/selected, keep/modify, private
  1317.     
  1318.     if (gPrefsR->DBNotifyPrefs.alarm == 1) {
  1319.         CtlSetValue(GetObjectPointer(frm, DateBookNotifyFormAlarm), 1);
  1320.  
  1321.         FrmShowObject(frm, 
  1322.                       FrmGetObjectIndex(frm, DateBookNotifyFormBefore));
  1323.  
  1324.         SysCopyStringResource(gAppErrStr, DateBookNotifyDaysString);
  1325.         SetFieldTextFromStr(DateBookNotifyFormLabelDays, gAppErrStr);
  1326.     }
  1327.     else {
  1328.         CtlSetValue(GetObjectPointer(frm, DateBookNotifyFormAlarm), 0);
  1329.         FrmHideObject(frm,
  1330.                       FrmGetObjectIndex(frm, DateBookNotifyFormBefore));
  1331.         ClearFieldText(DateBookNotifyFormLabelDays);
  1332.     }
  1333.     
  1334.     SetFieldTextFromStr(DateBookNotifyFormBefore,
  1335.                         StrIToA(gAppErrStr, gPrefsR->DBNotifyPrefs.notifybefore));
  1336.     SetFieldTextFromStr(DateBookNotifyFormDuration,
  1337.                         StrIToA(gAppErrStr, gPrefsR->DBNotifyPrefs.duration));
  1338.     
  1339.     TimeToAsciiLocal(gPrefsR->DBNotifyPrefs.when, gPreftfmts, gAppErrStr);
  1340.     CtlSetLabel(GetObjectPointer(frm, DateBookNotifyFormTime), gAppErrStr);
  1341. }
  1342.  
  1343. static void UnloadDBNotifyPrefsFields()
  1344. {
  1345.     FormPtr frm;
  1346.     ControlPtr ptr;
  1347.     
  1348.     if ((frm = FrmGetFormPtr(DateBookNotifyForm)) == 0) return;
  1349.  
  1350.     UnloadCommonNotifyPrefs(frm);       // all/selected, keey/modify, private
  1351.  
  1352.     ptr = GetObjectPointer(frm, DateBookNotifyFormAlarm);
  1353.     gPrefsR->DBNotifyPrefs.alarm = CtlGetValue(ptr);
  1354.  
  1355.     if (FldDirty(GetObjectPointer(frm, DateBookNotifyFormBefore))) {
  1356.         gPrefsR->DBNotifyPrefs.notifybefore =
  1357.             (int) StrAToI(FldGetTextPtr(
  1358.                 GetObjectPointer(frm, DateBookNotifyFormBefore)));
  1359.     }
  1360.     // notification duration
  1361.     if (FldDirty(GetObjectPointer(frm, DateBookNotifyFormDuration))) {
  1362.         gPrefsR->DBNotifyPrefs.duration =
  1363.             (int) StrAToI(FldGetTextPtr(
  1364.                 GetObjectPointer(frm, DateBookNotifyFormDuration)));
  1365.     }
  1366.     
  1367.     // DateBookNotifyFormTime is set by handler
  1368. }
  1369.  
  1370.  
  1371. static void LoadTDNotifyPrefsFields(void)
  1372. {
  1373.     FormPtr frm;
  1374.  
  1375.     if ((frm = FrmGetFormPtr(ToDoNotifyForm)) == 0) return;
  1376.  
  1377.     LoadCommonPrefsFields(frm);  // all/selected, keep/modify, private
  1378.  
  1379.     switch (gPrefsR->TDNotifyPrefs.priority) {
  1380.     case 1:
  1381.         CtlSetValue(GetObjectPointer(frm, ToDoNotifyPri1), 1);
  1382.         break;
  1383.     case 2:
  1384.         CtlSetValue(GetObjectPointer(frm, ToDoNotifyPri2), 1);
  1385.         break;
  1386.     case 3:
  1387.         CtlSetValue(GetObjectPointer(frm, ToDoNotifyPri3), 1);
  1388.         break;
  1389.     case 4:
  1390.         CtlSetValue(GetObjectPointer(frm, ToDoNotifyPri4), 1);
  1391.         break;
  1392.     case 5:
  1393.         CtlSetValue(GetObjectPointer(frm, ToDoNotifyPri5), 1);
  1394.         break;
  1395.     default:
  1396.     }
  1397.     DisplayCategory(ToDoPopupTrigger, gPrefsR->TDNotifyPrefs.todoCategory, false);
  1398.  
  1399.     // set gToDoCategory information
  1400.     //
  1401.     if ((gToDoCategory = CategoryFind(ToDoDB, gPrefsR->TDNotifyPrefs.todoCategory))
  1402.         == dmAllCategories) {
  1403.         MemSet(gPrefsR->TDNotifyPrefs.todoCategory,
  1404.                sizeof(gPrefsR->TDNotifyPrefs.todoCategory), 0);
  1405.         CategoryGetName(ToDoDB, gToDoCategory, gPrefsR->TDNotifyPrefs.todoCategory);
  1406.     }
  1407. }
  1408.  
  1409. static void UnloadTDNotifyPrefsFields()
  1410. {
  1411.     FormPtr frm;
  1412.     
  1413.     if ((frm = FrmGetFormPtr(ToDoNotifyForm)) == 0) return;
  1414.     UnloadCommonNotifyPrefs(frm);       // all/selected, keey/modify, private
  1415.  
  1416.     if ( CtlGetValue(GetObjectPointer(frm, ToDoNotifyPri1)) )
  1417.         gPrefsR->TDNotifyPrefs.priority = 1;
  1418.     else if ( CtlGetValue(GetObjectPointer(frm, ToDoNotifyPri2)) )
  1419.         gPrefsR->TDNotifyPrefs.priority = 2;
  1420.     else if ( CtlGetValue(GetObjectPointer(frm, ToDoNotifyPri3)) )
  1421.         gPrefsR->TDNotifyPrefs.priority = 3;
  1422.     else if ( CtlGetValue(GetObjectPointer(frm, ToDoNotifyPri4)) )
  1423.         gPrefsR->TDNotifyPrefs.priority = 4;
  1424.     else
  1425.         gPrefsR->TDNotifyPrefs.priority = 5;
  1426.     
  1427.     // ToDo Category is set by handler
  1428. }
  1429.  
  1430.  
  1431. static Int16 CheckDatebookRecord(DateType when, BirthDate birth)
  1432. {
  1433.     UInt16 numAppoints = 0;
  1434.     MemHandle apptListH;
  1435.     ApptDBRecordType dbRecord;
  1436.     ApptInfoPtr apptList;
  1437.     UInt16 recordNum;
  1438.     MemHandle recordH;
  1439.     Int16 i;
  1440.  
  1441.     // check the exisiting records
  1442.     //
  1443.     apptListH = ApptGetAppointments(DatebookDB, when, &numAppoints);
  1444.     if (apptListH) {
  1445.         apptList = MemHandleLock(apptListH);
  1446.         for (i = 0; i < numAppoints; i++) {
  1447.             ApptGetRecord(DatebookDB, apptList[i].recordNum,
  1448.                           &dbRecord, &recordH);
  1449.             // if matched one is exists, return recordNum;
  1450.             //
  1451.             if (recordH) {
  1452.                 if (IsSameRecord(dbRecord.note, birth)) {
  1453.                     recordNum = apptList[i].recordNum;
  1454.                     
  1455.                     MemHandleUnlock(recordH);
  1456.                     MemHandleUnlock(apptListH);
  1457.  
  1458.                     return recordNum;
  1459.                 }
  1460.                 else MemHandleUnlock(recordH);
  1461.             }
  1462.         }
  1463.         MemHandleUnlock(apptListH);
  1464.     }
  1465.     return -1;
  1466. }
  1467.  
  1468. static Int16 CheckToDoRecord(DateType when, BirthDate birth)
  1469. {
  1470.     UInt16 currIndex = 0;
  1471.     MemHandle recordH;
  1472.     ToDoDBRecordPtr toDoRec;
  1473.     Char *note;
  1474.     
  1475.     while (1) {
  1476.         recordH = DmQueryNextInCategory(ToDoDB, &currIndex,
  1477.                                         dmAllCategories);
  1478.         if (!recordH) break;
  1479.  
  1480.         toDoRec = MemHandleLock(recordH);
  1481.         note = GetToDoNotePtr(toDoRec);
  1482.         
  1483.         if ((DateToInt(when) == DateToInt(toDoRec->dueDate))
  1484.             && IsSameRecord(note, birth)) {
  1485.             MemHandleUnlock(recordH);
  1486.  
  1487.             return currIndex;
  1488.         }
  1489.         else {
  1490.             MemHandleUnlock(recordH);
  1491.             currIndex++;
  1492.         }
  1493.     }
  1494.     return -1;
  1495. }
  1496.  
  1497. // check global notify preference, and make datebookDB entry private
  1498. //
  1499. static void ChkNMakePrivateRecord(DmOpenRef db, Int16 index)
  1500. {
  1501.     Int16 attributes;
  1502.     // if private is set, make the record private
  1503.     //
  1504.     if (gPrefsR->private == 1) {
  1505.         DmRecordInfo(db, index, &attributes,
  1506.                      NULL,NULL);
  1507.         attributes |= dmRecAttrSecret;
  1508.         DmSetRecordInfo(db, index, &attributes, NULL);
  1509.     }
  1510. }
  1511.  
  1512. static Char* DateBk3IconString()
  1513. {
  1514.     static Char bk3Icon[11];
  1515.  
  1516.     StrCopy(bk3Icon, "##@@@@@@@\n");
  1517.     bk3Icon[5] = gPrefsR->DBNotifyPrefs.datebk3icon + 'A';
  1518.  
  1519.     return bk3Icon;
  1520. }
  1521.  
  1522. static Char* gNotifyFormatString[5] =
  1523. { "[+L] +e +y",     // [Jeong, JaeMok] B 1970
  1524.   "[+F] +e +y",     // [JaeMok Jeong] B 1970
  1525.   "+E - +L +y",     // Birthday - Jeong, JaeMok 1970
  1526.   "+E - +F +y",     // Birthday - JaeMok Jeong 1970
  1527.   "+F +y",            // JaeMok Jeong 1970
  1528. };
  1529.  
  1530. //
  1531. // Memory is alloced, after calling this routine, user must free the memory
  1532. //
  1533. static Char* NotifyDescString(DateType when, BirthDate birth, Boolean todo)
  1534. {
  1535.     Char* description, *pDesc;
  1536.     Char* pfmtString;
  1537.     Int16 age = 0;
  1538.     
  1539.     // make the description
  1540.     description = pDesc = MemPtrNew(1024);
  1541.     SysCopyStringResource(gAppErrStr, NotEnoughMemoryString);
  1542.     ErrFatalDisplayIf(!description, gAppErrStr);
  1543.  
  1544.     // boundary check must be inserted
  1545.     //
  1546.     pfmtString = gNotifyFormatString[(int)gPrefsR->BirthPrefs.notifyformat];
  1547.     while (*pfmtString) {
  1548.         if (*pfmtString == '+') {
  1549.             switch (*(pfmtString+1)) {
  1550.             case 'L':       // LastName, FirstName
  1551.                 StrCopy(pDesc, birth.name1);
  1552.                 if (birth.name2 && birth.name2[0]) {
  1553.                     if (birth.name1 && birth.name1[0])
  1554.                         StrCat(pDesc, ", ");
  1555.                     StrCat(pDesc, birth.name2);
  1556.                 }
  1557.  
  1558.                 pDesc += StrLen(pDesc);
  1559.                 break;
  1560.  
  1561.             case 'F':       // FirstName LastName
  1562.                 StrCopy(pDesc, birth.name2);
  1563.                 
  1564.                 if (birth.name1 && birth.name1[0]) {
  1565.                     if (birth.name2 && birth.name2[0])
  1566.                         StrCat(pDesc, " ");
  1567.                     StrCat(pDesc, birth.name1);
  1568.                 }
  1569.  
  1570.                 pDesc += StrLen(pDesc);
  1571.                 break;
  1572.                 
  1573.             case 'e':       // Event Type (like 'B')
  1574.                 // set event type
  1575.                 *pDesc++ = EventTypeString(birth)[0];
  1576.                 
  1577.                 break;
  1578.  
  1579.             case 'E':       // Event Type (like 'Birthday')
  1580.                 if (!birth.custom[0]) {
  1581.                     StrCopy(pDesc, gPrefsR->BirthPrefs.custom);
  1582.                 }
  1583.                 else {
  1584.                     StrCopy(pDesc, birth.custom);
  1585.                 }
  1586.                 pDesc += StrLen(pDesc);
  1587.                 break;
  1588.  
  1589.             case 'y':       // Year and age
  1590.                 if (birth.flag.bits.lunar || birth.flag.bits.lunar_leap) {
  1591.                     if (birth.flag.bits.lunar) {
  1592.                         StrCopy(pDesc, "-)");
  1593.                     }
  1594.                     else if (birth.flag.bits.lunar_leap) {
  1595.                         StrCopy(pDesc, "#)");
  1596.                     }
  1597.  
  1598.                     if (birth.flag.bits.year) {
  1599.                         DateToAscii(birth.date.month, birth.date.day,
  1600.                                     birth.date.year + 1904,
  1601.                                     gPrefdfmts, gAppErrStr);
  1602.                     }
  1603.                     else {
  1604.                         DateToAsciiLong(birth.date.month, birth.date.day, -1, gPrefdfmts,
  1605.                                         gAppErrStr);
  1606.                     }
  1607.     
  1608.                     StrCat(pDesc, gAppErrStr);
  1609.                     pDesc += StrLen(pDesc);
  1610.                 }
  1611.                 if (birth.flag.bits.year) {
  1612.                     if (!birth.flag.bits.solar 
  1613.                         || gPrefsR->DBNotifyPrefs.duration ==1 || todo) {
  1614.                         age = CalculateAge(when, birth.date, birth.flag);
  1615.                         if (age >= 0) {
  1616.                             StrPrintF(gAppErrStr, " (%d)", age);
  1617.                             StrCopy(pDesc, gAppErrStr);
  1618.                         }
  1619.                     }
  1620.                     else if (birth.flag.bits.solar 
  1621.                              && gPrefsR->DBNotifyPrefs.duration != 1) {
  1622.                         StrPrintF(gAppErrStr, "%d", birth.date.year + 1904 );
  1623.                         StrCopy(pDesc, gAppErrStr);
  1624.                     }
  1625.                     pDesc += StrLen(pDesc);
  1626.                 }
  1627.                 break;
  1628.  
  1629.             default:
  1630.             }
  1631.  
  1632.             pfmtString += 2;    // advance +char;
  1633.         }
  1634.         else {
  1635.             *pDesc++ = *pfmtString++;
  1636.         }
  1637.     }
  1638.     *pDesc = 0;
  1639.     
  1640.     return description;
  1641. }
  1642.     
  1643. static Int16 PerformNotifyDB(BirthDate birth, DateType when,
  1644.                              RepeatInfoType* repeatInfoPtr,
  1645.                              Int16 *created, Int16 *touched)
  1646. {
  1647.     ApptDBRecordType datebook;
  1648.     ApptDateTimeType dbwhen;
  1649.     AlarmInfoType dbalarm;
  1650.     Char* description = 0;
  1651.     Int16 existIndex;
  1652.     ApptDBRecordFlags changedFields;
  1653.     Char noteField[256];        // (datebk3: 10, AN:14), HD id: 5
  1654.  
  1655.     // for the performance, check this first 
  1656.     if ( ((existIndex = CheckDatebookRecord(when, birth)) >= 0)
  1657.          && !gPrefsR->existing ) {    // exist and keep the record
  1658.         (*touched)++;
  1659.         
  1660.         return 0;
  1661.     }
  1662.  
  1663.     /* Zero the memory */
  1664.     MemSet(&datebook, sizeof(ApptDBRecordType), 0);
  1665.     MemSet(&dbwhen, sizeof(ApptDateTimeType), 0);
  1666.     MemSet(&dbalarm, sizeof(AlarmInfoType), 0);
  1667.     datebook.when = &dbwhen;
  1668.     datebook.alarm = &dbalarm;
  1669.  
  1670.     // set the date and time
  1671.     //
  1672.     dbwhen.date = when;
  1673.     dbwhen.startTime = gPrefsR->DBNotifyPrefs.when;
  1674.     dbwhen.endTime = gPrefsR->DBNotifyPrefs.when;
  1675.  
  1676.     // if alarm checkbox is set, set alarm
  1677.     //
  1678.     if (gPrefsR->DBNotifyPrefs.alarm &&
  1679.         gPrefsR->DBNotifyPrefs.notifybefore >= 0) {
  1680.         dbalarm.advanceUnit = aauDays;
  1681.         dbalarm.advance = gPrefsR->DBNotifyPrefs.notifybefore;
  1682.         datebook.alarm = &dbalarm;
  1683.     }
  1684.     else {
  1685.         dbalarm.advance = apptNoAlarm;
  1686.         datebook.alarm = NULL;
  1687.     }
  1688.  
  1689.     // set the repeat
  1690.     //
  1691.     datebook.repeat = repeatInfoPtr;
  1692.  
  1693.     // forget the exceptions 
  1694.     //
  1695.     datebook.exceptions = NULL;
  1696.  
  1697.     // if datebook is datebk3 or AN, set icon
  1698.     //
  1699.     if (gPrefsR->DBNotifyPrefs.icon == 1) {     // AN
  1700.         StrCopy(noteField, "ICON: ");
  1701.         StrCat(noteField, gPrefsR->DBNotifyPrefs.an_icon);
  1702.         StrCat(noteField, "\n#AN\n");
  1703.     }
  1704.     else if (gPrefsR->DBNotifyPrefs.icon == 2) {
  1705.         StrCopy(noteField, DateBk3IconString());
  1706.     }
  1707.     else noteField[0] = 0;
  1708.  
  1709.     StrCat(noteField, gPrefsR->BirthPrefs.notifywith);
  1710.     StrPrintF(gAppErrStr, "%ld", Hash(birth.name1, birth.name2));
  1711.     StrCat(noteField, gAppErrStr);
  1712.     datebook.note = noteField;
  1713.  
  1714.     // make the description
  1715.         
  1716.     description = NotifyDescString(when, birth, false);    // todo = false
  1717.     datebook.description = description;
  1718.  
  1719.     if (existIndex < 0) {            // there is no same record
  1720.         //
  1721.         // if not exists
  1722.         // write the new record (be sure to fill index)
  1723.         //
  1724.         ApptNewRecord(DatebookDB, &datebook, &existIndex);
  1725.         // if private is set, make the record private
  1726.         //
  1727.         ChkNMakePrivateRecord(DatebookDB, existIndex);
  1728.         (*created)++;
  1729.     }
  1730.     else {                                      // if exists
  1731.         if (gPrefsR->existing == 0) {
  1732.             // keep the record
  1733.         }
  1734.         else {
  1735.             // modify
  1736.             changedFields.when = 1;
  1737.             changedFields.description = 1;
  1738.             changedFields.repeat = 1;
  1739.             changedFields.alarm = 1;
  1740.             changedFields.note = 1;
  1741.  
  1742.             ApptChangeRecord(DatebookDB, &existIndex, &datebook,
  1743.                              changedFields);
  1744.             // if private is set, make the record private
  1745.             //
  1746.             ChkNMakePrivateRecord(DatebookDB, existIndex);
  1747.         }
  1748.         (*touched)++;
  1749.     }
  1750.  
  1751.     if (description) MemPtrFree(description);
  1752.     
  1753.     return 0;
  1754. }
  1755.  
  1756. static Int16 PerformNotifyTD(BirthDate birth, DateType when,
  1757.                              Int16 *created, Int16 *touched)
  1758. {
  1759.     Char noteField[256];      // (datebk3: 10, AN:14), HD id: 5
  1760.     ToDoItemType todo;
  1761.     Char* description = 0;
  1762.     
  1763.     Int16 existIndex;
  1764.  
  1765.     // for the performance, check this first 
  1766.     if ( ((existIndex = CheckToDoRecord(when, birth)) >= 0)
  1767.          && !gPrefsR->existing ) {    // exists and keep the record
  1768.         (*touched)++;
  1769.         
  1770.         return 0;
  1771.     }
  1772.  
  1773.     /* Zero the memory */
  1774.     MemSet(&todo, sizeof(ToDoItemType), 0);
  1775.  
  1776.     // set the date 
  1777.     //
  1778.     todo.dueDate = when;
  1779.     todo.priority = gPrefsR->TDNotifyPrefs.priority;
  1780.  
  1781.     StrCopy(noteField, gPrefsR->BirthPrefs.notifywith);
  1782.     StrPrintF(gAppErrStr, "%ld", Hash(birth.name1, birth.name2));
  1783.     StrCat(noteField, gAppErrStr);
  1784.     todo.note = noteField;
  1785.  
  1786.     // make the description
  1787.         
  1788.     description = NotifyDescString(when, birth, true);        // todo = true
  1789.     todo.description = description;
  1790.  
  1791.     // category adjust
  1792.     if (gToDoCategory == dmAllCategories) gToDoCategory = dmUnfiledCategory;
  1793.     
  1794.     if (existIndex < 0) {            // there is no same record
  1795.         //
  1796.         // if not exists
  1797.         // write the new record (be sure to fill index)
  1798.         //
  1799.         ToDoNewRecord(ToDoDB, &todo, gToDoCategory, &existIndex);
  1800.         // if private is set, make the record private
  1801.         //
  1802.         ChkNMakePrivateRecord(ToDoDB, existIndex);
  1803.         (*created)++;
  1804.     }
  1805.     else {                                      // if exists
  1806.         if (gPrefsR->existing == 0) {
  1807.             // keep the record
  1808.         }
  1809.         else {
  1810.             // modify
  1811.  
  1812.             // remove the record
  1813.             DmDeleteRecord(ToDoDB, existIndex);
  1814.             // deleted records are stored at the end of the database
  1815.             //
  1816.             DmMoveRecord(ToDoDB, existIndex, DmNumRecords(ToDoDB));
  1817.             
  1818.             // make new record
  1819.             ToDoNewRecord(ToDoDB, &todo, gToDoCategory, &existIndex);
  1820.             // if private is set, make the record private
  1821.             //
  1822.             ChkNMakePrivateRecord(ToDoDB, existIndex);
  1823.         }
  1824.         (*touched)++;
  1825.     }
  1826.  
  1827.     if (description) MemPtrFree(description);
  1828.     
  1829.     return 0;
  1830. }
  1831.  
  1832. static void AdjustDesktopCompatible(DateType *when)
  1833. {
  1834.     int maxtry = 4;
  1835.     // get current date
  1836.     //
  1837.     
  1838.     if (when->year < 1970 - 1904) when->year = 1970 - 1904;
  1839.                 
  1840.     while (DaysInMonth(when->month, when->year) < when->day && maxtry-- >0) {
  1841.         when->year++;
  1842.     }
  1843. }
  1844.  
  1845. static void NotifyDatebook(int mainDBIndex, DateType when,
  1846.                            Int16 *created, Int16 *touched)
  1847. {
  1848.     MemHandle recordH = 0;
  1849.     PackedBirthDate* rp;
  1850.     BirthDate r;
  1851.     RepeatInfoType repeatInfo;
  1852.     Int16 i;
  1853.     
  1854.     // if duration is equal to 0, no notify record is created
  1855.     //
  1856.     if (gPrefsR->DBNotifyPrefs.duration == 0) return;
  1857.  
  1858.     if ((recordH = DmQueryRecord(MainDB, mainDBIndex))) {
  1859.         rp = (PackedBirthDate *) MemHandleLock(recordH);
  1860.         /*
  1861.          * Build the unpacked structure for an AddressDB record.  It
  1862.          * is just a bunch of pointers into the rp structure.
  1863.          */
  1864.         UnpackBirthdate(&r, rp);
  1865.  
  1866.         if (r.flag.bits.solar) {
  1867.             repeatInfo.repeatType = repeatYearly;
  1868.             repeatInfo.repeatFrequency = 1;
  1869.             repeatInfo.repeatOn = 0;
  1870.             repeatInfo.repeatStartOfWeek = 0;
  1871.  
  1872.             if (gPrefsR->DBNotifyPrefs.duration > 1) {
  1873.                 // if solar date, make entry repeat
  1874.                 //
  1875.                 repeatInfo.repeatEndDate = when;
  1876.                 repeatInfo.repeatEndDate.year +=
  1877.                     gPrefsR->DBNotifyPrefs.duration -1;
  1878.  
  1879.                 // if end year is more than 2301, set for ever
  1880.                 if (repeatInfo.repeatEndDate.year > lastYear) {
  1881.                     DateToInt(repeatInfo.repeatEndDate) = -1;
  1882.                 }
  1883.  
  1884.                 // if duration > 1, 'when' is the birthdate;
  1885.                 if (r.flag.bits.year) when = r.date;
  1886.  
  1887.                 // Because Palm Desktop doesn't support events before 1970.
  1888.                 //
  1889.                 AdjustDesktopCompatible(&when);
  1890.                 PerformNotifyDB(r, when, &repeatInfo, created, touched);
  1891.             }
  1892.             else if (gPrefsR->DBNotifyPrefs.duration == -1) {
  1893.                 // if duration > 1, 'when' is the birthdate;
  1894.                 if (r.flag.bits.year) when = r.date;
  1895.  
  1896.                 // Because Palm Desktop doesn't support events before 1970.
  1897.                 //
  1898.                 AdjustDesktopCompatible(&when);
  1899.  
  1900.                 DateToInt(repeatInfo.repeatEndDate) = -1;
  1901.                 PerformNotifyDB(r, when, &repeatInfo, created, touched);
  1902.             }
  1903.             else PerformNotifyDB(r, when, NULL, created, touched);
  1904.         }
  1905.         else if (r.flag.bits.lunar || r.flag.bits.lunar_leap) {
  1906.             // if lunar date, make each entry
  1907.             //
  1908.             int duration = gPrefsR->DBNotifyPrefs.duration;
  1909.             DateType current, converted;
  1910.             
  1911.             // for the convenience, -1 means 5 year in lunar calendar.
  1912.             //
  1913.             if (duration == -1) duration = DEFAULT_DURATION;
  1914.  
  1915.             // now use the same routine in lunar an lunar_leap
  1916.             //
  1917.             DateSecondsToDate(TimGetSeconds(), ¤t);
  1918.  
  1919.             for (i=0; i < duration; i++) {
  1920.                 converted = r.date;
  1921.                 if (!FindNearLunar(&converted, current,
  1922.                                    r.flag.bits.lunar_leap)) break;
  1923.  
  1924.                 PerformNotifyDB(r, converted, NULL, created, touched);
  1925.  
  1926.                 // process next target day
  1927.                 //
  1928.                 current = converted;
  1929.                 DateAdjust(¤t, 1);
  1930.             }
  1931.         }
  1932.         
  1933.         MemHandleUnlock(recordH);
  1934.     }
  1935. }
  1936.  
  1937. static void NotifyToDo(int mainDBIndex, DateType when,
  1938.                        Int16 *created, Int16 *touched)
  1939. {
  1940.     MemHandle recordH = 0;
  1941.     PackedBirthDate* rp;
  1942.     BirthDate r;
  1943.     
  1944.     if ((recordH = DmQueryRecord(MainDB, mainDBIndex))) {
  1945.         rp = (PackedBirthDate *) MemHandleLock(recordH);
  1946.         /*
  1947.          * Build the unpacked structure for an AddressDB record.  It
  1948.          * is just a bunch of pointers into the rp structure.
  1949.          */
  1950.         UnpackBirthdate(&r, rp);
  1951.  
  1952.         if (r.flag.bits.solar) {
  1953.             PerformNotifyTD(r, when, created, touched);
  1954.         }
  1955.         else if (r.flag.bits.lunar || r.flag.bits.lunar_leap) {
  1956.             // if lunar date, make each entry
  1957.             //
  1958.             DateType current, converted;
  1959.             
  1960.             // now use the same routine in lunar an lunar_leap
  1961.             //
  1962.             DateSecondsToDate(TimGetSeconds(), ¤t);
  1963.  
  1964.             converted = r.date;
  1965.             if (FindNearLunar(&converted, current,
  1966.                               r.flag.bits.lunar_leap)) {
  1967.                 PerformNotifyTD(r, converted, created, touched);
  1968.             }
  1969.         }
  1970.         
  1971.         MemHandleUnlock(recordH);
  1972.     }
  1973. }
  1974.  
  1975. static void NotifyAction(UInt32 whatAlert,
  1976.                          void (*func)(int mainDBIndex, DateType when,
  1977.                                       Int16 *created, Int16 *touched))
  1978. {
  1979.     Int16 created = 0, touched = 0;
  1980.     LineItemPtr ptr;
  1981.     FormPtr formPtr;
  1982.     Int16 i;
  1983.     Char* info = 0;
  1984.     Char tmp[25];
  1985.     Char temp[255];
  1986.  
  1987.     if (gMainTableTotals >0) {
  1988.         ptr = MemHandleLock(gTableRowHandle);
  1989.         if (gPrefsR->records == 0) {  // all
  1990.             if (whatAlert == DateBookNotifyForm) {
  1991.                 SysCopyStringResource(gAppErrStr, DateBookString);
  1992.             }
  1993.             else {
  1994.                 SysCopyStringResource(gAppErrStr, ToDoString);
  1995.             }
  1996.  
  1997.             StrPrintF(tmp, "%d", gMainTableTotals);
  1998.             if (FrmCustomAlert(NotifyWarning, gAppErrStr, tmp, " ")
  1999.                 == 0) {             // user select OK button
  2000.  
  2001.                 formPtr = FrmInitForm(ProgressForm);
  2002.                 FrmDrawForm(formPtr);
  2003.                 FrmSetActiveForm(formPtr);
  2004.                 
  2005.                 for (i=0; i < gMainTableTotals; i++) {
  2006.                     if (ptr[i].date.year != INVALID_CONV_DATE) {
  2007.                         (*func)(ptr[i].birthRecordNum,
  2008.                                 ptr[i].date, &created,
  2009.                                 &touched);
  2010.                     }
  2011.  
  2012.                     if ((i+1) % 20 == 0) {
  2013.                         // Reset the auto-off timer to make sure we don't
  2014.                         // fall asleep in the
  2015.                         //  middle of the update
  2016.                         EvtResetAutoOffTimer ();
  2017.                     }
  2018.  
  2019.                     StrPrintF(gAppErrStr, "%d/%d", i+1, gMainTableTotals);
  2020.                     FldDrawField(SetFieldTextFromStr(ProgressFormField, gAppErrStr));
  2021.                 }
  2022.  
  2023.                 FrmReturnToForm(0);
  2024.             }
  2025.             else {
  2026.                 MemPtrUnlock(ptr);
  2027.                 goto Exit_Notify_All;
  2028.             }
  2029.                     
  2030.         }
  2031.         else {                                      // selected
  2032.             if (ptr[gMainTableHandleRow].date.year
  2033.                 != INVALID_CONV_DATE) {
  2034.                 (*func)(ptr[gMainTableHandleRow].birthRecordNum,
  2035.                         ptr[gMainTableHandleRow].date,
  2036.                         &created, &touched);
  2037.             }
  2038.         }
  2039.         MemPtrUnlock(ptr);
  2040.  
  2041.         info = MemPtrNew(255);
  2042.         SysCopyStringResource(gAppErrStr, NotEnoughMemoryString);
  2043.         ErrFatalDisplayIf(!info, gAppErrStr);
  2044.  
  2045.         SysCopyStringResource(gAppErrStr, NotifyCreateString);
  2046.         StrPrintF(info, gAppErrStr, created);
  2047.  
  2048.         SysCopyStringResource(temp, ExistingEntryString);
  2049.         StrPrintF(gAppErrStr, temp, touched);
  2050.         StrNCat(info, gAppErrStr, 255);
  2051.  
  2052.         if (gPrefsR->existing == 0) {  // keep
  2053.             SysCopyStringResource(temp, UntouchedString);
  2054.             StrNCat(info, temp, 255);
  2055.         }
  2056.         else {                                      // modify
  2057.             SysCopyStringResource(temp, ModifiedString);
  2058.             StrNCat(info, temp, 255);
  2059.         }
  2060.  
  2061.         // notify the number of the created and touched record
  2062.         //
  2063.         FrmCustomAlert(ErrorAlert, info, " ", " ");
  2064.  
  2065.         MemPtrFree(info);
  2066.     }
  2067.  Exit_Notify_All:                
  2068.  
  2069.     return;
  2070. }
  2071.  
  2072.  
  2073. static Boolean DBNotifyFormHandleEvent(EventPtr e)
  2074. {
  2075.     Boolean handled = false;
  2076.     FormPtr frm = FrmGetFormPtr(DateBookNotifyForm);
  2077.  
  2078.     switch (e->eType) {
  2079.     case frmOpenEvent:
  2080.  
  2081.         LoadDBNotifyPrefsFields();
  2082.         FrmDrawForm(frm);
  2083.         handled = true;
  2084.         break;
  2085.  
  2086.     case ctlSelectEvent:
  2087.         switch(e->data.ctlSelect.controlID) {
  2088.         case DateBookNotifyFormOk: 
  2089.         {
  2090.             UnloadDBNotifyPrefsFields();
  2091.             WritePrefsRec();
  2092.  
  2093.             NotifyAction(DateBookNotifyForm, NotifyDatebook);
  2094.             FrmReturnToForm(0);
  2095.  
  2096.             handled = true;
  2097.             break;
  2098.         }
  2099.         
  2100.         case DateBookNotifyFormCancel:
  2101.             UnloadDBNotifyPrefsFields();
  2102.             WritePrefsRec();
  2103.  
  2104.             FrmReturnToForm(0);
  2105.  
  2106.             handled = true;
  2107.             break;
  2108.  
  2109.         case DateBookNotifyFormMore:
  2110.             FrmPopupForm(DateBookNotifyMoreForm);
  2111.  
  2112.             handled = true;
  2113.             break;
  2114.  
  2115.         case DateBookNotifyFormAlarm:
  2116.             if (CtlGetValue(GetObjectPointer(frm,
  2117.                                              DateBookNotifyFormAlarm))) {
  2118.                 FrmShowObject(frm,
  2119.                               FrmGetObjectIndex(frm, DateBookNotifyFormBefore));
  2120.  
  2121.                 SysCopyStringResource(gAppErrStr, DateBookNotifyDaysString);
  2122.                 FldDrawField(SetFieldTextFromStr(DateBookNotifyFormLabelDays, 
  2123.                                                  gAppErrStr));
  2124.             }
  2125.             else {
  2126.                 FrmHideObject(frm, 
  2127.                               FrmGetObjectIndex(frm, DateBookNotifyFormBefore));
  2128.                 FldDrawField(ClearFieldText(DateBookNotifyFormLabelDays));
  2129.             }
  2130.             
  2131.             handled = true;
  2132.             break;
  2133.  
  2134.         case DateBookNotifyFormTime:
  2135.         {
  2136.             TimeType startTime, endTime, lastTime;
  2137.             Boolean untimed = false;
  2138.  
  2139.             startTime = endTime = lastTime = gPrefsR->DBNotifyPrefs.when;
  2140.             if (TimeToInt(startTime) == noTime) {
  2141.                 untimed = true;
  2142.                 // set dummy time
  2143.                 startTime.hours = endTime.hours  = 8;
  2144.                 startTime.minutes = endTime.minutes = 0;
  2145.             }
  2146.             
  2147.             SysCopyStringResource(gAppErrStr, SelectTimeString);
  2148.             if (SelectTimeV33(&startTime, &endTime, untimed, gAppErrStr,8)) {
  2149.                 gPrefsR->DBNotifyPrefs.when = startTime;
  2150.                 TimeToAsciiLocal(gPrefsR->DBNotifyPrefs.when, gPreftfmts,
  2151.                                  gAppErrStr);
  2152.             }
  2153.             else {
  2154.                 TimeToAsciiLocal(lastTime, gPreftfmts, gAppErrStr);
  2155.             }
  2156.             
  2157.             CtlSetLabel(GetObjectPointer(frm, DateBookNotifyFormTime),
  2158.                         gAppErrStr);
  2159.  
  2160.             handled = true;
  2161.             break;
  2162.         }
  2163.             
  2164.         default:
  2165.             break;
  2166.         }
  2167.     case menuEvent:
  2168.         MenuEraseStatus(NULL);
  2169.         handled = true;
  2170.         break;
  2171.  
  2172.     default:
  2173.         break;
  2174.     }
  2175.  
  2176.     return handled;
  2177. }
  2178.  
  2179. static Boolean ToDoFormHandleEvent(EventPtr e)
  2180. {
  2181.     Boolean handled = false;
  2182.     FormPtr frm = FrmGetFormPtr(ToDoNotifyForm);
  2183.  
  2184.     switch (e->eType) {
  2185.     case frmOpenEvent:
  2186.  
  2187.         LoadTDNotifyPrefsFields();
  2188.         FrmDrawForm(frm);
  2189.         handled = true;
  2190.         break;
  2191.  
  2192.     case ctlSelectEvent:
  2193.         switch(e->data.ctlSelect.controlID) {
  2194.         case ToDoNotifyFormOk: 
  2195.         {
  2196.             UnloadTDNotifyPrefsFields();
  2197.             WritePrefsRec();
  2198.  
  2199.             NotifyAction(ToDoNotifyForm, NotifyToDo);
  2200.             FrmReturnToForm(0);
  2201.  
  2202.             handled = true;
  2203.             break;
  2204.         }
  2205.         
  2206.         case ToDoNotifyFormCancel:
  2207.             UnloadTDNotifyPrefsFields();
  2208.             WritePrefsRec();
  2209.  
  2210.             FrmReturnToForm(0);
  2211.  
  2212.             handled = true;
  2213.             break;
  2214.  
  2215.         case ToDoPopupTrigger: 
  2216.         {
  2217.             SelectCategoryPopup(ToDoDB, &gToDoCategory,
  2218.                                 ToDoNotifyCategory, ToDoPopupTrigger,
  2219.                                 gPrefsR->TDNotifyPrefs.todoCategory);
  2220.             handled = true;
  2221.             break;
  2222.         }
  2223.             
  2224.         default:
  2225.             break;
  2226.         }
  2227.     case menuEvent:
  2228.         MenuEraseStatus(NULL);
  2229.         handled = true;
  2230.         break;
  2231.  
  2232.     default:
  2233.         break;
  2234.     }
  2235.  
  2236.     return handled;
  2237. }
  2238.  
  2239. static int dec(char hex)
  2240. {
  2241.     if (hex >= '0' && hex <= '9') {
  2242.         return hex - '0';
  2243.     }
  2244.     else if (hex >= 'A' && hex <= 'F') {
  2245.         return hex - 'A' + 10;
  2246.     }
  2247.     else if (hex >= 'a' && hex <= 'f') {
  2248.         return hex - 'a' + 10;
  2249.     }
  2250.     else return 0;  
  2251. }
  2252.  
  2253. static Int8 convertWord(char first, char second)
  2254. {
  2255.     return dec(first) * 16 + dec(second);
  2256. }
  2257.  
  2258. static void DateBk3CustomDrawTable(MemPtr tableP, Int16 row, Int16 column, 
  2259.                                    RectanglePtr bounds)
  2260. {
  2261.     Int16 x, y;
  2262.     Int8 drawItem = row * 13 + column;
  2263.     Int8 drawFixel;
  2264.     Int8 i, j;
  2265.     
  2266.     x = bounds->topLeft.x;
  2267.     y = bounds->topLeft.y;
  2268.  
  2269.     for (i = 0; i < 8; i++) {
  2270.         drawFixel = gDateBk3Icon[drawItem][i];
  2271.         if (drawFixel) {        // if not 0
  2272.             for (j=0; j < 8; j++) {
  2273.                 if (drawFixel & 1) {
  2274.                     WinDrawLine(x+8-j, y+i+1, x+8-j, y+i+1);
  2275.                 }
  2276.                 drawFixel >>= 1;
  2277.             }
  2278.         }
  2279.     }
  2280. }
  2281.  
  2282. static Boolean loadDatebk3Icon()
  2283. {
  2284.     UInt16 currIndex = 0;
  2285.     MemHandle recordH = 0;
  2286.     Char* rp;       // memoPad record
  2287.     Boolean found = false;
  2288.     Char IconString[16];
  2289.     Char *p=0;
  2290.  
  2291.     if ((MemCmp((char*) &gMmcdate, gPrefsR->memcdate, 4) == 0) &&
  2292.         (MemCmp((char*) &gMmmdate, gPrefsR->memmdate, 4) == 0)) {
  2293.         // if matched, use the original datebk3 icon set
  2294.         //
  2295.         return gDateBk3IconLoaded;
  2296.     }
  2297.  
  2298.     while (1) {
  2299.         recordH = DmQueryNextInCategory(MemoDB, &currIndex,
  2300.                                         dmAllCategories);
  2301.         if (!recordH) break;
  2302.  
  2303.         rp = (Char*)MemHandleLock(recordH);
  2304.         if (StrNCompare(rp, DATEBK3_MEMO_STRING,
  2305.                         StrLen(DATEBK3_MEMO_STRING)) == 0) {
  2306.             p = StrChr(rp, '\n');
  2307.             found = true;
  2308.             break;
  2309.         }
  2310.             
  2311.         MemHandleUnlock(recordH);
  2312.         currIndex++;
  2313.     }
  2314.     if (found) {
  2315.         Int8 i=0, j;
  2316.         
  2317.         while ((p = StrChr(p+1, '=')) && i < 52) {
  2318.             MemMove(IconString, p+1, 16);
  2319.  
  2320.             for (j = 0; j < 8; j++) {
  2321.                 gDateBk3Icon[i][j] =
  2322.                     convertWord(IconString[j*2], IconString[j*2+1]);
  2323.             }
  2324.             i++;
  2325.         }
  2326.  
  2327.         for (; i < 52; i++) {
  2328.             MemSet(gDateBk3Icon[i], 8, 0);
  2329.         }
  2330.         MemHandleUnlock(recordH);
  2331.     }
  2332.     
  2333.     return (gDateBk3IconLoaded = found);
  2334. }
  2335.  
  2336. static Boolean DateBk3IconLoadTable(FormPtr frm, Boolean redraw)
  2337. {
  2338.     TablePtr tableP;
  2339.     Int8 row, column, numRows, numColumns;
  2340.  
  2341.     if (!loadDatebk3Icon()) return false;
  2342.  
  2343.     tableP = GetObjectPointer(frm, DateBookNotifyBk3Table);
  2344.     if (redraw) {
  2345.         TblEraseTable(tableP);
  2346.     }
  2347.  
  2348.     numRows = TblGetNumberOfRows(tableP);
  2349.     numColumns = 13;
  2350.     
  2351.     for (row=0; row < numRows; row++) {
  2352.         for (column=0; column < numColumns; column++) {
  2353.             TblSetItemStyle(tableP, row, column, customTableItem);
  2354.         }
  2355.         TblSetRowSelectable(tableP, row, true);
  2356.  
  2357.         TblSetRowHeight(tableP, row, 10);
  2358.         TblSetRowUsable(tableP, row, true);
  2359.     }
  2360.  
  2361.     for (column=0; column < numColumns; column++) {
  2362.         TblSetColumnUsable(tableP, column, true);
  2363.         TblSetCustomDrawProcedure(tableP, column, DateBk3CustomDrawTable);
  2364.     }
  2365.  
  2366.     if (redraw) {
  2367.         TblDrawTable(tableP);
  2368.     }
  2369.     return true;
  2370. }
  2371.  
  2372. static void HideIconStuff()
  2373. {
  2374.     FormPtr frm;
  2375.  
  2376.     if ((frm = FrmGetFormPtr(DateBookNotifyMoreForm)) == 0) return;
  2377.  
  2378.     FrmHideObject(frm, FrmGetObjectIndex(frm, DateBookNotifyFormBk3));
  2379.     FrmHideObject(frm, FrmGetObjectIndex(frm, DateBookNotifyFormAN));
  2380. }
  2381.  
  2382. static void ShowIconStuff()
  2383. {
  2384.     FormPtr frm;
  2385.  
  2386.     if ((frm = FrmGetFormPtr(DateBookNotifyMoreForm)) == 0) return;
  2387.  
  2388.     FrmShowObject(frm, FrmGetObjectIndex(frm, DateBookNotifyFormBk3));
  2389.     FrmShowObject(frm, FrmGetObjectIndex(frm, DateBookNotifyFormAN));
  2390. }
  2391.  
  2392. static Boolean LoadDBNotifyPrefsFieldsMore(void)
  2393. {
  2394.     FormPtr frm;
  2395.  
  2396.     if ((frm = FrmGetFormPtr(DateBookNotifyMoreForm)) == 0) return false;
  2397.  
  2398.     if (gPrefsR->DBNotifyPrefs.icon == 0) {
  2399.         CtlSetValue(GetObjectPointer(frm, DateBookNotifyFormIcon), 0);
  2400.         HideIconStuff();
  2401.     }
  2402.     else {
  2403.         CtlSetValue(GetObjectPointer(frm, DateBookNotifyFormIcon), 1);
  2404.     }
  2405.     
  2406.     if (gPrefsR->DBNotifyPrefs.icon == 1) {     // AN selected
  2407.         CtlSetValue(GetObjectPointer(frm, DateBookNotifyFormAN), 1);
  2408.     }
  2409.     else {                                      // Datebk3 Icon selected 
  2410.         CtlSetValue(GetObjectPointer(frm, DateBookNotifyFormBk3), 1);
  2411.     }
  2412.  
  2413.     SetFieldTextFromStr(DateBookNotifyANInput,
  2414.                         gPrefsR->DBNotifyPrefs.an_icon);
  2415.  
  2416.     return DateBk3IconLoadTable(frm, false);
  2417.  
  2418. }
  2419.  
  2420. static void UnloadDBNotifyPrefsFieldsMore()
  2421. {
  2422.     FormPtr frm;
  2423.     ControlPtr ptr;
  2424.     
  2425.     if ((frm = FrmGetFormPtr(DateBookNotifyMoreForm)) == 0) return;
  2426.     
  2427.     ptr = GetObjectPointer(frm, DateBookNotifyFormIcon);
  2428.     if (!CtlGetValue(ptr)) gPrefsR->DBNotifyPrefs.icon = 0;
  2429.     else {
  2430.         ptr = GetObjectPointer(frm, DateBookNotifyFormAN);
  2431.         if (CtlGetValue(ptr)) gPrefsR->DBNotifyPrefs.icon = 1;
  2432.         else gPrefsR->DBNotifyPrefs.icon = 2;
  2433.     }
  2434.     
  2435.     if (FldDirty(GetObjectPointer(frm, DateBookNotifyANInput))) {
  2436.         if (FldGetTextPtr(GetObjectPointer(frm, DateBookNotifyANInput))) {
  2437.             StrNCopy(gPrefsR->DBNotifyPrefs.an_icon,
  2438.                      FldGetTextPtr(GetObjectPointer(frm, DateBookNotifyANInput)), 9);
  2439.         }
  2440.     }
  2441. }
  2442.  
  2443. static Boolean DBNotifyFormMoreHandleEvent(EventPtr e)
  2444. {
  2445.     Boolean handled = false;
  2446.     FormPtr frm = FrmGetActiveForm();
  2447.     Boolean loadDateBk3Icon;
  2448.  
  2449.     switch (e->eType) {
  2450.     case frmOpenEvent: 
  2451.     {
  2452.         TablePtr tableP;
  2453.         Int16 numColumns;
  2454.  
  2455.         loadDateBk3Icon = LoadDBNotifyPrefsFieldsMore();
  2456.         FrmDrawForm(frm);
  2457.  
  2458.         if (loadDateBk3Icon) {
  2459.             tableP = GetObjectPointer(frm, DateBookNotifyBk3Table);
  2460.             numColumns = 13;
  2461.             TblSelectItem(tableP, gPrefsR->DBNotifyPrefs.datebk3icon/numColumns,
  2462.                           gPrefsR->DBNotifyPrefs.datebk3icon % numColumns);
  2463.         }
  2464.  
  2465.         handled = true;
  2466.         break;
  2467.     }
  2468.  
  2469.     case ctlSelectEvent:
  2470.         switch(e->data.ctlSelect.controlID) {
  2471.         case DateBookNotifyMoreFormOk: 
  2472.         {
  2473.             UnloadDBNotifyPrefsFieldsMore();
  2474.             // WritePrefsRec();
  2475.  
  2476.             FrmReturnToForm(0);
  2477.             handled = true;
  2478.             break;
  2479.         }
  2480.         case DateBookNotifyFormIcon:
  2481.         {
  2482.             if (CtlGetValue(GetObjectPointer(frm, DateBookNotifyFormIcon)))
  2483.             {
  2484.                 ShowIconStuff();
  2485.             }
  2486.             else {
  2487.                 HideIconStuff();
  2488.             }
  2489.             
  2490.             handled = true;
  2491.             break;
  2492.         }
  2493.       
  2494.         default:
  2495.             break;
  2496.         }
  2497.     case tblSelectEvent: 
  2498.     {
  2499.         if (e->data.tblSelect.tableID == DateBookNotifyBk3Table) {
  2500.             gPrefsR->DBNotifyPrefs.datebk3icon =
  2501.                 e->data.tblEnter.column + e->data.tblEnter.row * 13;
  2502.  
  2503.             handled = true;
  2504.             break;
  2505.         }
  2506.     }
  2507.  
  2508.     case menuEvent:
  2509.         MenuEraseStatus(NULL);
  2510.         handled = true;
  2511.         break;
  2512.  
  2513.     default:
  2514.         break;
  2515.     }
  2516.  
  2517.     return handled;
  2518. }
  2519.  
  2520. static void RecordViewScroll(Int16 chr)
  2521. {
  2522.     LineItemPtr ptr;
  2523.     
  2524.     // Before processing the scroll, be sure that the command bar has
  2525.     // been closed.
  2526.     MenuEraseStatus (0);
  2527.     
  2528.     switch (chr) {
  2529.     case vchrPageDown:
  2530.         if (gMainTableHandleRow < gMainTableTotals-1) {
  2531.             gMainTableHandleRow++;
  2532.             ptr = MemHandleLock(gTableRowHandle);
  2533.             SetBirthdateViewForm(ptr[gMainTableHandleRow].birthRecordNum,
  2534.                                  ptr[gMainTableHandleRow].date);
  2535.             MemPtrUnlock(ptr);
  2536.         
  2537.             SndPlaySystemSound(sndInfo);
  2538.             FrmDrawForm(FrmGetFormPtr(BirthdateForm));
  2539.         }
  2540.         break;
  2541.     case vchrPageUp:
  2542.         if (gMainTableHandleRow > 0 ) {
  2543.             gMainTableHandleRow--;
  2544.  
  2545.             ptr = MemHandleLock(gTableRowHandle);
  2546.             SetBirthdateViewForm(ptr[gMainTableHandleRow].birthRecordNum,
  2547.                                  ptr[gMainTableHandleRow].date);
  2548.             MemPtrUnlock(ptr);
  2549.         
  2550.             SndPlaySystemSound(sndInfo);
  2551.             FrmDrawForm(FrmGetFormPtr(BirthdateForm));
  2552.         }
  2553.         break;
  2554.     }
  2555. }
  2556.  
  2557. static Boolean BirthdateFormHandleEvent(EventPtr e)
  2558. {
  2559.     Boolean handled = false;
  2560.     FormPtr frm = FrmGetFormPtr(BirthdateForm);
  2561.  
  2562.     switch (e->eType) {
  2563.     case keyDownEvent:
  2564.     {
  2565.         if (EvtKeydownIsVirtual(e)) {
  2566.             RecordViewScroll(e->data.keyDown.chr);
  2567.             handled = true;
  2568.         }
  2569.     }
  2570.     
  2571.     case frmOpenEvent: 
  2572.     {
  2573.         // set appropriate information
  2574.         //
  2575.         LineItemPtr ptr = MemHandleLock(gTableRowHandle);
  2576.         SetBirthdateViewForm(ptr[gMainTableHandleRow].birthRecordNum,
  2577.                              ptr[gMainTableHandleRow].date);
  2578.         MemPtrUnlock(ptr);
  2579.         
  2580.         FrmDrawForm(frm);
  2581.         handled = true;
  2582.         break;
  2583.     }
  2584.     
  2585.     case menuEvent:
  2586.         handled = MenuHandler(frm, e);
  2587.         break;
  2588.         
  2589.     case ctlSelectEvent:
  2590.         switch(e->data.ctlSelect.controlID) {
  2591.         case BirthdateDone:
  2592.             FrmReturnToForm(0);
  2593.             HighlightAction(gMainTableHandleRow, false);
  2594.                 
  2595.             handled = true;
  2596.             break;
  2597.         case BirthdateNotifyDB:
  2598.             //  Notify event into the datebook entry
  2599.             //
  2600.             gPrefsR->records = 1;
  2601.             FrmPopupForm(DateBookNotifyForm);
  2602.  
  2603.             handled = true;
  2604.             break;
  2605.  
  2606.         case BirthdateNotifyTD:
  2607.             //  Notify event into the datebook entry
  2608.             //
  2609.             gPrefsR->records = 1;
  2610.             FrmPopupForm(ToDoNotifyForm);
  2611.  
  2612.             handled = true;
  2613.             break;
  2614.             
  2615.         case BirthdateGoto:
  2616.         {
  2617.             MemHandle recordH = 0;
  2618.             PackedBirthDate* rp;
  2619.             UInt32 gotoIndex;
  2620.             
  2621.             LineItemPtr ptr = MemHandleLock(gTableRowHandle);
  2622.             
  2623.             if ((recordH =
  2624.                  DmQueryRecord(MainDB,
  2625.                                ptr[gMainTableHandleRow].birthRecordNum))) {
  2626.                 rp = (PackedBirthDate *) MemHandleLock(recordH);
  2627.                 gotoIndex = rp->addrRecordNum;
  2628.                 MemHandleUnlock(recordH);
  2629.  
  2630.                 if (!GotoAddress(gotoIndex)) {
  2631.                     handled = true;
  2632.                 }
  2633.             }
  2634.             MemPtrUnlock(ptr);
  2635.             break;
  2636.         }
  2637.         default:
  2638.             break;
  2639.         }
  2640.         
  2641.     default:
  2642.         break;
  2643.     }
  2644.  
  2645.     return handled;
  2646. }
  2647.  
  2648. static Boolean StartFormHandlerEvent(EventPtr e)
  2649. {
  2650.     Int16 err;
  2651.     Boolean handled = false;
  2652.     FormPtr frm = FrmGetFormPtr(StartForm);
  2653.  
  2654.     // no event procedure
  2655.     //
  2656.     switch (e->eType) {
  2657.     case frmUpdateEvent:
  2658.     case frmOpenEvent:
  2659.         err = UpdateBirthdateDB(MainDB, frm);
  2660.         if (err == ENONBIRTHDATEFIELD) {
  2661.             char ErrStr[200];
  2662.  
  2663.             SysCopyStringResource(ErrStr, CantFindAddrString);
  2664.             StrPrintF(gAppErrStr, ErrStr, gPrefsR->BirthPrefs.custom);
  2665.  
  2666.             switch (FrmCustomAlert(CustomFieldAlert,
  2667.                                    gAppErrStr, " ", " ")) {
  2668.             case 0:             // OK
  2669.                 gProgramExit = true;
  2670.                 break;
  2671.             case 1:             // Help
  2672.                 FrmHelp(CustomErrorHelpString);
  2673.                 FrmUpdateForm(StartForm, 0);    // to redisplay startform
  2674.                 
  2675.                 break;
  2676.             case 2:             // Preferences
  2677.                 FrmPopupForm(PrefForm);
  2678.                 break;
  2679.             }
  2680.         }
  2681.         else FrmGotoForm(MainForm);
  2682.         handled = true;
  2683.         break;
  2684.     default:
  2685.         break;
  2686.     }
  2687.     
  2688.     return handled;
  2689. }
  2690.  
  2691. static Boolean ApplicationHandleEvent(EventPtr e)
  2692. {
  2693.     FormPtr frm;
  2694.     Boolean handled = false;
  2695.     UInt16 formID;
  2696.  
  2697.     if (e->eType == frmLoadEvent) {
  2698.         formID = e->data.frmLoad.formID;
  2699.         frm = FrmInitForm(formID);
  2700.         FrmSetActiveForm(frm);
  2701.  
  2702.         switch(formID) {
  2703.         case StartForm:
  2704.             FrmSetEventHandler(frm, StartFormHandlerEvent);
  2705.             break;
  2706.         case MainForm: 
  2707.         {
  2708.             FrmSetEventHandler(frm, MainFormHandleEvent);
  2709.             break;
  2710.         }
  2711.         case PrefForm:
  2712.             FrmSetEventHandler(frm, PrefFormHandleEvent);
  2713.             break;
  2714.         case DateBookNotifyForm:
  2715.             FrmSetEventHandler(frm, DBNotifyFormHandleEvent);
  2716.             break;
  2717.         case DateBookNotifyMoreForm:
  2718.             FrmSetEventHandler(frm, DBNotifyFormMoreHandleEvent);
  2719.             break;
  2720.         case ToDoNotifyForm:
  2721.             FrmSetEventHandler(frm, ToDoFormHandleEvent);
  2722.             break;
  2723.         case Sl2LnForm:
  2724.             FrmSetEventHandler(frm, Sl2LnFormHandleEvent);
  2725.             break;
  2726.         case Ln2SlForm:
  2727.             FrmSetEventHandler(frm, Ln2SlFormHandleEvent);
  2728.             break;
  2729.         case BirthdateForm:
  2730.             FrmSetEventHandler(frm, BirthdateFormHandleEvent);
  2731.             break;
  2732.         }
  2733.         handled = true;
  2734.     }
  2735.  
  2736.     return handled;
  2737. }
  2738.  
  2739. /*
  2740.  * These are called in StartApplication and StopApplication because they
  2741.  * are required memory allocations that exist from start to finish of the
  2742.  * application run.
  2743.  */
  2744. static void freememories(void)
  2745. {
  2746.     if (PrefsRHandle) {
  2747.         MemHandleFree(PrefsRHandle);
  2748.         PrefsRHandle = 0;
  2749.     }
  2750.     if (gTableRowHandle) {
  2751.         MemHandleFree(gTableRowHandle);
  2752.         gTableRowHandle = 0;
  2753.     }
  2754. }
  2755.  
  2756. static void ReadPrefsRec(void)
  2757. {
  2758.     MemPtr p;
  2759.  
  2760.     if (PrefsDB) {
  2761.         p = MemHandleLock(PrefsRecHandle);
  2762.         MemMove(gPrefsR, p, sizeof(struct sPrefsR));
  2763.         MemPtrUnlock(p);
  2764.         gPrefsRdirty = false;
  2765.         gPrefsWeredirty = false;
  2766.         if (gPrefsR->BirthPrefs.sysdateover)
  2767.             gPrefdfmts = gPrefsR->BirthPrefs.dateformat;
  2768.     }
  2769. }
  2770.  
  2771. static void WritePrefsRec(void)
  2772. {
  2773.     MemPtr p;
  2774.     Int8 err;
  2775.     
  2776.     if (PrefsDB) {
  2777.         p = MemHandleLock(PrefsRecHandle);
  2778.         err = DmWrite(p, 0, gPrefsR, sizeof(struct sPrefsR));
  2779.         SysCopyStringResource(gAppErrStr, CantWritePrefsString);
  2780.         ErrFatalDisplayIf(err, gAppErrStr);
  2781.         MemPtrUnlock(p);
  2782.         /*
  2783.          * This is set until we exit the app at which time the ReleaseRecord()
  2784.          * is called.
  2785.          */
  2786.         gPrefsWeredirty = true;
  2787.         gPrefsRdirty = false;
  2788.     }
  2789. }
  2790.     
  2791. static Int16 OpenDatabases(void)
  2792. {
  2793.     SystemPreferencesType sysPrefs;
  2794.     UInt16 mode = 0;
  2795.     UInt16 cardNo;
  2796.     LocalID dbID, appInfoID;
  2797.     char dbname[dmDBNameLength];
  2798.     Boolean newSearch = true;
  2799.     UInt32 creator;
  2800.     UInt16 attributes;
  2801.     MemPtr p, p2;
  2802.     Int8 err;
  2803.     DmSearchStateType searchInfo;
  2804.     UInt32 cdate, mdate;
  2805.     AddrAppInfoPtr appInfoPtr;
  2806.  
  2807.     /*
  2808.      * Set these to zero in case any of the opens fail.
  2809.      */
  2810.     PrefsDB = 0;
  2811.     MainDB = 0;
  2812.     DatebookDB = 0;
  2813.     AddressDB = 0;
  2814.     MemoDB = 0;
  2815.     PrefsRecHandle = 0;
  2816.  
  2817.     /* Determine if secret records should be shown. */
  2818.     PrefGetPreferences(&sysPrefs);
  2819.     if (!sysPrefs.hideSecretRecords) mode |= dmModeShowSecret;
  2820.  
  2821.     gSystemdfmts = gPrefdfmts = sysPrefs.dateFormat;// save global date format
  2822.     gPreftfmts = sysPrefs.timeFormat;               // save global time format
  2823.     
  2824.     /*
  2825.      * Quick way to open all needed databases.  Thanks to Danny Epstein.
  2826.      */
  2827.     while ((DmGetNextDatabaseByTypeCreator(newSearch, &searchInfo, 'DATA',
  2828.                                            NULL, false, &cardNo, &dbID) == 0) &&
  2829.            (!DatebookDB || !ToDoDB || !AddressDB
  2830.             || !MemoDB || !MainDB || !PrefsDB)) {
  2831.         if (DmDatabaseInfo(cardNo, dbID, dbname, NULL, NULL, &cdate, &mdate,
  2832.                            NULL, NULL, &appInfoID, NULL, NULL, &creator))
  2833.             break;
  2834.         if ((creator == DatebookAppID) &&
  2835.             (StrCaselessCompare(dbname, DatebookDBName) == 0)) {
  2836.             DatebookDB = DmOpenDatabase(cardNo, dbID, mode |
  2837.                                         dmModeReadWrite);
  2838.         }
  2839.         else if ((creator == ToDoAppID) &&
  2840.                  (StrCaselessCompare(dbname, ToDoDBName) == 0)) {
  2841.             gMmcdate = cdate;
  2842.             gMmmdate = mdate;
  2843.             ToDoDB = DmOpenDatabase(cardNo, dbID, mode | dmModeReadWrite);
  2844.         }
  2845.         else if ((creator == MemoAppID)
  2846.                  && (StrCaselessCompare(dbname, MemoDBName) == 0)) {
  2847.             MemoDB = DmOpenDatabase(cardNo, dbID, 
  2848.                         mode | dmModeReadWrite | dmModeShowSecret);
  2849.         }
  2850.         else if ((creator == AddressAppID) &&
  2851.                  (StrCaselessCompare(dbname, AddressDBName) == 0)) {
  2852.             gAdcdate = cdate;
  2853.             gAdmdate = mdate;
  2854.             AddressDB = DmOpenDatabase(cardNo, dbID, mode | dmModeReadOnly);
  2855.             /*
  2856.              * We want to get the sort order for the address book.  This
  2857.              * controls how to display the birthday list.
  2858.              */
  2859.             if (appInfoID) {
  2860.                 if ((appInfoPtr = (AddrAppInfoPtr)
  2861.                      MemLocalIDToLockedPtr(appInfoID, cardNo))) {
  2862.                     gSortByCompany = appInfoPtr->misc.sortByCompany;
  2863.                     MemPtrUnlock(appInfoPtr);
  2864.                 }
  2865.             }
  2866.         } else if ((creator == MainAppID) &&
  2867.                    (StrCaselessCompare(dbname, MainDBName) == 0)) {
  2868.             MainDB = DmOpenDatabase(cardNo, dbID, dmModeReadWrite);
  2869.         } else if ((creator == MainAppID) &&
  2870.                    (StrCaselessCompare(dbname, PrefsDBName) == 0)) {
  2871.             PrefsDB = DmOpenDatabase(cardNo, dbID, dmModeReadWrite);
  2872.         }
  2873.         newSearch = false;
  2874.     }
  2875.  
  2876.     if (!MainDB){
  2877.         //
  2878.         // Create our database if it doesn't exist yet
  2879.         //
  2880.         if (DmCreateDatabase(0, MainDBName, MainAppID, MainDBType, false))
  2881.             return EDBCREATE;
  2882.         MainDB = DmOpenDatabaseByTypeCreator(MainDBType, MainAppID,
  2883.                                              dmModeReadWrite);
  2884.         if (!MainDB) return EDBCREATE;
  2885.         /* Set the backup bit.  This is to aid syncs with non Palm software. */
  2886.         DmOpenDatabaseInfo(MainDB, &dbID, NULL, NULL, &cardNo, NULL);
  2887.         /*    turn-off
  2888.  
  2889.             DmDatabaseInfo(cardNo, dbID, NULL, &attributes, NULL, NULL,
  2890.             NULL, NULL, NULL, NULL, NULL, NULL, NULL);
  2891.             attributes |= dmHdrAttrBackup;
  2892.             DmSetDatabaseInfo(cardNo, dbID, NULL, &attributes, NULL, NULL,
  2893.             NULL, NULL, NULL, NULL, NULL, NULL, NULL);
  2894.         */
  2895.     }
  2896.     if (!PrefsDB) {
  2897.         //
  2898.         // Create our database if it doesn't exist yet
  2899.         //
  2900.         if (DmCreateDatabase(0, PrefsDBName, MainAppID, PrefsDBType, false))
  2901.             return EDBCREATE;
  2902.         PrefsDB = DmOpenDatabase(0, DmFindDatabase(0, PrefsDBName),
  2903.                                  dmModeReadWrite);
  2904.         if (!PrefsDB) return EDBCREATE;
  2905.         /* Set the backup bit.  This is to aid syncs with non Palm software. */
  2906.         DmOpenDatabaseInfo(PrefsDB, &dbID, NULL, NULL, &cardNo, NULL);
  2907.         DmDatabaseInfo(cardNo, dbID, NULL, &attributes, NULL, NULL,
  2908.                        NULL, NULL, NULL, NULL, NULL, NULL, NULL);
  2909.         attributes |= dmHdrAttrBackup;
  2910.         DmSetDatabaseInfo(cardNo, dbID, NULL, &attributes, NULL, NULL,
  2911.                           NULL, NULL, NULL, NULL, NULL, NULL, NULL);
  2912.     }
  2913.     
  2914.     PrefsRecIndex = 0;
  2915.     if ((PrefsRecHandle = DmQueryRecord(PrefsDB, PrefsRecIndex)) != 0) {
  2916.         // if prefs record already exist, so we will do a sanity check
  2917.         //
  2918.         if (MemHandleSize(PrefsRecHandle) >= sizeof(gPrefsR->version)) {
  2919.             p = MemHandleLock(PrefsRecHandle);
  2920.             if (*((char*)p) != (char) PREFSVERSION) {
  2921.                 MemPtrUnlock(p);
  2922.                 err = DmRemoveRecord(PrefsDB,PrefsRecIndex);
  2923.                 ErrFatalDisplayIf(err, SysErrString(err, gAppErrStr, AppErrStrLen));
  2924.                 PrefsRecHandle = 0;
  2925.             }
  2926.             else MemPtrUnlock(p);
  2927.         }
  2928.         else {
  2929.             err = DmRemoveRecord(PrefsDB, PrefsRecIndex);
  2930.             ErrFatalDisplayIf(err, SysErrString(err, gAppErrStr, AppErrStrLen));
  2931.             PrefsRecHandle = 0;
  2932.         }
  2933.     }
  2934.     if (PrefsRecHandle == 0) {
  2935.         /*
  2936.          * Create the preferences record if it doesn't exist and
  2937.          * preload it with default values.
  2938.          */
  2939.         PrefsRecHandle = DmNewRecord(PrefsDB, &PrefsRecIndex,
  2940.                                      sizeof(struct sPrefsR));
  2941.         ErrFatalDisplayIf(!PrefsRecHandle,
  2942.                           "Could not create prefs record.");
  2943.         p = MemHandleLock(PrefsRecHandle);
  2944.         p2 = (MemPtr) &DefaultPrefsR;
  2945.         err = DmWrite(p, 0, p2, sizeof(struct sPrefsR));
  2946.         ErrFatalDisplayIf(err, "Could not write default prefs.");
  2947.         MemPtrUnlock(p);
  2948.     }
  2949.     /*
  2950.      * Read in the preferences from the private database.  This also loads
  2951.      * some global variables with values for convenience.
  2952.      */
  2953.     ReadPrefsRec();
  2954.  
  2955.     // if hideSecret setting is changed
  2956.     if (gPrefsR->gHideSecretRecord != sysPrefs.hideSecretRecords) {
  2957.         // reset the address book timber
  2958.         MemSet(gPrefsR->adrcdate, sizeof(gPrefsR->adrcdate), 0);
  2959.         MemSet(gPrefsR->adrmdate, sizeof(gPrefsR->adrmdate), 0);
  2960.         
  2961.         gPrefsR->gHideSecretRecord = sysPrefs.hideSecretRecords;
  2962.         WritePrefsRec();
  2963.     }
  2964.  
  2965.     /*
  2966.      * Check if the other databases opened correctly.  Abort if not.
  2967.      */
  2968.     if (!DatebookDB || !ToDoDB || !AddressDB || !MemoDB) return EDBCREATE;
  2969.  
  2970.     return 0;
  2971. }
  2972.  
  2973. void CloseDatabases(void)
  2974. {
  2975.     if (DatebookDB) DmCloseDatabase(DatebookDB);
  2976.     if (AddressDB) DmCloseDatabase(AddressDB);
  2977.     if (ToDoDB) DmCloseDatabase(ToDoDB);
  2978.     if (MemoDB) DmCloseDatabase(MemoDB);
  2979.     if (MainDB) DmCloseDatabase(MainDB);
  2980.  
  2981.     if (PrefsDB) {
  2982.         if (PrefsRecHandle) {
  2983.             if (gPrefsRdirty) WritePrefsRec();
  2984.             DmReleaseRecord(PrefsDB, PrefsRecIndex, gPrefsWeredirty);
  2985.             /*
  2986.              * Unlock the PrefsR structure
  2987.              */
  2988.             MemPtrUnlock(gPrefsR);
  2989.         }
  2990.         DmCloseDatabase(PrefsDB);
  2991.     }
  2992. }
  2993.  
  2994.     
  2995. /* Get preferences, open (or create) app database */
  2996. static UInt16 StartApplication(void)
  2997. {
  2998.     Int8 err;
  2999. //    UInt32 romVersion;
  3000.  
  3001.     PrefsRHandle = 0;
  3002.     gTableRowHandle = 0;
  3003.     
  3004.     if ((PrefsRHandle = MemHandleNew(sizeof (struct sPrefsR))) == 0) {
  3005.         freememories();
  3006.         return 1;
  3007.     }
  3008.     gPrefsR = MemHandleLock(PrefsRHandle);
  3009.  
  3010. //    romVersion = 0;
  3011. //    err = FtrGet(sysFtrCreator, sysFtrNumROMVersion, &romVersion);
  3012. //    if (romVersion >= 0x03003000) {
  3013. //        gIsOS3_0 = true;
  3014. //    }
  3015. //    else gIsOS3_0 = false;
  3016.     
  3017.     if ((err = OpenDatabases()) < 0) {
  3018.         if (!DatebookDB) {
  3019.             /*
  3020.              * BTW make sure there is " " (single space) in unused ^1 ^2 ^3
  3021.              * entries or PalmOS <= 2 will blow up.
  3022.              */
  3023.  
  3024.             SysCopyStringResource(gAppErrStr, DateBookFirstAlertString);
  3025.             FrmCustomAlert(ErrorAlert, gAppErrStr, " ", " ");
  3026.         }
  3027.  
  3028.         freememories();
  3029.         return 1;
  3030.     }
  3031.     // set current date to  the starting date
  3032.     DateSecondsToDate(TimGetSeconds(), &gStartDate);
  3033.  
  3034.     FrmGotoForm(StartForm);     // read the address db and so
  3035.     
  3036.     return 0;
  3037. }
  3038.  
  3039. /* Save preferences, close forms, close app database */
  3040. static void StopApplication(void)
  3041. {
  3042.     FrmSaveAllForms();
  3043.     FrmCloseAllForms();
  3044.     CloseDatabases();
  3045.     freememories();
  3046. }
  3047.  
  3048. static Boolean SpecialKeyDown(EventPtr e) 
  3049. {
  3050.     Int16 chr;
  3051.     DateTimeType dt;
  3052.     static Int16 push_chr = '0';
  3053.     Int8 month;
  3054.  
  3055.     if (e->eType != keyDownEvent) return false;
  3056.  
  3057.     // softkey only work on main screen
  3058.     //
  3059.     if (FrmGetActiveFormID() != MainForm) return false;
  3060.  
  3061.     if (e->data.keyDown.modifiers & autoRepeatKeyMask) return false;
  3062.     if (e->data.keyDown.modifiers & poweredOnKeyMask) return false;
  3063.  
  3064.     chr = e->data.keyDown.chr;
  3065.     if (e->data.keyDown.modifiers & commandKeyMask) {
  3066.         if (keyboardAlphaChr == chr) {
  3067.             gPrefsR->BirthPrefs.sort = 0;     // sort by name
  3068.             SndPlaySystemSound(sndClick);
  3069.  
  3070.             gMainTableStart = 0;
  3071.             FrmUpdateForm(MainForm, frmRedrawUpdateCode);
  3072.             WritePrefsRec();
  3073.             return true;
  3074.         }
  3075.         else if (keyboardNumericChr == chr) {
  3076.             gPrefsR->BirthPrefs.sort = 1;     // sort by date
  3077.             SndPlaySystemSound(sndClick);
  3078.  
  3079.             gMainTableStart = 0;
  3080.             FrmUpdateForm(MainForm, frmRedrawUpdateCode);
  3081.             WritePrefsRec();
  3082.             return true;
  3083.         }
  3084.     }
  3085.     else if (chr >= '0' && chr <= '9') {
  3086.         // numeric character
  3087.         //
  3088.         if ( (push_chr == '1') && (chr >= '0' && chr <= '2')) {
  3089.             month = 10 + (chr - '0');
  3090.         }
  3091.         else {
  3092.             month = chr - '0';
  3093.         }
  3094.         if (month == 0) {
  3095.             push_chr = chr;
  3096.             return true;
  3097.         }
  3098.  
  3099.         TimSecondsToDateTime(TimGetSeconds(), &dt);
  3100.  
  3101.         if ( month < dt.month ) {
  3102.             dt.year++;
  3103.         }
  3104.         dt.month = month;
  3105.         dt.day = 1;
  3106.  
  3107.         // save the original
  3108.         push_chr = chr;
  3109.         
  3110.         HighlightMatchRowDate(dt);
  3111.         return true;
  3112.     }
  3113.     else if ((chr >= 'A' && chr <= 'Z') || (chr >= 'a' && chr <= 'z')) {
  3114.         // is alpha?
  3115.         //
  3116.  
  3117.         if (chr >= 'a') chr -=  'a' - 'A';      // make capital
  3118.         HighlightMatchRowName(chr);
  3119.  
  3120.         return true;
  3121.     }
  3122.     // else if ( chr == 0xA4 || (chr >= 0xA1 && chr <= 0xFE) ) {
  3123.     // is korean letter?
  3124.     //
  3125.     //}
  3126.     
  3127.     return false;
  3128. }
  3129.  
  3130. /* The main event loop */
  3131. static void EventLoop(void)
  3132. {
  3133.     UInt16 err;
  3134.     EventType e;
  3135.  
  3136.     do {
  3137.         EvtGetEvent(&e, evtWaitForever);
  3138.  
  3139.         if (SpecialKeyDown(&e)) continue;
  3140.         
  3141.         if (SysHandleEvent(&e)) continue;
  3142.         if (MenuHandleEvent (NULL, &e, &err)) continue;
  3143.         
  3144.         if (! ApplicationHandleEvent (&e))
  3145.             FrmDispatchEvent (&e);
  3146.     } while (e.eType != appStopEvent && !gProgramExit);
  3147. }
  3148.  
  3149. /* Main entry point; it is unlikely you will need to change this except to
  3150.    handle other launch command codes */
  3151. UInt32 PilotMain(UInt16 cmd, MemPtr cmdPBP, UInt16 launchFlags)
  3152. {
  3153.     UInt16 err;
  3154.  
  3155.     if (cmd == sysAppLaunchCmdNormalLaunch) {
  3156.         err = StartApplication();
  3157.         if (err) return err;
  3158.  
  3159.         EventLoop();
  3160.         StopApplication();
  3161.  
  3162.     } else {
  3163.         return sysErrParamErr;
  3164.     }
  3165.  
  3166.     return 0;
  3167. }
  3168.