home *** CD-ROM | disk | FTP | other *** search
/ linuxmafia.com 2016 / linuxmafia.com.tar / linuxmafia.com / pub / palmos / timber-0.0beta.tar.gz / timber-0.0beta.tar / Timber / Timber.c < prev    next >
C/C++ Source or Header  |  2000-01-14  |  73KB  |  2,789 lines

  1. /*
  2.   TIMBER - A logging program for numerical data with graphical views
  3.  
  4.   FILE:    Timber.c
  5.   VERSION: 0.0 BETA
  6.   DATE:    2000/01/14
  7.   AUTHOR:  Jens R. Woinowski (jrw)
  8.   
  9.   Timber is open source software under GNU GPL. Read LICENSE.txt. 
  10. */
  11.  
  12. #ifdef __GNUC__
  13. #include "Callbacks.h"
  14. #endif        
  15.  
  16. #define ERROR_CHECK_LEVEL 2
  17.  
  18. #include <Pilot.h>
  19. #include <DataMgr.h>
  20. #include <CharAttr.h>
  21. #include <SysEvtMgr.h>
  22. #include <Preferences.h>
  23. #include <DateTime.h>
  24.  
  25. #include "TimberRsc.h"
  26.  
  27. // database types and variables:
  28. // we use fixed comma with 4 digits fractional
  29. #define MaxPrecisionMult 10000
  30. #define MaxPrecision 4
  31. #define UseMaxAndCut -1
  32.  
  33. // smoothing of year graph, how many past values are used:
  34. #define SmoothNumber 5
  35.  
  36. // string length of weights
  37. #define WeightSize 32
  38.  
  39. typedef ULong WeightType;
  40. typedef WeightType *WeightPtr;
  41. typedef struct {
  42.   DateType date;
  43.   WeightType weight; 
  44. } RecordType;
  45. typedef RecordType *RecordPtr;
  46. static DmOpenRef gTimberDB;
  47.  
  48. // application start:
  49. static Err StartApplication(void);
  50. static void InitTable(void);
  51.  
  52. // database routines:
  53. static Err OpenOrCreateDB(DmOpenRef *dbP, ULong type, ULong creator, 
  54.                           ULong mode, UInt cardNo, char *name);
  55. static Int DateCompareInDB(RecordPtr p1, RecordPtr p2, Int i,
  56.                           SortRecordInfoPtr s1, SortRecordInfoPtr s2,
  57.                           VoidHand appInfoH);
  58. static Int DateCompare(DateType *p1, DateType *p2);
  59. static Long FindRecord(DateType date, Boolean *isNewPtr);
  60. static Err AddRecord(DateType date, WeightType weight, 
  61.                      Boolean ask, Long *newIndex);
  62. static Err DeleteRecord(DateType date, Boolean ask, Boolean *deleted);
  63. static Err ChangeRecord(DateType oldDate, DateType newDate,
  64.                         WeightType oldWeight, WeightType newWeight, 
  65.                         Boolean ask,Long *newIndex);
  66. static void MinMaxFind(void);
  67. static void MinMaxBetween(DateType start, DateType end,
  68.                           WeightType *minWeight, WeightType *maxWeight,
  69.                           Boolean *emptyRange);
  70.  
  71. // weight to ascii and back:
  72. static void WeightToAscii(CharPtr to, WeightType weight, Int precision);
  73. static void AsciiToWeight(CharPtr from, WeightPtr weight);
  74.  
  75. // list drawing:
  76. static void TimberListTableDrawItem(VoidPtr table, Word row, Word column,
  77.                                     RectanglePtr bounds);
  78. static void UpdateListView(Boolean firstVisibleUpdate, Boolean triggerUpdate);
  79. static void ScrollView(Int lines);
  80.  
  81. // graph drawing:
  82. static void TimberGraphDraw(void);
  83. static void CalcDrawYRanges(void);
  84. static Int ScreenY(WeightType y);
  85. static void DrawYScale(void);
  86. static void CalcDrawXRanges(void);
  87. static void CalcGoalCoordinates(void);
  88. static Int ScreenX(Int day, Int month);
  89. static void DrawXScale(void);
  90. static void DrawMonthScale(void);
  91. static void DrawQuarterOrYearScale(Int firstMonth);
  92. static void DrawTheGraph(void);
  93. static void DrawMonthGraph(void);
  94. static void DrawQuarterOrYearGraph(void);
  95. static void DrawSmoothYearGraph(void);
  96.  
  97. // Timber form handling:
  98. static void TimberSetActiveView(Word view);
  99. static void TimberDrawViewDate(FormPtr frm);
  100. static void TimberChangeDate(FormPtr frm, Int byMonths);
  101. static Boolean CommonKeyHandler(Word chr);
  102. static Boolean TimberHandleMenuEvent(Word itemID);
  103.  
  104. // Timber custom dialogs:
  105. static void ViewDateDialog(FormPtr frm);
  106. static void DoNewWeightDialog(Long *newIndex);
  107. static void NewWeightDialogDrawDate(void);
  108. static void DoNewDateDialog(FormPtr frm);
  109. static void DoEditWeightDialog(UInt index, Long *newIndex);
  110. static void EditWeightDialogDrawDate(void);
  111. static void DoPrefDialog(void);
  112. static void DoGoalDialog();
  113. static void GoalDialogDrawDates(void);
  114. static void DoDeleteDialog(Boolean *updateView);
  115. static void DeleteDialogDrawDate(void);
  116.  
  117. // helpful system shortcuts:
  118. static Word GetObjectIdxFromActive(Word objectID);
  119. static Word GetObjectIdxFromAny(Word FormID, Word objectID);
  120. static VoidPtr GetObjectPtrFromActive(Word objectID);
  121. static VoidPtr GetObjectPtrFromAny(Word formID, Word objectID);
  122.  
  123. // debugging:
  124. static void DoDebugAlertInt(CharPtr s1,Long i);
  125. static void DoDebugAlertInt2(CharPtr s1, Long i, Long j);
  126. static void DoDebugAlert1(CharPtr s1);
  127. static void DoDebugAlert2(CharPtr s1, CharPtr s2);
  128. static void DoDebugAlert3(CharPtr s1, CharPtr s2, CharPtr s3);
  129. static VoidHand TimberGetRecord(DmOpenRef dbR,UInt index);
  130. static VoidHand TimberQueryRecord(DmOpenRef dbR,UInt index);
  131. static VoidPtr TimberHandleLock(VoidHand h);
  132.  
  133. static Char gBuf1[128];
  134. static Char gBuf2[128];
  135. static Char gBuf3[128];
  136.  
  137. #define TimberListView 0
  138. #define TimberMonthView 1
  139. #define TimberQuarterView 2
  140. #define TimberYearView 3
  141. #define TimberSmoothView 4
  142. #define datePrevMonth -1
  143. #define dateNextMonth 1
  144.  
  145. #define kTimberCreator 'Tmbr'
  146. #define kTimberPrefID 0x00
  147. #define kTimberVersion 0x00
  148. #define kTimberDBType 'Cust'
  149. #define kTimberDBName "Timber-Tmbr"
  150.  
  151. static Boolean gTimberStart;
  152. static Word gTimberView;
  153. static Word gTimberGroup;
  154.  
  155. // preferences:
  156. static DateFormatType gSysPrefDateFormat;
  157. typedef struct {
  158.   Word ActiveView;
  159.   DateFormatType DateFormat;
  160.   DateFormatType LongDateFormat;
  161.   Byte ListPrecision;
  162.   Boolean RoundWeights;
  163.   Short DrawPrecision;
  164.   NumberFormatType NumberFormat;
  165.   Char ThousandSeparator;
  166.   Char DecimalSeparator;
  167.   Boolean ReplaceAsk;
  168.   Boolean DeleteAsk;
  169.   Int MonthScroll;
  170.   Boolean GlobalMinMax;
  171.   Boolean ShowGoal;
  172.   Boolean ShowAverage;
  173.   RecordType GoalStart;
  174.   RecordType GoalEnd;
  175. } PrefType;
  176. typedef PrefType *PrefPtr;
  177. static PrefPtr gPrefPtr;
  178. static PrefType gPrefDefault={TimberListView,0,0,1,true,
  179.                               1,0,0,0,true,true,false,false,false,false,
  180.                               {{0,0,0},0},{{0,0,0},0}};
  181. static PrefType gSavedPrefs;
  182.  
  183. // viewed or chosen date: 
  184. static DateType gViewDate;
  185. static DateType gChooseDate;
  186.  
  187. // drawing ranges:
  188. static Long gDrawXRange;
  189. static Long gDrawXMin;
  190. static Long gDrawXMax;
  191. static Int gScreenXMax=158;
  192. static Int gScreenXRange;
  193. static Int gScreenXOffset;
  194. static Int gDrawMonthOffset;
  195. static Int gDrawMonthDays[12];
  196. static Boolean gDrawBigRange;
  197. static WeightType gDrawYRange;
  198. static WeightType gDrawYMin;
  199. static WeightType gDrawYMax;
  200. static Long gScreenYRange=115;
  201. static Long gScreenYOffset=18;
  202. static WeightType gDrawPrecision;
  203. static WeightType gDrawPrecisionMult;
  204. static WeightType gDrawPrecisionRound;
  205. static DateType gDrawStartDate;
  206. static DateType gDrawEndDate;
  207. static Int gGoalStartX;
  208. static Int gGoalStartY;
  209. static Int gGoalEndX;
  210. static Int gGoalEndY;
  211. static Boolean gGoalOutOfBounds;
  212.  
  213. CustomPatternType AvgPattern={0xf0f0,0xf0f0,0xf0f0,0xf0f0};
  214. CustomPatternType GoalPattern={0xeedd,0xbb77,0xeedd,0xbb77};
  215.  
  216. // visible records:
  217. static Long gFirstVisibleRecord;
  218. TablePtr gTimberListTablePtr;
  219. static int gScrollPerButton;
  220. static int gTableRows;
  221. static WeightType gMaxWeight=0; 
  222. static WeightType gMinWeight=-1L; // cheating
  223. static Boolean gCheckWeight=true;
  224.  
  225. //
  226. // application start:
  227. //
  228.  
  229. static Err StartApplication(void)
  230. {
  231.   ULong seconds;
  232.   Err result;
  233.   Word prefSize;
  234.   Word appResult;
  235.  
  236.   // start flag to check if in application startup process
  237.   gTimberStart=true;
  238.  
  239.   // set view date to actual system date
  240.   seconds=TimGetSeconds();
  241.   DateSecondsToDate(seconds,&gViewDate);
  242.  
  243.   // get preference(s)
  244.   prefSize=sizeof(PrefType);
  245.   appResult=PrefGetAppPreferences(kTimberCreator,kTimberPrefID,
  246.                                   &gSavedPrefs,&prefSize,true);
  247.   if(appResult!=noPreferenceFound) {// &&(appResult>=prefSize)){
  248.     gPrefPtr=&gSavedPrefs;
  249.   }
  250.   else {
  251.     gPrefPtr=&gPrefDefault;
  252.   }
  253.  
  254.   gPrefPtr->DateFormat=PrefGetPreference(prefDateFormat);
  255.   gPrefPtr->LongDateFormat=PrefGetPreference(prefLongDateFormat);
  256.   gSysPrefDateFormat=PrefGetPreference(prefDateFormat);
  257.   gPrefPtr->NumberFormat=PrefGetPreference(prefNumberFormat);
  258.   LocGetNumberSeparators(gPrefPtr->NumberFormat,
  259.                          &gPrefPtr->ThousandSeparator,&gPrefPtr->DecimalSeparator);
  260.  
  261.   // set active view
  262.   TimberSetActiveView(gPrefPtr->ActiveView);
  263.  
  264.   // open database
  265.   if(result=OpenOrCreateDB(&gTimberDB, kTimberDBType, kTimberCreator, 
  266.                            dmModeReadWrite, 0, kTimberDBName)){
  267.     FrmAlert(NoTimberDBAlert);
  268.     return result;
  269.   }  
  270.  
  271.   // debugging:
  272.   /*
  273.   {
  274.     RecordType record;
  275.     
  276.     record.date.year=gViewDate.year;
  277.     for(record.date.month=1;record.date.month<13;record.date.month+=3)
  278.       for(record.date.day=1;record.date.day<29;record.date.day+=3)
  279.         AddRecord(record.date,
  280.                   ((UInt)SysRandom(0)+record.date.day+record.date.month)
  281.                   *MaxPrecisionMult/100,false,NULL);
  282.  
  283.   }
  284.   */
  285.  
  286.   // startup completed
  287.   gTimberStart=false;
  288.  
  289.   return 0;
  290. }
  291.  
  292. static void InitTable(void)
  293. {
  294.   TablePtr table;
  295.   Int rows,i;
  296.   Int columns,j;
  297.  
  298.   table=GetObjectPtrFromActive(TimberListTable);
  299.   gTimberListTablePtr=table;
  300.   rows=TblGetNumberOfRows(table);
  301.   columns=TimberListCols;
  302.   gTableRows=rows;
  303.   gScrollPerButton=rows;
  304.  
  305.   for(i=0;i<rows;i++){
  306.     TblSetItemStyle(table,i,0,customTableItem);
  307.     TblSetRowUsable(table,i,false);
  308.   }
  309.   for(j=0;j<columns;j++)
  310.     TblSetColumnUsable(table,j,true);
  311.   TblSetCustomDrawProcedure(table,0,TimberListTableDrawItem);
  312. }
  313.  
  314. static void StopApplication(void)
  315. {
  316.   // save preferences
  317.   gPrefPtr->ActiveView=gTimberView;
  318.   PrefSetAppPreferences(kTimberCreator,kTimberPrefID,kTimberVersion,
  319.                         gPrefPtr,sizeof(PrefType),true);
  320.  
  321.   // closing the database
  322.   // DmResetRecordStates(gTimberDB); // should be no performance problem
  323.   DmCloseDatabase(gTimberDB);
  324. }
  325.  
  326.  
  327. //
  328. // database routines:
  329. //
  330.  
  331. static Err OpenOrCreateDB(DmOpenRef *dbP, ULong type, ULong creator, 
  332.                           ULong mode, UInt cardNo, char *name)
  333. {
  334.   Err err;
  335.     
  336.   *dbP = DmOpenDatabaseByTypeCreator(type, creator, mode);
  337.   err = DmGetLastErr();
  338.   if (! *dbP)
  339.     {
  340.       err = DmCreateDatabase(0, name, creator, type, false);
  341.       if (err) 
  342.         return err;
  343.       
  344.       *dbP = DmOpenDatabaseByTypeCreator(type, creator, mode);
  345.       if (! *dbP) 
  346.         return DmGetLastErr();
  347.     }
  348.   return err;
  349. }
  350.  
  351. static Int DateCompareInDB(RecordPtr p1, RecordPtr p2, Int i,
  352.                           SortRecordInfoPtr s1, SortRecordInfoPtr s2,
  353.                           VoidHand appInfoH)
  354. {
  355.   Int result;
  356.  
  357.   result=(Int)(p1->date.year)-(Int)(p2->date.year);
  358.   if(result==0){
  359.     result=(Int)(p1->date.month)-(Int)(p2->date.month);
  360.     if(result==0)
  361.       result=(Int)(p1->date.day)-(Int)(p2->date.day);
  362.   }
  363.   return result;
  364. }
  365.  
  366. static Int DateCompare(DateType *p1, DateType *p2)
  367. {
  368.   Int result;
  369.  
  370.   result=(Int)(p1->year)-(Int)(p2->year);
  371.   if(result==0){
  372.     result=(Int)(p1->month)-(Int)(p2->month);
  373.     if(result==0)
  374.       result=(Int)(p1->day)-(Int)(p2->day);
  375.   }
  376.   return result;
  377. }
  378.  
  379. static Long FindRecord(DateType date, Boolean *isNewPtr)
  380. {
  381.   RecordType record;
  382.   Long index;
  383.   Boolean isNew=true;
  384.   FormPtr frm=FrmGetActiveForm();
  385.   Word frmID=FrmGetActiveFormID();
  386.  
  387.   // show busy message
  388.   switch(frmID){
  389.   case TimberListForm:
  390.     FrmShowObject(frm,GetObjectIdxFromActive(TimberListBusyLabel));
  391.     break;
  392.   case TimberGraphForm:
  393.     FrmShowObject(frm,GetObjectIdxFromActive(TimberGraphBusyLabel));
  394.     break;
  395.   }
  396.  
  397.   // find the record position
  398.   record.date.month=date.month;
  399.   record.date.day=date.day;
  400.   record.date.year=date.year;
  401.   index=(Long)DmFindSortPosition(gTimberDB,
  402.                                  &record,0,(DmComparF *)DateCompareInDB,0);
  403.   
  404.   // check if the date is new
  405.   if ((index>0) && (index<=DmNumRecords(gTimberDB))){
  406.     VoidHand foundRecordH;
  407.     RecordPtr foundRecordP;
  408.  
  409.     foundRecordH=TimberQueryRecord(gTimberDB,index-1);
  410.     foundRecordP=TimberHandleLock(foundRecordH);
  411.     isNew=DateCompare(&date,&(foundRecordP->date))!=0;
  412.     MemHandleUnlock(foundRecordH);
  413.   }
  414.   
  415.   if(isNewPtr)
  416.     *isNewPtr=isNew;
  417.  
  418.   // hide busy message
  419.   switch(frmID){
  420.   case TimberListForm:
  421.     FrmHideObject(frm,GetObjectIdxFromActive(TimberListBusyLabel));
  422.     break;
  423.   case TimberGraphForm:
  424.     FrmHideObject(frm,GetObjectIdxFromActive(TimberGraphBusyLabel));
  425.     break;
  426.   }
  427.  
  428.   if(isNew)
  429.     return index;
  430.   else
  431.     return index-1;
  432. }
  433.  
  434. static Err AddRecord(DateType date, WeightType weight, 
  435.                      Boolean ask, Long *newIndex)
  436. {
  437.   RecordType newRecord;
  438.   UInt index;
  439.   Boolean isNew;
  440.   VoidHand newRecordH;
  441.   RecordPtr newRecordP;
  442.   Char dateStr[longDateStrLength+9];
  443.   Boolean checkWeight;
  444.  
  445.   if(newIndex)
  446.     *newIndex=-1;
  447.  
  448.   index=FindRecord(date,&isNew);  
  449.   newRecord.date.month=date.month;
  450.   newRecord.date.day=date.day;
  451.   newRecord.date.year=date.year;
  452.   newRecord.weight=weight;
  453.  
  454.   if (isNew) {
  455.     newRecordH=DmNewRecord(gTimberDB,&index,sizeof(RecordType));
  456.     if(newRecordH==NULL) {
  457.       FrmAlert(MemoryFullAlert);
  458.       return DmGetLastErr();
  459.     }
  460.     newRecordP=TimberHandleLock(newRecordH);
  461.     DmWrite(newRecordP,0,&newRecord,sizeof(RecordType));
  462.     MemHandleUnlock(newRecordH);
  463.     DmReleaseRecord(gTimberDB,index,true);
  464.     if(weight<gMinWeight)
  465.       gMinWeight=weight;
  466.     if(weight>gMaxWeight)
  467.       gMaxWeight=weight;
  468.   } else {
  469.     if (ask) {
  470.       DateToDOWDMFormat(date.month,
  471.                         date.day,
  472.                         date.year+firstYear,
  473.                         gPrefPtr->DateFormat,
  474.                         dateStr);
  475.       if(FrmCustomAlert(ReplaceAlert,dateStr," "," ")!=StandardAlertOK)
  476.         return 0;
  477.     }
  478.     newRecordH=TimberGetRecord(gTimberDB,index);
  479.     newRecordP=TimberHandleLock(newRecordH);
  480.     checkWeight=(newRecordP->weight==gMinWeight)||
  481.       (newRecordP->weight==gMaxWeight);
  482.     DmWrite(newRecordP,0,&newRecord,sizeof(RecordType));
  483.     MemHandleUnlock(newRecordH);
  484.     DmReleaseRecord(gTimberDB,index,true);
  485.     if(checkWeight)
  486.       MinMaxFind();
  487.     else if(weight<gMinWeight)
  488.       gMinWeight=weight;
  489.     if(weight>gMaxWeight)
  490.       gMaxWeight=weight;
  491.   }
  492.   if(newIndex)
  493.     *newIndex=index;
  494.   return 0;
  495. }
  496.  
  497. static Err DeleteRecord(DateType date, Boolean ask, Boolean *deleted)
  498. {
  499.   UInt index;
  500.   Boolean isNew;
  501.   Char dateStr[longDateStrLength+9];
  502.  
  503.   // the flag tells if after the procedure a record with that date exists
  504.   if(deleted)
  505.     *deleted=true; 
  506.  
  507.   // we try only to delete existing records 
  508.   index=FindRecord(date,&isNew);
  509.   if (isNew)
  510.     return 0;
  511.  
  512.   if(deleted)
  513.     *deleted=false;
  514.  
  515.   // confirmation dialog
  516.   if (ask) {
  517.     DateToDOWDMFormat(date.month,
  518.                       date.day,
  519.                       date.year+firstYear,
  520.                       gPrefPtr->DateFormat,
  521.                       dateStr);
  522.     if(FrmCustomAlert(DeleteAlert,dateStr," "," ")!=StandardAlertOK)
  523.       return 0;
  524.   }
  525.  
  526.   // now, do it
  527.   DmRemoveRecord(gTimberDB,index);
  528.   MinMaxFind();
  529.   if(deleted)
  530.     *deleted=true; 
  531.   return 0;
  532. }
  533.  
  534. static Err ChangeRecord(DateType oldDate, DateType newDate,
  535.                         WeightType oldWeight, WeightType newWeight, 
  536.                         Boolean ask, Long *newIndex)
  537. {
  538.   RecordType newRecord;
  539.   Char oldDateStr[longDateStrLength+9];
  540.   Char newDateStr[longDateStrLength+9];
  541.   
  542.   if(newIndex)
  543.     *newIndex=-1;
  544.  
  545.   // no date change is like regular add
  546.   if (DateCompare(&oldDate,&newDate)==0)    
  547.     return AddRecord(oldDate,newWeight,ask,newIndex);
  548.  
  549.   // date change needs complcated dialog cases
  550.   if (ask) {
  551.     DateToDOWDMFormat(oldDate.month,
  552.                       oldDate.day,
  553.                       oldDate.year+firstYear,
  554.                       gPrefPtr->DateFormat,
  555.                       oldDateStr);
  556.     DateToDOWDMFormat(newDate.month,
  557.                       newDate.day,
  558.                       newDate.year+firstYear,
  559.                       gPrefPtr->DateFormat,
  560.                       newDateStr);
  561.     if(oldWeight==newWeight){
  562.       // simple move confirmation
  563.       if(FrmCustomAlert(MoveAlert,oldDateStr,newDateStr," ")
  564.          !=StandardAlertOK)
  565.         return 0;
  566.     } else {
  567.       // change value and move confirmation     
  568.       if(FrmCustomAlert(ChangeAlert,oldDateStr,newDateStr," ")
  569.          !=StandardAlertOK)
  570.         return 0;
  571.     }
  572.   }
  573.   
  574.   // and now, just do it:
  575.   DeleteRecord(oldDate,false,NULL);  
  576.   return AddRecord(newDate,newWeight,ask,newIndex);
  577. }
  578.  
  579.  
  580. static void MinMaxFind(void)
  581. {
  582.   UInt index,numRecords;
  583.   VoidHand recordH;
  584.   RecordPtr recordP;
  585.   WeightType weight;
  586.   FormPtr frm=FrmGetActiveForm();
  587.   Word frmID=FrmGetActiveFormID();
  588.  
  589.   // show busy message
  590.   switch(frmID){
  591.   case TimberListForm:
  592.     FrmShowObject(frm,GetObjectIdxFromActive(TimberListBusyLabel));
  593.     break;
  594.   case TimberGraphForm:
  595.     FrmShowObject(frm,GetObjectIdxFromActive(TimberGraphBusyLabel));
  596.     break;
  597.   }
  598.  
  599.   gMaxWeight=0; 
  600.   gMinWeight=-1L; // cheating
  601.  
  602.   numRecords=DmNumRecords(gTimberDB);
  603.   for(index=0;index<numRecords;index++){
  604.     recordH=TimberQueryRecord(gTimberDB,index);
  605.     recordP=TimberHandleLock(recordH);
  606.     weight=recordP->weight;
  607.     MemHandleUnlock(recordH);
  608.     if(weight<gMinWeight)
  609.       gMinWeight=weight;
  610.     if(weight>gMaxWeight)
  611.       gMaxWeight=weight;
  612.   }
  613.  
  614.   // hide busy message
  615.   switch(frmID){
  616.   case TimberListForm:
  617.     FrmHideObject(frm,GetObjectIdxFromActive(TimberListBusyLabel));
  618.     break;
  619.   case TimberGraphForm:
  620.     FrmHideObject(frm,GetObjectIdxFromActive(TimberGraphBusyLabel));
  621.     break;
  622.   }
  623. }
  624.  
  625. static void MinMaxBetween(DateType start, DateType end, 
  626.                           WeightType *minWeight, WeightType *maxWeight,
  627.                           Boolean *emptyRange)
  628. {
  629.   Long index,first,last;
  630.   VoidHand recordH;
  631.   RecordPtr recordP;
  632.   WeightType weight;
  633.   FormPtr frm=FrmGetActiveForm();
  634.   Word frmID=FrmGetActiveFormID();
  635.   Boolean isNew;
  636.  
  637.   // find the record range
  638.   *emptyRange=false;
  639.   first=FindRecord(start,&isNew);
  640.   if(first<0)
  641.     first=0;
  642.   last=FindRecord(end,&isNew);
  643.   if(isNew)
  644.     last--;
  645.   if(last<=first){
  646.     *emptyRange=true;
  647.     return;
  648.   }
  649.  if(gPrefPtr->GlobalMinMax)
  650.    return;
  651.  
  652.   // show busy message
  653.   switch(frmID){
  654.   case TimberListForm:
  655.     FrmShowObject(frm,GetObjectIdxFromActive(TimberListBusyLabel));
  656.     break;
  657.   case TimberGraphForm:
  658.     FrmShowObject(frm,GetObjectIdxFromActive(TimberGraphBusyLabel));
  659.     break;
  660.   }
  661.  
  662.   *maxWeight=0; 
  663.   *minWeight=-1L; // cheating
  664.  
  665.   for(index=first;index<=last;index++){
  666.     recordH=TimberQueryRecord(gTimberDB,index);
  667.     recordP=TimberHandleLock(recordH);
  668.     weight=recordP->weight;
  669.     MemHandleUnlock(recordH);
  670.     if(weight<*minWeight)
  671.       *minWeight=weight;
  672.     if(weight>*maxWeight)
  673.       *maxWeight=weight;
  674.   }
  675.   if(*minWeight==-1L)
  676.     *minWeight=*maxWeight;
  677.   
  678.   // hide busy message
  679.   switch(frmID){
  680.   case TimberListForm:
  681.     FrmHideObject(frm,GetObjectIdxFromActive(TimberListBusyLabel));
  682.     break;
  683.   case TimberGraphForm:
  684.     FrmHideObject(frm,GetObjectIdxFromActive(TimberGraphBusyLabel));
  685.     break;
  686.   }
  687. }
  688.  
  689. //
  690. // weight to ascii and back:
  691. //
  692.  
  693. static void WeightToAscii(CharPtr to, WeightType weight, Int precision)
  694. {
  695.   Int resultLen,i,p;
  696.   WeightType precisionMult;
  697.   Boolean useMaxAndCut=false;
  698.  
  699.   if(precision==UseMaxAndCut){
  700.     useMaxAndCut=true;
  701.     precision=MaxPrecision;
  702.   }
  703.  
  704.   for(precisionMult=1,p=precision;p;p--)
  705.     precisionMult*=10;
  706.  
  707.   if(gPrefPtr->RoundWeights)
  708.     weight+=(5*(ULong)MaxPrecisionMult/10)/precisionMult;
  709.   if(weight<MaxPrecisionMult){
  710.     StrIToA(to,weight+MaxPrecisionMult);
  711.     to[0]='0';
  712.   } else
  713.     StrIToA(to,weight);
  714.  
  715.   resultLen=StrLen(to)+precision-MaxPrecision;
  716.   to[resultLen+1]=0;
  717.  
  718.   for(i=0,p=resultLen;i<precision;i++,p--)
  719.     to[p]=to[p-1];
  720.   if(precision)
  721.     to[p]=gPrefPtr->DecimalSeparator;
  722.   else
  723.     to[p]=0;
  724.  
  725.   if(useMaxAndCut){
  726.     for(p=StrLen(to)-1;to[p]=='0';p--);
  727.     if(to[p]!=',')
  728.       p++;
  729.  
  730.     to[p]=0;
  731.   }
  732.  
  733.   StrLocalizeNumber(to,gPrefPtr->ThousandSeparator,gPrefPtr->DecimalSeparator);
  734. }
  735.  
  736. static void AsciiToWeight(CharPtr from, WeightPtr weight)
  737. {
  738.   ULong intPart,fracPart;
  739.   Int prec;
  740.  
  741.   intPart=0;
  742.   fracPart=0;
  743.   
  744.   while(*from&&(*from!=gPrefPtr->DecimalSeparator))
  745.     if(('0'<=*from)&&('9'>=*from)){
  746.       intPart=intPart*10+*from-'0';
  747.       from++;
  748.     } else
  749.       from++;
  750.   
  751.   if(*from)
  752.     from++;
  753.   prec=0;
  754.  
  755.   while((*from)&&(prec<MaxPrecision))
  756.     if(('0'<=*from)&&('9'>=*from)){
  757.       fracPart=fracPart*10+*from-'0';
  758.       from++;
  759.       prec++;
  760.     } else
  761.       from++;
  762.   while(prec++<MaxPrecision)
  763.     fracPart*=10;
  764.   
  765.   *weight=intPart*MaxPrecisionMult+fracPart;
  766. }
  767.  
  768.  
  769. //
  770. // list drawing:
  771. //
  772. static void TimberListTableDrawItem(VoidPtr table, Word row, Word column,
  773.                                     RectanglePtr bounds)
  774. {
  775.   Char dateStr[longDateStrLength+9];
  776.   Char weightStr[33];
  777.   UInt numRecords;
  778.   UInt index;
  779.   Int drawX;
  780.   Int width;
  781.   Int charsToDraw;
  782.   VoidHand recordH;
  783.   RecordPtr recordP;
  784.  
  785. #ifdef __GNUC__
  786.     CALLBACK_PROLOGUE
  787. #endif
  788.  
  789.   WinEraseRectangle(bounds,0);
  790.   numRecords=DmNumRecords(gTimberDB);
  791.   
  792.   // no drawing necessary
  793.   index=gFirstVisibleRecord+row;
  794.  
  795.   if((index==-1)||(index==numRecords)){
  796.     StrCopy(dateStr,"");
  797.     StrCopy(weightStr,"- - - - - - - - - - - - - - - - - - - - - - - -");
  798.   } else {
  799.     // lock, get and convert data
  800.     recordH=TimberQueryRecord(gTimberDB,index);
  801.     recordP=TimberHandleLock(recordH);
  802.     DateToDOWDMFormat(recordP->date.month,
  803.                       recordP->date.day,
  804.                       recordP->date.year+firstYear,
  805.                       gPrefPtr->DateFormat,
  806.                       dateStr);
  807.     WeightToAscii(weightStr,recordP->weight,gPrefPtr->ListPrecision);
  808.     MemHandleUnlock(recordH);
  809.   }
  810.   
  811.   // draw date
  812.   charsToDraw=StrLen(dateStr);
  813.   width=FntCharsWidth(dateStr,charsToDraw);
  814.   drawX=bounds->topLeft.x+(Int)bounds->extent.x*3/5-width;
  815.   WinDrawChars(dateStr, charsToDraw, drawX, bounds->topLeft.y);
  816.  
  817.   // draw weight
  818.   charsToDraw=StrLen(weightStr);
  819.   width=FntCharsWidth(weightStr,charsToDraw);
  820.   drawX=bounds->topLeft.x+bounds->extent.x-width;
  821.   WinDrawChars(weightStr, charsToDraw, drawX, bounds->topLeft.y);
  822.  
  823. #ifdef __GNUC__
  824.     CALLBACK_EPILOGUE
  825. #endif
  826. }
  827.  
  828. static void UpdateListView(Boolean firstVisibleUpdate, Boolean triggerUpdate)
  829. {
  830.   Long index;
  831.   VoidHand recordH;
  832.   RecordPtr recordP;
  833.   Long numRecords;
  834.   ScrollBarPtr scroll;
  835.   TablePtr table;
  836.   Int rows,i;
  837.   FormPtr frm;
  838.  
  839.   // get table info
  840.   table=gTimberListTablePtr;
  841.   if(table==NULL)
  842.     return;
  843.  
  844.   rows=TblGetNumberOfRows(table);
  845.   numRecords=DmNumRecords(gTimberDB);
  846.   
  847.   // find first visible record
  848.   gViewDate.day=0;
  849.   if((!firstVisibleUpdate)||triggerUpdate){
  850.     if(gFirstVisibleRecord>=numRecords)
  851.       index=numRecords-1;
  852.     if(gFirstVisibleRecord>=0)
  853.       index=gFirstVisibleRecord;
  854.     else
  855.       index=0;
  856.   } else 
  857.     index=FindRecord(gViewDate,NULL);
  858.   
  859.   if((0<=index)&&(index<numRecords)){
  860.     recordH=TimberQueryRecord(gTimberDB,index);
  861.     recordP=TimberHandleLock(recordH);
  862.   
  863.   // update view date trigger
  864.     if(triggerUpdate) {
  865.       if(index<numRecords){
  866.         gViewDate.month=recordP->date.month;
  867.         gViewDate.year=recordP->date.year;
  868.       }
  869.     } else if(firstVisibleUpdate) {
  870.       if(index<numRecords){
  871.         // check if view is before first record
  872.         if(index==0){
  873.           if((gViewDate.year<recordP->date.year)||
  874.              ((gViewDate.year=recordP->date.year)&&
  875.               (gViewDate.month<recordP->date.month)))
  876.             gFirstVisibleRecord=-1;
  877.           else 
  878.             gFirstVisibleRecord=0;
  879.         } else
  880.           gFirstVisibleRecord=index;
  881.       } else
  882.         gFirstVisibleRecord=numRecords;
  883.     }
  884.     
  885.     MemHandleUnlock(recordH);
  886.   }
  887.   
  888.   // update scrollbar
  889.   scroll=GetObjectPtrFromAny(TimberListForm,TimberListScrollBar);
  890.   SclSetScrollBar(scroll,gFirstVisibleRecord+1,0,numRecords+1,rows);
  891.   
  892.   // update usable
  893.   for(i=0;i<rows;i++)
  894.     TblSetRowUsable(table,i,
  895.                     ((i!=0)||(gFirstVisibleRecord>=-1))
  896.                     &&(i+gFirstVisibleRecord<=numRecords));
  897.   TblMarkTableInvalid(table);
  898. }
  899.  
  900. static void ScrollView(Int lines)
  901. {
  902.   Long newFirst;
  903.   Long maxLast=DmNumRecords(gTimberDB);
  904.  
  905.   newFirst=gFirstVisibleRecord+(Long)lines;
  906.   if(newFirst<-1)
  907.     newFirst=-1L;
  908.   if(newFirst>maxLast)
  909.     newFirst=maxLast;
  910.   gFirstVisibleRecord=newFirst;
  911.   UpdateListView(false,true);
  912. }
  913.  
  914. //
  915. // event handling:
  916. //
  917.  
  918. static Boolean TimberListFormHandleEvent(EventPtr event)
  919. {
  920.   Boolean handled;
  921.   FormPtr frm;
  922.   Word ctlID;
  923.  
  924. #ifdef __GNUC__
  925.   CALLBACK_PROLOGUE
  926. #endif
  927.  
  928.   handled = false;
  929.   frm=FrmGetActiveForm();
  930.  
  931.   switch (event->eType) {
  932.   case ctlSelectEvent:  // A control button was pressed and released.
  933.     ctlID=event->data.ctlSelect.controlID;
  934.     switch (ctlID) {
  935.     case TimberListMonthButton:
  936.     case TimberListQuarterButton:
  937.     case TimberListYearButton:
  938.     case TimberListSmoothButton:
  939.       TblUnhighlightSelection(gTimberListTablePtr);
  940.       TimberSetActiveView(ctlID-TimberListListButton);
  941.       handled = true;
  942.       break;
  943.     case TimberListDateTrigger:
  944.       TblUnhighlightSelection(gTimberListTablePtr);
  945.       ViewDateDialog(frm);
  946.       UpdateListView(true,true);
  947.       TblMarkTableInvalid(gTimberListTablePtr);
  948.       TblDrawTable(gTimberListTablePtr);
  949.       TimberDrawViewDate(frm);
  950.       handled = true;
  951.       break;
  952.     case TimberListNewButton:
  953.       {
  954.         TablePtr table=gTimberListTablePtr;
  955.         Long newIndex;
  956.         Long row;
  957.         Word selectRow,selectCol;
  958.         Boolean triggerUpdate,selected;
  959.  
  960.         DoNewWeightDialog(&newIndex);
  961.         if(newIndex>=0) {
  962.           TblUnhighlightSelection(table);
  963.           if(newIndex<gFirstVisibleRecord){
  964.             row=0;
  965.             gFirstVisibleRecord=newIndex;
  966.             triggerUpdate=true;
  967.           } 
  968.           else if(newIndex>=gFirstVisibleRecord+gTableRows){
  969.             row=gTableRows-1;
  970.             gFirstVisibleRecord=newIndex-(gTableRows-1);
  971.             triggerUpdate=true;
  972.           } else {
  973.             row=newIndex-gFirstVisibleRecord;
  974.             triggerUpdate=row==0;
  975.           }
  976.           UpdateListView(false,triggerUpdate);
  977.           if(triggerUpdate)
  978.             TimberDrawViewDate(frm);
  979.           TblMarkTableInvalid(gTimberListTablePtr);
  980.           TblDrawTable(gTimberListTablePtr);
  981.           TblSelectItem(table,row,0);
  982.         }
  983.       }
  984.       handled = true;
  985.       break;
  986.     case TimberListPrefButton:
  987.       DoPrefDialog();
  988.       TblMarkTableInvalid(gTimberListTablePtr);
  989.       TblDrawTable(gTimberListTablePtr);
  990.       handled=true;
  991.       break;
  992.     case TimberListGoalButton:
  993.       DoGoalDialog();
  994.       handled=true;
  995.       break;
  996.     }
  997.     break;      
  998.   case ctlRepeatEvent: 
  999.     { 
  1000.       Long saveFirstVisible=gFirstVisibleRecord;
  1001.       switch (event->data.ctlRepeat.controlID){
  1002.       case TimberListPrevButton:
  1003.         TblUnhighlightSelection(gTimberListTablePtr);
  1004.         TimberChangeDate(frm,datePrevMonth);
  1005.         UpdateListView(true,false);
  1006.         TimberDrawViewDate(frm);
  1007.         if(saveFirstVisible!=gFirstVisibleRecord){
  1008.           TblMarkTableInvalid(gTimberListTablePtr);
  1009.           TblDrawTable(gTimberListTablePtr);
  1010.         }
  1011.         // handled stays false because then system unhighlights button
  1012.         // if necessary!
  1013.         break;
  1014.       case TimberListNextButton:
  1015.         TblUnhighlightSelection(gTimberListTablePtr);
  1016.         TimberChangeDate(frm,dateNextMonth);
  1017.         UpdateListView(true,false);
  1018.         TimberDrawViewDate(frm);
  1019.         if(saveFirstVisible!=gFirstVisibleRecord){
  1020.           TblMarkTableInvalid(gTimberListTablePtr);
  1021.           TblDrawTable(gTimberListTablePtr);
  1022.         }
  1023.         // handled stays false because then system unhighlights button
  1024.         // if necessary!
  1025.         break;
  1026.       }
  1027.     }
  1028.     break;
  1029.   case menuEvent:
  1030.     handled=TimberHandleMenuEvent(event->data.menu.itemID);
  1031.     break;
  1032.   case keyDownEvent:
  1033.     { 
  1034.       Long saveFirstVisible=gFirstVisibleRecord;
  1035.       switch(event->data.keyDown.chr){
  1036.       case pageUpChr:
  1037.         TblUnhighlightSelection(gTimberListTablePtr);
  1038.         if(gPrefPtr->MonthScroll){
  1039.           TimberChangeDate(frm,datePrevMonth);
  1040.           UpdateListView(true,false);
  1041.         } else
  1042.           ScrollView(-gScrollPerButton);
  1043.         TimberDrawViewDate(frm);
  1044.         if(saveFirstVisible!=gFirstVisibleRecord)
  1045.           TblDrawTable(gTimberListTablePtr);
  1046.         handled = true;
  1047.         break;
  1048.       case pageDownChr:
  1049.         TblUnhighlightSelection(gTimberListTablePtr);
  1050.         if(gPrefPtr->MonthScroll){
  1051.           TimberChangeDate(frm,dateNextMonth);
  1052.           UpdateListView(true,false);
  1053.         } else
  1054.           ScrollView(gScrollPerButton);
  1055.         TimberDrawViewDate(frm);
  1056.         if(saveFirstVisible!=gFirstVisibleRecord)
  1057.           TblDrawTable(gTimberListTablePtr);
  1058.         handled = true;
  1059.         break;
  1060.       }
  1061.     }
  1062.     break;
  1063.   case tblEnterEvent:
  1064.     {
  1065.       TablePtr table=GetObjectPtrFromActive(event->data.tblEnter.tableID);
  1066.       Long row=event->data.tblEnter.row;
  1067.       Long index=row+gFirstVisibleRecord;
  1068.       Long newIndex;
  1069.       Boolean triggerUpdate;
  1070.  
  1071.       TblUnhighlightSelection(table);
  1072.       if((index>=0)&&(index<DmNumRecords(gTimberDB))){
  1073.         TblSelectItem(table,row,0);
  1074.         DoEditWeightDialog(index,&newIndex);
  1075.         triggerUpdate=(index==newIndex)&&(row==0);
  1076.         if(newIndex<0) 
  1077.           triggerUpdate=row==0;
  1078.         else if(newIndex<gFirstVisibleRecord){
  1079.           row=0;
  1080.           gFirstVisibleRecord=newIndex;
  1081.           triggerUpdate=true;
  1082.         } 
  1083.         else if(newIndex>=gFirstVisibleRecord+gTableRows){
  1084.           row=gTableRows-1;
  1085.           gFirstVisibleRecord=newIndex-(gTableRows-1);
  1086.           triggerUpdate=true;
  1087.         } else
  1088.           row=newIndex-gFirstVisibleRecord;
  1089.         if(newIndex!=-1){
  1090.           UpdateListView(false,triggerUpdate);
  1091.           TblMarkTableInvalid(gTimberListTablePtr);
  1092.           if(triggerUpdate)
  1093.             TimberDrawViewDate(frm);
  1094.           TblUnhighlightSelection(table);
  1095.           TblDrawTable(gTimberListTablePtr);
  1096.           TblSelectItem(table,row,0);
  1097.         }
  1098.       }
  1099.     }
  1100.     handled = true;
  1101.     break;
  1102.   case sclRepeatEvent:
  1103.     TblUnhighlightSelection(gTimberListTablePtr);
  1104.     ScrollView(event->data.sclRepeat.newValue-
  1105.                event->data.sclRepeat.value);
  1106.     TblDrawTable(gTimberListTablePtr);
  1107.     TimberDrawViewDate(frm);
  1108.     break;
  1109.   case frmOpenEvent:    
  1110.     FrmSetControlGroupSelection(frm,
  1111.                                 TimberListViewGroup, 
  1112.                                 TimberListListButton);
  1113.     // initialize list view
  1114.     InitTable();
  1115.     UpdateListView(true,true);
  1116.     FrmDrawForm(frm);
  1117.     TimberDrawViewDate(frm);
  1118.     if(gCheckWeight)
  1119.       MinMaxFind();
  1120.     gCheckWeight=false;
  1121.     FrmHideObject(frm,GetObjectIdxFromActive(TimberListBusyLabel));
  1122.     handled = true;
  1123.     break;
  1124.   }    
  1125.  
  1126. #ifdef __GNUC__
  1127.     CALLBACK_EPILOGUE
  1128. #endif
  1129.   return(handled);
  1130. }
  1131.  
  1132.  
  1133. static Boolean TimberGraphFormHandleEvent(EventPtr event)
  1134. {
  1135.   Boolean handled;
  1136.   FormPtr frm;
  1137.   Word ctlID;
  1138.  
  1139. #ifdef __GNUC__
  1140.   CALLBACK_PROLOGUE
  1141. #endif
  1142.  
  1143.   handled = false;
  1144.   frm=FrmGetActiveForm();
  1145.  
  1146.   switch (event->eType) {
  1147.   case ctlSelectEvent:  // A control button was pressed and released.
  1148.     switch (ctlID=event->data.ctlSelect.controlID) {
  1149.     case TimberGraphListButton:
  1150.     case TimberGraphMonthButton:
  1151.     case TimberGraphQuarterButton:
  1152.     case TimberGraphYearButton:
  1153.     case TimberGraphSmoothButton:
  1154.       TimberSetActiveView(ctlID-TimberGraphListButton);
  1155.       handled = true;
  1156.       break;
  1157.     case TimberGraphDateTrigger:
  1158.       ViewDateDialog(frm);
  1159.       TimberDrawViewDate(frm);
  1160.       TimberGraphDraw();
  1161.       handled = true;
  1162.       break;
  1163.     case TimberGraphNewButton:
  1164.       DoNewWeightDialog(NULL);
  1165.       TimberGraphDraw();
  1166.       handled = true;
  1167.       break;
  1168.     case TimberGraphPrefButton:
  1169.       DoPrefDialog();
  1170.       TimberGraphDraw();
  1171.       handled=true;
  1172.       break;
  1173.     case TimberGraphGoalButton:
  1174.       DoGoalDialog();
  1175.       TimberGraphDraw();
  1176.       handled=true;
  1177.       break;
  1178.     }
  1179.     break;
  1180.   case menuEvent:
  1181.     handled=TimberHandleMenuEvent(event->data.menu.itemID);
  1182.     break;
  1183.   case keyDownEvent:
  1184.     switch(event->data.keyDown.chr){
  1185.     case pageUpChr:
  1186.       TimberChangeDate(frm,datePrevMonth);
  1187.       TimberGraphDraw();
  1188.       handled = true;
  1189.       break;
  1190.     case pageDownChr:
  1191.       TimberChangeDate(frm,dateNextMonth);
  1192.       TimberGraphDraw();
  1193.       handled = true;
  1194.       break;
  1195.     }
  1196.     break;
  1197.   case ctlRepeatEvent: // View Date change:
  1198.     switch (event->data.ctlRepeat.controlID){
  1199.     case TimberGraphPrevButton:
  1200.       TimberChangeDate(frm,datePrevMonth);
  1201.       TimberGraphDraw();
  1202.       // handled stays false because then system unhighlights button
  1203.       // if necessary!
  1204.       break;
  1205.     case TimberGraphNextButton:
  1206.       TimberChangeDate(frm,dateNextMonth);
  1207.       TimberGraphDraw();
  1208.       // handled stays false because then system unhighlights button
  1209.       // if necessary!
  1210.       break;
  1211.     }
  1212.     break;
  1213.   case frmOpenEvent:    
  1214.     FrmSetControlGroupSelection(frm,
  1215.                                 TimberGraphViewGroup, 
  1216.                                 TimberGraphListButton+gTimberView);
  1217.     FrmDrawForm(frm);
  1218.     TimberDrawViewDate(frm);
  1219.     if(gCheckWeight)
  1220.       MinMaxFind();
  1221.     gCheckWeight=false;
  1222.     FrmHideObject(frm,GetObjectIdxFromActive(TimberGraphBusyLabel));
  1223.     TimberGraphDraw();
  1224.     handled = true;
  1225.     break;
  1226.   }    
  1227.  
  1228. #ifdef __GNUC__
  1229.     CALLBACK_EPILOGUE
  1230. #endif
  1231.     return(handled);
  1232. }
  1233.  
  1234.  
  1235. static Boolean TimberNewFormHandleEvent(EventPtr event)
  1236. {
  1237.   Boolean handled;
  1238.   FormPtr frm;
  1239.  
  1240. #ifdef __GNUC__
  1241.   CALLBACK_PROLOGUE
  1242. #endif
  1243.  
  1244.   handled = false;
  1245.   frm=FrmGetActiveForm();
  1246.  
  1247.   // Always keep focus in weight field:
  1248.   FrmSetFocus(frm,GetObjectIdxFromActive(NewWeightField));
  1249.  
  1250.   switch (event->eType) {
  1251.   case ctlSelectEvent:  // A control button was pressed and released.
  1252.     switch (event->data.ctlSelect.controlID) {
  1253.     case NewDateTrigger:
  1254.       DoNewDateDialog(frm);
  1255.       NewWeightDialogDrawDate();
  1256.       break;
  1257.     }
  1258.     break;
  1259.   case keyDownEvent:
  1260.     handled=CommonKeyHandler(event->data.keyDown.chr);
  1261.     break;
  1262.   case frmOpenEvent:    
  1263.     FrmDrawForm(frm);
  1264.     NewWeightDialogDrawDate();
  1265.     handled = true;
  1266.     break;
  1267.   }    
  1268.  
  1269. #ifdef __GNUC__
  1270.     CALLBACK_EPILOGUE
  1271. #endif
  1272.     return(handled);
  1273. }
  1274.  
  1275.  
  1276. static Boolean TimberEditFormHandleEvent(EventPtr event)
  1277. {
  1278.   Boolean handled;
  1279.   FormPtr frm;
  1280.  
  1281. #ifdef __GNUC__
  1282.   CALLBACK_PROLOGUE
  1283. #endif
  1284.  
  1285.   handled = false;
  1286.   frm=FrmGetActiveForm();
  1287.  
  1288.   // Always keep focus in weight field:
  1289.   FrmSetFocus(frm,GetObjectIdxFromActive(EditWeightField));
  1290.  
  1291.   switch (event->eType) {
  1292.   case ctlSelectEvent:  // A control button was pressed and released.
  1293.     switch (event->data.ctlSelect.controlID) {
  1294.     case EditDateTrigger:
  1295.       DoNewDateDialog(frm);
  1296.       EditWeightDialogDrawDate();
  1297.       break;
  1298.     }
  1299.     break;
  1300.   case keyDownEvent:
  1301.     handled=CommonKeyHandler(event->data.keyDown.chr);
  1302.     break;
  1303.   case frmOpenEvent:    
  1304.     FrmDrawForm(frm);
  1305.     EditWeightDialogDrawDate();
  1306.     handled = true;
  1307.     break;
  1308.   }    
  1309.  
  1310. #ifdef __GNUC__
  1311.     CALLBACK_EPILOGUE
  1312. #endif
  1313.     return(handled);
  1314. }
  1315.  
  1316.  
  1317. static Boolean TimberGoalFormHandleEvent(EventPtr event)
  1318. {
  1319.   Boolean handled;
  1320.   FormPtr frm;
  1321.  
  1322. #ifdef __GNUC__
  1323.   CALLBACK_PROLOGUE
  1324. #endif
  1325.  
  1326.   handled = false;
  1327.   frm=FrmGetActiveForm();
  1328.  
  1329.   switch (event->eType) {
  1330.   case ctlSelectEvent:  // A control button was pressed and released.
  1331.     switch (event->data.ctlSelect.controlID) {
  1332.     case GoalStartDateTrigger:
  1333.       gChooseDate=gPrefPtr->GoalStart.date;
  1334.       DoNewDateDialog(frm);
  1335.       gPrefPtr->GoalStart.date=gChooseDate;
  1336.       GoalDialogDrawDates();
  1337.       handled=true;
  1338.       break;
  1339.     case GoalEndDateTrigger:
  1340.       gChooseDate=gPrefPtr->GoalEnd.date;
  1341.       DoNewDateDialog(frm);
  1342.       gPrefPtr->GoalEnd.date=gChooseDate;
  1343.       GoalDialogDrawDates();
  1344.       handled=true;
  1345.       break;
  1346.     }
  1347.     break;
  1348.   case keyDownEvent:
  1349.     handled=CommonKeyHandler(event->data.keyDown.chr);
  1350.     break;
  1351.   case frmOpenEvent:    
  1352.     FrmDrawForm(frm);
  1353.     handled = true;
  1354.     break;
  1355.   }    
  1356.  
  1357. #ifdef __GNUC__
  1358.     CALLBACK_EPILOGUE
  1359. #endif
  1360.     return(handled);
  1361. }
  1362.  
  1363.  
  1364. static Boolean TimberDeleteFormHandleEvent(EventPtr event)
  1365. {
  1366.   Boolean handled;
  1367.   FormPtr frm;
  1368.  
  1369. #ifdef __GNUC__
  1370.   CALLBACK_PROLOGUE
  1371. #endif
  1372.  
  1373.   handled = false;
  1374.   frm=FrmGetActiveForm();
  1375.  
  1376.   switch (event->eType) {
  1377.   case ctlSelectEvent:  // A control button was pressed and released.
  1378.     switch (event->data.ctlSelect.controlID) {
  1379.     case EditDeleteDateTrigger:
  1380.       DoNewDateDialog(frm);
  1381.       DeleteDialogDrawDate();
  1382.       break;
  1383.     }
  1384.     break;
  1385.   case frmOpenEvent:    
  1386.     FrmDrawForm(frm);
  1387.     DeleteDialogDrawDate();
  1388.     handled = true;
  1389.     break;
  1390.   }    
  1391.  
  1392. #ifdef __GNUC__
  1393.     CALLBACK_EPILOGUE
  1394. #endif
  1395.     return(handled);
  1396. }
  1397.  
  1398. static Boolean CommonKeyHandler(Word chr)
  1399. {
  1400.   Boolean handled=true;
  1401.   switch(chr){
  1402.   case '0':
  1403.   case '1':
  1404.   case '2':
  1405.   case '3':
  1406.   case '4':
  1407.   case '5':
  1408.   case '6':
  1409.   case '7':
  1410.   case '8':
  1411.   case '9':
  1412.   case leftArrowChr:
  1413.   case rightArrowChr:
  1414.   case backspaceChr:
  1415.     handled=false;
  1416.       break;
  1417.   }
  1418.   if((chr>=0x100)||(chr==gPrefPtr->DecimalSeparator))
  1419.     handled=false;
  1420.   return handled;
  1421. }
  1422.  
  1423. static Boolean TimberHandleMenuEvent(Word itemID)
  1424. {
  1425.   Boolean updateView=false;
  1426.   Long newIndex; 
  1427.   Boolean handled=false;
  1428.   TablePtr table=gTimberListTablePtr;
  1429.   Long row;
  1430.   Word selectRow,selectCol;
  1431.   Boolean triggerUpdate=false;
  1432.   Boolean visibleUpdate=true;
  1433.   Boolean selected;
  1434.  
  1435.   switch(itemID){
  1436.   case EditAddItem:
  1437.     DoNewWeightDialog(&newIndex);
  1438.     updateView=newIndex>=0;
  1439.     if(updateView) {
  1440.       visibleUpdate=false;
  1441.       if(newIndex<gFirstVisibleRecord){
  1442.         row=0;
  1443.         gFirstVisibleRecord=newIndex;
  1444.         triggerUpdate=true;
  1445.       } 
  1446.       else if(newIndex>=gFirstVisibleRecord+gTableRows){
  1447.         row=gTableRows-1;
  1448.         gFirstVisibleRecord=newIndex-(gTableRows-1);
  1449.         triggerUpdate=true;
  1450.       } else {
  1451.         row=newIndex-gFirstVisibleRecord;
  1452.         triggerUpdate=row==0;
  1453.       }
  1454.     }
  1455.     handled=true;
  1456.     break;
  1457.   case EditDeleteItem:
  1458.     DoDeleteDialog(&updateView);
  1459.     handled=true;
  1460.     break;
  1461.   case OptionsGoalItem:
  1462.     DoGoalDialog();
  1463.     updateView=gTimberView!=TimberListView;
  1464.     visibleUpdate=true;
  1465.     triggerUpdate=false;
  1466.     handled=true;
  1467.     break;
  1468.   case OptionsPrefItem:
  1469.     DoPrefDialog();
  1470.     updateView=true;
  1471.     visibleUpdate=true;
  1472.     triggerUpdate=false;
  1473.     handled=true;
  1474.     break;
  1475.   case OptionsHelpItem:
  1476.     FrmHelp(OptionsHelpString);
  1477.     handled=true;
  1478.     break;
  1479.   case OptionsAboutGPLItem:
  1480.     FrmHelp(AboutGPLString);
  1481.     handled=true;
  1482.     break;
  1483.   case OptionsAboutItem:
  1484.     FrmAlert(OptionsAboutAlert);
  1485.     handled=true;
  1486.     break;
  1487.   }
  1488.  
  1489.   if(updateView){
  1490.     if(gTimberView==TimberListView){
  1491.       UpdateListView(visibleUpdate,triggerUpdate);
  1492.       TblUnhighlightSelection(table);
  1493.       TblMarkTableInvalid(table);
  1494.       TblDrawTable(table);
  1495.       if(itemID==EditAddItem)
  1496.         TblSelectItem(table,row,0);
  1497.     }
  1498.     else
  1499.       TimberGraphDraw();
  1500.   }
  1501.  
  1502.   return handled;
  1503. }
  1504.  
  1505. static Boolean ApplicationHandleEvent(EventPtr event)
  1506. {
  1507.   FormPtr    frm;
  1508.   Int        formId;
  1509.   Boolean    handled = false;
  1510.   
  1511.   if (event->eType == frmLoadEvent){
  1512.     // Load the form resource specified in the event then activate the form.
  1513.     formId = event->data.frmLoad.formID;
  1514.     frm = FrmInitForm(formId);
  1515.     FrmSetActiveForm(frm);
  1516.     
  1517.     // Set the event handler for the form.  The handler of the currently 
  1518.     // active form is called by FrmDispatchEvent each time it receives an event.
  1519.     switch (formId){
  1520.     case TimberListForm:
  1521.       FrmSetEventHandler(frm, TimberListFormHandleEvent);
  1522.       break;
  1523.     case TimberGraphForm:
  1524.       FrmSetEventHandler(frm, TimberGraphFormHandleEvent);
  1525.       break;
  1526.     case TimberNewForm:
  1527.       FrmSetEventHandler(frm, TimberNewFormHandleEvent);
  1528.       break;
  1529.     case TimberEditForm:
  1530.       FrmSetEventHandler(frm, TimberEditFormHandleEvent);
  1531.       break;
  1532.     case TimberGoalForm:
  1533.       FrmSetEventHandler(frm, TimberGoalFormHandleEvent);
  1534.       break;
  1535.     }
  1536.     handled = true;
  1537.   }
  1538.   return handled;
  1539. }
  1540.  
  1541.  
  1542. static void EventLoop(void)
  1543. {
  1544.   EventType    event;
  1545.   Word        error;
  1546.     
  1547.   do {
  1548.     EvtGetEvent(&event, evtWaitForever);
  1549.     if (! SysHandleEvent(&event))
  1550.       if (! MenuHandleEvent(0, &event, &error))
  1551.         if (! ApplicationHandleEvent(&event))
  1552.                     FrmDispatchEvent(&event);
  1553.   } while (event.eType != appStopEvent);
  1554. }
  1555.  
  1556. //
  1557. // program entry point
  1558. //
  1559.  
  1560. DWord PilotMain(Word cmd, Ptr cmdPBP, Word launchFlags)
  1561. {
  1562.   Err err = 0;        
  1563.   
  1564.   if (cmd == sysAppLaunchCmdNormalLaunch){
  1565.     if ((err = StartApplication()) == 0){
  1566.       EventLoop();
  1567.       StopApplication();
  1568.     }
  1569.   }
  1570.   return err;
  1571. }
  1572.  
  1573. //
  1574. // graph drawing:
  1575. //
  1576. static void TimberGraphDraw(void)
  1577. {
  1578.   RectangleType bounds;
  1579.   RectangleType oldBounds;
  1580.  
  1581.   bounds.topLeft.x=0;
  1582.   bounds.topLeft.y=gScreenYOffset-3;
  1583.   bounds.extent.x=160;
  1584.   bounds.extent.y=gScreenYRange+14;
  1585.   WinEraseRectangle(&bounds,0);
  1586.   if(DmNumRecords(gTimberDB)<1)
  1587.     return;
  1588.  
  1589.   // Y before X is essential!
  1590.   CalcDrawYRanges();
  1591.   if((gPrefPtr->ShowGoal)&&(gPrefPtr->GoalStart.date.month))
  1592.     CalcGoalCoordinates();
  1593.   bounds.topLeft.x=gScreenXOffset;
  1594.   bounds.topLeft.y=gScreenYOffset;
  1595.   bounds.extent.x=gScreenXRange+1;
  1596.   bounds.extent.y=gScreenYRange+1;
  1597.  
  1598.   DrawYScale();
  1599.   CalcDrawXRanges();
  1600.   DrawXScale();
  1601.   WinGetClip(&oldBounds);
  1602.  
  1603.   DrawTheGraph();
  1604.   WinSetPattern(GoalPattern);
  1605.   bounds.extent.x=gScreenXRange+1;
  1606.   // the +15 is necessary because the Palm UI Clipping has a bug:
  1607.   // sometimes lines to be partially clipped do not appear at all
  1608.   bounds.extent.y=gScreenYRange+1; // works on PalmV? 
  1609.  
  1610.   WinSetClip(&bounds);
  1611.   if(!gGoalOutOfBounds
  1612.      &&(gPrefPtr->ShowGoal)
  1613.      &&(gPrefPtr->GoalStart.date.month)){ 
  1614.     WinFillLine(gGoalStartX,gGoalStartY-1,gGoalEndX,gGoalEndY-1);
  1615.     WinFillLine(gGoalStartX-1,gGoalStartY,gGoalEndX-1,gGoalEndY);
  1616.     WinFillLine(gGoalStartX,gGoalStartY,gGoalEndX,gGoalEndY);
  1617.     WinFillLine(gGoalStartX+1,gGoalStartY,gGoalEndX+1,gGoalEndY);
  1618.     WinFillLine(gGoalStartX,gGoalStartY+1,gGoalEndX,gGoalEndY+1);
  1619.   }
  1620.   WinSetClip(&oldBounds);
  1621. }
  1622.  
  1623. static void CalcDrawYRanges(void)
  1624. {
  1625.  Int p;
  1626.  Int maxStrLen;
  1627.  Char weightStr[30];
  1628.  Int month,year;
  1629.  DateType date;
  1630.  WeightType minWeight,maxWeight;
  1631.  Boolean emptyRange;
  1632.  
  1633.  // calculate start and end date
  1634.  gDrawEndDate.month=gViewDate.month;
  1635.  gDrawEndDate.day=31;
  1636.  gDrawEndDate.year=gViewDate.year;
  1637.  switch(gTimberView){
  1638.  case TimberMonthView:
  1639.    month=gViewDate.month;
  1640.    break;
  1641.  case TimberQuarterView:
  1642.    month=(gViewDate.month+21)%12+1;
  1643.    break;
  1644.  case TimberYearView:
  1645.  case TimberSmoothView:
  1646.    month=gViewDate.month%12+1;
  1647.    break;
  1648.  }
  1649.  if(month>gViewDate.month)
  1650.    year=gViewDate.year-1;
  1651.  else
  1652.    year=gViewDate.year;
  1653.  if(year<0){
  1654.    year=0;
  1655.    month=1;
  1656.  }
  1657.  gDrawStartDate.month=month;
  1658.  gDrawStartDate.day=1;
  1659.  gDrawStartDate.year=year;
  1660.  
  1661.  MinMaxBetween(gDrawStartDate,gDrawEndDate,&minWeight,&maxWeight,&emptyRange);
  1662.  if(gPrefPtr->GlobalMinMax||emptyRange){
  1663.    minWeight=gMinWeight;
  1664.    maxWeight=gMaxWeight;
  1665.    if(gPrefPtr->GoalStart.date.day){
  1666.      if(minWeight>gPrefPtr->GoalStart.weight)
  1667.        minWeight=gPrefPtr->GoalStart.weight;
  1668.      if(minWeight>gPrefPtr->GoalEnd.weight)
  1669.        minWeight=gPrefPtr->GoalEnd.weight;
  1670.      if(maxWeight<gPrefPtr->GoalStart.weight)
  1671.        maxWeight=gPrefPtr->GoalStart.weight;
  1672.      if(maxWeight<gPrefPtr->GoalEnd.weight)
  1673.        maxWeight=gPrefPtr->GoalEnd.weight;
  1674.    }
  1675.  }
  1676.  
  1677.  // set and calculate precision values
  1678.  if(gPrefPtr->DrawPrecision<0)
  1679.    gDrawPrecision=gPrefPtr->ListPrecision;
  1680.  else
  1681.    gDrawPrecision=gPrefPtr->DrawPrecision;
  1682.  p=gDrawPrecision;
  1683.  
  1684.  gDrawPrecisionMult=1;
  1685.  for(p;p;p--)
  1686.    gDrawPrecisionMult*=10;
  1687.  gDrawPrecisionRound=MaxPrecisionMult/gDrawPrecisionMult;
  1688.  
  1689.  
  1690.  // vertical range values
  1691.  gDrawYMin=(minWeight/gDrawPrecisionRound)*gDrawPrecisionRound;
  1692.  gDrawYMax=(((maxWeight+gDrawPrecisionRound-1)/gDrawPrecisionRound)
  1693.             *gDrawPrecisionRound);
  1694.  if(gDrawYMax==gDrawYMin)
  1695.    gDrawYMax+=gDrawPrecisionRound;
  1696.  gDrawYRange=gDrawYMax-gDrawYMin;
  1697.  gDrawBigRange=gDrawYRange>10000L*(Long)MaxPrecision;
  1698.  if(gDrawBigRange)
  1699.    gDrawYRange/=1000;
  1700.  
  1701.  // horizontal offset
  1702.  WeightToAscii(weightStr,gDrawYMax,gDrawPrecision);
  1703.  maxStrLen=StrLen(weightStr);
  1704.  gScreenXOffset=FntCharsWidth(weightStr,maxStrLen)+3;
  1705.  gScreenXRange=gScreenXMax-gScreenXOffset;
  1706. }
  1707.  
  1708. static void CalcGoalCoordinates(void)
  1709. {
  1710.   Long startDay,endDay,firstDay,lastDay,dayFirst,dayRange;
  1711.   WeightType correctBig;
  1712.   Long firstWeight,lastWeight;
  1713.   WeightType weightDiff,weightRange;
  1714.   Long correctDiff;
  1715.  
  1716.   startDay=DateToDays(gPrefPtr->GoalStart.date);
  1717.   endDay=DateToDays(gPrefPtr->GoalEnd.date);
  1718.   firstDay=DateToDays(gDrawStartDate);
  1719.   lastDay=DateToDays(gDrawEndDate);
  1720.   dayRange=lastDay-firstDay;
  1721.   dayFirst=firstDay;
  1722.  
  1723.   gGoalOutOfBounds=(firstDay>=endDay)||(lastDay<=startDay)||(startDay>=endDay);
  1724.   if(gGoalOutOfBounds)
  1725.     return;
  1726.  
  1727.   if(firstDay<startDay)
  1728.     firstDay=startDay;
  1729.   if(lastDay>endDay)
  1730.     lastDay=endDay;
  1731.  
  1732.   if(gPrefPtr->GoalEnd.weight>=gPrefPtr->GoalStart.weight)
  1733.     weightRange=gPrefPtr->GoalEnd.weight-gPrefPtr->GoalStart.weight;
  1734.   else
  1735.     weightRange=gPrefPtr->GoalStart.weight-gPrefPtr->GoalEnd.weight;
  1736.  
  1737.   if(weightRange>10000L*(Long)MaxPrecision)
  1738.     correctBig=1000;
  1739.   else 
  1740.     correctBig=1;
  1741.  
  1742.   weightDiff=(Long)(gPrefPtr->GoalEnd.weight/correctBig)
  1743.              -(Long)(gPrefPtr->GoalStart.weight/correctBig);
  1744.   correctDiff=(Long)weightDiff;
  1745.  
  1746.   firstWeight=(((((firstDay-startDay)*correctDiff)
  1747.                 /(endDay-startDay))*correctBig)
  1748.                +gPrefPtr->GoalStart.weight);  
  1749.   lastWeight=(((((lastDay-startDay)*correctDiff)
  1750.                 /(endDay-startDay))*correctBig)
  1751.                +gPrefPtr->GoalStart.weight);  
  1752.  
  1753.  
  1754.   gGoalStartY=ScreenY(firstWeight);
  1755.   gGoalEndY=ScreenY(lastWeight);
  1756.  
  1757.   gGoalStartX=gScreenXOffset+((firstDay-dayFirst)*gScreenXRange)/dayRange;
  1758.   gGoalEndX=gScreenXOffset+((lastDay-dayFirst)*gScreenXRange)/dayRange;
  1759. }
  1760.  
  1761. static Int ScreenY(WeightType y)
  1762. {
  1763.   Long yDiff,result;
  1764.  
  1765.   if(y>=gDrawYMin)
  1766.     yDiff=(Long)y-(Long)gDrawYMin;
  1767.   else
  1768.     yDiff=y;
  1769.  
  1770.   // check, if a big range is used
  1771.   if(gDrawBigRange)
  1772.     result=(Long)(((yDiff/gDrawYRange)*gScreenYRange)/1000);
  1773.   else 
  1774.     result=(Long)((yDiff*gScreenYRange)/gDrawYRange);
  1775.  
  1776.   if(y<gDrawYMin){
  1777.     if(gDrawBigRange)
  1778.       result-=(Long)(((gDrawYMin/gDrawYRange)*gScreenYRange)/1000);
  1779.     else 
  1780.       result-=(Long)((gDrawYMin*gScreenYRange)/gDrawYRange);
  1781.       }
  1782.   return (Long)gScreenYOffset+(Long)gScreenYRange-result;
  1783. }
  1784.  
  1785. static void DrawYScale(void)
  1786. {
  1787.   Int i,stepCount;
  1788.   WeightType range;
  1789.   WeightType val;
  1790.   WeightType lastVal;
  1791.   WeightType step;
  1792.  
  1793.   Char weightStr[30];
  1794.   Int len,width;
  1795.   Int y;
  1796.  
  1797.   range=gDrawYMax-gDrawYMin;
  1798.   step=range/5;
  1799.   step=((step+gDrawPrecisionRound-1)/gDrawPrecisionRound)*gDrawPrecisionRound;
  1800.   if(!step)
  1801.     step=gDrawPrecisionRound;
  1802.   stepCount=1+range/step;
  1803.   while(((stepCount-1)*step<range)&&(stepCount<20))
  1804.     stepCount++;
  1805.  
  1806.   lastVal=-1;
  1807.  
  1808.   for(i=0;i<stepCount;i++){
  1809.     if(i==stepCount-1)
  1810.       val=gDrawYMax;
  1811.     else {
  1812.       val=gDrawYMin+i*step+(5*gDrawPrecisionRound)/10;
  1813.       val=(val/gDrawPrecisionRound)*gDrawPrecisionRound;
  1814.     }
  1815.     if(val!=lastVal){
  1816.       lastVal=val;
  1817.       WeightToAscii(weightStr,val,gDrawPrecision);
  1818.       len=StrLen(weightStr);
  1819.       width=FntCharsWidth(weightStr,len);
  1820.       y=ScreenY(val);
  1821.       WinDrawLine(gScreenXOffset-2,y,gScreenXOffset+2,y);
  1822.       if((i==stepCount-1)||(y<=gScreenYOffset+5))
  1823.         y=gScreenYOffset+4;
  1824.       WinDrawChars(weightStr,len,(gScreenXOffset-3)-width,y-5);
  1825.     }
  1826.   }  
  1827.   WinDrawLine(gScreenXOffset,gScreenYOffset,
  1828.               gScreenXOffset,gScreenYOffset+gScreenYRange);
  1829. }
  1830.  
  1831. static void CalcDrawXRanges(void)
  1832. {
  1833.   Int trickster,month,realMonth,realYear,days;
  1834.   trickster=-2;
  1835.   switch(gTimberView){
  1836.   case TimberMonthView:
  1837.     gDrawXMax=DaysInMonth(gViewDate.month,gViewDate.year+firstYear);
  1838.     gDrawMonthDays[gViewDate.month%12]=0;
  1839.     gDrawXMin=1;
  1840.     break;
  1841.   case TimberYearView:
  1842.   case TimberSmoothView:
  1843.     trickster=-11; // this is a little tricky, no break!
  1844.   case TimberQuarterView:
  1845.     gDrawXMax=0;
  1846.     for(month=trickster;trickster++<=0;month++){
  1847.       realMonth=((int)gViewDate.month+month+23)%12+1;
  1848.       if(realMonth>gViewDate.month)
  1849.         realYear=gViewDate.year-1;
  1850.       else
  1851.         realYear=gViewDate.year;
  1852.       gDrawMonthDays[realMonth%12]=gDrawXMax;
  1853.       
  1854.       if(realYear>0){
  1855.         gDrawXMax+=DaysInMonth(realMonth,realYear+firstYear);
  1856.       }
  1857.     }
  1858.     gDrawXMin=1;
  1859.     break;
  1860.   }
  1861.   gDrawXRange=gDrawXMax-gDrawXMin;
  1862. }
  1863.  
  1864. static Int ScreenX(Int day, Int month)
  1865. {
  1866.   return
  1867.     gScreenXOffset+
  1868.     (gDrawMonthDays[month%12]+day-gDrawXMin)
  1869.     *gScreenXRange
  1870.     /gDrawXRange;
  1871. }
  1872.  
  1873. static void DrawXScale(void)
  1874. {
  1875.   Int y;
  1876.  
  1877.   switch(gTimberView){
  1878.   case TimberMonthView:
  1879.     DrawMonthScale();
  1880.     break;
  1881.   case TimberQuarterView:
  1882.     DrawQuarterOrYearScale(-2);
  1883.     break;
  1884.   case TimberYearView:
  1885.   case TimberSmoothView:
  1886.     DrawQuarterOrYearScale(-11);
  1887.     break;
  1888.   }
  1889.   y=gScreenYRange+gScreenYOffset;
  1890.   WinDrawLine(gScreenXOffset,y,gScreenXOffset+gScreenXRange,y);
  1891. }
  1892.  
  1893. static void DrawMonthScale(void)
  1894. {
  1895.   Int i,month,days;
  1896.   Int x,y;
  1897.   Char dayStr[5];
  1898.   Int len,width;
  1899.  
  1900.   y=gScreenYRange+gScreenYOffset;
  1901.   month=gViewDate.month;
  1902.   days=gDrawXMax;
  1903.   for(i=1;i<days-1;i+=10){
  1904.     x=ScreenX(i,month);
  1905.     StrIToA(dayStr,i);
  1906.     len=StrLen(dayStr);
  1907.     width=FntCharsWidth(dayStr,len)/2;
  1908.     WinDrawChars(dayStr,len,x-width,y+2);
  1909.     WinDrawLine(x,y-2,x,y+2);    
  1910.   }
  1911.   x=gScreenXRange+gScreenXOffset;
  1912.   StrIToA(dayStr,days);
  1913.   len=StrLen(dayStr);
  1914.   width=FntCharsWidth(dayStr,len);
  1915.   WinDrawChars(dayStr,len,2+x-width,y+2);
  1916.   WinDrawLine(x,y-2,x,y+2);    
  1917. }
  1918.  
  1919. static void DrawQuarterOrYearScale(Int firstMonth)
  1920. {
  1921.   Char dateStr[longDateStrLength];
  1922.   Int lastX,nextX,i,width,y,month,letters;
  1923.  
  1924.   if(firstMonth==-2)
  1925.     letters=3;
  1926.   else
  1927.     letters=1;
  1928.  
  1929.   y=gScreenYRange+gScreenYOffset;
  1930.   lastX=gScreenXOffset;
  1931.   WinDrawLine(lastX,y-2,lastX,y+2);
  1932.   for(i=firstMonth;i<=0;i++) {
  1933.     month=((Int)gViewDate.month+i+23)%12+1;
  1934.     if(i)
  1935.       nextX=ScreenX(1,month+1);
  1936.     else
  1937.       nextX=gScreenXMax;
  1938.     DateToAscii(month,1,firstYear,dfMYMed,dateStr);
  1939.     width=FntCharsWidth(dateStr,letters)-2;
  1940.     WinDrawChars(dateStr,letters,(nextX+lastX)/2-width/2,y);
  1941.     WinDrawLine(nextX,y-2,nextX,y+2);
  1942.     lastX=nextX;
  1943.   }
  1944. }
  1945.  
  1946. static void DrawTheGraph(void)
  1947. {
  1948.   Int y;
  1949.  
  1950.   switch(gTimberView){
  1951.   case TimberMonthView:
  1952.     DrawMonthGraph();
  1953.     break;
  1954.   case TimberQuarterView:
  1955.     DrawQuarterOrYearGraph();
  1956.     break;
  1957.   case TimberYearView:
  1958.     DrawQuarterOrYearGraph();
  1959.     break;
  1960.   case TimberSmoothView:
  1961.     DrawSmoothYearGraph();
  1962.     break;
  1963.   }
  1964. }
  1965.  
  1966. static void DrawMonthGraph(void)
  1967. {
  1968.   Long index;
  1969.   DateType date=gViewDate;
  1970.   Int drawX,drawY,lastX,lastY,month;
  1971.   VoidHand recordH;
  1972.   RecordPtr recordP;
  1973.   UInt numRecords=DmNumRecords(gTimberDB);
  1974.   Long sum;
  1975.   Int count=1;
  1976.  
  1977.   date.day=1;
  1978.  
  1979.   index=FindRecord(date,NULL);
  1980.   if(index<0)
  1981.     index=0;
  1982.  
  1983.   if(index<numRecords){
  1984.     recordH=TimberQueryRecord(gTimberDB,index);
  1985.     recordP=TimberHandleLock(recordH);
  1986.     month=recordP->date.month;
  1987.     if(month==gViewDate.month){
  1988.       lastX=ScreenX(recordP->date.day,month);
  1989.       lastY=ScreenY(recordP->weight);
  1990.       if(gPrefPtr->ShowAverage)
  1991.         sum=lastY;
  1992.       MemHandleUnlock(recordH);
  1993.     } else {
  1994.       MemHandleUnlock(recordH);
  1995.       return;
  1996.     }
  1997.   } else
  1998.     return;
  1999.   WinDrawLine(lastX-1,lastY-1,lastX+1,lastY-1);
  2000.   WinDrawLine(lastX+1,lastY-1,lastX+1,lastY+1);
  2001.   WinDrawLine(lastX+1,lastY+1,lastX-1,lastY+1);
  2002.   WinDrawLine(lastX-1,lastY+1,lastX-1,lastY-1);
  2003.  
  2004.   while(++index<numRecords){
  2005.     recordH=TimberQueryRecord(gTimberDB,index);
  2006.     recordP=TimberHandleLock(recordH);
  2007.     month=recordP->date.month;
  2008.     if(month==gViewDate.month){
  2009.       drawX=ScreenX(recordP->date.day,month);
  2010.       drawY=ScreenY(recordP->weight);
  2011.       if(gPrefPtr->ShowAverage){
  2012.         sum+=drawY;
  2013.         count++;
  2014.       }
  2015.       MemHandleUnlock(recordH);
  2016.     } else {
  2017.       MemHandleUnlock(recordH);
  2018.       if(gPrefPtr->ShowAverage){
  2019.         WinSetPattern(AvgPattern);
  2020.         WinFillLine(gScreenXOffset,sum/count,
  2021.                     gScreenXOffset+gScreenXRange,sum/count);
  2022.       }
  2023.       return;
  2024.     }
  2025.     WinDrawLine(lastX,lastY,drawX,drawY);
  2026.     lastX=drawX;
  2027.     lastY=drawY;
  2028.     WinDrawLine(lastX-1,lastY-1,lastX+1,lastY-1);
  2029.     WinDrawLine(lastX+1,lastY-1,lastX+1,lastY+1);
  2030.     WinDrawLine(lastX+1,lastY+1,lastX-1,lastY+1);
  2031.     WinDrawLine(lastX-1,lastY+1,lastX-1,lastY-1);
  2032.   }
  2033.  
  2034.   if(gPrefPtr->ShowAverage){
  2035.     WinSetPattern(AvgPattern);
  2036.     WinFillLine(gScreenXOffset,sum/count,
  2037.                 gScreenXOffset+gScreenXRange,sum/count);
  2038.   }
  2039. }
  2040.  
  2041. static void DrawQuarterOrYearGraph(void)
  2042. {
  2043.   Long index;
  2044.   DateType date=gViewDate;
  2045.   Int drawX,drawY,lastX,lastY,month;
  2046.   VoidHand recordH;
  2047.   RecordPtr recordP;
  2048.   UInt numRecords=DmNumRecords(gTimberDB);
  2049.   Long sum;
  2050.   Int count=1;
  2051.  
  2052.   date.day=1;
  2053.  
  2054.   index=FindRecord(gDrawStartDate,NULL);
  2055.   if(index<0)
  2056.     index=0;
  2057.  
  2058.   if(index<numRecords){
  2059.     recordH=TimberQueryRecord(gTimberDB,index);
  2060.     recordP=TimberHandleLock(recordH);
  2061.     month=recordP->date.month;
  2062.     if(DateCompare(&(recordP->date),&gDrawEndDate)<=0){
  2063.       lastX=ScreenX(recordP->date.day,month);
  2064.       lastY=ScreenY(recordP->weight);
  2065.       if(gPrefPtr->ShowAverage)
  2066.         sum=lastY;
  2067.       MemHandleUnlock(recordH);
  2068.     } else {
  2069.       MemHandleUnlock(recordH);
  2070.       return;
  2071.     }
  2072.   } else
  2073.     return;
  2074.   WinDrawLine(lastX-1,lastY-1,lastX+1,lastY-1);
  2075.   WinDrawLine(lastX+1,lastY-1,lastX+1,lastY+1);
  2076.   WinDrawLine(lastX+1,lastY+1,lastX-1,lastY+1);
  2077.   WinDrawLine(lastX-1,lastY+1,lastX-1,lastY-1);
  2078.  
  2079.   while(++index<numRecords){
  2080.     recordH=TimberQueryRecord(gTimberDB,index);
  2081.     recordP=TimberHandleLock(recordH);
  2082.     month=recordP->date.month;
  2083.     if(DateCompare(&(recordP->date),&gDrawEndDate)<=0){
  2084.       drawX=ScreenX(recordP->date.day,month);
  2085.       drawY=ScreenY(recordP->weight);
  2086.       if(gPrefPtr->ShowAverage){
  2087.         sum+=drawY;
  2088.         count++;
  2089.       }
  2090.       MemHandleUnlock(recordH);
  2091.     } else {
  2092.       if(gPrefPtr->ShowAverage){
  2093.         WinSetPattern(AvgPattern);
  2094.         WinFillLine(gScreenXOffset,sum/count,
  2095.                     gScreenXOffset+gScreenXRange,sum/count);
  2096.       }
  2097.       MemHandleUnlock(recordH);
  2098.       return;
  2099.     }
  2100.     WinDrawLine(lastX,lastY,drawX,drawY);
  2101.     lastX=drawX;
  2102.     lastY=drawY;
  2103.     WinDrawLine(lastX-1,lastY-1,lastX+1,lastY-1);
  2104.     WinDrawLine(lastX+1,lastY-1,lastX+1,lastY+1);
  2105.     WinDrawLine(lastX+1,lastY+1,lastX-1,lastY+1);
  2106.     WinDrawLine(lastX-1,lastY+1,lastX-1,lastY-1);
  2107.   }
  2108.  
  2109.   if(gPrefPtr->ShowAverage){
  2110.     WinSetPattern(AvgPattern);
  2111.     WinFillLine(gScreenXOffset,sum/count,
  2112.                 gScreenXOffset+gScreenXRange,sum/count);
  2113.   }
  2114. }
  2115.  
  2116. static void DrawSmoothYearGraph(void)
  2117. {
  2118.   Long index;
  2119.   Int i;
  2120.   DateType date=gViewDate;
  2121.   Long drawX,drawY,lastX,lastY,month;
  2122.   VoidHand recordH;
  2123.   RecordPtr recordP;
  2124.   UInt numRecords=DmNumRecords(gTimberDB);
  2125.   Int smoothPos=0;
  2126.   Long smoothSave[SmoothNumber];
  2127.   Long smoothSum;
  2128.   Long sum;
  2129.   Int count=1;
  2130.  
  2131.   date.day=1;
  2132.  
  2133.   index=FindRecord(gDrawStartDate,NULL);
  2134.   if(index<0)
  2135.     index=0;
  2136.  
  2137.   if(index<numRecords){
  2138.     recordH=TimberQueryRecord(gTimberDB,index);
  2139.     recordP=TimberHandleLock(recordH);
  2140.     month=recordP->date.month;
  2141.     if(DateCompare(&(recordP->date),&gDrawEndDate)<=0){
  2142.       lastX=ScreenX(recordP->date.day,month);
  2143.       lastY=ScreenY(recordP->weight);
  2144.       if(gPrefPtr->ShowAverage)
  2145.         sum=lastY;
  2146.       MemHandleUnlock(recordH);
  2147.     } else {
  2148.       MemHandleUnlock(recordH);
  2149.       return;
  2150.     }
  2151.   } else
  2152.     return;
  2153.   smoothSum=SmoothNumber*lastY;
  2154.   for(i=0;i<SmoothNumber;i++)
  2155.     smoothSave[i]=lastY;
  2156.  
  2157.   while(++index<numRecords){
  2158.     recordH=TimberQueryRecord(gTimberDB,index);
  2159.     recordP=TimberHandleLock(recordH);
  2160.     month=recordP->date.month;
  2161.     if(DateCompare(&(recordP->date),&gDrawEndDate)<=0){
  2162.       drawX=ScreenX(recordP->date.day,month);
  2163.       drawY=ScreenY(recordP->weight);
  2164.       if(gPrefPtr->ShowAverage){
  2165.         sum+=drawY;
  2166.         count++;
  2167.       }
  2168.       MemHandleUnlock(recordH);
  2169.     } else {
  2170.       MemHandleUnlock(recordH);
  2171.       if(gPrefPtr->ShowAverage){
  2172.         WinSetPattern(AvgPattern);
  2173.         WinFillLine(gScreenXOffset,sum/count,
  2174.                     gScreenXOffset+gScreenXRange,sum/count);
  2175.       }
  2176.       return;
  2177.     }
  2178.     smoothSum=smoothSum+drawY-smoothSave[smoothPos];
  2179.     smoothSave[smoothPos++]=drawY;
  2180.     smoothPos=smoothPos%SmoothNumber;
  2181.     drawY=smoothSum/SmoothNumber;
  2182.     WinDrawLine(lastX,lastY,drawX,drawY);
  2183.     lastX=drawX;
  2184.     lastY=drawY;
  2185.   }
  2186.  
  2187.   if(gPrefPtr->ShowAverage){
  2188.     WinSetPattern(AvgPattern);
  2189.     WinFillLine(gScreenXOffset,sum/count,
  2190.                 gScreenXOffset+gScreenXRange,sum/count);
  2191.   }
  2192. }
  2193.  
  2194. //
  2195. // Timber form handling:
  2196. //
  2197.  
  2198. static void TimberSetActiveView(Word view)
  2199. {
  2200.   Word oldView=gTimberView;
  2201.   FormPtr frm;
  2202.  
  2203.   gTimberView=view;
  2204.   if (view==TimberListView) {
  2205.     gTimberGroup=TimberListViewGroup;
  2206.     frm=FrmInitForm(TimberListForm);
  2207.     FrmGotoForm(TimberListForm);
  2208.   } else {
  2209.     gTimberGroup=TimberGraphViewGroup;
  2210.     frm=FrmInitForm(TimberGraphForm);
  2211.     FrmGotoForm(TimberGraphForm);
  2212.     FrmSetControlGroupSelection(frm,
  2213.                                 TimberGraphViewGroup, 
  2214.                                 TimberGraphListButton+gTimberView);
  2215.   }
  2216. }
  2217.  
  2218. static void TimberDrawViewDate(FormPtr frm)
  2219. {
  2220.   Char dateStr[longDateStrLength];
  2221.   Char printStr[longDateStrLength];
  2222.   CharPtr dateP,printP;
  2223.   RectangleType bounds;
  2224.   Word charsToDraw;
  2225.   Int titleWidth;
  2226.  
  2227.   DateToAscii(gViewDate.month,
  2228.               gViewDate.day,
  2229.               gViewDate.year+firstYear,
  2230.               dfMYMed,
  2231.               dateStr);
  2232.  
  2233.   // the format without apostrophe does not work!
  2234.   for(dateP=dateStr,printP=printStr;*dateP!='\0';)
  2235.     if(*dateP=='\'')
  2236.       dateP++;
  2237.     else
  2238.       *printP++=*dateP++;
  2239.   *printP++='\0';
  2240.  
  2241.   if(gTimberView==TimberListView)
  2242.     FrmGetObjectBounds(frm,
  2243.                        GetObjectIdxFromActive(TimberListDateTrigger),
  2244.                        &bounds);
  2245.   else
  2246.     FrmGetObjectBounds(frm,
  2247.                        GetObjectIdxFromActive(TimberGraphDateTrigger),
  2248.                        &bounds);
  2249.   WinEraseRectangle(&bounds,0);
  2250.   charsToDraw = StrLen(printStr);
  2251.   titleWidth = FntCharsWidth (printStr, charsToDraw);
  2252.   WinDrawChars (printStr, 
  2253.                 charsToDraw,
  2254.                 (Int)bounds.topLeft.x+((Int)bounds.extent.x-titleWidth)/2,
  2255.                 bounds.topLeft.y);
  2256. }
  2257.  
  2258. static void TimberChangeDate(FormPtr frm, Int byMonths)
  2259. {
  2260.   Int month,day,year;
  2261.  
  2262.   month=(Int)gViewDate.month+byMonths%12;
  2263.   year=(Int)gViewDate.year+(Int)firstYear+byMonths/12;
  2264.   if ((year<firstYear)||((year==firstYear)&&(month<1))){
  2265.     month=1;
  2266.     year=firstYear;
  2267.   } else if ((year>lastYear)||((year==lastYear)&&(month>12))){
  2268.     month=12;
  2269.     year=lastYear;
  2270.   } else if(month<1){
  2271.     month+=12;
  2272.     year--;
  2273.   } else if (month>12){
  2274.     month-=12;
  2275.     year++;
  2276.   }    
  2277.   if((gViewDate.month!=month)||(gViewDate.year!=year-firstYear)){
  2278.     gViewDate.month=month;
  2279.     gViewDate.year=year-firstYear;
  2280.     TimberDrawViewDate(frm);
  2281.   } 
  2282. }
  2283.  
  2284. //
  2285. // Timber custom dialogs:
  2286. //
  2287.  
  2288. static void ViewDateDialog(FormPtr frm)
  2289. {
  2290.   SWord month,day,year;
  2291.   CharPtr title;
  2292.  
  2293.   title = TimberHandleLock(DmGetResource (strRsc, ChooseMonthTitle));
  2294.   month=gViewDate.month;
  2295.   day=gViewDate.day;
  2296.   year=gViewDate.year+firstYear;
  2297.   if (SelectDay (selectDayByMonth, &month, &day, &year, title)) {
  2298.     gViewDate.month=month;
  2299.     gViewDate.day=day;
  2300.     gViewDate.year=year-firstYear;
  2301.   }
  2302.   MemHandleUnlock(DmGetResource (strRsc, ChooseMonthTitle));
  2303. }
  2304.  
  2305. static void DoNewWeightDialog(Long *newIndex)
  2306. {
  2307.   ULong seconds;
  2308.   Word hitButton;
  2309.   FormPtr previousForm=FrmGetActiveForm();
  2310.   FormPtr frm;
  2311.   CharPtr weightStr;
  2312.   WeightType weight;
  2313.  
  2314.   if(newIndex)
  2315.     *newIndex=-1;
  2316.  
  2317.   // set dialog date to actual system date
  2318.   seconds=TimGetSeconds();
  2319.   DateSecondsToDate(seconds,&gChooseDate);
  2320.   
  2321.   // and on we go
  2322.   frm=FrmInitForm(TimberNewForm);
  2323.   NewWeightDialogDrawDate();
  2324.   FrmSetFocus(frm,GetObjectIdxFromAny(TimberNewForm,NewWeightField));
  2325.   FrmSetEventHandler(frm, TimberNewFormHandleEvent);
  2326.   hitButton=FrmDoDialog(frm);
  2327.   if(hitButton==NewAddButton){
  2328.     weightStr=FldGetTextPtr(GetObjectPtrFromAny(TimberNewForm,NewWeightField));
  2329.     AsciiToWeight(weightStr,&weight);
  2330.     AddRecord(gChooseDate,weight,gPrefPtr->ReplaceAsk,newIndex);
  2331.   }
  2332.   // done
  2333.   if (previousForm)
  2334.     FrmSetActiveForm(previousForm);
  2335.   FrmDeleteForm(frm);
  2336. }
  2337.  
  2338. static void NewWeightDialogDrawDate()
  2339. {
  2340.   CharPtr dateStr;
  2341.   ControlPtr ctl;
  2342.  
  2343.   ctl=GetObjectPtrFromAny(TimberNewForm,NewDateTrigger); 
  2344.   dateStr=CtlGetLabel(ctl);
  2345.   DateToDOWDMFormat(gChooseDate.month,
  2346.               gChooseDate.day,
  2347.               gChooseDate.year+firstYear,
  2348.               gPrefPtr->DateFormat,
  2349.               dateStr);
  2350.   CtlSetLabel(ctl,dateStr);
  2351. }
  2352.  
  2353. static void DoNewDateDialog(FormPtr frm)
  2354. {
  2355.   SWord month,day,year;
  2356.   CharPtr title;
  2357.  
  2358.   title = TimberHandleLock(DmGetResource (strRsc, ChooseDayTitle));
  2359.   month=gChooseDate.month;
  2360.   day=gChooseDate.day;
  2361.   year=gChooseDate.year+firstYear;
  2362.   if (SelectDay (selectDayByDay, &month, &day, &year, title)) {
  2363.     gChooseDate.month=month;
  2364.     gChooseDate.day=day;
  2365.     gChooseDate.year=year-firstYear;
  2366.   }
  2367.   MemHandleUnlock(DmGetResource (strRsc, ChooseDayTitle));
  2368. }
  2369.  
  2370. static void DoEditWeightDialog(UInt index,Long *newIndex)
  2371. {
  2372.   VoidHand recordH;
  2373.   RecordPtr recordP;
  2374.  
  2375.   Word hitButton;
  2376.   FormPtr previousForm=FrmGetActiveForm();
  2377.   FormPtr frm;
  2378.   FieldPtr fld;
  2379.   VoidHand weightH;
  2380.   Word fldIndex;
  2381.   DateType oldDate;
  2382.   WeightType oldWeight;
  2383.   CharPtr weightStr;
  2384.   WeightType weight;
  2385.   Boolean deleted;
  2386.  
  2387.   *newIndex=-1;
  2388.  
  2389.   // set dialog date to record date
  2390.   recordH=TimberQueryRecord(gTimberDB,index);
  2391.   recordP=TimberHandleLock(recordH);
  2392.   gChooseDate=recordP->date;
  2393.   oldDate=recordP->date;
  2394.   oldWeight=recordP->weight;
  2395.   MemHandleUnlock(recordH);
  2396.  
  2397.   // set dialog weight to record weight
  2398.   frm=FrmInitForm(TimberEditForm);
  2399.   fld=FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,EditWeightField));
  2400.   weightH=MemHandleNew(WeightSize);
  2401.   if(!weightH){
  2402.       FrmAlert(MemoryFullAlert);
  2403.       return;
  2404.   }
  2405.   weightStr=TimberHandleLock(weightH);
  2406.   WeightToAscii(weightStr,oldWeight,UseMaxAndCut);
  2407.   FldSetText(fld,weightH,0,WeightSize);
  2408.   FldSetSelection(fld,0,StrLen(weightStr));
  2409.  
  2410.   // and on we go
  2411.   EditWeightDialogDrawDate();
  2412.   
  2413.   fldIndex=GetObjectIdxFromAny(TimberEditForm,EditWeightField);
  2414.   FrmSetFocus(frm,fldIndex);
  2415.   FrmSetEventHandler(frm, TimberEditFormHandleEvent);
  2416.   hitButton=FrmDoDialog(frm);
  2417.  
  2418.   if(hitButton==EditOKButton){
  2419.     AsciiToWeight(weightStr,&weight);
  2420.     ChangeRecord(oldDate,gChooseDate,oldWeight,weight,gPrefPtr->ReplaceAsk,
  2421.                  newIndex);
  2422.   } else if(hitButton==EditDeleteButton) {
  2423.     DeleteRecord(oldDate,gPrefPtr->DeleteAsk,&deleted);
  2424.     if(deleted)
  2425.       *newIndex=-2;
  2426.   }
  2427.   
  2428.   // done
  2429.   if (previousForm)
  2430.     FrmSetActiveForm(previousForm);
  2431.   MemHandleUnlock(weightH);
  2432.   FrmDeleteForm(frm);
  2433. }
  2434.  
  2435. static void EditWeightDialogDrawDate()
  2436. {
  2437.   CharPtr dateStr;
  2438.   ControlPtr ctl;
  2439.  
  2440.   ctl=GetObjectPtrFromAny(TimberEditForm,EditDateTrigger); 
  2441.   dateStr=CtlGetLabel(ctl);
  2442.   DateToDOWDMFormat(gChooseDate.month,
  2443.               gChooseDate.day,
  2444.               gChooseDate.year+firstYear,
  2445.               gPrefPtr->DateFormat,
  2446.               dateStr);
  2447.   CtlSetLabel(ctl,dateStr);
  2448. }
  2449.  
  2450. static void DoPrefDialog(void)
  2451. {
  2452.   FormPtr frm;
  2453.   ListPtr listPrecisionList;
  2454.   ListPtr drawPrecisionList;
  2455.   ControlPtr listPrecisionLabel;
  2456.   ControlPtr drawPrecisionLabel;
  2457.   CharPtr listPrecisionStr;
  2458.   CharPtr drawPrecisionStr;
  2459.   ControlPtr globalMinMaxCheck;
  2460.   ControlPtr showGoalCheck;
  2461.   ControlPtr showAverageCheck;
  2462.   ControlPtr monthScrollCheck;
  2463.   ControlPtr replaceAskCheck;
  2464.   ControlPtr deleteAskCheck;
  2465.   Word hitButton;
  2466.  
  2467.   // init pointers 
  2468.   frm=FrmInitForm(TimberPrefForm);
  2469.   listPrecisionList=GetObjectPtrFromAny(TimberPrefForm,PrefListPrecisionList);
  2470.   drawPrecisionList=GetObjectPtrFromAny(TimberPrefForm,PrefDrawPrecisionList);
  2471.   listPrecisionLabel=GetObjectPtrFromAny(TimberPrefForm,
  2472.                                          PrefListPrecisionTrigger);
  2473.   drawPrecisionLabel=GetObjectPtrFromAny(TimberPrefForm,
  2474.                                          PrefDrawPrecisionTrigger);
  2475.   listPrecisionStr=CtlGetLabel(listPrecisionLabel);
  2476.   drawPrecisionStr=CtlGetLabel(drawPrecisionLabel);
  2477.   globalMinMaxCheck=GetObjectPtrFromAny(TimberPrefForm,PrefGlobalMinMaxCheck);
  2478.   showGoalCheck=GetObjectPtrFromAny(TimberPrefForm,PrefShowGoalCheck);
  2479.   showAverageCheck=GetObjectPtrFromAny(TimberPrefForm,PrefShowAverageCheck);
  2480.   monthScrollCheck=GetObjectPtrFromAny(TimberPrefForm,PrefMonthScrollCheck);
  2481.   replaceAskCheck=GetObjectPtrFromAny(TimberPrefForm,PrefReplaceAskCheck);
  2482.   deleteAskCheck=GetObjectPtrFromAny(TimberPrefForm,PrefDeleteAskCheck);
  2483.  
  2484.   // init values
  2485.   LstSetSelection(listPrecisionList,gPrefPtr->ListPrecision);
  2486.   LstSetSelection(drawPrecisionList,gPrefPtr->DrawPrecision);
  2487.   listPrecisionStr[0]=gPrefPtr->ListPrecision+'0';
  2488.   drawPrecisionStr[0]=gPrefPtr->DrawPrecision+'0';
  2489.   CtlSetLabel(listPrecisionLabel,listPrecisionStr);
  2490.   CtlSetLabel(drawPrecisionLabel,drawPrecisionStr);
  2491.   CtlSetValue(globalMinMaxCheck,gPrefPtr->GlobalMinMax);
  2492.   CtlSetValue(showGoalCheck,gPrefPtr->ShowGoal);
  2493.   CtlSetValue(showAverageCheck,gPrefPtr->ShowAverage);
  2494.   CtlSetValue(monthScrollCheck,gPrefPtr->MonthScroll);
  2495.   CtlSetValue(replaceAskCheck,gPrefPtr->ReplaceAsk);
  2496.   CtlSetValue(deleteAskCheck,gPrefPtr->DeleteAsk);
  2497.   
  2498.   if(FrmDoDialog(frm)==PrefOKButton){
  2499.     // retrieve new values
  2500.     gPrefPtr->ListPrecision=LstGetSelection(listPrecisionList);
  2501.     gPrefPtr->DrawPrecision=LstGetSelection(drawPrecisionList);
  2502.     gPrefPtr->GlobalMinMax=CtlGetValue(globalMinMaxCheck);
  2503.     gPrefPtr->ShowGoal=CtlGetValue(showGoalCheck);
  2504.     gPrefPtr->ShowAverage=CtlGetValue(showAverageCheck);
  2505.     gPrefPtr->MonthScroll=CtlGetValue(monthScrollCheck);
  2506.     gPrefPtr->ReplaceAsk=CtlGetValue(replaceAskCheck);
  2507.     gPrefPtr->DeleteAsk=CtlGetValue(deleteAskCheck);
  2508.   }
  2509.  
  2510.   FrmDeleteForm(frm);
  2511. }
  2512.  
  2513.  
  2514. static void DoGoalDialog()
  2515. {
  2516.   Word hitButton;
  2517.   FormPtr previousForm=FrmGetActiveForm();
  2518.   FormPtr frm;
  2519.   FieldPtr startFld,endFld;
  2520.   VoidHand startWeightH,endWeightH;
  2521.   Word startFldIndex,endFldIndex;
  2522.   CharPtr startWeightStr,endWeightStr;
  2523.   WeightType startWeight,endWeight;
  2524.   RecordType oldStart, oldEnd;
  2525.  
  2526.   /*
  2527.     There are some seemingly unnecessary labels without contents in this
  2528.     form. Without them, some of the necessary ones would not shuw up in
  2529.     POSE.
  2530.  
  2531.     I suspect this to be a bug either in pilrc or in the 3.0 debug rom.
  2532.   */
  2533.  
  2534.   frm=FrmInitForm(TimberGoalForm);
  2535.  
  2536.   // backup for cancel
  2537.   oldStart=gPrefPtr->GoalStart;
  2538.   oldEnd=gPrefPtr->GoalEnd;
  2539.   
  2540.   // set start weight
  2541.   startFld=FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,GoalStartWeightField));
  2542.   startWeightH=MemHandleNew(WeightSize);
  2543.   if(!startWeightH){
  2544.       FrmAlert(MemoryFullAlert);
  2545.       return;
  2546.   }
  2547.   startWeightStr=TimberHandleLock(startWeightH);
  2548.   WeightToAscii(startWeightStr,gPrefPtr->GoalStart.weight,UseMaxAndCut);
  2549.   FldSetText(startFld,startWeightH,0,WeightSize);
  2550.  
  2551.   // set end weight
  2552.   endFld=FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,GoalEndWeightField));
  2553.   endWeightH=MemHandleNew(WeightSize);
  2554.   if(!endWeightH){
  2555.       FrmAlert(MemoryFullAlert);
  2556.       return;
  2557.   }
  2558.   endWeightStr=TimberHandleLock(endWeightH);
  2559.   WeightToAscii(endWeightStr,gPrefPtr->GoalEnd.weight,UseMaxAndCut);
  2560.   FldSetText(endFld,endWeightH,0,WeightSize);
  2561.  
  2562.   // and on we go
  2563.   GoalDialogDrawDates();
  2564.   
  2565.   FrmSetEventHandler(frm, TimberGoalFormHandleEvent);
  2566.   hitButton=FrmDoDialog(frm);
  2567.  
  2568.   if(hitButton==GoalOKButton){
  2569.     //    startWeightStr=FldGetTextPtr(GetObjectPtrFromAny(TimberGoalForm,
  2570.     //                                            GoalStartWeightField));
  2571.     AsciiToWeight(startWeightStr,&gPrefPtr->GoalStart.weight);
  2572.   //endWeightStr=FldGetTextPtr(GetObjectPtrFromAny(TimberGoalForm,
  2573.   //                                            GoalEndWeightField));
  2574.     AsciiToWeight(endWeightStr,&gPrefPtr->GoalEnd.weight);
  2575.     gPrefPtr->ShowGoal=true;
  2576.   } else {
  2577.     gPrefPtr->GoalStart=oldStart;
  2578.     gPrefPtr->GoalEnd=oldEnd;
  2579.   }
  2580.   
  2581.   // done
  2582.   if (previousForm)
  2583.     FrmSetActiveForm(previousForm);
  2584.   MemHandleUnlock(startWeightH);
  2585.   MemHandleUnlock(endWeightH);
  2586.   FrmDeleteForm(frm);
  2587. }
  2588.  
  2589. static void GoalDialogDrawDates(void)
  2590. {
  2591.   CharPtr dateStr;
  2592.   ControlPtr ctl;
  2593.  
  2594.   // set start date
  2595.   ctl=GetObjectPtrFromAny(TimberGoalForm,GoalStartDateTrigger); 
  2596.   dateStr=CtlGetLabel(ctl);
  2597.   if(gPrefPtr->GoalStart.date.day==0){
  2598.     gPrefPtr->GoalStart.date=gViewDate;
  2599.     gPrefPtr->GoalStart.date.day=1;
  2600.   }
  2601.   DateToDOWDMFormat(gPrefPtr->GoalStart.date.month,
  2602.               gPrefPtr->GoalStart.date.day,
  2603.               gPrefPtr->GoalStart.date.year+firstYear,
  2604.               gPrefPtr->DateFormat,
  2605.               dateStr);
  2606.   CtlSetLabel(ctl,dateStr);
  2607.  
  2608.   // set end date
  2609.   ctl=GetObjectPtrFromAny(TimberGoalForm,GoalEndDateTrigger); 
  2610.   dateStr=CtlGetLabel(ctl);
  2611.   if(gPrefPtr->GoalEnd.date.day==0){
  2612.     gPrefPtr->GoalEnd.date=gViewDate;
  2613.     gPrefPtr->GoalEnd.date.day=DaysInMonth(gViewDate.month,
  2614.                                            gViewDate.year+firstYear);
  2615.   }
  2616.   DateToDOWDMFormat(gPrefPtr->GoalEnd.date.month,
  2617.               gPrefPtr->GoalEnd.date.day,
  2618.               gPrefPtr->GoalEnd.date.year+firstYear,
  2619.               gPrefPtr->DateFormat,
  2620.               dateStr);
  2621.   CtlSetLabel(ctl,dateStr);
  2622. }
  2623.  
  2624. static void DoDeleteDialog(Boolean *updateView)
  2625. {
  2626.   ULong seconds;
  2627.   Word hitButton;
  2628.   FormPtr previousForm=FrmGetActiveForm();
  2629.   FormPtr frm;
  2630.   DateType oldDate;
  2631.   WeightType oldWeight;
  2632.   ControlPtr ctl;
  2633.   Long index;
  2634.   Boolean isNew;
  2635.  
  2636.   if(updateView)
  2637.     *updateView=false;
  2638.  
  2639.   // set dialog date to actual system date
  2640.   seconds=TimGetSeconds();
  2641.   DateSecondsToDate(seconds,&gChooseDate);
  2642.  
  2643.   // set dialog weight to record weight
  2644.   frm=FrmInitForm(TimberDeleteForm);
  2645.  
  2646.   // uncheck prior checkbox
  2647.   ctl=FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,EditDeletePriorCheck));
  2648.   CtlSetValue(ctl,false);
  2649.  
  2650.   // and on we go
  2651.   DeleteDialogDrawDate();
  2652.   
  2653.   FrmSetEventHandler(frm, TimberDeleteFormHandleEvent);
  2654.   hitButton=FrmDoDialog(frm);
  2655.  
  2656.   if(hitButton==EditDeleteOKButton){
  2657.     if(CtlGetValue(ctl)){
  2658.       index=FindRecord(gChooseDate,&isNew);
  2659.       if(isNew)
  2660.         index--;
  2661.       if((index>=0)&&(index<DmNumRecords(gTimberDB))){
  2662.         if(index==0){
  2663.           if(isNew){
  2664.             VoidHand recordH;
  2665.             RecordPtr recordP;
  2666.  
  2667.             recordH=TimberQueryRecord(gTimberDB,0);
  2668.             recordP=TimberHandleLock(recordH);
  2669.             gChooseDate=recordP->date;
  2670.             MemHandleUnlock(recordH);
  2671.           }
  2672.           DeleteRecord(gChooseDate,gPrefPtr->DeleteAsk,updateView);
  2673.         } else {
  2674.           StrIToA(gBuf1,++index);
  2675.           if(FrmCustomAlert(DeleteOldAlert,gBuf1," "," ")==0){
  2676.             while(index--)
  2677.               DmRemoveRecord(gTimberDB,0);
  2678.             if(updateView)
  2679.               *updateView=true;
  2680.           }
  2681.         }
  2682.       }
  2683.     } else
  2684.       DeleteRecord(gChooseDate,gPrefPtr->DeleteAsk,updateView);
  2685.   }
  2686.  
  2687.   // done
  2688.   if (previousForm)
  2689.     FrmSetActiveForm(previousForm);
  2690.   FrmDeleteForm(frm);
  2691. }
  2692.  
  2693. static void DeleteDialogDrawDate(void)
  2694. {
  2695.   CharPtr dateStr;
  2696.   ControlPtr ctl;
  2697.  
  2698.   ctl=GetObjectPtrFromAny(TimberDeleteForm,EditDeleteDateTrigger); 
  2699.   dateStr=CtlGetLabel(ctl);
  2700.   DateToDOWDMFormat(gChooseDate.month,
  2701.               gChooseDate.day,
  2702.               gChooseDate.year+firstYear,
  2703.               gPrefPtr->DateFormat,
  2704.               dateStr);
  2705.   CtlSetLabel(ctl,dateStr);
  2706. }
  2707.  
  2708. //
  2709. // helpful system shortcuts:
  2710. //
  2711.  
  2712. static Word GetObjectIdxFromActive(Word objectID)
  2713. {
  2714.   return FrmGetObjectIndex(FrmGetActiveForm(), objectID);
  2715. }
  2716.  
  2717. static Word GetObjectIdxFromAny(Word formID, Word objectID)
  2718. {
  2719.   FormPtr form = FrmGetFormPtr(formID);
  2720.   return FrmGetObjectIndex(form, objectID);
  2721. }
  2722.  
  2723. static VoidPtr GetObjectPtrFromActive(Word objectID)
  2724. {
  2725.   FormPtr currentForm = FrmGetActiveForm();
  2726.   return FrmGetObjectPtr(currentForm, 
  2727.                          FrmGetObjectIndex(currentForm, objectID));
  2728. }
  2729.  
  2730. static VoidPtr GetObjectPtrFromAny(Word formID, Word objectID)
  2731. {
  2732.   FormPtr form = FrmGetFormPtr(formID);
  2733.     return FrmGetObjectPtr(form, 
  2734.         FrmGetObjectIndex(form,objectID));
  2735. }
  2736.  
  2737. //
  2738. // debugging:
  2739. //
  2740.  
  2741. static void DoDebugAlertInt(CharPtr s1, Long i)
  2742. {
  2743.   StrIToA(gBuf1,i);
  2744.   FrmCustomAlert(DebugAlert3,s1,gBuf1," ");
  2745. }
  2746.  
  2747. static void DoDebugAlertInt2(CharPtr s1, Long i, Long j)
  2748. {
  2749.   StrIToA(gBuf1,i);
  2750.   StrIToA(gBuf2,j);
  2751.   FrmCustomAlert(DebugAlert3,s1,gBuf1,gBuf2);
  2752. }
  2753.  
  2754. static void DoDebugAlert1(CharPtr s1)
  2755. {
  2756.   FrmCustomAlert(DebugAlert3,s1," "," ");
  2757. }
  2758.  
  2759. static void DoDebugAlert2(CharPtr s1, CharPtr s2)
  2760. {
  2761.   FrmCustomAlert(DebugAlert3,s1,s2," ");
  2762. }
  2763.  
  2764. static void DoDebugAlert3(CharPtr s1, CharPtr s2, CharPtr s3)
  2765. {
  2766.   FrmCustomAlert(DebugAlert3,s1,s2,s3);
  2767. }
  2768.  
  2769. static VoidHand TimberQueryRecord(DmOpenRef dbR,UInt index)
  2770. {
  2771.   ErrFatalDisplayIf(index>=DmNumRecords(dbR),"Query tried index too high");
  2772.   return DmQueryRecord(dbR,index);
  2773. }
  2774.  
  2775. static VoidHand TimberGetRecord(DmOpenRef dbR,UInt index)
  2776. {
  2777.   ErrFatalDisplayIf(index>=DmNumRecords(dbR),"Get tried index too high");
  2778.   return DmGetRecord(dbR,index);
  2779. }
  2780.  
  2781. static VoidPtr TimberHandleLock(VoidHand h)
  2782. {
  2783.   VoidPtr r;
  2784.   ErrFatalDisplayIf(!h,"Tried to lock NULL handle");
  2785.   r=MemHandleLock(h);
  2786.   ErrFatalDisplayIf(!r,"Tried to lock illegal handle");
  2787.   return r;
  2788. }
  2789.