home *** CD-ROM | disk | FTP | other *** search
/ Chip 2001 Mobile / Chip_Mobile_2001.iso / palm / business / freecurr / freecurr.EXE / FreeCurrency.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2000-09-29  |  40.6 KB  |  1,430 lines

  1. /*
  2.  
  3. This software is open source pursuant to the "BSD License."
  4.  
  5.    Copyright (c) 2000, Josh Goldfoot
  6.    All rights reserved.
  7.    
  8.    Redistribution and use in source and binary forms, with or without
  9.    modification, are permitted provided that the following conditions are
  10.    met:
  11.    
  12.      * Redistributions of source code must retain the above copyright
  13.        notice, this list of conditions and the following disclaimer.
  14.      * Redistributions in binary form must reproduce the above copyright
  15.        notice, this list of conditions and the following disclaimer in
  16.        the documentation and/or other materials provided with the
  17.        distribution.
  18.      * Neither the name of Goldfoot Biochemical nor the names of its
  19.        contributors may be used to endorse or promote products derived
  20.        from this software without specific prior written permission.
  21.        
  22.    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  23.    ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  24.    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  25.    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  26.    HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  27.    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  28.    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  29.    OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  30.    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
  31.    TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
  32.    USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
  33.    DAMAGE.
  34.  
  35. */
  36.  
  37. /* Code version 1.0 */
  38.  
  39. #include <Pilot.h>
  40. #include <SysEvtMgr.h>
  41. #include <ExgMgr.h>
  42. #include <NewFloatMgr.h>
  43. #include "FreeCurrencyRsc.h"
  44.  
  45. // Internal Structures
  46.  
  47.  enum  field_type { Unit1Qty, Unit2Qty, Unit1Put, Unit2Put, Unit1Name, Unit2Name, 
  48.              blank };
  49. struct myPreferenceType
  50.     {
  51.     Long        prefversion;
  52.     char        pUnit1Name[80];
  53.     char        pUnit2Name[80];
  54.     double        pUnit1Qty, pUnit2Qty, pUnit1Put, pUnit2Put;
  55.     UInt        current_record_index;
  56.     } ;
  57.  
  58. struct StarterAppInfoType
  59.     {
  60.     Byte replaceme;
  61.     } ;
  62.  
  63. class    cRecord
  64. {
  65. public:
  66.     cRecord();
  67.     cRecord(const CharPtr unit1name, const CharPtr unit2name, double unit1qty, double unit2qty);
  68.     ~cRecord();
  69.     Byte        version;
  70.     char        unit_1_name[80];
  71.     char        unit_2_name[80];
  72.     double        unit_1_qty;
  73.     double        unit_2_qty;
  74. };
  75.  
  76. // Constants
  77.  
  78. const unsigned long appFileCreator = 'fCUR';
  79. const unsigned short appVersionNum = 0x01;
  80. const unsigned short appPrefID = 0x00;
  81. const unsigned short appPrefVersionNum = 0x01;
  82. const unsigned long version30 = 0x03000000;
  83. const    CharPtr        currency_DB_name = "CurrencyDatabase-fCUR";
  84. const    ULong        currency_DB_type = 'crdb';
  85.  
  86. // Globals
  87.  
  88. myPreferenceType             prefs;
  89. DmOpenRef                    gDB;
  90. CharPtr                        popup_label;
  91.  
  92. // Function prototypes
  93.  
  94. void            print_names();
  95. double            ascii2double(CharPtr s);
  96. Short            double2ascii(const double x, CharPtr s);
  97. Short            do_conversion(field_type    which_changed);
  98. void            record2fields();
  99. Boolean            handle_keydown_event(EventPtr eventP);
  100. FieldPtr        set_field_text_from_handle(Word field_id, VoidHand text_handle);
  101. FieldPtr        set_field_text_from_string(Word field_id, CharPtr string_pointer);
  102. void            clear_field_text(Word field_id);
  103. CharPtr            get_field_text(Word field_id);
  104. Boolean            handle_button_event(EventPtr eventP);
  105. void            swap_fields(Word field1, Word field2);
  106. void            flip_values();
  107. void            populate_new_database();
  108. UInt            add_record_to_database(const CharPtr unit1name, const CharPtr unit2name, double unit1qty, 
  109.             double unit2qty, UInt actual_index);
  110. void            get_record_name(UInt item_num, CharPtr list_entry);
  111. void            currency_list_draw_function(UInt item_num, RectanglePtr bounds, CharPtr* pUnused);
  112. Boolean            select_new_currency(Int16 selection);
  113. void            do_save_command();
  114. void            do_new_command();
  115. void            do_delete_command();
  116. void            do_restore_to_original_command();
  117. void            create_new_database();
  118. void            beam_send();
  119. void            beam_receive(ExgSocketPtr socket_pointer, Boolean app_active);
  120. void            goto_item(GoToParamsPtr goToParams, Boolean launchingApp);
  121. void             search_find(FindParamsPtr findParams);
  122.  
  123.  
  124. /***********************************************************************
  125.  *
  126.  * FUNCTION:    RomVersionCompatible
  127.  *
  128.  * DESCRIPTION: This routine checks that a ROM version is meet your
  129.  *              minimum requirement.
  130.  *
  131.  * PARAMETERS:  requiredVersion - minimum rom version required
  132.  *                                (see sysFtrNumROMVersion in SystemMgr.h 
  133.  *                                for format)
  134.  *              launchFlags     - flags that indicate if the application 
  135.  *                                UI is initialized.
  136.  *
  137.  * RETURNED:    error code or zero if rom is compatible
  138.  *
  139.  * REVISION HISTORY:
  140.  *
  141.  ***********************************************************************/
  142. static Err RomVersionCompatible(DWord requiredVersion, Word launchFlags)
  143. {
  144.     DWord romVersion;
  145.  
  146.     // See if we're on in minimum required version of the ROM or later.
  147.     FtrGet(sysFtrCreator, sysFtrNumROMVersion, &romVersion);
  148.     if (romVersion < requiredVersion)
  149.         {
  150.         if ((launchFlags & (sysAppLaunchFlagNewGlobals | sysAppLaunchFlagUIApp)) ==
  151.             (sysAppLaunchFlagNewGlobals | sysAppLaunchFlagUIApp))
  152.             {
  153.             FrmAlert (RomIncompatibleAlert);
  154.         
  155.             // Pilot 1.0 will continuously relaunch this app unless we switch to 
  156.             // another safe one.
  157.             if (romVersion < version30)
  158.                 {
  159.                 // Err err;
  160.                 
  161.                 AppLaunchWithCommand(sysFileCDefaultApp, sysAppLaunchCmdNormalLaunch, NULL);
  162.                 }
  163.             }
  164.         
  165.         return (sysErrRomIncompatible);
  166.         }
  167.  
  168.     return (0);
  169. }
  170.  
  171.  
  172. /***********************************************************************
  173.  *
  174.  * FUNCTION:    GetObjectPtr
  175.  *
  176.  * DESCRIPTION: This routine returns a pointer to an object in the current
  177.  *              form.
  178.  *
  179.  * PARAMETERS:  formId - id of the form to display
  180.  *
  181.  * RETURNED:    VoidPtr
  182.  *
  183.  * REVISION HISTORY:
  184.  *
  185.  *
  186.  ***********************************************************************/
  187. static VoidPtr GetObjectPtr(Word objectID)
  188.     {
  189.     FormPtr frmP;
  190.  
  191.     frmP = FrmGetActiveForm();
  192.  
  193.     return (FrmGetObjectPtr(frmP, FrmGetObjectIndex(frmP, objectID)));
  194. }
  195.  
  196.  
  197. /***********************************************************************
  198.  *
  199.  * FUNCTION:    MainFormInit
  200.  *
  201.  * DESCRIPTION: This routine initializes the MainForm form.
  202.  *
  203.  * PARAMETERS:  frm - pointer to the MainForm form.
  204.  *
  205.  * RETURNED:    nothing
  206.  *
  207.  * REVISION HISTORY:
  208.  *
  209.  *
  210.  ***********************************************************************/
  211. static void MainFormInit(FormPtr frmP)
  212. {
  213.  
  214.     
  215.     ListPtr        currency_list_ptr = (ListPtr) FrmGetObjectPtr(frmP, FrmGetObjectIndex(frmP, MainCurrenciesList));
  216.  
  217.     LstSetListChoices( currency_list_ptr, NULL, DmNumRecords (gDB) );
  218.     LstSetDrawFunction(currency_list_ptr, currency_list_draw_function);
  219.     LstSetSelection(currency_list_ptr, 0);
  220.     
  221.     
  222.     CharPtr        buf = (CharPtr) MemPtrNew(128);
  223.     
  224.     if (prefs.current_record_index == -1)
  225.         select_new_currency(0);
  226.     else {
  227.         set_field_text_from_string(MainUnit1NameField, prefs.pUnit1Name);
  228.         set_field_text_from_string(MainUnit2NameField, prefs.pUnit2Name);
  229.         double2ascii(prefs.pUnit1Qty, buf);
  230.         set_field_text_from_string(MainUnit1QtyField, buf);
  231.         double2ascii(prefs.pUnit2Qty, buf);
  232.         set_field_text_from_string(MainUnit2QtyField, buf);
  233.         double2ascii(prefs.pUnit1Put, buf);
  234.         set_field_text_from_string(MainUnit1PutField, buf);
  235.         double2ascii(prefs.pUnit2Put, buf);
  236.         set_field_text_from_string(MainUnit2PutField, buf);
  237.         
  238.         get_record_name(prefs.current_record_index, popup_label);
  239.         CtlSetLabel (ControlPtr(GetObjectPtr(MainCurrenciesPopTrigger)), popup_label);
  240.  
  241.     }
  242.     
  243.     MemPtrFree( (VoidPtr) buf);
  244.     print_names();
  245. }
  246.  
  247.  
  248. /***********************************************************************
  249.  *
  250.  * FUNCTION:    MainFormDoCommand
  251.  *
  252.  * DESCRIPTION: This routine performs the menu command specified.
  253.  *
  254.  * PARAMETERS:  command  - menu item id
  255.  *
  256.  * RETURNED:    nothing
  257.  *
  258.  * REVISION HISTORY:
  259.  *
  260.  *
  261.  ***********************************************************************/
  262. static Boolean MainFormDoCommand(Word command)
  263. {
  264.     Boolean handled = false;
  265.     
  266.     FormPtr        frmP = FrmGetActiveForm();
  267.     Word        active_field_id = FrmGetObjectId(frmP, FrmGetFocus(FrmGetActiveForm()));    
  268.     FieldPtr    active_field = (FieldPtr) GetObjectPtr(active_field_id);
  269.  
  270.     switch (command)
  271.         {
  272.         case MainOptionsAboutFreeCurrency:
  273.             FrmAlert(AboutAlert);
  274.             handled = true;
  275.             break;
  276.         
  277.         case EditUndo:
  278.             handled = true;
  279.             if (active_field)
  280.                 FldUndo(active_field);
  281.             break;
  282.         
  283.         case EditGraffitiHelp:
  284.             SysGraffitiReferenceDialog(referenceDefault);
  285.             handled = true;
  286.             break;
  287.         
  288.         case EditKeyboard:
  289.             SysKeyboardDialog (kbdDefault);
  290.             handled = true;
  291.             break;
  292.         
  293.         case EditCopy:
  294.             if (active_field)
  295.                 FldCopy(active_field);
  296.             break;
  297.         
  298.         case EditCut:
  299.             if (active_field)
  300.                 FldCut(active_field);
  301.             break;
  302.         
  303.         case EditPaste:
  304.             if (active_field)
  305.                 FldPaste(active_field);
  306.             break;
  307.         
  308.         case CurrencyNew:
  309.             do_new_command();
  310.             handled = true;
  311.             break;
  312.         
  313.         case CurrencySave:
  314.             do_save_command();
  315.             handled = true;
  316.             break;
  317.         
  318.         case CurrencyDelete:
  319.             do_delete_command();
  320.             handled = true;
  321.             break;
  322.         
  323.         case CurrencyBeam:
  324.             beam_send();
  325.             handled = true;
  326.             break;
  327.         
  328.         case CurrencyRestoretooriginal:
  329.             do_restore_to_original_command();
  330.             handled = true;
  331.             break;
  332.  
  333.         }
  334.     return handled;
  335. }
  336.  
  337.  
  338. /***********************************************************************
  339.  *
  340.  * FUNCTION:    MainFormHandleEvent
  341.  *
  342.  * DESCRIPTION: This routine is the event handler for the 
  343.  *              "MainForm" of this application.
  344.  *
  345.  * PARAMETERS:  eventP  - a pointer to an EventType structure
  346.  *
  347.  * RETURNED:    true if the event has handle and should not be passed
  348.  *              to a higher level handler.
  349.  *
  350.  * REVISION HISTORY:
  351.  *
  352.  *
  353.  ***********************************************************************/
  354. static Boolean MainFormHandleEvent(EventPtr eventP)
  355. {
  356.     Boolean handled = false;
  357.     FormPtr frmP = FrmGetActiveForm();
  358.  
  359.  
  360.     switch (eventP->eType) 
  361.         {
  362.         case menuEvent:
  363.             return MainFormDoCommand(eventP->data.menu.itemID);
  364.  
  365.         case frmOpenEvent:
  366.             frmP = FrmGetActiveForm();
  367.             MainFormInit( frmP);
  368.             FrmDrawForm ( frmP);
  369.             handled = true;
  370.             break;
  371.         
  372.         
  373.         case keyDownEvent:
  374.             handled = handle_keydown_event(eventP);
  375.             break;
  376.         
  377.         case popSelectEvent:
  378.             handled = false;
  379.             if (eventP->data.popSelect.controlID == MainCurrenciesPopTrigger) 
  380.                 handled = select_new_currency(eventP->data.popSelect.selection);
  381.             break;
  382.             
  383.         
  384.         case ctlSelectEvent:
  385.             handled = handle_button_event(eventP);
  386.             break;
  387.  
  388.         case frmGotoEvent:
  389.             prefs.current_record_index = eventP->data.frmGoto.recordNum;
  390.             select_new_currency(prefs.current_record_index);
  391.             if (eventP->data.frmGoto.matchFieldNum) {
  392.                 FieldPtr selectedField = FieldPtr(GetObjectPtr(eventP->data.frmGoto.matchFieldNum));
  393.                 FldSetScrollPosition(selectedField, eventP->data.frmGoto.matchPos);
  394.                 FrmSetFocus(frmP, FrmGetObjectIndex(frmP, eventP->data.frmGoto.matchFieldNum));
  395.                 FldSetSelection(selectedField, eventP->data.frmGoto.matchPos, eventP->data.frmGoto.matchPos + eventP->data.frmGoto.matchLen);
  396.  
  397.             }
  398.             handled = true;
  399.             break;
  400.         
  401.         
  402.         default:
  403.             break;
  404.         
  405.         }
  406.     
  407.     return handled;
  408. }
  409.  
  410. /***********************************************************************
  411.  *
  412.  * FUNCTION:    AppHandleEvent
  413.  *
  414.  * DESCRIPTION: This routine loads form resources and set the event
  415.  *              handler for the form loaded.
  416.  *
  417.  * PARAMETERS:  event  - a pointer to an EventType structure
  418.  *
  419.  * RETURNED:    true if the event has handle and should not be passed
  420.  *              to a higher level handler.
  421.  *
  422.  * REVISION HISTORY:
  423.  *
  424.  *
  425.  ***********************************************************************/
  426. static Boolean AppHandleEvent( EventPtr eventP)
  427. {
  428.     Word formId;
  429.     FormPtr frmP;
  430.  
  431.  
  432.     if (eventP->eType == frmLoadEvent)
  433.         {
  434.         // Load the form resource.
  435.         formId = eventP->data.frmLoad.formID;
  436.         frmP = FrmInitForm(formId);
  437.         FrmSetActiveForm(frmP);
  438.  
  439.         // Set the event handler for the form.  The handler of the currently
  440.         // active form is called by FrmHandleEvent each time is receives an
  441.         // event.
  442.         switch (formId)
  443.             {
  444.             case MainForm:
  445.                 {
  446.                     FrmSetEventHandler(frmP, MainFormHandleEvent);
  447.                 }
  448.                 break;
  449.  
  450.             default:
  451. //                ErrFatalDisplay("Invalid Form Load Event");
  452.                 break;
  453.  
  454.             }
  455.         return true;
  456.         }
  457.     
  458.     return false;
  459. }
  460.  
  461.  
  462. /***********************************************************************
  463.  *
  464.  * FUNCTION:    AppEventLoop
  465.  *
  466.  * DESCRIPTION: This routine is the event loop for the application.  
  467.  *
  468.  * PARAMETERS:  nothing
  469.  *
  470.  * RETURNED:    nothing
  471.  *
  472.  * REVISION HISTORY:
  473.  *
  474.  *
  475.  ***********************************************************************/
  476. static void AppEventLoop(void)
  477. {
  478.     Word error;
  479.     EventType event;
  480.  
  481.  
  482.     do {
  483.         EvtGetEvent(&event, evtWaitForever);
  484.         
  485.         
  486.         if (! SysHandleEvent(&event))
  487.             if (! MenuHandleEvent(0, &event, &error))
  488.                 if (! AppHandleEvent(&event))
  489.                     FrmDispatchEvent(&event);
  490.         //if (event.eType == keyDownEvent)
  491.         //    handle_keydown_event(&event);
  492.  
  493.     } while (event.eType != appStopEvent);
  494. }
  495.  
  496.  
  497. /***********************************************************************
  498.  *
  499.  * FUNCTION:     AppStart
  500.  *
  501.  * DESCRIPTION:  Get the current application's preferences.
  502.  *
  503.  * PARAMETERS:   nothing
  504.  *
  505.  * RETURNED:     Err value 0 if nothing went wrong
  506.  *
  507.  * REVISION HISTORY:
  508.  *
  509.  *
  510.  ***********************************************************************/
  511. static Err AppStart(void)
  512. {
  513.     
  514.     Word prefsSize;
  515.  
  516.     // Read the saved preferences / saved-state information.
  517.     prefsSize = sizeof(myPreferenceType);
  518.     if (PrefGetAppPreferences(appFileCreator, appPrefID, &prefs, &prefsSize, true) == 
  519.         noPreferenceFound)
  520.         {
  521.             MemSet( VoidPtr(&prefs), prefsSize, 0);
  522.             prefs.prefversion = 1;
  523.             StrCopy(prefs.pUnit1Name, "xNew");
  524.             StrCopy(prefs.pUnit2Name, "xNew");
  525.             prefs.pUnit1Qty = 1;
  526.             prefs.pUnit2Qty = 1;
  527.             prefs.pUnit1Put = 0;
  528.             prefs.pUnit2Put = 0;
  529.             prefs.current_record_index = -1;
  530.             
  531.         }
  532.     
  533.     gDB = DmOpenDatabaseByTypeCreator(currency_DB_type, appFileCreator, dmModeReadWrite);
  534.     if (! gDB) {
  535.         create_new_database();
  536.     }
  537.  
  538.     popup_label = (CharPtr) MemPtrNew(128);
  539.     
  540.     
  541.    return 0;
  542. }
  543.  
  544.  
  545. /***********************************************************************
  546.  *
  547.  * FUNCTION:    AppStop
  548.  *
  549.  * DESCRIPTION: Save the current state of the application.
  550.  *
  551.  * PARAMETERS:  nothing
  552.  *
  553.  * RETURNED:    nothing
  554.  *
  555.  * REVISION HISTORY:
  556.  *
  557.  *
  558.  ***********************************************************************/
  559. static void AppStop(void)
  560. {
  561.  
  562.     
  563.     StrCopy(prefs.pUnit1Name, get_field_text(MainUnit1NameField));
  564.     StrCopy(prefs.pUnit2Name, get_field_text(MainUnit2NameField));
  565.     prefs.pUnit1Qty = ascii2double(get_field_text(MainUnit1QtyField));
  566.     prefs.pUnit2Qty = ascii2double(get_field_text(MainUnit2QtyField));
  567.     prefs.pUnit1Put = ascii2double(get_field_text(MainUnit1PutField));
  568.     prefs.pUnit2Put = ascii2double(get_field_text(MainUnit2PutField));
  569.    
  570.     MemPtrFree( (VoidPtr) popup_label);
  571.    
  572.     // Write the saved preferences / saved-state information.  This data 
  573.     // will be backed up during a HotSync.
  574.     PrefSetAppPreferences (appFileCreator, appPrefID, appPrefVersionNum, 
  575.         &prefs, sizeof (prefs), true);
  576.     
  577.     if (gDB != NULL)
  578.         DmCloseDatabase(gDB);
  579.     
  580. }
  581.  
  582.  
  583.  
  584. /***********************************************************************
  585.  *
  586.  * FUNCTION:    FreeCurrencyPilotMain
  587.  *
  588.  * DESCRIPTION: This is the main entry point for the application.
  589.  * PARAMETERS:  cmd - word value specifying the launch code. 
  590.  *              cmdPB - pointer to a structure that is associated with the launch code. 
  591.  *              launchFlags -  word value providing extra information about the launch.
  592.  *
  593.  * RETURNED:    Result of launch
  594.  *
  595.  * REVISION HISTORY:
  596.  *
  597.  *
  598.  ***********************************************************************/
  599. static DWord FreeCurrencyPilotMain(Word cmd, Ptr cmdPBP, Word launchFlags)
  600. {
  601.     Err error;
  602.     Boolean    app_active;
  603.  
  604.  
  605.     error = RomVersionCompatible (version30, launchFlags);
  606.     if (error) return (error);
  607.  
  608.  
  609.     switch (cmd)
  610.         {
  611.         case sysAppLaunchCmdNormalLaunch:
  612.             error = AppStart();
  613.             if (error) 
  614.                 return error;
  615.                 
  616.             FrmGotoForm(MainForm);
  617.             AppEventLoop();
  618.             AppStop();
  619.             break;
  620.             
  621.         case sysAppLaunchCmdSyncNotify:
  622.             // Use this if I have more than one data type, but I don't...
  623.             break;
  624.         
  625.         
  626.         case sysAppLaunchCmdExgReceiveData:
  627.             app_active = (launchFlags & sysAppLaunchFlagSubCall);
  628.             beam_receive(ExgSocketPtr(cmdPBP), app_active);
  629.             break;
  630.         
  631.         case sysAppLaunchCmdSaveData:
  632.             // Do nothing; we don't need to save data
  633.             break;
  634.         
  635.         case sysAppLaunchCmdGoTo:
  636.             Boolean        launched = launchFlags & sysAppLaunchFlagNewGlobals;
  637.             if (launched) {
  638.                 error = AppStart();
  639.                 if (! error) {
  640.                     goto_item((GoToParamsPtr) cmdPBP, launched);
  641.                     AppEventLoop();
  642.                     AppStop();
  643.                 }
  644.             } else
  645.                 goto_item((GoToParamsPtr) cmdPBP, launched);
  646.             break;
  647.         
  648.         case sysAppLaunchCmdFind:
  649.             search_find((FindParamsPtr)cmdPBP);
  650.  
  651.         default:
  652.             break;
  653.  
  654.         }
  655.     
  656.     return 0;
  657. }
  658.  
  659.  
  660. /***********************************************************************
  661.  *
  662.  * FUNCTION:    PilotMain
  663.  *
  664.  * DESCRIPTION: This is the main entry point for the application.
  665.  *
  666.  * PARAMETERS:  cmd - word value specifying the launch code. 
  667.  *              cmdPB - pointer to a structure that is associated with the launch code. 
  668.  *              launchFlags -  word value providing extra information about the launch.
  669.  * RETURNED:    Result of launch
  670.  *
  671.  * REVISION HISTORY:
  672.  *
  673.  *
  674.  ***********************************************************************/
  675. DWord PilotMain( Word cmd, Ptr cmdPBP, Word launchFlags)
  676. {
  677.     return FreeCurrencyPilotMain(cmd, cmdPBP, launchFlags);
  678. }
  679.  
  680.  
  681. // print_names:  Gets the names of the currencies, and prints them
  682. // in the lower part of the form.
  683. void            print_names()
  684. {
  685.     CharPtr            cpUnit1Name = get_field_text(MainUnit1NameField);
  686.     CharPtr            cpUnit2Name = get_field_text(MainUnit2NameField);
  687.     RectangleType    r;
  688.     RctSetRectangle(&r, 104, 75, 56, 50);
  689.     WinEraseRectangle(&r, 0);
  690.     WinDrawChars(cpUnit1Name, StrLen(cpUnit1Name), 104, 80);
  691.     WinDrawChars(cpUnit2Name, StrLen(cpUnit2Name), 104, 112);
  692. }
  693.  
  694. // ascii2double: Given a string containing a number (s), return a double
  695. // representing the value of the string.
  696. double            ascii2double(CharPtr s)
  697. {
  698.     char    thousand_separator, decimal_separator;
  699.     
  700.     if (s != NULL) {
  701.         if (StrLen(s) > 0) {
  702.             LocGetNumberSeparators ((NumberFormatType) PrefGetPreference(prefNumberFormat), 
  703.                     &thousand_separator, &decimal_separator);
  704.             StrDelocalizeNumber (s, thousand_separator, decimal_separator);
  705.             
  706.             FlpCompDouble theCompdouble;
  707.             theCompdouble.fd = FlpAToF(s);
  708.             double        result = theCompdouble.d;
  709.             return result;
  710.         } else
  711.             return 0;
  712.     } else
  713.         return 0;
  714. }
  715.  
  716. // double2ascii: given a double and a pointer to a string buffer,
  717. // put an ASCII representation of the double into the buffer, using normal
  718. // (i.e. non-scientific) notation, rounding to hundredths.
  719.  
  720. Short            double2ascii(const double x, CharPtr s)
  721. {
  722.     char        temp[80];
  723.     char    thousand_separator, decimal_separator;
  724.     
  725.     if (s == NULL)
  726.         return 0;
  727.     
  728.     // Use the OS to convert the double to ASCII.
  729.     FlpCompDouble    theCompdouble;
  730.     theCompdouble.d = x;
  731.     if (FlpFToA( theCompdouble.fd, s)) {
  732.         StrCopy(s, "Overflow");
  733.         return StrLen(s);
  734.     }
  735.     
  736.     // De-localize the number string so that it uses U.S. conventions
  737.     LocGetNumberSeparators ((NumberFormatType) PrefGetPreference(prefNumberFormat), 
  738.             &thousand_separator, &decimal_separator);
  739.     StrDelocalizeNumber (s, thousand_separator, decimal_separator);
  740.     
  741.     
  742.     
  743.     // FlpFToA leaves us with a number in scientific notation, in the form:
  744.     // [-]x.yyyyyyyye[-]zz.  We must convert this to normal notation.
  745.     
  746.     // Locate the "e" towards the end of the string, and extract the exponent.
  747.     Short        string_length = StrLen(s);
  748.     Short        e_position = string_length - 3;
  749.     if (s[e_position] == '-')
  750.         e_position--;
  751.     Long        exponent = StrAToI( & s[e_position + 1] );
  752.     
  753.     
  754.     // Wipe out the "e[-]zz" at the end of the string
  755.     Short        i = e_position;
  756.     while (s[i] != 0) {
  757.         s[i] = 0;
  758.         i++;
  759.     }
  760.     string_length = e_position;
  761.     
  762.     
  763.     // Find the decimal point.
  764.     // Because we have delocalized, it is OK to assume it is a "."
  765.     Short        decimal_position = 1;
  766.     if (s[decimal_position] != '.')
  767.         decimal_position++;
  768.     
  769.     if (exponent != 0) {
  770.         // Delete the decimal point, moving all other numbers forward one.
  771.         for (i = decimal_position; i < string_length - 1; i++)
  772.             s[i] = s[i + 1];
  773.         string_length--;
  774.     }
  775.     
  776.     if (exponent < 0) {
  777.         // If exponent is negative, add zeros to the front.
  778.         StrCopy(temp, "0.0000000000000000000000000000000");
  779.         temp[1 - exponent] = 0;
  780.         StrCat(temp, s);
  781.         StrCopy(s, temp);
  782.         string_length += 1 - exponent;
  783.     } else if (exponent > 0) {
  784.         // If exponent is positive, insert decimal point into the string at the proper point.
  785.         decimal_position += exponent;
  786.         for (i = string_length; i > decimal_position; i--)
  787.             s[i] = s[i - 1];
  788.         s[decimal_position] = '.';
  789.         string_length++;
  790.     }
  791.     
  792.     // Remove trailing zeroes.
  793.     i = string_length - 1;
  794.     while ((s[i] == '0') && (i > 0)) {
  795.         s[i--] = 0;
  796.         string_length--;
  797.     }
  798.     
  799.     // If there is a thousandths place (or more), round to hundredths.
  800.     // (This is good to do both because we are dealing with currencies and because the Palm's
  801.     // floating point accuracy isn't too good).
  802.     if (string_length - decimal_position > 3) {
  803.         if (s[decimal_position + 3] > '4')
  804.             s[decimal_position + 2]++;
  805.         for (i = decimal_position + 3; i < string_length; i++)
  806.             s[i] = 0;
  807.     } 
  808.     
  809.     // Remove trailing garbage, if any
  810.     while (((s[string_length - 1] < '0') || (s[string_length - 1] > '9'))
  811.          && (string_length > 0))
  812.         s[--string_length] = 0;
  813.     
  814.     // Re-localize the number, and return the length.    
  815.     LocGetNumberSeparators ((NumberFormatType) PrefGetPreference(prefNumberFormat), 
  816.             &thousand_separator, &decimal_separator);
  817.     StrLocalizeNumber (s, thousand_separator, decimal_separator);
  818.     
  819.     // Recalculate string length in case it was changed during localization.
  820.     return StrLen(s);
  821. }
  822.  
  823. // do_conversion: the main currency conversion routine.  "which_changed"
  824. // tells the routine which of the fields the user has changed.  Based on that,
  825. // the routine decides which field to recalculate, and does so.
  826. Short            do_conversion(field_type    which_changed)
  827. {
  828.     // Get the text of all the fields.
  829.     CharPtr            cpUnit1Qty = get_field_text(MainUnit1QtyField);
  830.     CharPtr            cpUnit2Qty = get_field_text(MainUnit2QtyField);
  831.     if ((StrLen(cpUnit1Qty) == 0) || (StrLen(cpUnit2Qty) == 0))
  832.         return 0;
  833.         
  834.     double            dUnit1Qty, dUnit1Put, dUnit2Qty, dUnit2Put;
  835.     
  836.     // Convert from strings to floating point numbers for calculating.
  837.     dUnit1Qty = ascii2double(cpUnit1Qty);
  838.     dUnit2Qty = ascii2double(cpUnit2Qty);
  839.     dUnit1Put = ascii2double(get_field_text(MainUnit1PutField));
  840.     dUnit2Put = ascii2double(get_field_text(MainUnit2PutField));
  841.     
  842.     char        buf[255];
  843.     
  844.     // If either of the main unit quantities are zero, quit to avoid division
  845.     // by zero errors.
  846.     if ((dUnit1Qty == 0) || (dUnit2Qty == 0)) {
  847.         clear_field_text(MainUnit1PutField);
  848.         clear_field_text(MainUnit2PutField);
  849.         return 0;
  850.     }
  851.     
  852.     // Do the calculation.  Note that if the "quantity" fields change, only
  853.     // unit 2 put gets changed.
  854.     
  855.     if (which_changed != Unit2Put) {
  856.         dUnit2Put = dUnit1Put * dUnit2Qty / dUnit1Qty;
  857.         double2ascii(dUnit2Put, buf);
  858.         set_field_text_from_string(MainUnit2PutField, buf);
  859.     }
  860.  
  861.     if (which_changed != Unit1Put) {
  862.         dUnit1Put = dUnit2Put * dUnit1Qty / dUnit2Qty;
  863.         double2ascii(dUnit1Put, buf); 
  864.         set_field_text_from_string(MainUnit1PutField, buf);
  865.     }    
  866.     
  867.  
  868.     return 0;
  869. }
  870.  
  871.  
  872.  
  873. // handle_keydown_event: called after the user enters a character.
  874. Boolean            handle_keydown_event(EventPtr eventP)
  875. {
  876.     FormPtr        frmP = FrmGetActiveForm();
  877.     Word        active_field_id = FrmGetObjectId(frmP, FrmGetFocus(FrmGetActiveForm()));
  878.     FldHandleEvent ( FieldPtr(FrmGetObjectPtr(frmP, 
  879.                         FrmGetObjectIndex(frmP, active_field_id))), eventP);
  880.  
  881.     switch ( active_field_id ) {
  882.         case MainUnit1QtyField:
  883.             do_conversion(Unit1Qty);
  884.             break;
  885.         case MainUnit2QtyField:
  886.             do_conversion(Unit2Qty);
  887.             break;
  888.         case MainUnit1PutField:
  889.             do_conversion(Unit1Put);
  890.             break;
  891.         case MainUnit2PutField:
  892.             do_conversion(Unit2Put);
  893.             break;
  894.         case MainUnit1NameField:
  895.             print_names();
  896.             break;
  897.         case MainUnit2NameField:
  898.             print_names();
  899.             break;
  900.         default:
  901.             break;
  902.     }
  903.     
  904.     return true;
  905. }
  906.  
  907.  
  908. FieldPtr        set_field_text_from_handle(Word field_id, VoidHand text_handle)
  909. {
  910.     VoidHand    old_text_handle;
  911.     FormPtr    frm = FrmGetActiveForm();
  912.     FieldPtr    field_pointer;
  913.     
  914.     field_pointer = FieldPtr(FrmGetObjectPtr(frm, FrmGetObjectIndex(frm, field_id)));
  915.     ErrNonFatalDisplayIf(!field_pointer, "missing field");
  916.     old_text_handle = (VoidHand) FldGetTextHandle(field_pointer);
  917.     
  918.     FldSetTextHandle(field_pointer, (Handle) text_handle);
  919.     FldDrawField(field_pointer);
  920.     
  921.     if (old_text_handle)
  922.         MemHandleFree(old_text_handle);
  923.     
  924.     return field_pointer;
  925. }
  926.  
  927. FieldPtr        set_field_text_from_string(Word field_id, CharPtr string_pointer)
  928. {
  929.     VoidHand text_handle;
  930.     text_handle = MemHandleNew(StrLen(string_pointer) + 1);
  931.     if (!text_handle)
  932.         return NULL;
  933.     StrCopy(CharPtr(MemHandleLock(text_handle)), string_pointer);
  934.     MemHandleUnlock(text_handle);
  935.     return set_field_text_from_handle(field_id, text_handle);
  936. }
  937.  
  938. void            clear_field_text(Word field_id)
  939. {
  940.     set_field_text_from_handle(field_id, NULL);
  941. }
  942.  
  943. CharPtr            get_field_text(Word field_id)
  944. {
  945.     FormPtr            frmP = FrmGetActiveForm();
  946.     return FldGetTextPtr(FieldPtr(FrmGetObjectPtr(frmP, FrmGetObjectIndex(frmP, field_id))));
  947. }
  948.  
  949. Boolean            handle_button_event(EventPtr eventP)
  950. {
  951.     Boolean        handled = false;
  952.     
  953.     switch (eventP->data.ctlSelect.controlID) {
  954.         case MainSaveButton:
  955.             handled = true;
  956.             do_save_command();
  957.             break;
  958.         case MainNewButton:
  959.             handled = true;
  960.             do_new_command();
  961.             break;
  962.         case MainDeleteButton:
  963.             handled = true;
  964.             do_delete_command();
  965.             break;
  966.         default:
  967.             handled = false;
  968.             break;
  969.         
  970.     }
  971.  
  972.     return handled;
  973. }
  974.  
  975. void            do_save_command()
  976. {
  977.     char    question[200] = "Really save over \"";
  978.     char    record_name[80];
  979.     get_record_name(prefs.current_record_index, record_name);
  980.     StrCat(question, record_name);
  981.     StrCat(question, "?\"");
  982.     
  983.     Word    button_tapped = FrmCustomAlert (QueryAlert, question, NULL, NULL);
  984.     if (button_tapped == QueryNo)
  985.         return;
  986.  
  987.     cRecord            my_record(get_field_text(MainUnit1NameField), 
  988.                 get_field_text(MainUnit2NameField), 
  989.                 ascii2double(get_field_text(MainUnit1QtyField)), 
  990.                 ascii2double(get_field_text(MainUnit2QtyField)) );
  991.     
  992.     VoidHand    record_handle = DmGetRecord( gDB, prefs.current_record_index );
  993.     if (! record_handle)
  994.         return;
  995.  
  996.     VoidPtr        record_pointer = MemHandleLock( record_handle );
  997.     UInt        the_size = sizeof(cRecord);
  998.     DmWrite( record_pointer, 0, &my_record, the_size );
  999.     MemHandleUnlock( record_handle );
  1000.     DmReleaseRecord( gDB, prefs.current_record_index, true);
  1001.     get_record_name(prefs.current_record_index, popup_label);
  1002.     CtlSetLabel (ControlPtr(GetObjectPtr(MainCurrenciesPopTrigger)), popup_label);
  1003.  
  1004.     
  1005. }
  1006.  
  1007. void            do_new_command()
  1008. {
  1009.     UInt    new_index = add_record_to_database("USA Dollar", "Foreign", 1, 3, prefs.current_record_index);
  1010.     LstSetListChoices( ListPtr(GetObjectPtr(MainCurrenciesList)), 
  1011.         NULL, DmNumRecords (gDB) );
  1012.     select_new_currency(new_index);
  1013. }
  1014.  
  1015. void            do_delete_command()
  1016. {
  1017.     if (DmNumRecords(gDB) == 1) {
  1018.         FrmCustomAlert(InfoAlert, "Sorry, that's your last record.", NULL, NULL);
  1019.         return;
  1020.     }
  1021.     
  1022.     char    question[200] = "Really delete \"";
  1023.     char    record_name[80];
  1024.     get_record_name(prefs.current_record_index, record_name);
  1025.     StrCat(question, record_name);
  1026.     StrCat(question, "?\"");
  1027.     
  1028.     Word    button_tapped = FrmCustomAlert (QueryAlert, question, NULL, NULL);
  1029.     if (button_tapped == QueryNo)
  1030.         return;
  1031.     
  1032.     Err        myErr = DmRemoveRecord (gDB, prefs.current_record_index);
  1033.     if (prefs.current_record_index > 0) {
  1034.         prefs.current_record_index--;
  1035.     }
  1036.     LstSetListChoices( ListPtr(GetObjectPtr(MainCurrenciesList)), 
  1037.         NULL, DmNumRecords (gDB) );
  1038.     select_new_currency(prefs.current_record_index);
  1039.  
  1040. }
  1041.  
  1042. void            do_restore_to_original_command()
  1043. {
  1044.     
  1045.     Word    button_tapped = FrmCustomAlert (QueryAlert, "Really delete ALL records and replace them with a default set?", NULL, NULL);
  1046.     if (button_tapped == QueryNo)
  1047.         return;
  1048.     
  1049.     UInt    num_records = DmNumRecords(gDB);
  1050.     while (num_records > 0) {
  1051.         DmRemoveRecord (gDB, 0);
  1052.         num_records--;
  1053.     }
  1054.     populate_new_database();
  1055.     LstSetListChoices( ListPtr(GetObjectPtr(MainCurrenciesList)), 
  1056.         NULL, DmNumRecords (gDB) );
  1057.     select_new_currency(0);
  1058. }
  1059.  
  1060.  
  1061. cRecord::cRecord()
  1062. {
  1063.     version = 1;
  1064.     StrCopy(unit_1_name, "");
  1065.     StrCopy(unit_2_name, "");
  1066.     unit_1_qty = 1;
  1067.     unit_2_qty = 1;
  1068. }
  1069.  
  1070. cRecord::cRecord(const CharPtr unit1name, const CharPtr unit2name, double unit1qty, double unit2qty)
  1071. {
  1072.     version = 1;
  1073.     StrCopy(unit_1_name, unit1name);
  1074.     StrCopy(unit_2_name, unit2name);
  1075.     unit_1_qty = unit1qty;
  1076.     unit_2_qty = unit2qty;
  1077. }
  1078.  
  1079. cRecord::~cRecord()
  1080. {
  1081.  
  1082. }
  1083.  
  1084.  
  1085. UInt            add_record_to_database(const CharPtr unit1name, const CharPtr unit2name, double unit1qty, 
  1086.             double unit2qty, UInt actual_index)
  1087. {
  1088.     cRecord            my_record(unit1name, unit2name, unit1qty, unit2qty);
  1089.     ULong            record_size = sizeof(cRecord);
  1090.     VoidHand        record_handle = DmNewHandle( gDB, record_size );
  1091.     if (! record_handle)
  1092.         return -1;
  1093.     VoidPtr            record_pointer = MemHandleLock( record_handle );
  1094.     DmWrite( record_pointer, 0, &my_record, record_size );
  1095.     MemHandleUnlock( record_handle );
  1096.     DmAttachRecord( gDB, &actual_index, (char **) record_handle, NULL );
  1097.     return    actual_index;
  1098. }
  1099.  
  1100. void            populate_new_database()
  1101. {
  1102.     // Defaults to major USA trading partners
  1103.     // Country abbreviations are taken from ISO 3166 for consistency
  1104.     
  1105.     add_record_to_database("USA Dollar", "CAN Dollar", 1, 1.48, dmMaxRecordIndex);
  1106.     add_record_to_database("USA Dollar", "CHN Yuan", 1, 8.28, dmMaxRecordIndex);
  1107.     add_record_to_database("USA Dollar", "DEU Mark", 1, 2.30, dmMaxRecordIndex);
  1108.     add_record_to_database("USA Dollar", "Euro", 1, 1.17, dmMaxRecordIndex);
  1109.     add_record_to_database("USA Dollar", "FRA Franc", 1, 7.70, dmMaxRecordIndex);
  1110.     add_record_to_database("USA Dollar", "GBR Pound", 1.41, 1, dmMaxRecordIndex);
  1111.     add_record_to_database("USA Dollar", "ITA Lira", 1, 2273.68, dmMaxRecordIndex);
  1112.     add_record_to_database("USA Dollar", "JPN Yen", 1, 107.27, dmMaxRecordIndex);
  1113.     add_record_to_database("USA Dollar", "KOR Won", 1, 1119, dmMaxRecordIndex);
  1114.     add_record_to_database("USA Dollar", "MEX Peso", 1, 9.36, dmMaxRecordIndex);
  1115.     add_record_to_database("USA Dollar", "NLD Guilder", 1, 1.74, dmMaxRecordIndex);
  1116.     add_record_to_database("USA Dollar", "TWN Dollar", 1, 31.19, dmMaxRecordIndex);
  1117. }
  1118.  
  1119. // Given a database index (zero-based), return in list_entry the "name" of that
  1120. // record, e.g. "USA Dollar/FRA Franc"
  1121. void            get_record_name(UInt item_num, CharPtr list_entry)
  1122. {
  1123.     if (list_entry == NULL)
  1124.         return;
  1125.     
  1126.     if (! gDB) {
  1127.         StrCopy(list_entry, "Error!");
  1128.         return;
  1129.     }
  1130.     
  1131.     VoidHand     record_handle = DmQueryRecord (gDB, item_num);
  1132.     if (! record_handle)
  1133.         return;
  1134.     VoidPtr        record_pointer = MemHandleLock( record_handle );    
  1135.     cRecord*    the_record = (cRecord*) record_pointer;
  1136.     StrCopy(list_entry, the_record->unit_1_name);
  1137.     StrCat(list_entry, "/");
  1138.     StrCat(list_entry, the_record->unit_2_name);
  1139.     MemHandleUnlock(record_handle);
  1140. }
  1141.  
  1142. // The draw function called by the OS to draw each entry in the list function.
  1143. // The best way I know of to implement a dynamic list.
  1144. void            currency_list_draw_function(UInt item_num, RectanglePtr bounds, CharPtr* pUnused)
  1145. {
  1146.     FormPtr        pForm = FrmGetActiveForm();
  1147.     CharPtr        list_entry = (CharPtr) MemPtrNew(256);
  1148.  
  1149.     get_record_name(item_num, list_entry);
  1150.  
  1151.     WinDrawChars(list_entry, StrLen(list_entry), bounds->topLeft.x, bounds->topLeft.y);
  1152.     MemPtrFree( (VoidPtr) list_entry);
  1153. }
  1154.  
  1155. // select_new_currency: called when the user picks a new currency, or when the
  1156. // app otherwise has to move to a new currency.  Updates the displays.
  1157. Boolean            select_new_currency(Int16 selection)
  1158. {
  1159.     if (! gDB)
  1160.         return false;
  1161.     
  1162.     VoidHand     record_handle = DmQueryRecord (gDB, selection);
  1163.     if (! record_handle)
  1164.         return true;
  1165.     VoidPtr        record_pointer = MemHandleLock( record_handle );    
  1166.     cRecord*    the_record = (cRecord*) record_pointer;
  1167.     
  1168.     CharPtr        buf = (CharPtr) MemPtrNew(128);
  1169.     
  1170.     set_field_text_from_string(MainUnit1NameField, the_record->unit_1_name);
  1171.     set_field_text_from_string(MainUnit2NameField, the_record->unit_2_name);
  1172.     double2ascii(the_record->unit_1_qty, buf);
  1173.     set_field_text_from_string(MainUnit1QtyField, buf);
  1174.     double2ascii(the_record->unit_2_qty, buf);
  1175.     set_field_text_from_string(MainUnit2QtyField, buf);
  1176.     StrCopy(buf, "0");
  1177.     set_field_text_from_string(MainUnit1PutField, buf);
  1178.     set_field_text_from_string(MainUnit2PutField, buf);
  1179.  
  1180.     MemHandleUnlock(record_handle);
  1181.     
  1182.     print_names();
  1183.     prefs.current_record_index = selection;
  1184.     get_record_name(selection, popup_label);
  1185.     CtlSetLabel (ControlPtr(GetObjectPtr(MainCurrenciesPopTrigger)), popup_label);
  1186.     
  1187.     MemPtrFree( (VoidPtr) buf);
  1188.     
  1189.     return true;
  1190. }
  1191.  
  1192. void            create_new_database()
  1193. {
  1194.     Err    err = DmCreateDatabase(0, currency_DB_name, appFileCreator, currency_DB_type, false);
  1195.     if (err) return;
  1196.     gDB = DmOpenDatabaseByTypeCreator(currency_DB_type, appFileCreator, dmModeReadWrite);
  1197.     if (! gDB) return;
  1198.     populate_new_database();
  1199. }
  1200.  
  1201. // Beam the current currency record to another Palm device.
  1202. void        beam_send()
  1203. {
  1204.     ExgSocketPtr        socket_pointer = (ExgSocketPtr) MemPtrNew(sizeof(ExgSocketType));
  1205.     Err                    error;
  1206.     
  1207.     MemSet(socket_pointer, sizeof(ExgSocketType), 0);
  1208.     
  1209.     // Fill in the fields to describe this entry
  1210.     socket_pointer->description = (CharPtr) MemPtrNew(120);
  1211.     get_record_name(prefs.current_record_index, socket_pointer->description);
  1212.     socket_pointer->name = "currency.cur";
  1213.     socket_pointer->target = appFileCreator;
  1214.     
  1215.     // Begin the beam
  1216.     error = ExgPut (socket_pointer);
  1217.     
  1218.     // Load the record, and call ExgSend repeatedly (if need be) to send it
  1219.     if (! error) {
  1220.         VoidHand    record_handle = DmQueryRecord( gDB, prefs.current_record_index );
  1221.         if (record_handle) {
  1222.             VoidPtr            record_pointer = MemHandleLock( record_handle );
  1223.             ULong            record_size = MemPtrSize(record_pointer);
  1224.             VoidPtr         local_copy_ptr = MemPtrNew( record_size );
  1225.             MemMove( local_copy_ptr, record_pointer, record_size );
  1226.             MemHandleUnlock( record_handle );
  1227.             cRecord*        buf = (cRecord*) local_copy_ptr;
  1228.             ULong            amount_sent;
  1229.             while (record_size) {
  1230.                 amount_sent = ExgSend(socket_pointer, local_copy_ptr, record_size, &error);
  1231.                 if (error)
  1232.                     break;
  1233.                 buf += amount_sent;
  1234.                 record_size -= amount_sent;
  1235.             }
  1236.             MemPtrFree(local_copy_ptr);
  1237.         }
  1238.     }
  1239.     
  1240.     // Disconnect to free the port and internal memory structures
  1241.     ExgDisconnect(socket_pointer, error);
  1242.     
  1243.     MemPtrFree( (VoidPtr) socket_pointer->description);
  1244.     MemPtrFree( (VoidPtr) socket_pointer);
  1245.     
  1246. }
  1247.  
  1248. // Receive a currency record from another Palm device.
  1249. void        beam_receive(ExgSocketPtr socket_pointer, Boolean app_active)
  1250. {
  1251.     UInt            record_index;
  1252.     DmOpenRef*        local_db;
  1253.     
  1254.     // Because this could be called when we are not the active application, do not
  1255.     // assume that globals like gDB are available.
  1256.     if (app_active) 
  1257.         local_db = &gDB;
  1258.     else {
  1259.         local_db = (DmOpenRef*) MemPtrNew(sizeof(DmOpenRef));
  1260.         *local_db = DmOpenDatabaseByTypeCreator(currency_DB_type, appFileCreator, dmModeReadWrite);
  1261.         if (! *local_db)
  1262.             return;
  1263.     }
  1264.     
  1265.     // Call ExgAccept, then call ExgReceive to get the new record.
  1266.     Err                error = 0;
  1267.     if ( ! ExgAccept (socket_pointer)) {
  1268.         VoidPtr            buf = MemPtrNew(sizeof(cRecord));
  1269.         ULong            record_size = ExgReceive(socket_pointer, buf, sizeof(cRecord), &error);
  1270.         if ((! error) && (buf != NULL)) {
  1271.             // Add the record to our database
  1272.             VoidHand    record_handle = DmNewHandle( *local_db, record_size );
  1273.             if (!record_handle)
  1274.                 return;
  1275.             VoidPtr        record_pointer = MemHandleLock( record_handle );
  1276.             DmWrite( record_pointer, 0, buf, record_size );
  1277.             MemHandleUnlock( record_handle );
  1278.             record_index = 0;
  1279.             DmAttachRecord( *local_db, &record_index, (char **) record_handle, NULL );
  1280.         }
  1281.         if (buf != NULL)
  1282.             MemPtrFree(buf);
  1283.     }
  1284.     error = ExgDisconnect(socket_pointer, error);
  1285.     
  1286.     // Set the "goto" params so that the OS will send us a "goto" event to take us to the
  1287.     // newly-received record.
  1288.     if (! error) {
  1289.         DmRecordInfo(*local_db, record_index, NULL, &socket_pointer->goToParams.uniqueID,
  1290.             NULL);
  1291.         DmOpenDatabaseInfo(*local_db, &socket_pointer->goToParams.dbID, NULL, NULL,
  1292.             &socket_pointer->goToParams.dbCardNo, NULL);
  1293.         socket_pointer->goToParams.recordNum = record_index;
  1294.         socket_pointer->goToCreator = appFileCreator;
  1295.     }
  1296.     
  1297.     if (! app_active) {
  1298.         DmCloseDatabase( *local_db );
  1299.         MemPtrFree( (VoidPtr) local_db);
  1300.     }
  1301.     
  1302. }
  1303.  
  1304.  
  1305. void            goto_item(GoToParamsPtr goToParams, Boolean launchingApp)
  1306. {
  1307.     EventType        event;
  1308.     UInt            recordNum = goToParams->recordNum;
  1309.     
  1310.     if (! launchingApp) {
  1311.         ULong        uniqueID;
  1312.         
  1313.         DmRecordInfo(gDB, recordNum, NULL, &uniqueID, NULL);
  1314.         DmFindRecordByID(gDB, uniqueID, &recordNum);
  1315.     }
  1316.     
  1317.     FrmGotoForm(MainForm);
  1318.     
  1319.     MemSet (&event, 0, sizeof(EventType));
  1320.     
  1321.     event.eType = frmGotoEvent;
  1322.     event.data.frmGoto.formID = MainForm;
  1323.     event.data.frmGoto.recordNum = recordNum;
  1324.     event.data.frmGoto.matchPos = goToParams->matchPos;
  1325.     event.data.frmGoto.matchLen = goToParams->searchStrLen;
  1326.     event.data.frmGoto.matchFieldNum = goToParams->matchFieldNum;
  1327.     event.data.frmGoto.matchCustom = goToParams->matchCustom;
  1328.  
  1329.     EvtAddEventToQueue(&event);
  1330. }
  1331.  
  1332.  
  1333. // search_find: Handles the "Find" open application message.
  1334. // searches the database for the find string, and sends self
  1335. // a goto event if it finds it.
  1336. void             search_find(FindParamsPtr find_params)
  1337. {
  1338.     Boolean             done, truncate;
  1339.     CharPtr                unit1_qty_str, unit2_qty_str, scratch_string;
  1340.     DmOpenRef             local_db;
  1341.     DmSearchStateType    search_state;
  1342.     Err                 error;
  1343.     LocalID             database_id;
  1344.     RectangleType         r;
  1345.     SWord                 string_length, pixel_width;
  1346.     UInt                 card_number, field_number, record_number;
  1347.     VoidHand             record_handle;
  1348.     VoidPtr                record;
  1349.     Word                 pos;
  1350.  
  1351.     
  1352.     find_params->more = false;    
  1353.     card_number = 0;
  1354.     error = DmGetNextDatabaseByTypeCreator(true, &search_state, currency_DB_type, appFileCreator, true,
  1355.          &card_number, &database_id);
  1356.     if (error) 
  1357.         return;
  1358.     local_db = DmOpenDatabase(card_number, database_id, find_params->dbAccesMode);
  1359.     if (! local_db)
  1360.         return;
  1361.  
  1362.     // Display the heading line.
  1363.     scratch_string = (CharPtr) MemPtrNew(128);
  1364.     unit1_qty_str = (CharPtr) MemPtrNew(128);
  1365.     unit2_qty_str = (CharPtr) MemPtrNew(128);
  1366.     StrCopy(scratch_string, "Currency");
  1367.     done = FindDrawHeader(find_params, scratch_string);
  1368.     
  1369.     if (done)
  1370.         find_params->more = true;
  1371.     else {
  1372.         record_number = find_params->recordNum;    
  1373.         cRecord*        record_ptr;
  1374.         
  1375.         while (true) {
  1376.             Boolean match = false;
  1377.             
  1378.             if ( (record_number % 10 == 0) && EvtSysEventAvail(true)) {
  1379.                 find_params->more = true;
  1380.                 break;
  1381.             } 
  1382.  
  1383.             record_handle = DmQueryNextInCategory(local_db, &record_number, dmAllCategories);
  1384.             if (! record_handle)
  1385.                 break;
  1386.             
  1387.             record = MemHandleLock(record_handle);
  1388.             record_ptr = (cRecord*) record;
  1389.             double2ascii(record_ptr->unit_1_qty, unit1_qty_str);
  1390.             double2ascii(record_ptr->unit_2_qty, unit2_qty_str);
  1391.             
  1392.             if ((match = FindStrInStr((CharPtr) record_ptr->unit_1_name, find_params->strToFind, &pos)) != false)
  1393.                 field_number = MainUnit1NameField;
  1394.             else if ((match = FindStrInStr((CharPtr) record_ptr->unit_2_name, find_params->strToFind, &pos)) != false)
  1395.                 field_number = MainUnit2NameField; 
  1396.             else if ((match = FindStrInStr(unit1_qty_str, find_params->strToFind, &pos)) != false)
  1397.                 field_number = MainUnit1QtyField;
  1398.             else if ((match = FindStrInStr( unit2_qty_str, find_params->strToFind, &pos)) != false)
  1399.                 field_number = MainUnit2QtyField; 
  1400.                 
  1401.             if (match) {
  1402.                 done = FindSaveMatch(find_params, record_number, pos, field_number, 0, 
  1403.                     card_number, database_id);
  1404.                 if (done) 
  1405.                     break;
  1406.                 
  1407.                 StrCopy(scratch_string, record_ptr->unit_1_name);
  1408.                 StrCat(scratch_string, "/");
  1409.                 StrCat(scratch_string, record_ptr->unit_2_name);
  1410.                 FindGetLineBounds(find_params, &r);
  1411.                 string_length = StrLen(scratch_string);
  1412.                 pixel_width = r.extent.x;
  1413.                 FntCharsInWidth(scratch_string, &pixel_width, &string_length, &truncate);
  1414.                 WinDrawChars(scratch_string, string_length, r.topLeft.x, r.topLeft.y);
  1415.     
  1416.                 find_params->lineNumber++;
  1417.             }
  1418.             MemHandleUnlock(record_handle);
  1419.             if (done)
  1420.                 break;
  1421.             record_number++;
  1422.         }
  1423.     }    
  1424.     DmCloseDatabase(local_db);    
  1425.     MemPtrFree(scratch_string);
  1426.     MemPtrFree( (VoidPtr) unit1_qty_str);
  1427.     MemPtrFree( (VoidPtr) unit2_qty_str);
  1428.  
  1429. }
  1430.