/******************************************************************************
 *
 * File: Starter.c
 *
 * Project : ExampleG
 *
 *****************************************************************************/

#include <PalmOS.h>
#include "StarterRsc.h"

#include "Silk.h"  

/***********************************************************************
 *
 *   Entry Points
 *
 ***********************************************************************/


/***********************************************************************
 *
 *   Internal Structures
 *
 ***********************************************************************/
typedef struct 
{
    UInt8 replaceme;
} StarterPreferenceType;

typedef struct 
{
    UInt8 replaceme;
} StarterAppInfoType;

typedef StarterAppInfoType* StarterAppInfoPtr;

Boolean silkPresent;

/***********************************************************************
 *
 *   Global variables
 *
 ***********************************************************************/


/***********************************************************************
 *
 *   Internal Constants
 *
 ***********************************************************************/
#define appFileCreator            'Ex_G'
#define appVersionNum              0x01
#define appPrefID                  0x00
#define appPrefVersionNum          0x01

#define NUM_NEW_BUTTONS            11

#define KEYPAD_TOPX    141
#define KEYPAD_TOPY     21
#define BUTTON_WIDTH    14
#define BUTTON_HEIGHT   14

// Define the minimum OS version we support (2.0 for now).
#define ourMinVersion   sysMakeROMVersion(2,0,0,sysROMStageRelease,0)

/***********************************************************************
 *
 *   Internal Functions
 *
 ***********************************************************************/


/***********************************************************************
 *
 * FUNCTION:    RomVersionCompatible
 *
 * DESCRIPTION: This routine checks that a ROM version is meet your
 *              minimum requirement.
 *
 * PARAMETERS:  requiredVersion - minimum rom version required
 *                                (see sysFtrNumROMVersion in SystemMgr.h 
 *                                for format)
 *              launchFlags     - flags that indicate if the application 
 *                                UI is initialized.
 *
 * RETURNED:    error code or zero if rom is compatible
 *
 * REVISION HISTORY:
 *
 *
 ***********************************************************************/
static Err RomVersionCompatible(UInt32 requiredVersion, UInt16 launchFlags)
{
    UInt32 romVersion;
 
/*------------------------------------------------------------------------
 * See if we're on in minimum required version of the ROM or later.
 *----------------------------------------------------------------------*/
    FtrGet(sysFtrCreator, sysFtrNumROMVersion, &romVersion);
    if (romVersion < requiredVersion)
    {
        if ((launchFlags & (sysAppLaunchFlagNewGlobals | sysAppLaunchFlagUIApp)) ==
           (sysAppLaunchFlagNewGlobals | sysAppLaunchFlagUIApp))
        {
            FrmAlert (RomIncompatibleAlert);
        
/*------------------------------------------------------------------------
 * Palm OS 1.0 will continuously relaunch this app unless we switch to 
 * another safe one.
 *----------------------------------------------------------------------*/
            if (romVersion < ourMinVersion)
            {
                  AppLaunchWithCommand(sysFileCDefaultApp, sysAppLaunchCmdNormalLaunch, NULL);
            }
        }
        return sysErrRomIncompatible;
   }
 
   return errNone;
}


/***********************************************************************
 *
 * FUNCTION:    GetObjectPtr
 *
 * DESCRIPTION: This routine returns a pointer to an object in the current
 *              form.
 *
 * PARAMETERS:  formId - id of the form to display
 *
 * RETURNED:    void *
 *
 * REVISION HISTORY:
 *
 *
 ***********************************************************************/
static void * GetObjectPtr(UInt16 objectID)
{
    FormPtr frmP;
 
    frmP = FrmGetActiveForm();
    return(FrmGetObjectPtr(frmP, FrmGetObjectIndex(frmP, objectID)));
}

/***********************************************************************
 *
 * FUNCTION:    SetNewButtons
 *
 * DESCRIPTION: Sets up the numeric keypad structure.
 *
 * PARAMETERS:  
 *
 * RETURNED:    void *
 *
 * REVISION HISTORY:
 *
 *
 ***********************************************************************/
