home *** CD-ROM | disk | FTP | other *** search
/ linuxmafia.com 2016 / linuxmafia.com.tar / linuxmafia.com / pub / palmos / bart-1.3b2.tar.gz / bart-1.3b2.tar / bart-1.3b2 / bart.c next >
C/C++ Source or Header  |  2000-03-12  |  53KB  |  1,915 lines

  1. /* BART Scheduler Palm Pilot App
  2.  *    
  3.  * Copyright (c) 1999, 2000 Michael Wittman
  4.  * 
  5.  * Permission is hereby granted, free of charge, to any person obtaining a
  6.  * copy of this software and associated documentation files (the "Software"),
  7.  * to deal in the Software without restriction, including without limitation
  8.  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  9.  * and/or sell copies of the Software, and to permit persons to whom the
  10.  * Software is furnished to do so, subject to the following conditions:
  11.  * 
  12.  * The above copyright notice and this permission notice shall be included
  13.  * in all copies or substantial portions of the Software.
  14.  * 
  15.  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16.  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17.  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  18.  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
  19.  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  20.  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  21.  * OTHER DEALINGS IN THE SOFTWARE.
  22.  *    
  23.  */
  24.  
  25. #include <Pilot.h>
  26. #include "callback.h"
  27. #include "bartRsc.h"
  28. #include "bart.h"
  29. #include "bartdata.h"
  30.  
  31. #define PREFS_VERSION 1
  32.  
  33. #define UI_SETTINGS 2
  34.  
  35. /* This creator ID is registered with 3Com.  You MUST change it and
  36.    register a new ID if you modify this software and redistribute
  37.    it. */
  38. #define CREATOR_ID    'BART'
  39.  
  40.  
  41. /*
  42.  * UI-related data
  43.  */
  44.  
  45. static DmOpenRef DB = (DmOpenRef)0;
  46. static char DBCreationDateString[] = "10/20/2000"; /* just a placeholder */
  47.  
  48. static VoidHand StationNameArrayH = (VoidHand)0;
  49. static SWord StationNameListWidth = -1;
  50. static VoidHand StationAbbrevArrayH = (VoidHand)0;
  51. static VoidHand CategoryArrayH = (VoidHand)0;
  52. static SWord CategoryListWidth = -1;
  53.  
  54. static VoidHand InitialRecordH = (VoidHand)0;
  55. static Byte Categories = 0;
  56. static Byte Stations = 0;
  57.  
  58. #if UI_SETTINGS == 0
  59. /* debug initial user interface settings */
  60. static Word Category = 0;
  61. static Word FromStation = 7;
  62. static Word ToStation = 0;
  63. static Word LeavingArriving = 1;
  64. static Word BikeTrainsOnly = 0;
  65. static Word UITime = 7*60;
  66. #elif UI_SETTINGS == 1
  67. /* my default initial user interface settings */
  68. static Word Category = -1;
  69. static Word FromStation = 15;
  70. static Word ToStation = 15;
  71. static Word LeavingArriving = -1;
  72. static Word BikeTrainsOnly = 0;
  73. static Word UITime = -1;
  74. #else
  75. /* default initial user interface settings */
  76. static Word Category = -1;
  77. static Word FromStation = -1;
  78. static Word ToStation = -1;
  79. static Word LeavingArriving = -1;
  80. static Word BikeTrainsOnly = -1;
  81. static Word UITime = -1;
  82. #endif
  83.  
  84.  
  85. static Char FromStationTitle[] = "From Station";
  86. static Char ToStationTitle[] = "To Station";
  87. static CharPtr StationSelectFormTitle = NULL;
  88. /* points to variable to change in station select form */
  89. static WordPtr SelectedStation = NULL;
  90.  
  91. static Word NewUITime;
  92. static char UITimeString[] = "12:00pm";    /* time is just a placeholder */
  93.  
  94. static Word ResultsTime;
  95. static ResultsCellType resultsInfo[TABLE_ROWS][TABLE_COLUMNS];
  96. /* destination info for trains represented in resultsInfo.  we only
  97.    really need TABLE_ROWS/2 rows, but we use TABLE_ROWS so things
  98.    match up with resultsInfo. */
  99. static Byte destination[TABLE_ROWS][TABLE_COLUMNS/2];
  100.  
  101. static UInt dayEndTime;
  102.  
  103. typedef struct {
  104.    Word fromStation;
  105.    Word toStation;
  106.    Byte bikeTrainsOnly;
  107. } PrefsType;
  108.  
  109.  
  110. /*
  111.  * Stuff used for finding trips
  112.  */
  113.  
  114. static VoidHand infoRecordH = (VoidHand)0;
  115. static VoidHand transfersH = (VoidHand)0;
  116.  
  117. Byte station_bike_restrictions;
  118. StationBikeRestrictionType *station_bike_restriction;
  119.  
  120. Byte currentLines;
  121. LineDataType *lineInfo;
  122.  
  123. /* defined in find.c */
  124. void FindTrips(Byte source, Byte dest, UInt time, Boolean leaving,
  125.            Boolean bike_trains_only,
  126.            ResultsCellType resultsInfo[TABLE_ROWS][TABLE_COLUMNS],
  127.            Byte destination[TABLE_ROWS][TABLE_COLUMNS/2]);
  128.  
  129.  
  130. #if RESULTS_POPUP
  131. /* variables used for selection and popup windows in results form */
  132. static WinHandle resultsPopupBackingWinH = (WinHandle)0;
  133. static WinHandle resultsSelectionBackingWinH = (WinHandle)0;
  134. static RectangleType selectionRect;
  135. /* Use 0 ticks to mean uninitialized.  Tick counter only wraps around
  136.    every 497.1 days, so we should be OK. :) */
  137. static ULong resultsPopupExpireTick = 0;
  138. static ULong resultsPopupUpPenDownTick = 0;
  139. #endif
  140.  
  141. #define foreverTick        0xffffffff
  142. #define MySetNullEventTick(tick)                \
  143. if (nullEventTick > (tick) || nullEventTick <= TimGetTicks())    \
  144.    nullEventTick = (tick);
  145. static ULong nullEventTick = foreverTick;
  146.  
  147.  
  148. /*
  149.  *
  150.  * misc utility functions
  151.  *
  152.  */
  153.  
  154. #define TIMESTRING_AMPM        2
  155. #define TIMESTRING_AP        1
  156. #define TIMESTRING_NOAMPM    0
  157.  
  158. static void SetUITimeString(Char s[8], UInt time, Byte ampm_chars)
  159. {
  160.    UInt hour = (time / 60) % 12;
  161.    UInt min = time % 60;
  162.    Boolean pm = ((time / 60) / 12) % 2;    /* handles time > 24 hours */
  163.  
  164.    if (hour == 0) hour = 12;
  165.  
  166.    s += StrPrintF(s,"%d:",hour);
  167.    if (min < 10)
  168.       *s++ = '0';
  169.    s += StrPrintF(s,"%d",min);
  170.    if (ampm_chars >= 1) {
  171.       s+= StrPrintF(s,(pm ? "p" : "a"));
  172.       if (ampm_chars >= 2)
  173.      StrPrintF(s,"m");
  174.    }
  175. }
  176.  
  177. static SWord FindListWidth(char **string, UInt strings)
  178. {
  179.    SWord maxwidth = 0;
  180.    UInt i;
  181.    
  182.    for (i = 0; i < strings; i++) {
  183.       int width = FntLineWidth(string[i],StrLen(string[i]));
  184.       if (width > maxwidth)
  185.      maxwidth = width;
  186.    }
  187.    
  188.    return maxwidth + 3;        /* add padding to right side */
  189. }
  190.  
  191. /* find a record by a category and the one byte ID number in the initial
  192.    byte */
  193. static VoidHand FindRecord(DmOpenRef db, UInt category, Byte recordnum)
  194. {
  195.    UInt records = DmNumRecordsInCategory(DB,category);
  196.    UInt index = 0;
  197.    VoidHand h = (VoidHand)0;
  198.    BytePtr recptr;
  199.    Boolean found_record = false;
  200.    UInt i;
  201.  
  202.    for (i = 0; i < records; i++, index++) {
  203.       h = DmQueryNextInCategory(DB,&index,category);
  204.       ErrFatalDisplayIf(!h,"got null handle from DmQueryNextInCategory");
  205.       recptr = MemHandleLock(h);
  206.       if (*recptr == recordnum) {
  207.      /* found it */
  208.      found_record = true;
  209.      MemHandleUnlock(h);
  210.      break;
  211.       }
  212.       else {
  213.      MemHandleUnlock(h);
  214.       }
  215.    }
  216.  
  217.    if (found_record)
  218.       return h;
  219.    else
  220.       return (VoidHand)0;
  221. }
  222.  
  223.  
  224.  
  225. /*
  226.  *
  227.  * Main form stuff
  228.  *
  229.  */
  230.  
  231. static void MainFormInit()
  232. {
  233.    FormPtr frm = FrmGetActiveForm();
  234.  
  235.    char **StationNameArray, **CategoryArray;
  236.    ListPtr list_ptr;
  237.    Word list_idx;
  238.    RectangleType rect;
  239.  
  240.    StationNameArray = (char **)MemHandleLock(StationNameArrayH);
  241.    CategoryArray = (char **)MemHandleLock(CategoryArrayH);
  242.  
  243.    /* set up the From station selector */
  244.    if (FromStation != -1)
  245.       CtlSetLabel(FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Station_From_Selector)),
  246.           StationNameArray[FromStation]);
  247.  
  248.    /* set up the To station selector */
  249.    if (ToStation != -1)
  250.       CtlSetLabel(FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Station_To_Selector)),
  251.           StationNameArray[ToStation]);
  252.  
  253.    /* set up the category list */
  254.    list_idx = FrmGetObjectIndex(frm,ID_Category_List);
  255.    list_ptr = FrmGetObjectPtr(frm,list_idx);
  256.    LstSetListChoices(list_ptr,CategoryArray,Categories);
  257.    FrmGetObjectBounds(frm,list_idx,&rect);
  258.    rect.extent.x = CategoryListWidth;
  259.    FrmSetObjectBounds(frm,list_idx,&rect);
  260.    /* max list height of 12 is arbitrary; we only use 3 for BART */
  261.    LstSetHeight(list_ptr,(Categories <= 12 ? Categories : 12));
  262.    LstSetSelection(list_ptr,Category);
  263.    CtlSetLabel(FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Category_Popup)),
  264.            CategoryArray[Category]);
  265.  
  266.    /* set the time pushbutton to the time string */
  267.    CtlSetLabel(FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Time_Selector)),
  268.            UITimeString);
  269.  
  270.    /* set the arriving/leaving list */
  271.    list_idx = FrmGetObjectIndex(frm,ID_Arriving_Leaving_List);
  272.    list_ptr = FrmGetObjectPtr(frm,list_idx);
  273.    if (LeavingArriving != -1) {
  274.       LstSetSelection(list_ptr,LeavingArriving);
  275.       /* selection will always be visible */
  276.    }
  277.  
  278.    /* set the bike trains checkbox */
  279.    FrmSetControlValue(frm,FrmGetObjectIndex(frm,ID_Bike_Checkbox),BikeTrainsOnly != 0 && BikeTrainsOnly != -1);
  280.  
  281.    MemHandleUnlock(StationNameArrayH);
  282.    MemHandleUnlock(CategoryArrayH);
  283. }
  284.  
  285. /* save the UI settings to their corresponding global variables */
  286. static void MainFormSaveUISettings()
  287. {
  288.    FormPtr frm = FrmGetActiveForm();
  289.  
  290.    ListPtr list_ptr;
  291.    Word list_idx;
  292.    
  293.    /* save the category selection */
  294.    list_idx = FrmGetObjectIndex(frm,ID_Category_List);
  295.    list_ptr = FrmGetObjectPtr(frm,list_idx);
  296.    Category = LstGetSelection(list_ptr);
  297.  
  298.    /* time pushbutton is already saved in UITime */
  299.  
  300.    /* save the arriving/leaving selection */
  301.    list_idx = FrmGetObjectIndex(frm,ID_Arriving_Leaving_List);
  302.    list_ptr = FrmGetObjectPtr(frm,list_idx);
  303.    LeavingArriving = LstGetSelection(list_ptr);
  304.  
  305.    /* save the bike trains checkbox */
  306.    BikeTrainsOnly = FrmGetControlValue(frm,FrmGetObjectIndex(frm,ID_Bike_Checkbox));
  307. }
  308.  
  309. static void MainFormUnInit()
  310. {
  311.    MainFormSaveUISettings();
  312. }
  313.  
  314. static void SwapStations()
  315. {
  316.    Word temp_station;
  317.    CharPtr temp_label;
  318.    FormPtr frm = FrmGetActiveForm();
  319.    ControlPtr fromControl, toControl;
  320.  
  321.    temp_station = ToStation;
  322.    ToStation = FromStation;
  323.    FromStation = temp_station;
  324.           
  325.    fromControl = FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Station_From_Selector));
  326.    toControl = FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Station_To_Selector));
  327.           
  328.    temp_label = CtlGetLabel(toControl);
  329.    CtlSetLabel(FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Station_To_Selector)),
  330.            CtlGetLabel(fromControl));
  331.    CtlSetLabel(FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Station_From_Selector)),
  332.            temp_label);
  333. }
  334.  
  335. static Boolean MainFormHandleEvent (EventPtr e)
  336. {
  337.     Boolean handled = false;
  338.     FormPtr frm;
  339.  
  340.     CALLBACK_PROLOGUE;
  341.  
  342.     switch (e->eType) {
  343.     case frmOpenEvent:
  344.     frm = FrmGetActiveForm();
  345.     MainFormInit();
  346.     FrmDrawForm(frm);
  347.     handled = true;
  348.     break;
  349.  
  350.     case menuEvent:
  351.     MenuEraseStatus(NULL);
  352.  
  353.     switch(e->data.menu.itemID) {
  354.     case ID_About_Item:
  355.        {
  356.           VoidHand h;
  357.           CharPtr version;
  358.           
  359.           h = DmGet1Resource('tver',ID_Version);
  360.           ErrFatalDisplayIf(!h,"couldn't find version resource");
  361.           version = (CharPtr)MemHandleLock(h);
  362.           
  363.           FrmCustomAlert(ID_About_Alert,version,DBCreationDateString," ");
  364.           MemHandleUnlock(h);
  365.           DmReleaseResource(h);
  366.  
  367.           handled = true;
  368.           break;
  369.        }
  370.     case ID_BART_Info_Item:
  371.        FrmAlert(ID_BART_Info_Alert);
  372.        handled = true;
  373.        break;
  374. #if 0
  375.     case ID_Prefs_Item:
  376.        MainFormUnInit();
  377.        FrmGotoForm(PrefsForm);
  378.        handled = true;
  379.        break;
  380. #endif
  381.     }
  382.  
  383.     break;
  384.  
  385.     case ctlSelectEvent:
  386.     switch(e->data.ctlSelect.controlID) {
  387.     case ID_Station_From_Selector:
  388.        MainFormUnInit();
  389.        SelectedStation = &FromStation;
  390.        StationSelectFormTitle = FromStationTitle;
  391.        FrmGotoForm(StationSelectForm);
  392.        handled = true;
  393.        break;
  394.     case ID_Station_To_Selector:
  395.        MainFormUnInit();
  396.        SelectedStation = &ToStation;
  397.        StationSelectFormTitle = ToStationTitle;
  398.        FrmGotoForm(StationSelectForm);
  399.        handled = true;
  400.        break;
  401.     case ID_Swap_Button:
  402.        SwapStations();
  403.        handled = true;
  404.        break;
  405.     case ID_Time_Selector:
  406.        MainFormUnInit();
  407.        FrmGotoForm(TimeForm);
  408.        handled = true;
  409.        break;
  410.     case ID_Find:
  411.        /* save settings so we know what we're looking for when
  412.               finding trips */
  413.        MainFormSaveUISettings();
  414.        FrmPopupForm(ResultsForm);
  415.        handled = true;
  416.        break;
  417.     }
  418.     break;
  419.  
  420.     case keyDownEvent:
  421.        {
  422.       char secret_code[] = "SRC";
  423.       static Int letter = 0;
  424.       Word chr = e->data.keyDown.chr;
  425.  
  426. #if TUNING
  427.       if (chr >= '0' && chr <= '9') {
  428.          tuningSetting = chr - '0';
  429.          DebugPrintF("setting tuningSetting to %u",tuningSetting);
  430.       }
  431.       else if (chr >= 'a' && chr <= 'z') {
  432.          miscSetting = chr - 'a';
  433.          DebugPrintF("setting miscSetting to %u",miscSetting);
  434.       }
  435. #endif
  436.  
  437.       if (chr == secret_code[letter])
  438.          letter++;
  439.       else
  440.          letter = 0;
  441.  
  442.       if (letter == StrLen(secret_code)) {
  443.          letter = 0;
  444.          useSourceForDestination = !useSourceForDestination;
  445.          DebugPrintF("showing train %s in popup window",
  446.              useSourceForDestination ? "source" : "destination");
  447.       }
  448.       
  449.       handled = true;
  450.       break;
  451.        }
  452.  
  453.     case appStopEvent:
  454.        /* save settings so they will be stored in preferences properly
  455.       by StopApplication() */
  456.        MainFormSaveUISettings();
  457.        break;
  458.  
  459.     default:
  460.         break;
  461.     }
  462.  
  463.     CALLBACK_EPILOGUE;
  464.  
  465.     return handled;
  466. }
  467.  
  468.  
  469.  
  470. /*
  471.  *
  472.  * Time selection form stuff
  473.  *
  474.  */
  475.  
  476. static void TimeFormInit()
  477. {
  478.    FormPtr frm = FrmGetActiveForm();
  479.    ListPtr hour_list, min_list;
  480.    Word hour_item, min_item;
  481.  
  482.    /* we use NewUITime to record time selected in this form, setting
  483.       UITime to NewUITime when user taps OK */
  484.    NewUITime = UITime;
  485.    SetUITimeString(UITimeString,NewUITime,TIMESTRING_AMPM);
  486.    CtlSetLabel(FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Time_Button)),
  487.            UITimeString);
  488.  
  489.    hour_item = NewUITime/60;
  490.    hour_list = FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Time_Hour_List));
  491.    LstScrollList(hour_list,down,8);
  492.    LstMakeItemVisible(hour_list,hour_item);
  493.    LstSetSelection(hour_list,hour_item);
  494.  
  495.    min_item = ((NewUITime%60+2)/5); /* nearest 5 minutes */
  496.    min_list = FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Time_Min_List));
  497.    LstSetSelection(min_list,min_item);
  498. }
  499.  
  500. static void TimeFormUnInit()
  501. {
  502. }
  503.  
  504. static Boolean TimeFormHandleEvent (EventPtr e)
  505. {
  506.     Boolean handled = false;
  507.     FormPtr frm;
  508.  
  509.     ListPtr hour_list, min_list;
  510.     Word hour_item, min_item;
  511.  
  512.     CALLBACK_PROLOGUE
  513.  
  514.     switch (e->eType) {
  515.     case frmOpenEvent:
  516.     frm = FrmGetActiveForm();
  517.     TimeFormInit();
  518.     FrmDrawForm(frm);
  519.     handled = true;
  520.     break;
  521.  
  522.     case ctlSelectEvent:
  523.     switch(e->data.ctlSelect.controlID) {
  524.     case ID_Time_Now:
  525.        {
  526.           /* set time to current time */
  527.           DateTimeType datetime;
  528.           ULong seconds_today;
  529.           
  530.           frm = FrmGetActiveForm();
  531.           TimSecondsToDateTime(TimGetSeconds(),&datetime);
  532.           seconds_today = (ULong)datetime.hour*60*60 + (ULong)datetime.minute*60 + (ULong)datetime.second;
  533.           /* round to the nearest 5 min */
  534.           NewUITime = (seconds_today+150)/300 * 5;
  535.           SetUITimeString(UITimeString,NewUITime,TIMESTRING_AMPM);
  536.           CtlSetLabel(FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Time_Button)),UITimeString);
  537.  
  538.           hour_list = FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Time_Hour_List));
  539.           LstSetSelection(hour_list,NewUITime/60);
  540.           min_list = FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Time_Min_List));
  541.           LstSetSelection(min_list,(NewUITime%60)/5);
  542.           
  543.           handled = true;
  544.        }
  545.        break;
  546.     case ID_Time_OK:
  547.        UITime = NewUITime;
  548.     case ID_Time_Cancel:
  549.        TimeFormUnInit();
  550.        FrmGotoForm(MainForm);
  551.        handled = true;
  552.        break;
  553.     }
  554.  
  555.     break;
  556.     
  557.     case lstSelectEvent:
  558.        switch(e->data.lstSelect.listID) {
  559.     case ID_Time_Hour_List:
  560.        frm = FrmGetActiveForm();
  561.        hour_list = FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Time_Hour_List));
  562.        hour_item = LstGetSelection(hour_list);
  563.        NewUITime = hour_item*60 + NewUITime%60;
  564.        SetUITimeString(UITimeString,NewUITime,TIMESTRING_AMPM);
  565.        CtlSetLabel(FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Time_Button)),UITimeString);
  566.        handled = true;
  567.        break;
  568.     case ID_Time_Min_List:
  569.        frm = FrmGetActiveForm();
  570.        min_list = FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Time_Min_List));
  571.        min_item = LstGetSelection(min_list);
  572.        NewUITime = (NewUITime/60)*60 + min_item*5;
  573.        SetUITimeString(UITimeString,NewUITime,TIMESTRING_AMPM);
  574.        CtlSetLabel(FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Time_Button)),UITimeString);
  575.        handled = true;
  576.        break;
  577.        }
  578.  
  579.        break;
  580.  
  581.     default:
  582.         break;
  583.     }
  584.  
  585.     CALLBACK_EPILOGUE
  586.  
  587.     return handled;
  588. }
  589.  
  590.  
  591.  
  592. #if 0
  593. /*
  594.  *
  595.  * Prefs form stuff
  596.  *
  597.  */
  598.  
  599. static void PrefsFormInit()
  600. {
  601.    FormPtr frm = FrmGetActiveForm();
  602.    PrefsType prefs;
  603.    Word size = sizeof(prefs);
  604.    char **StationNameArray;
  605.    RectangleType rect;
  606.    ListPtr list_ptr;
  607.    Word list_idx;
  608.    
  609.    StationNameArray = (char **)MemHandleLock(StationNameArrayH);
  610.    
  611.    if (PrefGetAppPreferences(CREATOR_ID,0,(VoidPtr)&prefs,&size,true)
  612.        == noPreferenceFound) {
  613.       prefs.station = -1;
  614.    }
  615.    
  616.    /* set up the station list */
  617.    list_idx = FrmGetObjectIndex(frm,ID_Prefs_Station_List);
  618.    list_ptr = FrmGetObjectPtr(frm,list_idx);
  619.    LstSetListChoices(list_ptr,StationNameArray,Stations);
  620.    FrmGetObjectBounds(frm,list_idx,&rect);
  621.    rect.extent.x = StationNameListWidth;
  622.    FrmSetObjectBounds(frm,list_idx,&rect);
  623.    LstSetSelection(list_ptr,prefs.station);
  624.    if (prefs.station != -1) {
  625.       LstMakeItemVisible(list_ptr,prefs.station);
  626.       CtlSetLabel(FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Prefs_Station_Popup)),
  627.           StationNameArray[prefs.station]);
  628.    }
  629.    
  630.    MemHandleUnlock(StationNameArrayH);
  631. }
  632.  
  633. static void PrefsFormUnInit()
  634. {
  635. }
  636.  
  637. static Boolean PrefsFormHandleEvent (EventPtr e)
  638. {
  639.     Boolean handled = false;
  640.     FormPtr frm;
  641.     PrefsType prefs;
  642.     PrefsType old_prefs;
  643.     Word size = sizeof(prefs);
  644.  
  645.     ListPtr station_list;
  646.  
  647.     CALLBACK_PROLOGUE
  648.  
  649.     switch (e->eType) {
  650.     case frmOpenEvent:
  651.     frm = FrmGetActiveForm();
  652.     PrefsFormInit();
  653.     FrmDrawForm(frm);
  654.     handled = true;
  655.     break;
  656.  
  657.     case ctlSelectEvent:
  658.     switch(e->data.ctlSelect.controlID) {
  659.     case ID_Prefs_OK:
  660.        frm = FrmGetActiveForm();
  661.  
  662.        /* get old prefs */
  663.        if (PrefGetAppPreferences(CREATOR_ID,0,(VoidPtr)&old_prefs,&size,true)
  664.            == noPreferenceFound) {
  665.           old_prefs.station = -1;
  666.        }
  667.  
  668.        /* set new prefs if a list item was selected */
  669.        station_list = FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Prefs_Station_List));
  670.        prefs.station = LstGetSelection(station_list);
  671.        if (prefs.station != -1) {
  672.           PrefSetAppPreferences(CREATOR_ID,0,PREFS_VERSION,(VoidPtr)&prefs,sizeof(prefs),true);
  673.           /* change To/From UI settings to new home station if the old
  674.          both of the old values were old home station */
  675.           if (old_prefs.station != -1 &&
  676.           FromStation == old_prefs.station &&
  677.           ToStation == old_prefs.station) {
  678.          FromStation = prefs.station;
  679.          ToStation = prefs.station;
  680.           }
  681.        }
  682.     case ID_Prefs_Cancel:
  683.        PrefsFormUnInit();
  684.        FrmGotoForm(MainForm);
  685.        handled = true;
  686.        break;
  687.     }
  688.  
  689.     break;
  690.     
  691.     default:
  692.         break;
  693.     }
  694.  
  695.     CALLBACK_EPILOGUE
  696.  
  697.     return handled;
  698. }
  699. #endif
  700.  
  701.  
  702.  
  703. /*
  704.  *
  705.  * Results form stuff.  Nearly all of the logic for actually finding trips
  706.  * is in the file find.c.
  707.  *
  708.  */
  709.  
  710. /* look up fare in the fare matrix in the database */
  711. static SWord GetFare(Byte category, Byte station1, Byte station2)
  712. {
  713.    VoidHand handle;
  714.    BytePtr recptr = NULL;
  715.    SWord multiplier, min_fare;
  716.    SWord fare;
  717.    BytePtr fareptr = NULL;
  718.    Byte s1, s2;
  719.  
  720.    handle = FindRecord(DB,category,FARES_RECORD_NUMBER);
  721.    if (!handle)
  722.       handle = FindRecord(DB,GENERAL_CATEGORY,FARES_RECORD_NUMBER);
  723.  
  724.    ErrFatalDisplayIf(!handle,"couldn't find fare record!");
  725.    /* no fare record available */
  726.    if (!handle)
  727.       return -1;
  728.    
  729.    recptr = MemHandleLock(handle);
  730.    multiplier = *(recptr+1);    /* skip initial record number */
  731.    min_fare = *(recptr+2);
  732.    min_fare *= multiplier;
  733.    fareptr = recptr+3;
  734.  
  735.    s1 = min(station1,station2);
  736.    s2 = max(station1,station2);
  737.  
  738.    fare = fareptr[s1*(Stations-s1) + s1*(s1+1)/2 + s2-s1]*multiplier + min_fare;
  739.    MemHandleUnlock(handle);
  740.    
  741.    return fare;
  742. }
  743.  
  744. static void SetFareLabel(SWord fare, Boolean excursion)
  745. {
  746.    FormPtr frm = FrmGetActiveForm();
  747.  
  748.    if (fare >= 0) {
  749.       char string[] = "Fare: $3.80"; /* max string length */
  750.       char *p = string;
  751.       UInt dollars = (UInt)fare / 100;
  752.       UInt cents = (UInt)fare % 100;
  753.       
  754.       p += StrPrintF(p,"Fare: $%u.",dollars);
  755.       if (cents < 10)
  756.      *p++ = '0';
  757.       p += StrPrintF(p,"%u",cents);
  758.    
  759.       FrmCopyLabel(frm,ID_Results_Fare_Label,string);
  760.       FrmShowObject(frm,FrmGetObjectIndex(frm,ID_Results_Fare_Label));
  761.    }
  762.    else {
  763.       /* no fare, so hide label */
  764.       FrmHideObject(frm,FrmGetObjectIndex(frm,ID_Results_Fare_Label));
  765.    }
  766. }
  767.  
  768. /* reads data for all the lines in a category into the lineInfo array */
  769. static void SetLineData(Byte category)
  770. {
  771.    VoidHand handle = (VoidHand)0;
  772.    Byte *recptr = NULL;
  773.    Byte *trans_recptr = NULL;
  774.    Byte curr_transfer_line = 0, transfer_lines;
  775.    Byte i;
  776.  
  777.    currentLines = 0;
  778.    
  779.    infoRecordH = FindRecord(DB,category,INFO_RECORD_NUMBER);
  780.    ErrFatalDisplayIf(!infoRecordH,"couldn't find info record");
  781.  
  782.    recptr = MemHandleLock(infoRecordH);
  783.    recptr++;            /* skip initial record number */
  784.    currentLines = *recptr++;
  785.    station_bike_restrictions = *recptr++;
  786.    recptr++;            /* skip null byte */
  787.    station_bike_restriction = (StationBikeRestrictionType *)recptr;
  788.    /* we're done with info record */
  789.  
  790.    lineInfo = MemPtrNew(currentLines*sizeof(LineDataType));
  791.    ErrFatalDisplayIf(!lineInfo,"couldn't allocate memory for lineInfo array");
  792.  
  793.    /* look first for a transfer record for this category number; if
  794.       not found, use global transfer record */
  795.    transfersH = FindRecord(DB,category,TRANSFER_RECORD_NUMBER);
  796.    if (!transfersH)
  797.       transfersH = FindRecord(DB,GENERAL_CATEGORY,TRANSFER_RECORD_NUMBER);
  798.    ErrFatalDisplayIf(!transfersH,"couldn't find transfer info record");
  799.    trans_recptr = MemHandleLock(transfersH);
  800.    trans_recptr++;        /* skip record number */
  801.    transfer_lines = *trans_recptr++;
  802.    
  803.    for (i = 0; i < currentLines; i++) {
  804.       Byte *ptr;
  805.       LineDataType *line = &lineInfo[i];
  806.       
  807.       handle = FindRecord(DB,category,INITIAL_LINE_RECORD_NUMBER+i);
  808.       ErrFatalDisplayIf(!handle,"couldn't find a line record (see README)");
  809.       line->record_handle = handle;
  810.       
  811.       ptr = MemHandleLock(handle);
  812.       ptr++;            /* skip record number */
  813.       line->stations = *ptr++;
  814.       line->station = ptr;
  815.       ptr += line->stations;
  816.       line->inc_vectors = *ptr++;
  817.       line->inc_vector = ptr;
  818.       ptr += line->inc_vectors*line->stations;
  819.       line->nobike_pairs = *ptr++;
  820.       line->nobike_pair = (Byte (*)[2])ptr;
  821.       ptr += 2*line->nobike_pairs;
  822.       /* the next word may not be aligned */
  823.       line->initial_time = (UInt)*ptr++ << 8;
  824.       line->initial_time |= *ptr++;
  825.       line->train_blocks = *ptr++;
  826.       line->train_block = (TrainBlockType *)ptr;
  827.  
  828.       line->trains = 0;
  829.       line->departure_time_handle = (VoidHand)0;
  830.  
  831.       /* record transfers only if there are some for this line */
  832.       if (curr_transfer_line < transfer_lines && *trans_recptr == i) {
  833.      trans_recptr++;
  834.      line->transfers = *trans_recptr++;
  835.      line->transfer = (TransferInfoType *)trans_recptr;
  836.      trans_recptr += sizeof(TransferInfoType)*line->transfers;
  837.      curr_transfer_line++;
  838.       }
  839.       else {
  840.      line->transfers = 0;
  841.       }
  842.    }
  843. }
  844.  
  845. /* Allocate memory for, and set non-compacted lists of departure times,
  846.    inc_vector_idx's, and nobike_pair_idx's.  This and the corresponding
  847.    deallocate function are called from functions in find.c. */
  848. void AllocateDepartureTimes(LineDataType *line)
  849. {
  850.    UInt time = line->initial_time;
  851.    UInt *departure_time;
  852.    Byte *inc_vector_idx;
  853.    Byte *nobike_pair_idx;
  854.    Byte train;
  855.    Byte i, j;
  856.  
  857.    ErrFatalDisplayIf(line->departure_time_handle,"attempt to set departure times twice");
  858.    
  859.    line->trains = 0;
  860.    for (i = 0; i < line->train_blocks; i++) {
  861.       line->trains += line->train_block[i].trains;
  862.    }
  863.    
  864.    line->departure_time_handle = MemHandleNew(line->trains*sizeof(UInt));
  865.    ErrFatalDisplayIf(!line->departure_time_handle,"unable to allocate handle for departure times");
  866.    departure_time = MemHandleLock(line->departure_time_handle);
  867.    
  868.    line->inc_vector_idx_handle = MemHandleNew(line->trains*sizeof(Byte));
  869.    ErrFatalDisplayIf(!line->inc_vector_idx_handle,"unable to allocate handle for departure times");
  870.    inc_vector_idx = MemHandleLock(line->inc_vector_idx_handle);
  871.  
  872.    line->nobike_pair_idx_handle = MemHandleNew(line->trains*sizeof(Byte));
  873.    ErrFatalDisplayIf(!line->nobike_pair_idx_handle,"unable to allocate handle for departure times");
  874.    nobike_pair_idx = MemHandleLock(line->nobike_pair_idx_handle);
  875.  
  876.    train = 0;
  877.    for (i = 0; i < line->train_blocks; i++) {
  878.       TrainBlockType *block = &line->train_block[i];
  879.       for (j = 0; j < block->trains; j++) {
  880.      inc_vector_idx[train] = block->inc_vector_idx;
  881.      nobike_pair_idx[train] = block->nobike_pair_idx;
  882.      time += block->time_diff;
  883.      departure_time[train] = time;
  884.      train++;
  885.       }
  886.    }
  887.  
  888.    MemHandleUnlock(line->departure_time_handle);
  889.    MemHandleUnlock(line->inc_vector_idx_handle);
  890.    MemHandleUnlock(line->nobike_pair_idx_handle);
  891. }
  892.  
  893. void FreeDepartureTimes(LineDataType *line)
  894. {
  895.    ErrFatalDisplayIf(!line->departure_time_handle,"attempt to deallocate unset departure times");
  896.    
  897.    line->trains = 0;
  898.    MemHandleFree(line->departure_time_handle);
  899.    MemHandleFree(line->inc_vector_idx_handle);
  900.    MemHandleFree(line->nobike_pair_idx_handle);
  901.    line->departure_time_handle = (VoidHand)0;
  902.    line->inc_vector_idx_handle = (VoidHand)0;
  903.    line->nobike_pair_idx_handle = (VoidHand)0;
  904. }
  905.  
  906. static void UnSetLineData()
  907. {
  908.    Byte i;
  909.  
  910.    ErrFatalDisplayIf(!lineInfo,"attempt to free non-existent lineInfo array");
  911.    for (i = 0; i < currentLines; i++) {
  912.       ErrFatalDisplayIf(!lineInfo[i].record_handle,"attempt to unlock non-existent handle");
  913.       MemHandleUnlock(lineInfo[i].record_handle);
  914.       if (lineInfo[i].departure_time_handle)
  915.      FreeDepartureTimes(&lineInfo[i]);
  916.    }
  917.    MemPtrFree(lineInfo);
  918.    MemHandleUnlock(transfersH);
  919.    MemHandleUnlock(infoRecordH);
  920.    
  921.    lineInfo = NULL;
  922.    currentLines = 0;
  923. }
  924.  
  925.  
  926. static void ClearResultsInfo()
  927. {
  928.    UInt i, j;
  929.  
  930.    for (i = 0; i < TABLE_ROWS; i++) {
  931.       for (j = 0; j < TABLE_COLUMNS; j++) {
  932.      resultsInfo[i][j].type = resultsCellEmpty;
  933.       }
  934.    }
  935. }
  936.  
  937. static Err TableLoadItem(VoidPtr table, Word row, Word column,
  938.              Boolean editable, VoidHand *dataH, WordPtr dataOffset,
  939.              WordPtr dataSize, FieldPtr fld)
  940. {
  941.    VoidHand handle = (VoidHand)0;
  942.    FieldAttrType attr;
  943.    
  944.    CALLBACK_PROLOGUE;
  945.  
  946.    FldGetAttributes(fld,&attr);
  947.    
  948.    switch (resultsInfo[row][column].type) {
  949.    case resultsCellEmpty:
  950.       {
  951.      *dataH = (VoidHand)0;
  952.      *dataOffset = 0;
  953.      *dataSize = 0;
  954.  
  955.      attr.editable = 0;
  956.      attr.underlined = 0;
  957.      attr.singleLine = 1;
  958.      break;
  959.       }
  960.    case resultsCellStation:
  961.       {
  962.      char **StationAbbrevArray = MemHandleLock(StationAbbrevArrayH);
  963.      char *abbrev = StationAbbrevArray[resultsInfo[row][column].data.station.number];
  964.      
  965.      handle = MemHandleNew(StrLen(abbrev)+1);
  966.      ErrFatalDisplayIf(!handle,"Got null handle trying to allocate space for table entry!");
  967.      StrCopy(MemHandleLock(handle),abbrev);
  968.      MemHandleUnlock(handle);
  969.      MemHandleUnlock(StationAbbrevArrayH);
  970.    
  971.      *dataH = handle;
  972.      *dataOffset = 0;
  973.      *dataSize = MemHandleSize(*dataH);
  974.  
  975.      attr.editable = 0;
  976.      attr.underlined = 0;
  977.      attr.singleLine = 1;
  978.      attr.justification = rightAlign;
  979.      break;
  980.       }
  981.    case resultsCellTime:
  982.       {
  983.      /* max time string length is 6 chars + null ("12:00p") */
  984.      handle = MemHandleNew(7);
  985.      ErrFatalDisplayIf(!handle,"Got null handle trying to allocate space for table entry!");
  986.      SetUITimeString(MemHandleLock(handle),resultsInfo[row][column].data.time.time,TIMESTRING_NOAMPM);
  987.      MemHandleUnlock(handle);
  988.    
  989.      *dataH = handle;
  990.      *dataOffset = 0;
  991.      *dataSize = MemHandleSize(*dataH);
  992.  
  993.      attr.editable = 0;
  994.      attr.underlined = 0;
  995.      attr.singleLine = 1;
  996.      attr.justification = rightAlign;
  997.      break;
  998.       }
  999. #if TUNING
  1000.    case resultsCellNumber:
  1001.       {
  1002.      handle = MemHandleNew(10);
  1003.      ErrFatalDisplayIf(!handle,"Got null handle trying to allocate space for table entry!");
  1004.      StrPrintF(MemHandleLock(handle),"%lu",resultsInfo[row][column].data.number.number);
  1005.      MemHandleUnlock(handle);
  1006.    
  1007.      *dataH = handle;
  1008.      *dataOffset = 0;
  1009.      *dataSize = MemHandleSize(*dataH);
  1010.  
  1011.      attr.editable = 0;
  1012.      attr.underlined = 0;
  1013.      attr.singleLine = 1;
  1014.      attr.justification = rightAlign;
  1015.      break;
  1016.       }
  1017. #endif
  1018.    }
  1019.  
  1020.    FldSetAttributes(fld,&attr);
  1021.    
  1022.    CALLBACK_EPILOGUE;
  1023.  
  1024.    return 0;
  1025. }
  1026.  
  1027. static void UpdateResultsTime(Boolean drawField)
  1028. {
  1029.    FormPtr frm = FrmGetActiveForm();
  1030.    DateTimeType datetime;
  1031.    Word NewResultsTime;
  1032.    ULong updateticks;
  1033.    char *fieldstring;
  1034.    FieldPtr fieldP = FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Results_Time));
  1035.    VoidHand fieldH = (VoidHand)0;
  1036.    
  1037.    TimSecondsToDateTime(TimGetSeconds(),&datetime);
  1038.    NewResultsTime = (ULong)datetime.hour*60 + (ULong)datetime.minute;
  1039.  
  1040.    /* Generate a null event at the next minute. */
  1041.    updateticks = TimGetTicks() + (60-(ULong)datetime.second)*SysTicksPerSecond();
  1042.    MySetNullEventTick(updateticks);
  1043.    
  1044.    if (NewResultsTime != ResultsTime) {
  1045.       ResultsTime = NewResultsTime;
  1046.  
  1047.       if (!(fieldH = (VoidHand)FldGetTextHandle(fieldP))) {
  1048.      fieldH = MemHandleNew(8*sizeof(char));
  1049.       }
  1050.  
  1051.       fieldstring = MemHandleLock(fieldH);
  1052.       SetUITimeString(fieldstring,ResultsTime,TIMESTRING_AMPM);
  1053.       MemHandleUnlock(fieldH);
  1054.       FldSetTextHandle(fieldP,(Handle)fieldH);
  1055.       if (drawField)
  1056.      FldDrawField(fieldP);
  1057.    }
  1058. }
  1059.  
  1060.  
  1061.  
  1062. #if RESULTS_POPUP
  1063. /* schedule a nilEvent for later to erase the popup window and selection */
  1064. static void SetResultsPopupExpireTick()
  1065. {
  1066.    ULong t1, t2;
  1067.  
  1068.    t1 = resultsPopupUpPenDownTick + RESULTS_POPUP_TIME1*SysTicksPerSecond();
  1069.    t2 = TimGetTicks() + RESULTS_POPUP_TIME2*SysTicksPerSecond();
  1070.    resultsPopupExpireTick = max(t1,t2);
  1071.    MySetNullEventTick(resultsPopupExpireTick);
  1072. }
  1073.  
  1074. /* Draw the results popup window with the train destination, and times and
  1075.    stations, for the segment selected. */
  1076. static void DrawLinePopup(Byte dest_station,
  1077.               Byte from_station, UInt from_time,
  1078.               Byte to_station, UInt to_time)
  1079. {
  1080.    RectangleType r;
  1081.    WinHandle winH, drawWinH;
  1082.    FontID prevFont = FntSetFont(stdFont);
  1083.    short fontlineheight = FntLineHeight();
  1084.    Word error;
  1085.    
  1086.    drawWinH = WinSetDrawWindow(WinGetDisplayWindow());
  1087.    
  1088.    WinGetWindowExtent(&r.extent.x,&r.extent.y);
  1089.    r.topLeft.x = 0;
  1090.    r.topLeft.y = 0;
  1091.    r.extent.y = fontlineheight*3 + 4;
  1092.  
  1093.    winH = WinSaveBits(&r,&error);
  1094.    ErrFatalDisplayIf(!winH,"unable to save window");
  1095.  
  1096.    WinEraseRectangle(&r,0);
  1097.    /* make room for frame */
  1098.    r.topLeft.x++;
  1099.    r.topLeft.y++;
  1100.    r.extent.x -= 3;
  1101.    r.extent.y -= 3;
  1102.    WinDrawRectangleFrame(popupFrame,&r);
  1103.  
  1104.    {
  1105.       char **StationNameArray;
  1106.       char string[8];
  1107.       int destwidth;
  1108.  
  1109.       StationNameArray = (char **)MemHandleLock(StationNameArrayH);
  1110.  
  1111.       /* we implicitly assume bold font is same height as standard font here */
  1112.       FntSetFont(boldFont);
  1113.       destwidth = FntCharsWidth(StationNameArray[dest_station],
  1114.                 StrLen(StationNameArray[dest_station]));
  1115.       WinDrawChars(StationNameArray[dest_station],
  1116.            StrLen(StationNameArray[dest_station]),
  1117.            r.topLeft.x+2, r.topLeft.y);
  1118.       FntSetFont(stdFont);
  1119.       WinDrawChars(" train",6,r.topLeft.x+2+destwidth,r.topLeft.y);
  1120.       
  1121.       SetUITimeString(string,from_time,TIMESTRING_AMPM);
  1122.       WinDrawChars(string, StrLen(string), r.topLeft.x+2, r.topLeft.y + fontlineheight);
  1123.       WinDrawChars(StationNameArray[from_station],
  1124.            StrLen(StationNameArray[from_station]),
  1125.            r.topLeft.x+2+37, r.topLeft.y + fontlineheight);
  1126.       
  1127.       SetUITimeString(string,to_time,TIMESTRING_AMPM);
  1128.       WinDrawChars(string, StrLen(string), r.topLeft.x+2, r.topLeft.y + 2*fontlineheight);
  1129.       WinDrawChars(StationNameArray[to_station],
  1130.            StrLen(StationNameArray[to_station]),
  1131.            r.topLeft.x+2+37, r.topLeft.y + 2*fontlineheight);
  1132.       
  1133.       MemHandleUnlock(StationNameArrayH);
  1134.    }
  1135.  
  1136.    WinSetDrawWindow(drawWinH);
  1137.    FntSetFont(prevFont);
  1138.    
  1139.    resultsPopupBackingWinH = winH;
  1140. }
  1141.  
  1142. /* Draw the results popup window with the full name of the station
  1143.    abbreviation selected. */
  1144. static void DrawStationPopup(Byte station)
  1145. {
  1146.    RectangleType r;
  1147.    WinHandle winH, drawWinH;
  1148.    FontID prevFont = FntSetFont(boldFont);
  1149.    short fontlineheight = FntLineHeight();
  1150.    Word error;
  1151.    
  1152.    drawWinH = WinSetDrawWindow(WinGetDisplayWindow());
  1153.    
  1154.    WinGetWindowExtent(&r.extent.x,&r.extent.y);
  1155.    r.topLeft.x = 0;
  1156.    r.topLeft.y = 0;
  1157.    r.extent.y = fontlineheight + 4;
  1158.  
  1159.    winH = WinSaveBits(&r,&error);
  1160.    ErrFatalDisplayIf(!winH,"unable to save window");
  1161.  
  1162.    WinEraseRectangle(&r,0);
  1163.    r.topLeft.x++;
  1164.    r.topLeft.y++;
  1165.    r.extent.x -= 3;
  1166.    r.extent.y -= 3;
  1167.    WinDrawRectangleFrame(popupFrame,&r);
  1168.  
  1169.    {
  1170.       char **StationNameArray;
  1171.  
  1172.       StationNameArray = (char **)MemHandleLock(StationNameArrayH);
  1173.       WinDrawChars(StationNameArray[station],
  1174.            StrLen(StationNameArray[station]),
  1175.            r.topLeft.x+2, r.topLeft.y);
  1176.       MemHandleUnlock(StationNameArrayH);
  1177.    }
  1178.  
  1179.    WinSetDrawWindow(drawWinH);
  1180.    FntSetFont(prevFont);
  1181.    
  1182.    resultsPopupBackingWinH = winH;
  1183. }
  1184.  
  1185. /* draw an oval around the given rectangle */
  1186. static void DrawSelection(RectanglePtr rptr)
  1187. {
  1188.    RectangleType r = *rptr;
  1189.    WinHandle winH;
  1190.    Word selection_width = 2;
  1191.    Word error;
  1192.  
  1193.    RctInsetRectangle(&r,-selection_width);
  1194.    selectionRect = r;
  1195.    winH = WinSaveBits(&r,&error);
  1196.    RctInsetRectangle(&r,selection_width);
  1197.    WinDrawRectangleFrame(roundFrame,&r);
  1198.    
  1199.    resultsSelectionBackingWinH = winH;
  1200. }
  1201.  
  1202. /* erase popup window and selection oval */
  1203. static void EraseResultsPopup(Boolean force)
  1204. {
  1205.    if (resultsPopupBackingWinH) {
  1206.       if (force || (resultsPopupUpPenDownTick == 0 && TimGetTicks() >= resultsPopupExpireTick)) {
  1207.      WinHandle drawWinH = WinSetDrawWindow(WinGetDisplayWindow());
  1208.      WinRestoreBits(resultsPopupBackingWinH,0,0);
  1209.      resultsPopupBackingWinH = (WinHandle)0;
  1210.      WinSetDrawWindow(drawWinH);
  1211.      
  1212.      WinRestoreBits(resultsSelectionBackingWinH,
  1213.             selectionRect.topLeft.x,selectionRect.topLeft.y);
  1214.      resultsSelectionBackingWinH = (WinHandle)0;
  1215.       }
  1216.       else {
  1217.      MySetNullEventTick(resultsPopupExpireTick);
  1218.       }
  1219.    }
  1220. }
  1221.  
  1222. /* draw popup window and selection oval */
  1223. static void DrawResultsPopup(Word row, Word column)
  1224. {
  1225.    FormPtr frm = FrmGetActiveForm();
  1226.    TablePtr tableP = FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Results_Table));
  1227.    RectangleType r;
  1228.  
  1229.    if (resultsInfo[row][column].type == resultsCellTime) {
  1230.       Word c1, c2;
  1231.       Word station_row = 0;
  1232.       RectangleType r1, r2;
  1233.       int i;
  1234.  
  1235.       for (i = row-1; i >= 0; i--) {
  1236.      if (resultsInfo[i][column].type == resultsCellStation) {
  1237.         station_row = i;
  1238.         break;
  1239.      }
  1240.       }
  1241.       /* c1 and c2 are the first and second times in this trip segment */
  1242.       c1 = (column/2)*2; c2 = c1+1;
  1243.  
  1244.       TblGetItemBounds(tableP,row,c1,&r1);
  1245.       TblGetItemBounds(tableP,row,c2,&r2);
  1246.       r.topLeft = r1.topLeft;
  1247.       r.extent.x = r1.extent.x + r2.extent.x;
  1248.       r.extent.y = r1.extent.y;
  1249.       /* shift right 3 pixels to be more centered */
  1250.       RctOffsetRectangle(&r,3,0);
  1251.  
  1252.       /* erase any previous popup and selection oval so we can
  1253.          deallocate the memory saved for the windows behind it */
  1254.       EraseResultsPopup(true);
  1255.       DrawSelection(&r);
  1256.       DrawLinePopup(destination[row][column/2],
  1257.             resultsInfo[station_row][c1].data.station.number,
  1258.             resultsInfo[row][c1].data.time.time,
  1259.             resultsInfo[station_row][c2].data.station.number,
  1260.             resultsInfo[row][c2].data.time.time);
  1261.       
  1262.       resultsPopupUpPenDownTick = TimGetTicks();
  1263.    }
  1264.    else if (resultsInfo[row][column].type == resultsCellStation) {
  1265.       SWord abbrev_length;
  1266.       
  1267.       /* we'll be anal here and measure the width of the station
  1268.      abbreviation to make sure the selection oval is centered */
  1269.       {
  1270.      FontID prevFont = FntSetFont(stdFont);
  1271.      char **StationAbbrevArray = MemHandleLock(StationAbbrevArrayH);
  1272.      char *abbrev = StationAbbrevArray[resultsInfo[row][column].data.station.number];
  1273.      abbrev_length = FntCharsWidth(abbrev,StrLen(abbrev));
  1274.      MemHandleUnlock(StationAbbrevArrayH);
  1275.      FntSetFont(prevFont);
  1276.       }
  1277.       
  1278.       TblGetItemBounds(tableP,row,column,&r);
  1279.       /* shift to be more centered */
  1280.       RctOffsetRectangle(&r,(r.extent.x-abbrev_length)/2,0);
  1281.  
  1282.       /* erase any previous popup and selection oval so we can
  1283.          deallocate the memory saved for the windows behind it */
  1284.       EraseResultsPopup(true);
  1285.       DrawSelection(&r);
  1286.       DrawStationPopup(resultsInfo[row][column].data.station.number);
  1287.       
  1288.       resultsPopupUpPenDownTick = TimGetTicks();
  1289.    }
  1290. }
  1291. #endif
  1292.  
  1293. static void ResultsFormInit()
  1294. {
  1295.    FormPtr frm = FrmGetActiveForm();
  1296.    SWord fare = -1;
  1297.    UInt adjusted_time;
  1298.    UInt i, j;
  1299.  
  1300.    /* table stuff */
  1301.    TablePtr tableP = FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Results_Table));
  1302.  
  1303.    /* set to invalid time to force assigning the time the first time */
  1304.    ResultsTime = 24*60 * 2;
  1305.    UpdateResultsTime(false);
  1306.    
  1307.    {
  1308.       /* set title */
  1309.       char title[30];
  1310.       StrPrintF(title,"Trains %s ~%s     ",
  1311.         (LeavingArriving==1 ? "arriving" : "leaving"),
  1312.         UITimeString);
  1313.       FrmCopyTitle(frm,title);
  1314.    }
  1315.    
  1316.    /* record category is UI list category+1 since we don't show general
  1317.       category in list */
  1318.    SetLineData(Category+1);
  1319.    adjusted_time = (UITime < dayEndTime ? UITime+24*60 : UITime);
  1320.    
  1321.    ClearResultsInfo();
  1322.  
  1323.    for (i = 0; i < TABLE_ROWS; i++) {
  1324.       for (j = 0; j < TABLE_COLUMNS; j++) {
  1325.      TblSetItemStyle(tableP,i,j,textTableItem);
  1326.       }
  1327.    }
  1328.    for (i = 0; i < TABLE_COLUMNS; i++) {
  1329.       TblSetLoadDataProcedure(tableP,i,TableLoadItem);
  1330.       TblSetColumnUsable(tableP,i,true);
  1331.    }
  1332.  
  1333.    if (FromStation != (Word)-1 && ToStation != (Word)-1) {
  1334.       fare = GetFare(Category,(Byte)FromStation,(Byte)ToStation);
  1335.       FindTrips((Byte)FromStation,(Byte)ToStation,adjusted_time,
  1336.         LeavingArriving == 0, BikeTrainsOnly != 0, resultsInfo,
  1337.         destination);
  1338.    }
  1339.    SetFareLabel(fare,FromStation == ToStation);
  1340. }
  1341.  
  1342. static void ResultsFormUnInit()
  1343. {
  1344.    /* don't need to deallocate table entries; the fields should take
  1345.       care of it automatically */
  1346.    nullEventTick = foreverTick;
  1347.  
  1348.    UnSetLineData();
  1349. }
  1350.  
  1351. static Boolean ResultsFormHandleEvent (EventPtr e)
  1352. {
  1353.     Boolean handled = false;
  1354.     FormPtr frm;
  1355.  
  1356.     CALLBACK_PROLOGUE
  1357.  
  1358.     switch (e->eType) {
  1359.     case frmOpenEvent:
  1360.     frm = FrmGetActiveForm();
  1361.     ResultsFormInit();
  1362.     FrmDrawForm(frm);
  1363.     handled = true;
  1364.     break;
  1365.  
  1366.     case penDownEvent:
  1367.        /* workaround for waiting to update time after being turned on; tap
  1368.       the screen to update the time */
  1369.        UpdateResultsTime(true);
  1370.        break;
  1371.  
  1372. #if RESULTS_POPUP
  1373.     case penUpEvent:
  1374.        if (resultsPopupUpPenDownTick != 0) {
  1375.       SetResultsPopupExpireTick();
  1376.       resultsPopupUpPenDownTick = 0;
  1377.        }
  1378.        break;
  1379. #endif
  1380.        
  1381.     case tblEnterEvent:
  1382. #if RESULTS_POPUP
  1383.        DrawResultsPopup(e->data.tblEnter.row,e->data.tblEnter.column);
  1384. #endif
  1385.        handled = true;
  1386.        break;
  1387.  
  1388.     case ctlSelectEvent:
  1389.     switch(e->data.ctlSelect.controlID) {
  1390.     case ID_Results_OK:
  1391. #if RESULTS_POPUP
  1392.        EraseResultsPopup(true);
  1393. #endif
  1394.        ResultsFormUnInit();
  1395.        FrmReturnToForm(MainForm);
  1396.        handled = true;
  1397.        break;
  1398.     }
  1399.  
  1400.     break;
  1401.  
  1402.     case nilEvent:
  1403.        UpdateResultsTime(true);
  1404. #if RESULTS_POPUP
  1405.        EraseResultsPopup(false);
  1406. #endif
  1407.        handled = true;
  1408.        break;
  1409.  
  1410.     case appStopEvent:
  1411. #if RESULTS_POPUP
  1412.        EraseResultsPopup(true);
  1413. #endif
  1414.        /* uninit results form to avoid overlocking handles */
  1415.        ResultsFormUnInit();
  1416.        FrmReturnToForm(MainForm);
  1417.        break;
  1418.        
  1419.     default:
  1420.         break;
  1421.     }
  1422.  
  1423.     CALLBACK_EPILOGUE
  1424.  
  1425.     return handled;
  1426. }
  1427.  
  1428.  
  1429. static void StationSelectFormInit(Int station, CharPtr title)
  1430. {
  1431.    FormPtr frm = FrmGetActiveForm();
  1432.  
  1433.    char **StationNameArray;
  1434.    ListPtr list_ptr;
  1435.    Word list_idx;
  1436.    RectangleType rect;
  1437.  
  1438.    if (title)
  1439.       FrmCopyTitle(frm,title);
  1440.  
  1441.    StationNameArray = (char **)MemHandleLock(StationNameArrayH);
  1442.    
  1443.    /* set up the station list */
  1444.    list_idx = FrmGetObjectIndex(frm,ID_Station_List);
  1445.    list_ptr = FrmGetObjectPtr(frm,list_idx);
  1446.    LstSetListChoices(list_ptr,StationNameArray,Stations);
  1447.    FrmGetObjectBounds(frm,list_idx,&rect);
  1448.    rect.extent.x = StationNameListWidth;
  1449.    FrmSetObjectBounds(frm,list_idx,&rect);
  1450.    LstSetSelection(list_ptr,station);
  1451.    if (station != -1)
  1452.       LstMakeItemVisible(list_ptr,station);
  1453.    
  1454.    MemHandleUnlock(StationNameArrayH);
  1455. }
  1456.  
  1457. static void StationSelectFormUnInit()
  1458. {
  1459. }
  1460.  
  1461. static Int SelectListItem(Word listID, CharPtr string)
  1462. {
  1463.    FormPtr frm = FrmGetActiveForm();
  1464.    ListPtr list_ptr;
  1465.    Word list_idx;
  1466.    Word selected_item, old_selected_item;
  1467.  
  1468.    list_idx = FrmGetObjectIndex(frm,listID);
  1469.    list_ptr = FrmGetObjectPtr(frm,list_idx);
  1470.  
  1471.    old_selected_item = LstGetSelection(list_ptr);
  1472.    selected_item = noListSelection;
  1473.  
  1474.    /* default to previous selection if NULL string or empty field. */
  1475.    if (string == NULL || string[0] == '\0') {
  1476.       selected_item = old_selected_item;
  1477.    }
  1478.    else {
  1479.       Word len = StrLen(string);
  1480.       Word items = LstGetNumberOfItems(list_ptr);
  1481.       Word i;
  1482.  
  1483.       for (i = 0; i < items; i++) {
  1484.      CharPtr item_text = LstGetSelectionText(list_ptr,i);
  1485.      Int comparison = StrNCaselessCompare(string,item_text,len);
  1486.      if (comparison == 0) {
  1487.         /* this is the first item that matches the string; select it */
  1488.         selected_item = i;
  1489.         break;
  1490.      }
  1491.      /* we can use this short circuit if the station list is in strict
  1492.             lexicographic order */
  1493. /*      else if (comparison < 0) */
  1494. /*         break; */
  1495.       }
  1496.    }
  1497.  
  1498.    if (selected_item != old_selected_item) {
  1499.       LstSetSelection(list_ptr,selected_item);
  1500.       LstEraseList(list_ptr);
  1501.       LstDrawList(list_ptr);
  1502.    }
  1503.  
  1504.    return selected_item;
  1505. }
  1506.  
  1507. static Boolean StationSelectFormHandleEvent (EventPtr e)
  1508. {
  1509.     Boolean handled = false;
  1510.     FormPtr frm;
  1511.  
  1512.     CALLBACK_PROLOGUE
  1513.  
  1514.     switch (e->eType) {
  1515.     case frmOpenEvent:
  1516.     frm = FrmGetActiveForm();
  1517.     StationSelectFormInit(*SelectedStation,StationSelectFormTitle);
  1518.     FrmDrawForm(frm);
  1519.     FrmSetFocus(frm,FrmGetObjectIndex(frm,ID_Station_Field));
  1520.     handled = true;
  1521.     break;
  1522.  
  1523.     case menuEvent:
  1524.        {
  1525.       FieldPtr fld;
  1526.       
  1527.       MenuEraseStatus(NULL);
  1528.       frm = FrmGetActiveForm();
  1529.       fld = FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Station_Field));
  1530.  
  1531.       handled = true;
  1532.       switch(e->data.menu.itemID) {
  1533.       case ID_Edit_Undo:
  1534.          FldUndo(fld);
  1535.          break;
  1536.       case ID_Edit_Cut:
  1537.          FldCut(fld);
  1538.          break;
  1539.       case ID_Edit_Copy:
  1540.          FldCopy(fld);
  1541.          break;
  1542.       case ID_Edit_Paste:
  1543.          FldPaste(fld);
  1544.          break;
  1545.       case ID_Edit_Select_All:
  1546.          FldSetSelection(fld,0,FldGetTextLength(fld));
  1547.          break;
  1548.       case ID_Edit_Keyboard:
  1549.          SysKeyboardDialog(kbdDefault);
  1550.          break;
  1551.       case ID_Edit_Graffiti_Help:
  1552.          SysGraffitiReferenceDialog(referenceDefault);
  1553.          break;
  1554.       default:
  1555.          handled = false;
  1556.          break;
  1557.       }
  1558.        }
  1559.     
  1560.     case keyDownEvent:
  1561.        {
  1562.       enum directions dir = down;
  1563.       ListPtr lst;
  1564.       FieldPtr fld;
  1565.       
  1566.       frm = FrmGetActiveForm();
  1567.       switch (e->data.keyDown.chr) {
  1568.       case pageUpChr:    /* hardware up button */
  1569.          dir = up;
  1570.       case pageDownChr:    /* hardware down button */
  1571.          lst = FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Station_List));
  1572.          LstScrollList(lst,dir,(short)LstGetVisibleItems(lst)-1);
  1573.          break;
  1574.       default:
  1575.          fld = FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Station_Field));
  1576.          FldHandleEvent(fld,e);
  1577.          SelectListItem(ID_Station_List,FldGetTextPtr(fld));
  1578.          break;
  1579.       }
  1580.       handled = true;
  1581.        }
  1582.        break;
  1583.  
  1584.     case ctlSelectEvent:
  1585.     switch(e->data.ctlSelect.controlID) {
  1586.     case ID_Station_Cancel:
  1587.        StationSelectFormUnInit();
  1588.        FrmGotoForm(MainForm);
  1589.        handled = true;
  1590.        break;
  1591.     case ID_Station_OK:
  1592.        {
  1593.           ListPtr list;
  1594.           Word selection;
  1595.           
  1596.           frm = FrmGetActiveForm();
  1597.           list = FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Station_List));
  1598.           selection = LstGetSelection(list);
  1599.           
  1600.           if (selection != noListSelection) {
  1601.          *SelectedStation = selection;
  1602.          SelectedStation = NULL;
  1603.          StationSelectFormUnInit();
  1604.          FrmGotoForm(MainForm);
  1605.           }
  1606.        }
  1607.        handled = true;
  1608.        break;
  1609.     }
  1610.  
  1611.     break;
  1612.  
  1613.     case appStopEvent:
  1614.        SelectedStation = NULL;
  1615.        FrmReturnToForm(MainForm);
  1616.        break;
  1617.        
  1618.     default:
  1619.         break;
  1620.     }
  1621.  
  1622.     CALLBACK_EPILOGUE
  1623.  
  1624.     return handled;
  1625. }
  1626.  
  1627.  
  1628.  
  1629. /*
  1630.  *
  1631.  * generic application stuff
  1632.  *
  1633.  */
  1634.  
  1635. static Boolean ApplicationHandleEvent(EventPtr e)
  1636. {
  1637.     FormPtr frm;
  1638.     Word    formId;
  1639.     Boolean handled = false;
  1640.  
  1641.     if (e->eType == frmLoadEvent) {
  1642.     formId = e->data.frmLoad.formID;
  1643.     frm = FrmInitForm(formId);
  1644.     FrmSetActiveForm(frm);
  1645.  
  1646.     switch(formId) {
  1647.     case MainForm:
  1648.         FrmSetEventHandler(frm, MainFormHandleEvent);
  1649.         break;
  1650.     case TimeForm:
  1651.         FrmSetEventHandler(frm, TimeFormHandleEvent);
  1652.         break;
  1653. #if 0
  1654.     case PrefsForm:
  1655.         FrmSetEventHandler(frm, PrefsFormHandleEvent);
  1656.         break;
  1657. #endif
  1658.     case ResultsForm:
  1659.         FrmSetEventHandler(frm, ResultsFormHandleEvent);
  1660.         break;
  1661.     case StationSelectForm:
  1662.         FrmSetEventHandler(frm, StationSelectFormHandleEvent);
  1663.         break;
  1664.     }
  1665.     handled = true;
  1666.     }
  1667.  
  1668.     return handled;
  1669. }
  1670.  
  1671. static Word StartApplication(void)
  1672. {
  1673.    char *recptr = NULL;
  1674.    
  1675.    char **StationNameArray = NULL;
  1676.    char **StationAbbrevArray = NULL;
  1677.    char **CategoryArray = NULL;
  1678.  
  1679.    ULong DBCreationDate;
  1680.  
  1681.    /* read prefs */
  1682.    {
  1683.       PrefsType prefs;
  1684.       Word size = sizeof(prefs), old_prefs_size = 4;
  1685.  
  1686.       // erase old home station saved prefs (from v1.21 and earlier)
  1687.       if (PrefGetAppPreferences(CREATOR_ID,0,(VoidPtr)&prefs,&old_prefs_size,true) != noPreferenceFound)
  1688.      PrefSetAppPreferences(CREATOR_ID,0,PREFS_VERSION,(VoidPtr)&prefs,0,true);
  1689.    
  1690.       if (PrefGetAppPreferences(CREATOR_ID,0,(VoidPtr)&prefs,&size,false)
  1691.       != noPreferenceFound) {
  1692.      ErrFatalDisplayIf(size != sizeof(prefs),"size != sizeof(prefs)");
  1693.      /* set to and from stations, bike settings, if they weren't
  1694.             globally initialized */
  1695.      if (FromStation == -1)
  1696.         FromStation = prefs.fromStation;
  1697.      if (ToStation == -1)
  1698.         ToStation = prefs.toStation;
  1699.      if (BikeTrainsOnly == -1)
  1700.         BikeTrainsOnly = prefs.bikeTrainsOnly;
  1701.       }
  1702.    }
  1703.  
  1704.    /* set up DB */
  1705.    if (!DB)
  1706.       DB = DmOpenDatabaseByTypeCreator(DB_ID,CREATOR_ID,dmModeReadOnly);
  1707.  
  1708.    ErrFatalDisplayIf(!DB,"Can't find train schedule database!  Did you install bartdata.pdb?");
  1709.  
  1710.    /* check database version */
  1711.    {
  1712.       LocalID local_id;
  1713.       UInt card;
  1714.       UInt db_version;
  1715.       Err err;
  1716.       
  1717.       err = DmOpenDatabaseInfo(DB,&local_id,NULL,NULL,&card,NULL);
  1718.       ErrFatalDisplayIf(err != 0, "error from DmOpenDatabaseInfo");
  1719.  
  1720.       err = DmDatabaseInfo(card,local_id,NULL,NULL,&db_version,NULL,
  1721.                NULL,NULL,NULL,NULL,NULL,NULL,NULL);
  1722.       ErrFatalDisplayIf(err != 0, "error from DmDatabaseInfo");
  1723.       {
  1724.      char string[100];
  1725.      StrPrintF(string,"Schedule database version %d installed, but I only know version %d.",db_version,DB_VERSION);
  1726.      ErrFatalDisplayIf(db_version != DB_VERSION,string);
  1727.       }
  1728.    }
  1729.    
  1730.    InitialRecordH = FindRecord(DB,GENERAL_CATEGORY,INFO_RECORD_NUMBER);
  1731.    ErrFatalDisplayIf(!InitialRecordH,"couldn't find DB info record");
  1732.    recptr = (char *)MemHandleLock(InitialRecordH);
  1733.  
  1734.    recptr++;            /* skip initial record number */
  1735.    recptr += 3;            /* skip 3 null bytes */
  1736.  
  1737.    DBCreationDate = *(ULong *)recptr; /* this value must be aligned */
  1738.    recptr += 4;
  1739.    
  1740.    {
  1741.       DateType date;
  1742.       CharPtr p = DBCreationDateString;
  1743.       Int year;
  1744.  
  1745.       DateSecondsToDate(DBCreationDate,&date);
  1746.       
  1747.       p += StrPrintF(p,"%d/%d/",(Int)date.month,(Int)date.day);
  1748.       /* To hell with Y2K compliance, I say! */
  1749.       year = ((Int)date.year+1904) % 100;
  1750.       StrPrintF(p,"%s%d",(year < 10 ? "0" : ""),year);
  1751.    }
  1752.    
  1753.    /* this value should be aligned if the above one is */
  1754.    dayEndTime = *(UInt *)recptr;
  1755.    recptr += 2;
  1756.    
  1757.    Categories = *(Byte *)recptr++;
  1758.    CategoryArrayH = SysFormPointerArrayToStrings(recptr,Categories);
  1759.    CategoryArray = (char **)MemHandleLock(CategoryArrayH);
  1760.    CategoryListWidth = FindListWidth(CategoryArray,Categories);
  1761.    recptr = CategoryArray[Categories-1] + StrLen(CategoryArray[Categories-1])+1;
  1762.    MemHandleUnlock(CategoryArrayH);
  1763.    
  1764.    /* Set up an array of pointers to strings pointing to the station names.
  1765.       This is used in MainFormInit() to set the To/From Station lists.
  1766.       
  1767.       Find the width of the widest station string in pixels as well, so
  1768.       we can set the list width in MainFormInit() without having to check
  1769.       each time we load the form.
  1770.    */
  1771.    Stations = *(Byte *)recptr++;
  1772.    /* record has 1 byte for ID, then one byte for # of stations, then station
  1773.       names as packed strings, then station abbrevs as packed strings */
  1774.    StationNameArrayH = SysFormPointerArrayToStrings(recptr,Stations);
  1775.    StationNameArray = (char **)MemHandleLock(StationNameArrayH);
  1776.    StationNameListWidth = FindListWidth(StationNameArray,Stations);
  1777.    recptr = StationNameArray[Stations-1] + StrLen(StationNameArray[Stations-1])+1;
  1778.    MemHandleUnlock(StationNameArrayH);
  1779.  
  1780.    StationAbbrevArrayH = SysFormPointerArrayToStrings(recptr,Stations);
  1781.    StationAbbrevArray = (char **)MemHandleLock(StationAbbrevArrayH);
  1782.    recptr = StationAbbrevArray[Stations-1] + StrLen(StationAbbrevArray[Stations-1])+1;
  1783.    MemHandleUnlock(StationAbbrevArrayH);
  1784.  
  1785.    /* we close database in StopApplication() */
  1786.    
  1787.    /* get time so we can set up category and time */
  1788.    if (Category == -1 || UITime == -1) {
  1789.       DateTimeType datetime;
  1790.       TimSecondsToDateTime(TimGetSeconds(),&datetime);
  1791.  
  1792.       /* set category if not set by global initialization */
  1793.       if (Category == -1) {
  1794.      /* BART SPECIFIC CODE:
  1795.       *     We assume the Weekday/Saturday/Sunday categories
  1796.       *     present in the BART version of the database, and set
  1797.       *     the category pull-down menu based on the current time
  1798.       *     and date.
  1799.       */
  1800.      SWord schedule_day;
  1801.      if (datetime.hour*60+datetime.minute <= dayEndTime)
  1802.         /* we're still on the previous day's schedule */
  1803.         schedule_day = (datetime.weekDay+6)%7;
  1804.      else
  1805.         schedule_day = datetime.weekDay;
  1806.       
  1807.      switch (schedule_day) {
  1808.      case 0:            /* Sunday */
  1809.         Category = 2;
  1810.         break;
  1811.      case 6:            /* Saturday */
  1812.         Category = 1;
  1813.         break;
  1814.      default:            /* Weekday */
  1815.         Category = 0;
  1816.         break;
  1817.      }
  1818.       }
  1819.  
  1820.       /* set time to current time if not set by global initialization */
  1821.       if (UITime == -1) {
  1822.      ULong seconds_today;
  1823.       
  1824.      seconds_today = (ULong)datetime.hour*60*60 + (ULong)datetime.minute*60 + (ULong)datetime.second;
  1825.      UITime = (seconds_today+150)/300 * 5; /* round to the nearest 5 min */
  1826.       }
  1827.       /* initialize time string */
  1828.       SetUITimeString(UITimeString,UITime,TIMESTRING_AMPM);
  1829.    }
  1830.    
  1831.    FrmGotoForm(MainForm);
  1832.    return 0;
  1833. }
  1834.  
  1835. /* Save preferences, close forms, close app database */
  1836. static void StopApplication(void)
  1837. {
  1838.    PrefsType prefs;
  1839.       
  1840.    prefs.fromStation = FromStation;
  1841.    prefs.toStation = ToStation;
  1842.    prefs.bikeTrainsOnly = BikeTrainsOnly;
  1843.    PrefSetAppPreferences(CREATOR_ID,0,PREFS_VERSION,(VoidPtr)&prefs,sizeof(prefs),false);
  1844.    
  1845.    FrmSaveAllForms();
  1846.    FrmCloseAllForms();
  1847.  
  1848.    MemHandleFree(CategoryArrayH);
  1849.    CategoryArrayH = (VoidHand)0;
  1850.    MemHandleFree(StationNameArrayH);
  1851.    StationNameArrayH = (VoidHand)0;
  1852.    MemHandleFree(StationAbbrevArrayH);
  1853.    StationAbbrevArrayH = (VoidHand)0;
  1854.    MemHandleUnlock(InitialRecordH);
  1855.    InitialRecordH = (VoidHand)0;
  1856.    DmCloseDatabase(DB);
  1857. }
  1858.  
  1859. /* If we are turning the power off, schedule a nilEvent soon after we
  1860.    wake up so that we can update the time immediately. */
  1861. static void CheckPowerOff(EventType *e)
  1862. {
  1863.    if (e->eType == keyDownEvent && (e->data.keyDown.chr == autoOffChr ||
  1864.                     e->data.keyDown.chr == hardPowerChr)) {
  1865.       /* give the system 5 ticks to power off and on */
  1866.       ULong wakeupTick = TimGetTicks()+5;
  1867.       MySetNullEventTick(wakeupTick);
  1868.    }
  1869. }
  1870.  
  1871. /* The main event loop */
  1872. static void EventLoop(void)
  1873. {
  1874.    Word err;
  1875.    EventType e;
  1876.  
  1877.    do {
  1878.       Long timeout;
  1879.  
  1880.       if (nullEventTick == foreverTick)
  1881.      timeout = evtWaitForever;
  1882.       else if ((timeout = (Long)(nullEventTick-TimGetTicks())) < 0)
  1883.      timeout = 0;
  1884.       
  1885.       EvtGetEvent(&e, timeout);
  1886.       if (lineInfo != NULL)    /* check if we are in the results form */
  1887.      CheckPowerOff(&e);
  1888.       if (! SysHandleEvent (&e))
  1889.      if (! MenuHandleEvent (NULL, &e, &err))
  1890.         if (! ApplicationHandleEvent (&e))
  1891.            FrmDispatchEvent (&e);
  1892.    } while (e.eType != appStopEvent);
  1893. }
  1894.  
  1895. /* Main entry point; it is unlikely you will need to change this except to
  1896.    handle other launch command codes */
  1897. DWord PilotMain(Word cmd, Ptr cmdPBP, Word launchFlags)
  1898. {
  1899.     Word err;
  1900.  
  1901.     if (cmd == sysAppLaunchCmdNormalLaunch) {
  1902.  
  1903.     err = StartApplication();
  1904.     if (err) return err;
  1905.  
  1906.     EventLoop();
  1907.     StopApplication();
  1908.  
  1909.     } else {
  1910.     return sysErrParamErr;
  1911.     }
  1912.  
  1913.     return 0;
  1914. }
  1915.