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 >
Wrap
C/C++ Source or Header
|
2000-03-12
|
53KB
|
1,915 lines
/* BART Scheduler Palm Pilot App
*
* Copyright (c) 1999, 2000 Michael Wittman
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
*/
#include <Pilot.h>
#include "callback.h"
#include "bartRsc.h"
#include "bart.h"
#include "bartdata.h"
#define PREFS_VERSION 1
#define UI_SETTINGS 2
/* This creator ID is registered with 3Com. You MUST change it and
register a new ID if you modify this software and redistribute
it. */
#define CREATOR_ID 'BART'
/*
* UI-related data
*/
static DmOpenRef DB = (DmOpenRef)0;
static char DBCreationDateString[] = "10/20/2000"; /* just a placeholder */
static VoidHand StationNameArrayH = (VoidHand)0;
static SWord StationNameListWidth = -1;
static VoidHand StationAbbrevArrayH = (VoidHand)0;
static VoidHand CategoryArrayH = (VoidHand)0;
static SWord CategoryListWidth = -1;
static VoidHand InitialRecordH = (VoidHand)0;
static Byte Categories = 0;
static Byte Stations = 0;
#if UI_SETTINGS == 0
/* debug initial user interface settings */
static Word Category = 0;
static Word FromStation = 7;
static Word ToStation = 0;
static Word LeavingArriving = 1;
static Word BikeTrainsOnly = 0;
static Word UITime = 7*60;
#elif UI_SETTINGS == 1
/* my default initial user interface settings */
static Word Category = -1;
static Word FromStation = 15;
static Word ToStation = 15;
static Word LeavingArriving = -1;
static Word BikeTrainsOnly = 0;
static Word UITime = -1;
#else
/* default initial user interface settings */
static Word Category = -1;
static Word FromStation = -1;
static Word ToStation = -1;
static Word LeavingArriving = -1;
static Word BikeTrainsOnly = -1;
static Word UITime = -1;
#endif
static Char FromStationTitle[] = "From Station";
static Char ToStationTitle[] = "To Station";
static CharPtr StationSelectFormTitle = NULL;
/* points to variable to change in station select form */
static WordPtr SelectedStation = NULL;
static Word NewUITime;
static char UITimeString[] = "12:00pm"; /* time is just a placeholder */
static Word ResultsTime;
static ResultsCellType resultsInfo[TABLE_ROWS][TABLE_COLUMNS];
/* destination info for trains represented in resultsInfo. we only
really need TABLE_ROWS/2 rows, but we use TABLE_ROWS so things
match up with resultsInfo. */
static Byte destination[TABLE_ROWS][TABLE_COLUMNS/2];
static UInt dayEndTime;
typedef struct {
Word fromStation;
Word toStation;
Byte bikeTrainsOnly;
} PrefsType;
/*
* Stuff used for finding trips
*/
static VoidHand infoRecordH = (VoidHand)0;
static VoidHand transfersH = (VoidHand)0;
Byte station_bike_restrictions;
StationBikeRestrictionType *station_bike_restriction;
Byte currentLines;
LineDataType *lineInfo;
/* defined in find.c */
void FindTrips(Byte source, Byte dest, UInt time, Boolean leaving,
Boolean bike_trains_only,
ResultsCellType resultsInfo[TABLE_ROWS][TABLE_COLUMNS],
Byte destination[TABLE_ROWS][TABLE_COLUMNS/2]);
#if RESULTS_POPUP
/* variables used for selection and popup windows in results form */
static WinHandle resultsPopupBackingWinH = (WinHandle)0;
static WinHandle resultsSelectionBackingWinH = (WinHandle)0;
static RectangleType selectionRect;
/* Use 0 ticks to mean uninitialized. Tick counter only wraps around
every 497.1 days, so we should be OK. :) */
static ULong resultsPopupExpireTick = 0;
static ULong resultsPopupUpPenDownTick = 0;
#endif
#define foreverTick 0xffffffff
#define MySetNullEventTick(tick) \
if (nullEventTick > (tick) || nullEventTick <= TimGetTicks()) \
nullEventTick = (tick);
static ULong nullEventTick = foreverTick;
/*
*
* misc utility functions
*
*/
#define TIMESTRING_AMPM 2
#define TIMESTRING_AP 1
#define TIMESTRING_NOAMPM 0
static void SetUITimeString(Char s[8], UInt time, Byte ampm_chars)
{
UInt hour = (time / 60) % 12;
UInt min = time % 60;
Boolean pm = ((time / 60) / 12) % 2; /* handles time > 24 hours */
if (hour == 0) hour = 12;
s += StrPrintF(s,"%d:",hour);
if (min < 10)
*s++ = '0';
s += StrPrintF(s,"%d",min);
if (ampm_chars >= 1) {
s+= StrPrintF(s,(pm ? "p" : "a"));
if (ampm_chars >= 2)
StrPrintF(s,"m");
}
}
static SWord FindListWidth(char **string, UInt strings)
{
SWord maxwidth = 0;
UInt i;
for (i = 0; i < strings; i++) {
int width = FntLineWidth(string[i],StrLen(string[i]));
if (width > maxwidth)
maxwidth = width;
}
return maxwidth + 3; /* add padding to right side */
}
/* find a record by a category and the one byte ID number in the initial
byte */
static VoidHand FindRecord(DmOpenRef db, UInt category, Byte recordnum)
{
UInt records = DmNumRecordsInCategory(DB,category);
UInt index = 0;
VoidHand h = (VoidHand)0;
BytePtr recptr;
Boolean found_record = false;
UInt i;
for (i = 0; i < records; i++, index++) {
h = DmQueryNextInCategory(DB,&index,category);
ErrFatalDisplayIf(!h,"got null handle from DmQueryNextInCategory");
recptr = MemHandleLock(h);
if (*recptr == recordnum) {
/* found it */
found_record = true;
MemHandleUnlock(h);
break;
}
else {
MemHandleUnlock(h);
}
}
if (found_record)
return h;
else
return (VoidHand)0;
}
/*
*
* Main form stuff
*
*/
static void MainFormInit()
{
FormPtr frm = FrmGetActiveForm();
char **StationNameArray, **CategoryArray;
ListPtr list_ptr;
Word list_idx;
RectangleType rect;
StationNameArray = (char **)MemHandleLock(StationNameArrayH);
CategoryArray = (char **)MemHandleLock(CategoryArrayH);
/* set up the From station selector */
if (FromStation != -1)
CtlSetLabel(FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Station_From_Selector)),
StationNameArray[FromStation]);
/* set up the To station selector */
if (ToStation != -1)
CtlSetLabel(FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Station_To_Selector)),
StationNameArray[ToStation]);
/* set up the category list */
list_idx = FrmGetObjectIndex(frm,ID_Category_List);
list_ptr = FrmGetObjectPtr(frm,list_idx);
LstSetListChoices(list_ptr,CategoryArray,Categories);
FrmGetObjectBounds(frm,list_idx,&rect);
rect.extent.x = CategoryListWidth;
FrmSetObjectBounds(frm,list_idx,&rect);
/* max list height of 12 is arbitrary; we only use 3 for BART */
LstSetHeight(list_ptr,(Categories <= 12 ? Categories : 12));
LstSetSelection(list_ptr,Category);
CtlSetLabel(FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Category_Popup)),
CategoryArray[Category]);
/* set the time pushbutton to the time string */
CtlSetLabel(FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Time_Selector)),
UITimeString);
/* set the arriving/leaving list */
list_idx = FrmGetObjectIndex(frm,ID_Arriving_Leaving_List);
list_ptr = FrmGetObjectPtr(frm,list_idx);
if (LeavingArriving != -1) {
LstSetSelection(list_ptr,LeavingArriving);
/* selection will always be visible */
}
/* set the bike trains checkbox */
FrmSetControlValue(frm,FrmGetObjectIndex(frm,ID_Bike_Checkbox),BikeTrainsOnly != 0 && BikeTrainsOnly != -1);
MemHandleUnlock(StationNameArrayH);
MemHandleUnlock(CategoryArrayH);
}
/* save the UI settings to their corresponding global variables */
static void MainFormSaveUISettings()
{
FormPtr frm = FrmGetActiveForm();
ListPtr list_ptr;
Word list_idx;
/* save the category selection */
list_idx = FrmGetObjectIndex(frm,ID_Category_List);
list_ptr = FrmGetObjectPtr(frm,list_idx);
Category = LstGetSelection(list_ptr);
/* time pushbutton is already saved in UITime */
/* save the arriving/leaving selection */
list_idx = FrmGetObjectIndex(frm,ID_Arriving_Leaving_List);
list_ptr = FrmGetObjectPtr(frm,list_idx);
LeavingArriving = LstGetSelection(list_ptr);
/* save the bike trains checkbox */
BikeTrainsOnly = FrmGetControlValue(frm,FrmGetObjectIndex(frm,ID_Bike_Checkbox));
}
static void MainFormUnInit()
{
MainFormSaveUISettings();
}
static void SwapStations()
{
Word temp_station;
CharPtr temp_label;
FormPtr frm = FrmGetActiveForm();
ControlPtr fromControl, toControl;
temp_station = ToStation;
ToStation = FromStation;
FromStation = temp_station;
fromControl = FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Station_From_Selector));
toControl = FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Station_To_Selector));
temp_label = CtlGetLabel(toControl);
CtlSetLabel(FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Station_To_Selector)),
CtlGetLabel(fromControl));
CtlSetLabel(FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Station_From_Selector)),
temp_label);
}
static Boolean MainFormHandleEvent (EventPtr e)
{
Boolean handled = false;
FormPtr frm;
CALLBACK_PROLOGUE;
switch (e->eType) {
case frmOpenEvent:
frm = FrmGetActiveForm();
MainFormInit();
FrmDrawForm(frm);
handled = true;
break;
case menuEvent:
MenuEraseStatus(NULL);
switch(e->data.menu.itemID) {
case ID_About_Item:
{
VoidHand h;
CharPtr version;
h = DmGet1Resource('tver',ID_Version);
ErrFatalDisplayIf(!h,"couldn't find version resource");
version = (CharPtr)MemHandleLock(h);
FrmCustomAlert(ID_About_Alert,version,DBCreationDateString," ");
MemHandleUnlock(h);
DmReleaseResource(h);
handled = true;
break;
}
case ID_BART_Info_Item:
FrmAlert(ID_BART_Info_Alert);
handled = true;
break;
#if 0
case ID_Prefs_Item:
MainFormUnInit();
FrmGotoForm(PrefsForm);
handled = true;
break;
#endif
}
break;
case ctlSelectEvent:
switch(e->data.ctlSelect.controlID) {
case ID_Station_From_Selector:
MainFormUnInit();
SelectedStation = &FromStation;
StationSelectFormTitle = FromStationTitle;
FrmGotoForm(StationSelectForm);
handled = true;
break;
case ID_Station_To_Selector:
MainFormUnInit();
SelectedStation = &ToStation;
StationSelectFormTitle = ToStationTitle;
FrmGotoForm(StationSelectForm);
handled = true;
break;
case ID_Swap_Button:
SwapStations();
handled = true;
break;
case ID_Time_Selector:
MainFormUnInit();
FrmGotoForm(TimeForm);
handled = true;
break;
case ID_Find:
/* save settings so we know what we're looking for when
finding trips */
MainFormSaveUISettings();
FrmPopupForm(ResultsForm);
handled = true;
break;
}
break;
case keyDownEvent:
{
char secret_code[] = "SRC";
static Int letter = 0;
Word chr = e->data.keyDown.chr;
#if TUNING
if (chr >= '0' && chr <= '9') {
tuningSetting = chr - '0';
DebugPrintF("setting tuningSetting to %u",tuningSetting);
}
else if (chr >= 'a' && chr <= 'z') {
miscSetting = chr - 'a';
DebugPrintF("setting miscSetting to %u",miscSetting);
}
#endif
if (chr == secret_code[letter])
letter++;
else
letter = 0;
if (letter == StrLen(secret_code)) {
letter = 0;
useSourceForDestination = !useSourceForDestination;
DebugPrintF("showing train %s in popup window",
useSourceForDestination ? "source" : "destination");
}
handled = true;
break;
}
case appStopEvent:
/* save settings so they will be stored in preferences properly
by StopApplication() */
MainFormSaveUISettings();
break;
default:
break;
}
CALLBACK_EPILOGUE;
return handled;
}
/*
*
* Time selection form stuff
*
*/
static void TimeFormInit()
{
FormPtr frm = FrmGetActiveForm();
ListPtr hour_list, min_list;
Word hour_item, min_item;
/* we use NewUITime to record time selected in this form, setting
UITime to NewUITime when user taps OK */
NewUITime = UITime;
SetUITimeString(UITimeString,NewUITime,TIMESTRING_AMPM);
CtlSetLabel(FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Time_Button)),
UITimeString);
hour_item = NewUITime/60;
hour_list = FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Time_Hour_List));
LstScrollList(hour_list,down,8);
LstMakeItemVisible(hour_list,hour_item);
LstSetSelection(hour_list,hour_item);
min_item = ((NewUITime%60+2)/5); /* nearest 5 minutes */
min_list = FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Time_Min_List));
LstSetSelection(min_list,min_item);
}
static void TimeFormUnInit()
{
}
static Boolean TimeFormHandleEvent (EventPtr e)
{
Boolean handled = false;
FormPtr frm;
ListPtr hour_list, min_list;
Word hour_item, min_item;
CALLBACK_PROLOGUE
switch (e->eType) {
case frmOpenEvent:
frm = FrmGetActiveForm();
TimeFormInit();
FrmDrawForm(frm);
handled = true;
break;
case ctlSelectEvent:
switch(e->data.ctlSelect.controlID) {
case ID_Time_Now:
{
/* set time to current time */
DateTimeType datetime;
ULong seconds_today;
frm = FrmGetActiveForm();
TimSecondsToDateTime(TimGetSeconds(),&datetime);
seconds_today = (ULong)datetime.hour*60*60 + (ULong)datetime.minute*60 + (ULong)datetime.second;
/* round to the nearest 5 min */
NewUITime = (seconds_today+150)/300 * 5;
SetUITimeString(UITimeString,NewUITime,TIMESTRING_AMPM);
CtlSetLabel(FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Time_Button)),UITimeString);
hour_list = FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Time_Hour_List));
LstSetSelection(hour_list,NewUITime/60);
min_list = FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Time_Min_List));
LstSetSelection(min_list,(NewUITime%60)/5);
handled = true;
}
break;
case ID_Time_OK:
UITime = NewUITime;
case ID_Time_Cancel:
TimeFormUnInit();
FrmGotoForm(MainForm);
handled = true;
break;
}
break;
case lstSelectEvent:
switch(e->data.lstSelect.listID) {
case ID_Time_Hour_List:
frm = FrmGetActiveForm();
hour_list = FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Time_Hour_List));
hour_item = LstGetSelection(hour_list);
NewUITime = hour_item*60 + NewUITime%60;
SetUITimeString(UITimeString,NewUITime,TIMESTRING_AMPM);
CtlSetLabel(FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Time_Button)),UITimeString);
handled = true;
break;
case ID_Time_Min_List:
frm = FrmGetActiveForm();
min_list = FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Time_Min_List));
min_item = LstGetSelection(min_list);
NewUITime = (NewUITime/60)*60 + min_item*5;
SetUITimeString(UITimeString,NewUITime,TIMESTRING_AMPM);
CtlSetLabel(FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Time_Button)),UITimeString);
handled = true;
break;
}
break;
default:
break;
}
CALLBACK_EPILOGUE
return handled;
}
#if 0
/*
*
* Prefs form stuff
*
*/
static void PrefsFormInit()
{
FormPtr frm = FrmGetActiveForm();
PrefsType prefs;
Word size = sizeof(prefs);
char **StationNameArray;
RectangleType rect;
ListPtr list_ptr;
Word list_idx;
StationNameArray = (char **)MemHandleLock(StationNameArrayH);
if (PrefGetAppPreferences(CREATOR_ID,0,(VoidPtr)&prefs,&size,true)
== noPreferenceFound) {
prefs.station = -1;
}
/* set up the station list */
list_idx = FrmGetObjectIndex(frm,ID_Prefs_Station_List);
list_ptr = FrmGetObjectPtr(frm,list_idx);
LstSetListChoices(list_ptr,StationNameArray,Stations);
FrmGetObjectBounds(frm,list_idx,&rect);
rect.extent.x = StationNameListWidth;
FrmSetObjectBounds(frm,list_idx,&rect);
LstSetSelection(list_ptr,prefs.station);
if (prefs.station != -1) {
LstMakeItemVisible(list_ptr,prefs.station);
CtlSetLabel(FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Prefs_Station_Popup)),
StationNameArray[prefs.station]);
}
MemHandleUnlock(StationNameArrayH);
}
static void PrefsFormUnInit()
{
}
static Boolean PrefsFormHandleEvent (EventPtr e)
{
Boolean handled = false;
FormPtr frm;
PrefsType prefs;
PrefsType old_prefs;
Word size = sizeof(prefs);
ListPtr station_list;
CALLBACK_PROLOGUE
switch (e->eType) {
case frmOpenEvent:
frm = FrmGetActiveForm();
PrefsFormInit();
FrmDrawForm(frm);
handled = true;
break;
case ctlSelectEvent:
switch(e->data.ctlSelect.controlID) {
case ID_Prefs_OK:
frm = FrmGetActiveForm();
/* get old prefs */
if (PrefGetAppPreferences(CREATOR_ID,0,(VoidPtr)&old_prefs,&size,true)
== noPreferenceFound) {
old_prefs.station = -1;
}
/* set new prefs if a list item was selected */
station_list = FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Prefs_Station_List));
prefs.station = LstGetSelection(station_list);
if (prefs.station != -1) {
PrefSetAppPreferences(CREATOR_ID,0,PREFS_VERSION,(VoidPtr)&prefs,sizeof(prefs),true);
/* change To/From UI settings to new home station if the old
both of the old values were old home station */
if (old_prefs.station != -1 &&
FromStation == old_prefs.station &&
ToStation == old_prefs.station) {
FromStation = prefs.station;
ToStation = prefs.station;
}
}
case ID_Prefs_Cancel:
PrefsFormUnInit();
FrmGotoForm(MainForm);
handled = true;
break;
}
break;
default:
break;
}
CALLBACK_EPILOGUE
return handled;
}
#endif
/*
*
* Results form stuff. Nearly all of the logic for actually finding trips
* is in the file find.c.
*
*/
/* look up fare in the fare matrix in the database */
static SWord GetFare(Byte category, Byte station1, Byte station2)
{
VoidHand handle;
BytePtr recptr = NULL;
SWord multiplier, min_fare;
SWord fare;
BytePtr fareptr = NULL;
Byte s1, s2;
handle = FindRecord(DB,category,FARES_RECORD_NUMBER);
if (!handle)
handle = FindRecord(DB,GENERAL_CATEGORY,FARES_RECORD_NUMBER);
ErrFatalDisplayIf(!handle,"couldn't find fare record!");
/* no fare record available */
if (!handle)
return -1;
recptr = MemHandleLock(handle);
multiplier = *(recptr+1); /* skip initial record number */
min_fare = *(recptr+2);
min_fare *= multiplier;
fareptr = recptr+3;
s1 = min(station1,station2);
s2 = max(station1,station2);
fare = fareptr[s1*(Stations-s1) + s1*(s1+1)/2 + s2-s1]*multiplier + min_fare;
MemHandleUnlock(handle);
return fare;
}
static void SetFareLabel(SWord fare, Boolean excursion)
{
FormPtr frm = FrmGetActiveForm();
if (fare >= 0) {
char string[] = "Fare: $3.80"; /* max string length */
char *p = string;
UInt dollars = (UInt)fare / 100;
UInt cents = (UInt)fare % 100;
p += StrPrintF(p,"Fare: $%u.",dollars);
if (cents < 10)
*p++ = '0';
p += StrPrintF(p,"%u",cents);
FrmCopyLabel(frm,ID_Results_Fare_Label,string);
FrmShowObject(frm,FrmGetObjectIndex(frm,ID_Results_Fare_Label));
}
else {
/* no fare, so hide label */
FrmHideObject(frm,FrmGetObjectIndex(frm,ID_Results_Fare_Label));
}
}
/* reads data for all the lines in a category into the lineInfo array */
static void SetLineData(Byte category)
{
VoidHand handle = (VoidHand)0;
Byte *recptr = NULL;
Byte *trans_recptr = NULL;
Byte curr_transfer_line = 0, transfer_lines;
Byte i;
currentLines = 0;
infoRecordH = FindRecord(DB,category,INFO_RECORD_NUMBER);
ErrFatalDisplayIf(!infoRecordH,"couldn't find info record");
recptr = MemHandleLock(infoRecordH);
recptr++; /* skip initial record number */
currentLines = *recptr++;
station_bike_restrictions = *recptr++;
recptr++; /* skip null byte */
station_bike_restriction = (StationBikeRestrictionType *)recptr;
/* we're done with info record */
lineInfo = MemPtrNew(currentLines*sizeof(LineDataType));
ErrFatalDisplayIf(!lineInfo,"couldn't allocate memory for lineInfo array");
/* look first for a transfer record for this category number; if
not found, use global transfer record */
transfersH = FindRecord(DB,category,TRANSFER_RECORD_NUMBER);
if (!transfersH)
transfersH = FindRecord(DB,GENERAL_CATEGORY,TRANSFER_RECORD_NUMBER);
ErrFatalDisplayIf(!transfersH,"couldn't find transfer info record");
trans_recptr = MemHandleLock(transfersH);
trans_recptr++; /* skip record number */
transfer_lines = *trans_recptr++;
for (i = 0; i < currentLines; i++) {
Byte *ptr;
LineDataType *line = &lineInfo[i];
handle = FindRecord(DB,category,INITIAL_LINE_RECORD_NUMBER+i);
ErrFatalDisplayIf(!handle,"couldn't find a line record (see README)");
line->record_handle = handle;
ptr = MemHandleLock(handle);
ptr++; /* skip record number */
line->stations = *ptr++;
line->station = ptr;
ptr += line->stations;
line->inc_vectors = *ptr++;
line->inc_vector = ptr;
ptr += line->inc_vectors*line->stations;
line->nobike_pairs = *ptr++;
line->nobike_pair = (Byte (*)[2])ptr;
ptr += 2*line->nobike_pairs;
/* the next word may not be aligned */
line->initial_time = (UInt)*ptr++ << 8;
line->initial_time |= *ptr++;
line->train_blocks = *ptr++;
line->train_block = (TrainBlockType *)ptr;
line->trains = 0;
line->departure_time_handle = (VoidHand)0;
/* record transfers only if there are some for this line */
if (curr_transfer_line < transfer_lines && *trans_recptr == i) {
trans_recptr++;
line->transfers = *trans_recptr++;
line->transfer = (TransferInfoType *)trans_recptr;
trans_recptr += sizeof(TransferInfoType)*line->transfers;
curr_transfer_line++;
}
else {
line->transfers = 0;
}
}
}
/* Allocate memory for, and set non-compacted lists of departure times,
inc_vector_idx's, and nobike_pair_idx's. This and the corresponding
deallocate function are called from functions in find.c. */
void AllocateDepartureTimes(LineDataType *line)
{
UInt time = line->initial_time;
UInt *departure_time;
Byte *inc_vector_idx;
Byte *nobike_pair_idx;
Byte train;
Byte i, j;
ErrFatalDisplayIf(line->departure_time_handle,"attempt to set departure times twice");
line->trains = 0;
for (i = 0; i < line->train_blocks; i++) {
line->trains += line->train_block[i].trains;
}
line->departure_time_handle = MemHandleNew(line->trains*sizeof(UInt));
ErrFatalDisplayIf(!line->departure_time_handle,"unable to allocate handle for departure times");
departure_time = MemHandleLock(line->departure_time_handle);
line->inc_vector_idx_handle = MemHandleNew(line->trains*sizeof(Byte));
ErrFatalDisplayIf(!line->inc_vector_idx_handle,"unable to allocate handle for departure times");
inc_vector_idx = MemHandleLock(line->inc_vector_idx_handle);
line->nobike_pair_idx_handle = MemHandleNew(line->trains*sizeof(Byte));
ErrFatalDisplayIf(!line->nobike_pair_idx_handle,"unable to allocate handle for departure times");
nobike_pair_idx = MemHandleLock(line->nobike_pair_idx_handle);
train = 0;
for (i = 0; i < line->train_blocks; i++) {
TrainBlockType *block = &line->train_block[i];
for (j = 0; j < block->trains; j++) {
inc_vector_idx[train] = block->inc_vector_idx;
nobike_pair_idx[train] = block->nobike_pair_idx;
time += block->time_diff;
departure_time[train] = time;
train++;
}
}
MemHandleUnlock(line->departure_time_handle);
MemHandleUnlock(line->inc_vector_idx_handle);
MemHandleUnlock(line->nobike_pair_idx_handle);
}
void FreeDepartureTimes(LineDataType *line)
{
ErrFatalDisplayIf(!line->departure_time_handle,"attempt to deallocate unset departure times");
line->trains = 0;
MemHandleFree(line->departure_time_handle);
MemHandleFree(line->inc_vector_idx_handle);
MemHandleFree(line->nobike_pair_idx_handle);
line->departure_time_handle = (VoidHand)0;
line->inc_vector_idx_handle = (VoidHand)0;
line->nobike_pair_idx_handle = (VoidHand)0;
}
static void UnSetLineData()
{
Byte i;
ErrFatalDisplayIf(!lineInfo,"attempt to free non-existent lineInfo array");
for (i = 0; i < currentLines; i++) {
ErrFatalDisplayIf(!lineInfo[i].record_handle,"attempt to unlock non-existent handle");
MemHandleUnlock(lineInfo[i].record_handle);
if (lineInfo[i].departure_time_handle)
FreeDepartureTimes(&lineInfo[i]);
}
MemPtrFree(lineInfo);
MemHandleUnlock(transfersH);
MemHandleUnlock(infoRecordH);
lineInfo = NULL;
currentLines = 0;
}
static void ClearResultsInfo()
{
UInt i, j;
for (i = 0; i < TABLE_ROWS; i++) {
for (j = 0; j < TABLE_COLUMNS; j++) {
resultsInfo[i][j].type = resultsCellEmpty;
}
}
}
static Err TableLoadItem(VoidPtr table, Word row, Word column,
Boolean editable, VoidHand *dataH, WordPtr dataOffset,
WordPtr dataSize, FieldPtr fld)
{
VoidHand handle = (VoidHand)0;
FieldAttrType attr;
CALLBACK_PROLOGUE;
FldGetAttributes(fld,&attr);
switch (resultsInfo[row][column].type) {
case resultsCellEmpty:
{
*dataH = (VoidHand)0;
*dataOffset = 0;
*dataSize = 0;
attr.editable = 0;
attr.underlined = 0;
attr.singleLine = 1;
break;
}
case resultsCellStation:
{
char **StationAbbrevArray = MemHandleLock(StationAbbrevArrayH);
char *abbrev = StationAbbrevArray[resultsInfo[row][column].data.station.number];
handle = MemHandleNew(StrLen(abbrev)+1);
ErrFatalDisplayIf(!handle,"Got null handle trying to allocate space for table entry!");
StrCopy(MemHandleLock(handle),abbrev);
MemHandleUnlock(handle);
MemHandleUnlock(StationAbbrevArrayH);
*dataH = handle;
*dataOffset = 0;
*dataSize = MemHandleSize(*dataH);
attr.editable = 0;
attr.underlined = 0;
attr.singleLine = 1;
attr.justification = rightAlign;
break;
}
case resultsCellTime:
{
/* max time string length is 6 chars + null ("12:00p") */
handle = MemHandleNew(7);
ErrFatalDisplayIf(!handle,"Got null handle trying to allocate space for table entry!");
SetUITimeString(MemHandleLock(handle),resultsInfo[row][column].data.time.time,TIMESTRING_NOAMPM);
MemHandleUnlock(handle);
*dataH = handle;
*dataOffset = 0;
*dataSize = MemHandleSize(*dataH);
attr.editable = 0;
attr.underlined = 0;
attr.singleLine = 1;
attr.justification = rightAlign;
break;
}
#if TUNING
case resultsCellNumber:
{
handle = MemHandleNew(10);
ErrFatalDisplayIf(!handle,"Got null handle trying to allocate space for table entry!");
StrPrintF(MemHandleLock(handle),"%lu",resultsInfo[row][column].data.number.number);
MemHandleUnlock(handle);
*dataH = handle;
*dataOffset = 0;
*dataSize = MemHandleSize(*dataH);
attr.editable = 0;
attr.underlined = 0;
attr.singleLine = 1;
attr.justification = rightAlign;
break;
}
#endif
}
FldSetAttributes(fld,&attr);
CALLBACK_EPILOGUE;
return 0;
}
static void UpdateResultsTime(Boolean drawField)
{
FormPtr frm = FrmGetActiveForm();
DateTimeType datetime;
Word NewResultsTime;
ULong updateticks;
char *fieldstring;
FieldPtr fieldP = FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Results_Time));
VoidHand fieldH = (VoidHand)0;
TimSecondsToDateTime(TimGetSeconds(),&datetime);
NewResultsTime = (ULong)datetime.hour*60 + (ULong)datetime.minute;
/* Generate a null event at the next minute. */
updateticks = TimGetTicks() + (60-(ULong)datetime.second)*SysTicksPerSecond();
MySetNullEventTick(updateticks);
if (NewResultsTime != ResultsTime) {
ResultsTime = NewResultsTime;
if (!(fieldH = (VoidHand)FldGetTextHandle(fieldP))) {
fieldH = MemHandleNew(8*sizeof(char));
}
fieldstring = MemHandleLock(fieldH);
SetUITimeString(fieldstring,ResultsTime,TIMESTRING_AMPM);
MemHandleUnlock(fieldH);
FldSetTextHandle(fieldP,(Handle)fieldH);
if (drawField)
FldDrawField(fieldP);
}
}
#if RESULTS_POPUP
/* schedule a nilEvent for later to erase the popup window and selection */
static void SetResultsPopupExpireTick()
{
ULong t1, t2;
t1 = resultsPopupUpPenDownTick + RESULTS_POPUP_TIME1*SysTicksPerSecond();
t2 = TimGetTicks() + RESULTS_POPUP_TIME2*SysTicksPerSecond();
resultsPopupExpireTick = max(t1,t2);
MySetNullEventTick(resultsPopupExpireTick);
}
/* Draw the results popup window with the train destination, and times and
stations, for the segment selected. */
static void DrawLinePopup(Byte dest_station,
Byte from_station, UInt from_time,
Byte to_station, UInt to_time)
{
RectangleType r;
WinHandle winH, drawWinH;
FontID prevFont = FntSetFont(stdFont);
short fontlineheight = FntLineHeight();
Word error;
drawWinH = WinSetDrawWindow(WinGetDisplayWindow());
WinGetWindowExtent(&r.extent.x,&r.extent.y);
r.topLeft.x = 0;
r.topLeft.y = 0;
r.extent.y = fontlineheight*3 + 4;
winH = WinSaveBits(&r,&error);
ErrFatalDisplayIf(!winH,"unable to save window");
WinEraseRectangle(&r,0);
/* make room for frame */
r.topLeft.x++;
r.topLeft.y++;
r.extent.x -= 3;
r.extent.y -= 3;
WinDrawRectangleFrame(popupFrame,&r);
{
char **StationNameArray;
char string[8];
int destwidth;
StationNameArray = (char **)MemHandleLock(StationNameArrayH);
/* we implicitly assume bold font is same height as standard font here */
FntSetFont(boldFont);
destwidth = FntCharsWidth(StationNameArray[dest_station],
StrLen(StationNameArray[dest_station]));
WinDrawChars(StationNameArray[dest_station],
StrLen(StationNameArray[dest_station]),
r.topLeft.x+2, r.topLeft.y);
FntSetFont(stdFont);
WinDrawChars(" train",6,r.topLeft.x+2+destwidth,r.topLeft.y);
SetUITimeString(string,from_time,TIMESTRING_AMPM);
WinDrawChars(string, StrLen(string), r.topLeft.x+2, r.topLeft.y + fontlineheight);
WinDrawChars(StationNameArray[from_station],
StrLen(StationNameArray[from_station]),
r.topLeft.x+2+37, r.topLeft.y + fontlineheight);
SetUITimeString(string,to_time,TIMESTRING_AMPM);
WinDrawChars(string, StrLen(string), r.topLeft.x+2, r.topLeft.y + 2*fontlineheight);
WinDrawChars(StationNameArray[to_station],
StrLen(StationNameArray[to_station]),
r.topLeft.x+2+37, r.topLeft.y + 2*fontlineheight);
MemHandleUnlock(StationNameArrayH);
}
WinSetDrawWindow(drawWinH);
FntSetFont(prevFont);
resultsPopupBackingWinH = winH;
}
/* Draw the results popup window with the full name of the station
abbreviation selected. */
static void DrawStationPopup(Byte station)
{
RectangleType r;
WinHandle winH, drawWinH;
FontID prevFont = FntSetFont(boldFont);
short fontlineheight = FntLineHeight();
Word error;
drawWinH = WinSetDrawWindow(WinGetDisplayWindow());
WinGetWindowExtent(&r.extent.x,&r.extent.y);
r.topLeft.x = 0;
r.topLeft.y = 0;
r.extent.y = fontlineheight + 4;
winH = WinSaveBits(&r,&error);
ErrFatalDisplayIf(!winH,"unable to save window");
WinEraseRectangle(&r,0);
r.topLeft.x++;
r.topLeft.y++;
r.extent.x -= 3;
r.extent.y -= 3;
WinDrawRectangleFrame(popupFrame,&r);
{
char **StationNameArray;
StationNameArray = (char **)MemHandleLock(StationNameArrayH);
WinDrawChars(StationNameArray[station],
StrLen(StationNameArray[station]),
r.topLeft.x+2, r.topLeft.y);
MemHandleUnlock(StationNameArrayH);
}
WinSetDrawWindow(drawWinH);
FntSetFont(prevFont);
resultsPopupBackingWinH = winH;
}
/* draw an oval around the given rectangle */
static void DrawSelection(RectanglePtr rptr)
{
RectangleType r = *rptr;
WinHandle winH;
Word selection_width = 2;
Word error;
RctInsetRectangle(&r,-selection_width);
selectionRect = r;
winH = WinSaveBits(&r,&error);
RctInsetRectangle(&r,selection_width);
WinDrawRectangleFrame(roundFrame,&r);
resultsSelectionBackingWinH = winH;
}
/* erase popup window and selection oval */
static void EraseResultsPopup(Boolean force)
{
if (resultsPopupBackingWinH) {
if (force || (resultsPopupUpPenDownTick == 0 && TimGetTicks() >= resultsPopupExpireTick)) {
WinHandle drawWinH = WinSetDrawWindow(WinGetDisplayWindow());
WinRestoreBits(resultsPopupBackingWinH,0,0);
resultsPopupBackingWinH = (WinHandle)0;
WinSetDrawWindow(drawWinH);
WinRestoreBits(resultsSelectionBackingWinH,
selectionRect.topLeft.x,selectionRect.topLeft.y);
resultsSelectionBackingWinH = (WinHandle)0;
}
else {
MySetNullEventTick(resultsPopupExpireTick);
}
}
}
/* draw popup window and selection oval */
static void DrawResultsPopup(Word row, Word column)
{
FormPtr frm = FrmGetActiveForm();
TablePtr tableP = FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Results_Table));
RectangleType r;
if (resultsInfo[row][column].type == resultsCellTime) {
Word c1, c2;
Word station_row = 0;
RectangleType r1, r2;
int i;
for (i = row-1; i >= 0; i--) {
if (resultsInfo[i][column].type == resultsCellStation) {
station_row = i;
break;
}
}
/* c1 and c2 are the first and second times in this trip segment */
c1 = (column/2)*2; c2 = c1+1;
TblGetItemBounds(tableP,row,c1,&r1);
TblGetItemBounds(tableP,row,c2,&r2);
r.topLeft = r1.topLeft;
r.extent.x = r1.extent.x + r2.extent.x;
r.extent.y = r1.extent.y;
/* shift right 3 pixels to be more centered */
RctOffsetRectangle(&r,3,0);
/* erase any previous popup and selection oval so we can
deallocate the memory saved for the windows behind it */
EraseResultsPopup(true);
DrawSelection(&r);
DrawLinePopup(destination[row][column/2],
resultsInfo[station_row][c1].data.station.number,
resultsInfo[row][c1].data.time.time,
resultsInfo[station_row][c2].data.station.number,
resultsInfo[row][c2].data.time.time);
resultsPopupUpPenDownTick = TimGetTicks();
}
else if (resultsInfo[row][column].type == resultsCellStation) {
SWord abbrev_length;
/* we'll be anal here and measure the width of the station
abbreviation to make sure the selection oval is centered */
{
FontID prevFont = FntSetFont(stdFont);
char **StationAbbrevArray = MemHandleLock(StationAbbrevArrayH);
char *abbrev = StationAbbrevArray[resultsInfo[row][column].data.station.number];
abbrev_length = FntCharsWidth(abbrev,StrLen(abbrev));
MemHandleUnlock(StationAbbrevArrayH);
FntSetFont(prevFont);
}
TblGetItemBounds(tableP,row,column,&r);
/* shift to be more centered */
RctOffsetRectangle(&r,(r.extent.x-abbrev_length)/2,0);
/* erase any previous popup and selection oval so we can
deallocate the memory saved for the windows behind it */
EraseResultsPopup(true);
DrawSelection(&r);
DrawStationPopup(resultsInfo[row][column].data.station.number);
resultsPopupUpPenDownTick = TimGetTicks();
}
}
#endif
static void ResultsFormInit()
{
FormPtr frm = FrmGetActiveForm();
SWord fare = -1;
UInt adjusted_time;
UInt i, j;
/* table stuff */
TablePtr tableP = FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Results_Table));
/* set to invalid time to force assigning the time the first time */
ResultsTime = 24*60 * 2;
UpdateResultsTime(false);
{
/* set title */
char title[30];
StrPrintF(title,"Trains %s ~%s ",
(LeavingArriving==1 ? "arriving" : "leaving"),
UITimeString);
FrmCopyTitle(frm,title);
}
/* record category is UI list category+1 since we don't show general
category in list */
SetLineData(Category+1);
adjusted_time = (UITime < dayEndTime ? UITime+24*60 : UITime);
ClearResultsInfo();
for (i = 0; i < TABLE_ROWS; i++) {
for (j = 0; j < TABLE_COLUMNS; j++) {
TblSetItemStyle(tableP,i,j,textTableItem);
}
}
for (i = 0; i < TABLE_COLUMNS; i++) {
TblSetLoadDataProcedure(tableP,i,TableLoadItem);
TblSetColumnUsable(tableP,i,true);
}
if (FromStation != (Word)-1 && ToStation != (Word)-1) {
fare = GetFare(Category,(Byte)FromStation,(Byte)ToStation);
FindTrips((Byte)FromStation,(Byte)ToStation,adjusted_time,
LeavingArriving == 0, BikeTrainsOnly != 0, resultsInfo,
destination);
}
SetFareLabel(fare,FromStation == ToStation);
}
static void ResultsFormUnInit()
{
/* don't need to deallocate table entries; the fields should take
care of it automatically */
nullEventTick = foreverTick;
UnSetLineData();
}
static Boolean ResultsFormHandleEvent (EventPtr e)
{
Boolean handled = false;
FormPtr frm;
CALLBACK_PROLOGUE
switch (e->eType) {
case frmOpenEvent:
frm = FrmGetActiveForm();
ResultsFormInit();
FrmDrawForm(frm);
handled = true;
break;
case penDownEvent:
/* workaround for waiting to update time after being turned on; tap
the screen to update the time */
UpdateResultsTime(true);
break;
#if RESULTS_POPUP
case penUpEvent:
if (resultsPopupUpPenDownTick != 0) {
SetResultsPopupExpireTick();
resultsPopupUpPenDownTick = 0;
}
break;
#endif
case tblEnterEvent:
#if RESULTS_POPUP
DrawResultsPopup(e->data.tblEnter.row,e->data.tblEnter.column);
#endif
handled = true;
break;
case ctlSelectEvent:
switch(e->data.ctlSelect.controlID) {
case ID_Results_OK:
#if RESULTS_POPUP
EraseResultsPopup(true);
#endif
ResultsFormUnInit();
FrmReturnToForm(MainForm);
handled = true;
break;
}
break;
case nilEvent:
UpdateResultsTime(true);
#if RESULTS_POPUP
EraseResultsPopup(false);
#endif
handled = true;
break;
case appStopEvent:
#if RESULTS_POPUP
EraseResultsPopup(true);
#endif
/* uninit results form to avoid overlocking handles */
ResultsFormUnInit();
FrmReturnToForm(MainForm);
break;
default:
break;
}
CALLBACK_EPILOGUE
return handled;
}
static void StationSelectFormInit(Int station, CharPtr title)
{
FormPtr frm = FrmGetActiveForm();
char **StationNameArray;
ListPtr list_ptr;
Word list_idx;
RectangleType rect;
if (title)
FrmCopyTitle(frm,title);
StationNameArray = (char **)MemHandleLock(StationNameArrayH);
/* set up the station list */
list_idx = FrmGetObjectIndex(frm,ID_Station_List);
list_ptr = FrmGetObjectPtr(frm,list_idx);
LstSetListChoices(list_ptr,StationNameArray,Stations);
FrmGetObjectBounds(frm,list_idx,&rect);
rect.extent.x = StationNameListWidth;
FrmSetObjectBounds(frm,list_idx,&rect);
LstSetSelection(list_ptr,station);
if (station != -1)
LstMakeItemVisible(list_ptr,station);
MemHandleUnlock(StationNameArrayH);
}
static void StationSelectFormUnInit()
{
}
static Int SelectListItem(Word listID, CharPtr string)
{
FormPtr frm = FrmGetActiveForm();
ListPtr list_ptr;
Word list_idx;
Word selected_item, old_selected_item;
list_idx = FrmGetObjectIndex(frm,listID);
list_ptr = FrmGetObjectPtr(frm,list_idx);
old_selected_item = LstGetSelection(list_ptr);
selected_item = noListSelection;
/* default to previous selection if NULL string or empty field. */
if (string == NULL || string[0] == '\0') {
selected_item = old_selected_item;
}
else {
Word len = StrLen(string);
Word items = LstGetNumberOfItems(list_ptr);
Word i;
for (i = 0; i < items; i++) {
CharPtr item_text = LstGetSelectionText(list_ptr,i);
Int comparison = StrNCaselessCompare(string,item_text,len);
if (comparison == 0) {
/* this is the first item that matches the string; select it */
selected_item = i;
break;
}
/* we can use this short circuit if the station list is in strict
lexicographic order */
/* else if (comparison < 0) */
/* break; */
}
}
if (selected_item != old_selected_item) {
LstSetSelection(list_ptr,selected_item);
LstEraseList(list_ptr);
LstDrawList(list_ptr);
}
return selected_item;
}
static Boolean StationSelectFormHandleEvent (EventPtr e)
{
Boolean handled = false;
FormPtr frm;
CALLBACK_PROLOGUE
switch (e->eType) {
case frmOpenEvent:
frm = FrmGetActiveForm();
StationSelectFormInit(*SelectedStation,StationSelectFormTitle);
FrmDrawForm(frm);
FrmSetFocus(frm,FrmGetObjectIndex(frm,ID_Station_Field));
handled = true;
break;
case menuEvent:
{
FieldPtr fld;
MenuEraseStatus(NULL);
frm = FrmGetActiveForm();
fld = FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Station_Field));
handled = true;
switch(e->data.menu.itemID) {
case ID_Edit_Undo:
FldUndo(fld);
break;
case ID_Edit_Cut:
FldCut(fld);
break;
case ID_Edit_Copy:
FldCopy(fld);
break;
case ID_Edit_Paste:
FldPaste(fld);
break;
case ID_Edit_Select_All:
FldSetSelection(fld,0,FldGetTextLength(fld));
break;
case ID_Edit_Keyboard:
SysKeyboardDialog(kbdDefault);
break;
case ID_Edit_Graffiti_Help:
SysGraffitiReferenceDialog(referenceDefault);
break;
default:
handled = false;
break;
}
}
case keyDownEvent:
{
enum directions dir = down;
ListPtr lst;
FieldPtr fld;
frm = FrmGetActiveForm();
switch (e->data.keyDown.chr) {
case pageUpChr: /* hardware up button */
dir = up;
case pageDownChr: /* hardware down button */
lst = FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Station_List));
LstScrollList(lst,dir,(short)LstGetVisibleItems(lst)-1);
break;
default:
fld = FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Station_Field));
FldHandleEvent(fld,e);
SelectListItem(ID_Station_List,FldGetTextPtr(fld));
break;
}
handled = true;
}
break;
case ctlSelectEvent:
switch(e->data.ctlSelect.controlID) {
case ID_Station_Cancel:
StationSelectFormUnInit();
FrmGotoForm(MainForm);
handled = true;
break;
case ID_Station_OK:
{
ListPtr list;
Word selection;
frm = FrmGetActiveForm();
list = FrmGetObjectPtr(frm,FrmGetObjectIndex(frm,ID_Station_List));
selection = LstGetSelection(list);
if (selection != noListSelection) {
*SelectedStation = selection;
SelectedStation = NULL;
StationSelectFormUnInit();
FrmGotoForm(MainForm);
}
}
handled = true;
break;
}
break;
case appStopEvent:
SelectedStation = NULL;
FrmReturnToForm(MainForm);
break;
default:
break;
}
CALLBACK_EPILOGUE
return handled;
}
/*
*
* generic application stuff
*
*/
static Boolean ApplicationHandleEvent(EventPtr e)
{
FormPtr frm;
Word formId;
Boolean handled = false;
if (e->eType == frmLoadEvent) {
formId = e->data.frmLoad.formID;
frm = FrmInitForm(formId);
FrmSetActiveForm(frm);
switch(formId) {
case MainForm:
FrmSetEventHandler(frm, MainFormHandleEvent);
break;
case TimeForm:
FrmSetEventHandler(frm, TimeFormHandleEvent);
break;
#if 0
case PrefsForm:
FrmSetEventHandler(frm, PrefsFormHandleEvent);
break;
#endif
case ResultsForm:
FrmSetEventHandler(frm, ResultsFormHandleEvent);
break;
case StationSelectForm:
FrmSetEventHandler(frm, StationSelectFormHandleEvent);
break;
}
handled = true;
}
return handled;
}
static Word StartApplication(void)
{
char *recptr = NULL;
char **StationNameArray = NULL;
char **StationAbbrevArray = NULL;
char **CategoryArray = NULL;
ULong DBCreationDate;
/* read prefs */
{
PrefsType prefs;
Word size = sizeof(prefs), old_prefs_size = 4;
// erase old home station saved prefs (from v1.21 and earlier)
if (PrefGetAppPreferences(CREATOR_ID,0,(VoidPtr)&prefs,&old_prefs_size,true) != noPreferenceFound)
PrefSetAppPreferences(CREATOR_ID,0,PREFS_VERSION,(VoidPtr)&prefs,0,true);
if (PrefGetAppPreferences(CREATOR_ID,0,(VoidPtr)&prefs,&size,false)
!= noPreferenceFound) {
ErrFatalDisplayIf(size != sizeof(prefs),"size != sizeof(prefs)");
/* set to and from stations, bike settings, if they weren't
globally initialized */
if (FromStation == -1)
FromStation = prefs.fromStation;
if (ToStation == -1)
ToStation = prefs.toStation;
if (BikeTrainsOnly == -1)
BikeTrainsOnly = prefs.bikeTrainsOnly;
}
}
/* set up DB */
if (!DB)
DB = DmOpenDatabaseByTypeCreator(DB_ID,CREATOR_ID,dmModeReadOnly);
ErrFatalDisplayIf(!DB,"Can't find train schedule database! Did you install bartdata.pdb?");
/* check database version */
{
LocalID local_id;
UInt card;
UInt db_version;
Err err;
err = DmOpenDatabaseInfo(DB,&local_id,NULL,NULL,&card,NULL);
ErrFatalDisplayIf(err != 0, "error from DmOpenDatabaseInfo");
err = DmDatabaseInfo(card,local_id,NULL,NULL,&db_version,NULL,
NULL,NULL,NULL,NULL,NULL,NULL,NULL);
ErrFatalDisplayIf(err != 0, "error from DmDatabaseInfo");
{
char string[100];
StrPrintF(string,"Schedule database version %d installed, but I only know version %d.",db_version,DB_VERSION);
ErrFatalDisplayIf(db_version != DB_VERSION,string);
}
}
InitialRecordH = FindRecord(DB,GENERAL_CATEGORY,INFO_RECORD_NUMBER);
ErrFatalDisplayIf(!InitialRecordH,"couldn't find DB info record");
recptr = (char *)MemHandleLock(InitialRecordH);
recptr++; /* skip initial record number */
recptr += 3; /* skip 3 null bytes */
DBCreationDate = *(ULong *)recptr; /* this value must be aligned */
recptr += 4;
{
DateType date;
CharPtr p = DBCreationDateString;
Int year;
DateSecondsToDate(DBCreationDate,&date);
p += StrPrintF(p,"%d/%d/",(Int)date.month,(Int)date.day);
/* To hell with Y2K compliance, I say! */
year = ((Int)date.year+1904) % 100;
StrPrintF(p,"%s%d",(year < 10 ? "0" : ""),year);
}
/* this value should be aligned if the above one is */
dayEndTime = *(UInt *)recptr;
recptr += 2;
Categories = *(Byte *)recptr++;
CategoryArrayH = SysFormPointerArrayToStrings(recptr,Categories);
CategoryArray = (char **)MemHandleLock(CategoryArrayH);
CategoryListWidth = FindListWidth(CategoryArray,Categories);
recptr = CategoryArray[Categories-1] + StrLen(CategoryArray[Categories-1])+1;
MemHandleUnlock(CategoryArrayH);
/* Set up an array of pointers to strings pointing to the station names.
This is used in MainFormInit() to set the To/From Station lists.
Find the width of the widest station string in pixels as well, so
we can set the list width in MainFormInit() without having to check
each time we load the form.
*/
Stations = *(Byte *)recptr++;
/* record has 1 byte for ID, then one byte for # of stations, then station
names as packed strings, then station abbrevs as packed strings */
StationNameArrayH = SysFormPointerArrayToStrings(recptr,Stations);
StationNameArray = (char **)MemHandleLock(StationNameArrayH);
StationNameListWidth = FindListWidth(StationNameArray,Stations);
recptr = StationNameArray[Stations-1] + StrLen(StationNameArray[Stations-1])+1;
MemHandleUnlock(StationNameArrayH);
StationAbbrevArrayH = SysFormPointerArrayToStrings(recptr,Stations);
StationAbbrevArray = (char **)MemHandleLock(StationAbbrevArrayH);
recptr = StationAbbrevArray[Stations-1] + StrLen(StationAbbrevArray[Stations-1])+1;
MemHandleUnlock(StationAbbrevArrayH);
/* we close database in StopApplication() */
/* get time so we can set up category and time */
if (Category == -1 || UITime == -1) {
DateTimeType datetime;
TimSecondsToDateTime(TimGetSeconds(),&datetime);
/* set category if not set by global initialization */
if (Category == -1) {
/* BART SPECIFIC CODE:
* We assume the Weekday/Saturday/Sunday categories
* present in the BART version of the database, and set
* the category pull-down menu based on the current time
* and date.
*/
SWord schedule_day;
if (datetime.hour*60+datetime.minute <= dayEndTime)
/* we're still on the previous day's schedule */
schedule_day = (datetime.weekDay+6)%7;
else
schedule_day = datetime.weekDay;
switch (schedule_day) {
case 0: /* Sunday */
Category = 2;
break;
case 6: /* Saturday */
Category = 1;
break;
default: /* Weekday */
Category = 0;
break;
}
}
/* set time to current time if not set by global initialization */
if (UITime == -1) {
ULong seconds_today;
seconds_today = (ULong)datetime.hour*60*60 + (ULong)datetime.minute*60 + (ULong)datetime.second;
UITime = (seconds_today+150)/300 * 5; /* round to the nearest 5 min */
}
/* initialize time string */
SetUITimeString(UITimeString,UITime,TIMESTRING_AMPM);
}
FrmGotoForm(MainForm);
return 0;
}
/* Save preferences, close forms, close app database */
static void StopApplication(void)
{
PrefsType prefs;
prefs.fromStation = FromStation;
prefs.toStation = ToStation;
prefs.bikeTrainsOnly = BikeTrainsOnly;
PrefSetAppPreferences(CREATOR_ID,0,PREFS_VERSION,(VoidPtr)&prefs,sizeof(prefs),false);
FrmSaveAllForms();
FrmCloseAllForms();
MemHandleFree(CategoryArrayH);
CategoryArrayH = (VoidHand)0;
MemHandleFree(StationNameArrayH);
StationNameArrayH = (VoidHand)0;
MemHandleFree(StationAbbrevArrayH);
StationAbbrevArrayH = (VoidHand)0;
MemHandleUnlock(InitialRecordH);
InitialRecordH = (VoidHand)0;
DmCloseDatabase(DB);
}
/* If we are turning the power off, schedule a nilEvent soon after we
wake up so that we can update the time immediately. */
static void CheckPowerOff(EventType *e)
{
if (e->eType == keyDownEvent && (e->data.keyDown.chr == autoOffChr ||
e->data.keyDown.chr == hardPowerChr)) {
/* give the system 5 ticks to power off and on */
ULong wakeupTick = TimGetTicks()+5;
MySetNullEventTick(wakeupTick);
}
}
/* The main event loop */
static void EventLoop(void)
{
Word err;
EventType e;
do {
Long timeout;
if (nullEventTick == foreverTick)
timeout = evtWaitForever;
else if ((timeout = (Long)(nullEventTick-TimGetTicks())) < 0)
timeout = 0;
EvtGetEvent(&e, timeout);
if (lineInfo != NULL) /* check if we are in the results form */
CheckPowerOff(&e);
if (! SysHandleEvent (&e))
if (! MenuHandleEvent (NULL, &e, &err))
if (! ApplicationHandleEvent (&e))
FrmDispatchEvent (&e);
} while (e.eType != appStopEvent);
}
/* Main entry point; it is unlikely you will need to change this except to
handle other launch command codes */
DWord PilotMain(Word cmd, Ptr cmdPBP, Word launchFlags)
{
Word err;
if (cmd == sysAppLaunchCmdNormalLaunch) {
err = StartApplication();
if (err) return err;
EventLoop();
StopApplication();
} else {
return sysErrParamErr;
}
return 0;
}