static void SetNewButtons(PenBtnListType *buttonList)
{
    UInt16 index, i;

    index = buttonList->numButtons;

/*------------------------------------------------------------------------
 * Look for the numeric keyboard button.  Simply set the width to 0 for this
 * example.  A complete app would competely remove it.
 *----------------------------------------------------------------------*/
    for (i = 0;i < index; i++)
    {
        if (buttonList->buttons[i].asciiCode == vchrKeyboardNumeric)
        {
            buttonList->buttons[i].boundsR.extent.x = 1;
            buttonList->buttons[i].boundsR.extent.y = 1;
        }   
    }   

/*------------------------------------------------------------------------
 * '1'
 *----------------------------------------------------------------------*/
    buttonList->buttons[index+0].boundsR.topLeft.x = KEYPAD_TOPX ;
    buttonList->buttons[index+0].boundsR.topLeft.y = KEYPAD_TOPY;
    buttonList->buttons[index+0].boundsR.extent.x  = BUTTON_WIDTH;
    buttonList->buttons[index+0].boundsR.extent.y  = BUTTON_HEIGHT;
    buttonList->buttons[index+0].asciiCode         = '1';
    buttonList->buttons[index+0].keyCode           = 0;
    buttonList->buttons[index+0].modifiers         = 0;

/*------------------------------------------------------------------------
 * '2'
 *----------------------------------------------------------------------*/
    buttonList->buttons[index+1].boundsR.topLeft.x = KEYPAD_TOPX + BUTTON_WIDTH;
    buttonList->buttons[index+1].boundsR.topLeft.y = KEYPAD_TOPY;
    buttonList->buttons[index+1].boundsR.extent.x  = BUTTON_WIDTH;
    buttonList->buttons[index+1].boundsR.extent.y  = BUTTON_HEIGHT;
    buttonList->buttons[index+1].asciiCode         = '2';
    buttonList->buttons[index+1].keyCode           = 0;
    buttonList->buttons[index+1].modifiers         = 0;

/*------------------------------------------------------------------------
 * '3'
 *----------------------------------------------------------------------*/
    buttonList->buttons[index+2].boundsR.topLeft.x = KEYPAD_TOPX + (BUTTON_WIDTH*2);
    buttonList->buttons[index+2].boundsR.topLeft.y = KEYPAD_TOPY;
    buttonList->buttons[index+2].boundsR.extent.x  = BUTTON_WIDTH;
    buttonList->buttons[index+2].boundsR.extent.y  = BUTTON_HEIGHT;
    buttonList->buttons[index+2].asciiCode         = '3';
    buttonList->buttons[index+2].keyCode           = 0;
    buttonList->buttons[index+2].modifiers         = 0;

/*------------------------------------------------------------------------
 * '4'
 *----------------------------------------------------------------------*/
    buttonList->buttons[index+3].boundsR.topLeft.x = KEYPAD_TOPX;
    buttonList->buttons[index+3].boundsR.topLeft.y = KEYPAD_TOPY + BUTTON_HEIGHT;
    buttonList->buttons[index+3].boundsR.extent.x  = BUTTON_WIDTH;
    buttonList->buttons[index+3].boundsR.extent.y  = BUTTON_HEIGHT;
    buttonList->buttons[index+3].asciiCode         = '4';
    buttonList->buttons[index+3].keyCode           = 0;
    buttonList->buttons[index+3].modifiers         = 0;

/*------------------------------------------------------------------------
 * '5'
 *----------------------------------------------------------------------*/
    buttonList->buttons[index+4].boundsR.topLeft.x = KEYPAD_TOPX + BUTTON_WIDTH;
    buttonList->buttons[index+4].boundsR.topLeft.y = KEYPAD_TOPY + BUTTON_HEIGHT;
    buttonList->buttons[index+4].boundsR.extent.x  = BUTTON_WIDTH;
    buttonList->buttons[index+4].boundsR.extent.y  = BUTTON_HEIGHT;
    buttonList->buttons[index+4].asciiCode         = '5';
    buttonList->buttons[index+4].keyCode           = 0;
    buttonList->buttons[index+4].modifiers         = 0;

/*------------------------------------------------------------------------
 * '6'
 *----------------------------------------------------------------------*/
    buttonList->buttons[index+5].boundsR.topLeft.x = KEYPAD_TOPX + (BUTTON_WIDTH*2);
    buttonList->buttons[index+5].boundsR.topLeft.y = KEYPAD_TOPY + BUTTON_HEIGHT;
    buttonList->buttons[index+5].boundsR.extent.x  = BUTTON_WIDTH;
    buttonList->buttons[index+5].boundsR.extent.y  = BUTTON_HEIGHT;
    buttonList->buttons[index+5].asciiCode         = '6';
    buttonList->buttons[index+5].keyCode           = 0;
    buttonList->buttons[index+5].modifiers         = 0;

/*------------------------------------------------------------------------
 * '7'
 *----------------------------------------------------------------------*/
    buttonList->buttons[index+6].boundsR.topLeft.x = KEYPAD_TOPX;
    buttonList->buttons[index+6].boundsR.topLeft.y = KEYPAD_TOPY + (BUTTON_HEIGHT*2);
    buttonList->buttons[index+6].boundsR.extent.x  = BUTTON_WIDTH;
    buttonList->buttons[index+6].boundsR.extent.y  = BUTTON_HEIGHT;
    buttonList->buttons[index+6].asciiCode         = '7';
    buttonList->buttons[index+6].keyCode           = 0;
    buttonList->buttons[index+6].modifiers         = 0;

/*------------------------------------------------------------------------
 * '8'
 *----------------------------------------------------------------------*/
    buttonList->buttons[index+7].boundsR.topLeft.x = KEYPAD_TOPX + BUTTON_WIDTH;
    buttonList->buttons[index+7].boundsR.topLeft.y = KEYPAD_TOPY + (BUTTON_HEIGHT*2);
    buttonList->buttons[index+7].boundsR.extent.x  = BUTTON_WIDTH;
    buttonList->buttons[index+7].boundsR.extent.y  = BUTTON_HEIGHT;
    buttonList->buttons[index+7].asciiCode         = '8';
    buttonList->buttons[index+7].keyCode           = 0;
    buttonList->buttons[index+7].modifiers         = 0;

/*------------------------------------------------------------------------
 * '9'
 *----------------------------------------------------------------------*/
    buttonList->buttons[index+8].boundsR.topLeft.x = KEYPAD_TOPX + (BUTTON_WIDTH*2);
    buttonList->buttons[index+8].boundsR.topLeft.y = KEYPAD_TOPY + (BUTTON_HEIGHT*2);
    buttonList->buttons[index+8].boundsR.extent.x  = BUTTON_WIDTH;
    buttonList->buttons[index+8].boundsR.extent.y  = BUTTON_HEIGHT;
    buttonList->buttons[index+8].asciiCode         = '9';
    buttonList->buttons[index+8].keyCode           = 0;
    buttonList->buttons[index+8].modifiers         = 0;

/*------------------------------------------------------------------------
 * '0'
 *----------------------------------------------------------------------*/
    buttonList->buttons[index+9].boundsR.topLeft.x = KEYPAD_TOPX;
    buttonList->buttons[index+9].boundsR.topLeft.y = KEYPAD_TOPY + (BUTTON_HEIGHT*3);
    buttonList->buttons[index+9].boundsR.extent.x  = BUTTON_WIDTH * 2;
    buttonList->buttons[index+9].boundsR.extent.y  = BUTTON_HEIGHT;
    buttonList->buttons[index+9].asciiCode         = '0';
    buttonList->buttons[index+9].keyCode           = 0;
    buttonList->buttons[index+9].modifiers         = 0;

/*------------------------------------------------------------------------
 * '.'
 *----------------------------------------------------------------------*/
    buttonList->buttons[index+10].boundsR.topLeft.x = KEYPAD_TOPX + (BUTTON_WIDTH*2);
    buttonList->buttons[index+10].boundsR.topLeft.y = KEYPAD_TOPY + (BUTTON_HEIGHT*3);
    buttonList->buttons[index+10].boundsR.extent.x  = BUTTON_WIDTH;
    buttonList->buttons[index+10].boundsR.extent.y  = BUTTON_HEIGHT;
    buttonList->buttons[index+10].asciiCode         = '.';
    buttonList->buttons[index+10].keyCode           = 0;
    buttonList->buttons[index+10].modifiers         = 0;
 
    buttonList->numButtons += NUM_NEW_BUTTONS;

}

/***********************************************************************
 *
 * FUNCTION:    SetTemplate
 *
 * DESCRIPTION: This routine changes the template to our template
 *
 * PARAMETERS:  
 *
 * RETURNED:    void *
 *
 * REVISION HISTORY:
 *
 *
 ***********************************************************************/
static void SetTemplate(void)
{
    MemHandle       normalH, invertedH;
    BitmapPtr       normal, inverted;
    RectangleType   alpha, numeric;
    UInt16          numNewButtons;
    PenBtnListType *currentPtr, *newPtr;
    UInt16          currentSize, newSize;

/*------------------------------------------------------------------------
 * First draw the new silscreen template
 *----------------------------------------------------------------------*/
    normalH = DmGetResource(bitmapRsc, NormalSilkBitmapFamily);
    ErrFatalDisplayIf(!normalH, "Missing normal bitmap");
    normal = MemHandleLock(normalH);

    invertedH = DmGetResource(bitmapRsc, InvertedSilkBitmapFamily);
    ErrFatalDisplayIf(!invertedH, "Missing inverted bitmap");
    inverted = MemHandleLock(invertedH);

    SilkSetTemplateBitmaps(normal, inverted, NULL, NULL);
    SilkMaximizeWindow();

    MemPtrUnlock(normal);
    DmReleaseResource(normalH);
    MemPtrUnlock(inverted);
    DmReleaseResource(invertedH);

/*------------------------------------------------------------------------
 * The numeric graffiti area can not be removed, so make its width 1, which 
 * will effectively remove it.
 *----------------------------------------------------------------------*/
    SilkGetAreas(&alpha, &numeric);
    numeric.extent.x = 1;
    SilkSetAreas(&alpha, &numeric);

/*------------------------------------------------------------------------
 * First get the number of silk buttons so we know how much to allocate,
 * with our additional buttons.
 *----------------------------------------------------------------------*/
    currentSize = SilkGetButtonListSize(true);

    if ((currentPtr = MemPtrNew(currentSize)) == NULL)
    {
        ErrFatalDisplay("Allcoate Errror");
        return;
    }    

    SilkGetButtonList(currentPtr, true);

/*------------------------------------------------------------------------
 * Allocate space for new button list.  
 *----------------------------------------------------------------------*/
    numNewButtons = currentPtr->numButtons + NUM_NEW_BUTTONS;
    newSize = sizeof(PenBtnListType) + (sizeof(PenBtnInfoType) * (numNewButtons - 1));

    if ((newPtr = MemPtrNew(newSize)) == NULL)
    {
        MemPtrFree(currentPtr);
        ErrFatalDisplay("Allcoate Errror");
        return;
    }    

    MemMove(newPtr, currentPtr, currentSize);

    SetNewButtons(newPtr);
    SilkSetButtonList(newPtr, true);

/*------------------------------------------------------------------------
 * Since the button list is copied by the system, we can free our copy.
 *----------------------------------------------------------------------*/
    MemPtrFree(currentPtr);
    MemPtrFree(newPtr);

}

/***********************************************************************
 *
 * FUNCTION:    ResetTemplate
 *
 * DESCRIPTION: This routine restores the defaul template, areas and 
 *              silk buttons.
 *
 * PARAMETERS:  
 *
 * RETURNED:    void *
 *
 * REVISION HISTORY:
 *
 *
 ***********************************************************************/
static void ResetTemplate(void)
{
    SilkRestoreDefaultTemplates();
    SilkMaximizeWindow();
}

/***********************************************************************
 *
 * FUNCTION:    MainFormInit
 *
 * DESCRIPTION: This routine initializes the MainForm form.
 *
 * PARAMETERS:  frm - pointer to the MainForm form.
 *
 * RETURNED:    nothing
 *
 * REVISION HISTORY:
 *
 *
 ***********************************************************************/
static void MainFormInit(FormPtr /*frmP*/)
{
}


/***********************************************************************
 *
 * FUNCTION:    MainFormDoCommand
 *
 * DESCRIPTION: This routine performs the menu command specified.
 *
 * PARAMETERS:  command  - menu item id
 *
 * RETURNED:    nothing
 *
 * REVISION HISTORY:
 *
 *
 ***********************************************************************/
static Boolean MainFormDoCommand(UInt16 command)
{
    Boolean handled = false;
    FormPtr frmP;
 
    switch (command)
    {
        case MainOptionsAboutExampleG :
            MenuEraseStatus(0);              // Clear the menu status from the display.
            frmP = FrmInitForm (AboutForm);
            FrmDoDialog (frmP);              // Display the About Box.
            FrmDeleteForm (frmP);
            handled = true;
            break;
           
    }
    return(handled);
}


/***********************************************************************
 *
 * FUNCTION:    MainFormHandleEvent
 *
 * DESCRIPTION: This routine is the event handler for the 
 *              "MainForm" of this application.
 *
 * PARAMETERS:  eventP  - a pointer to an EventType structure
 *
 * RETURNED:    true if the event has handle and should not be passed
 *              to a higher level handler.
 *
 * REVISION HISTORY:
 *
 *
 ***********************************************************************/
static Boolean MainFormHandleEvent(EventPtr eventP)
{
    Boolean handled = false;
    FormPtr frmP;
 
    switch (eventP->eType) 
    {
        case menuEvent:
            return MainFormDoCommand(eventP->data.menu.itemID);
 
        case ctlSelectEvent :
            switch(eventP->data.ctlSelect.controlID)
            {
                case MainSetTemplateButton :
                    if (!silkPresent)
                        FrmAlert(SilkExtNotFoundAlert);
                    else    
                        SetTemplate();
                    break;

                case MainResetTemplateButton :
                    if (!silkPresent)
                        FrmAlert(SilkExtNotFoundAlert);
                    else    
                        ResetTemplate();
                    break;
            }
            break;    

        case frmOpenEvent:
            frmP = FrmGetActiveForm();
            MainFormInit( frmP);
            FrmDrawForm ( frmP);
            handled = true;
            break;

        default:
            break;
    }
    return(handled);
}


/***********************************************************************
 *
 * FUNCTION:    AppHandleEvent
 *
 * DESCRIPTION: This routine loads form resources and set the event
 *              handler for the form loaded.
 *
 * PARAMETERS:  event  - a pointer to an EventType structure
 *
 * RETURNED:    true if the event has handle and should not be passed
 *              to a higher level handler.
 *
 * REVISION HISTORY:
 *
 *
 ***********************************************************************/
static Boolean AppHandleEvent(EventPtr eventP)
{
    UInt16  formId;
    FormPtr frmP;

    if (eventP->eType == frmLoadEvent)
    {
/*------------------------------------------------------------------------
 * Load the form resource.
  *----------------------------------------------------------------------*/
        formId = eventP->data.frmLoad.formID;
        frmP   = FrmInitForm(formId);
        FrmSetActiveForm(frmP);
/*------------------------------------------------------------------------
 * Set the event handler for the form.  The handler of the currently
 * active form is called by FrmHandleEvent each time is receives an event.
 *----------------------------------------------------------------------*/
        switch (formId)
        {
            case MainForm:
                FrmSetEventHandler(frmP, MainFormHandleEvent);
                break;
            default:
                ErrFatalDisplay("Invalid Form Load Event");
                break;
 
        }
        return(true);
    }
    
    return(false);
}


/***********************************************************************
 *
 * FUNCTION:    AppEventLoop
 *
 * DESCRIPTION: This routine is the event loop for the application.  
 *
 * PARAMETERS:  nothing
 *
 * RETURNED:    nothing
 *
 * REVISION HISTORY:
 *
 *
 ***********************************************************************/
static void AppEventLoop(void)
{
    UInt16 error;
    EventType event;

    do 
    {
        EvtGetEvent(&event, evtWaitForever);
 
        if (! SysHandleEvent(&event))
            if (! MenuHandleEvent(0, &event, &error))
                if (! AppHandleEvent(&event))
                    FrmDispatchEvent(&event);
                    
    } while (event.eType != appStopEvent);
}


/***********************************************************************
 *
 * FUNCTION:     AppStart
 *
 * DESCRIPTION:  Get the current application's preferences.
 *
 * PARAMETERS:   nothing
 *
 * RETURNED:     Err value 0 if nothing went wrong
 *
 * REVISION HISTORY:
 *
 *
 ***********************************************************************/
static Err AppStart(void)
{
    StarterPreferenceType prefs;
    UInt16                prefsSize;
    UInt32                version;

/*------------------------------------------------------------------------
 * Read the saved preferences / saved-state information.
 *----------------------------------------------------------------------*/
    prefsSize = sizeof(StarterPreferenceType);
    if (PrefGetAppPreferences(appFileCreator, appPrefID, &prefs, &prefsSize, true) != 
            noPreferenceFound)
    {
    }

    if (_TRGSilkFeaturePresent(&version))          
        silkPresent = true;
    else
        silkPresent = false;

   
    return errNone;
}


/***********************************************************************
 *
 * FUNCTION:    AppStop
 *
 * DESCRIPTION: Save the current state of the application.
 *
 * PARAMETERS:  nothing
 *
 * RETURNED:    nothing
 *
 * REVISION HISTORY:
 *
 *
 ***********************************************************************/
static void AppStop(void)
{
    StarterPreferenceType prefs;

/*------------------------------------------------------------------------
 * Write the saved preferences / saved-state information.  This data 
 * will be backed up during a HotSync.
 *----------------------------------------------------------------------*/
    PrefSetAppPreferences (appFileCreator, appPrefID, appPrefVersionNum, 
                           &prefs, sizeof (prefs), true);
      
/*------------------------------------------------------------------------
 * Close all the open forms.
 *----------------------------------------------------------------------*/
    FrmCloseAllForms ();
}


/***********************************************************************
 *
 * FUNCTION:    StarterPalmMain
 *
 * DESCRIPTION: This is the main entry point for the application.
 *
 * PARAMETERS:  cmd - word value specifying the launch code. 
 *              cmdPB - pointer to a structure that is associated with the launch code. 
 *              launchFlags -  word value providing extra information about the launch.
 *
 * RETURNED:    Result of launch
 *
 * REVISION HISTORY:
 *
 *
 ***********************************************************************/
static UInt32 StarterPalmMain(UInt16 cmd, MemPtr /*cmdPBP*/, UInt16 launchFlags)
{
    Err error;
 
    if ((error = RomVersionCompatible (ourMinVersion, launchFlags)) != 0)
        return (error);
 
    switch (cmd)
    {
        case sysAppLaunchCmdNormalLaunch:
            error = AppStart();
            if (error) 
                return error;
              
            FrmGotoForm(MainForm);
            AppEventLoop();
            AppStop();
            break;
  
        default:
            break;
  
    }
    return(errNone);
}


/***********************************************************************
 *
 * FUNCTION:    PilotMain
 *
 * DESCRIPTION: This is the main entry point for the application.
 *
 * PARAMETERS:  cmd - word value specifying the launch code. 
 *              cmdPB - pointer to a structure that is associated with the launch code. 
 *              launchFlags -  word value providing extra information about the launch.
 * RETURNED:    Result of launch
 *
 * REVISION HISTORY:
 *
 *
 ***********************************************************************/
UInt32 PilotMain( UInt16 cmd, MemPtr cmdPBP, UInt16 launchFlags)
{
    return StarterPalmMain(cmd, cmdPBP, launchFlags);
}

