/**************************************************************
 PROJECT:  EC Version 1.1
 FILE:     ec.c
 AUTHOR:   Michael Yam, Y Technology Inc.
 		   myam@ytechnology.com
 		   July 1, 1999

 DECLARER: Ec

 DESCRIPTION: 
 A EuroCalculator for the Palm Computing(R) Platform.
 EC is freeware.  Version 1.1 source code is public domain.	  
**************************************************************/

#include <Pilot.h>
#include <SysEvtMgr.h>
#include <NewFloatMgr.h>
#include <WindowNew.h>
#include "ec.h"
#include "ec_res.h"


/**************************************************************
	Internal Structures
**************************************************************/
typedef struct 
{
	Byte round;					// for future
	short rateSelection;
	Word activeField;
	FlpDouble operand1;
	FlpDouble operand2;
	int calcOp;
	Boolean currencyHasDecimal;
	Boolean euroHasDecimal;
	char currencyBuffer [SIZE_DISPLAY+1];	// future: save in
	char euroBuffer [SIZE_DISPLAY+1];		// binary to
} EcPreferenceType;							// conserve space?

typedef struct 
	{
	Byte replaceme;
	} EcAppInfoType;

typedef EcAppInfoType* EcAppInfoPtr;


/**************************************************************
	Global variables
**************************************************************/
//static Boolean HideSecretRecords;

static VoidHand gCurrencyH;
static VoidHand gEuroH;

static char gCurrencyBuffer [SIZE_DISPLAY+1];
static char gEuroBuffer [SIZE_DISPLAY+1];

static FlpDouble gOperand1;
static FlpDouble gOperand2;
static int gCalcOp;
static short gRateSelection;
static Word gActiveField;
static Boolean gCurrencyHasDecimal = false;
static Boolean gEuroHasDecimal = false;

static char gThousandSeparator, gDecimalSeparator;

// 1 Euro equals...
static char *gConversionRates[numCurrencies] = 
{
	"13.760300",		// ATS
	"40.339900",		// BEF
	"1.955830",		// DEM
	"166.386000",		// ESP
	"5.945370",		// FIM
	"6.559570",		// FRF
	"0.787564",		// IEP
	"1936.270000",	// ITL
	"40.339900",		// LUF
	"2.203710",		// NLG
	"200.482000"		// PTE
};

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


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

// Palm OS 3.0 Abs in sysutils.h not sufficiently protected
// with parens.
#ifdef Abs
#undef Abs
#endif

#define Abs(a) (((a) >= 0) ? (a) : -(a))

/**************************************************************
   Internal Functions
**************************************************************/
 
// pragma mark, when used with CodeWarrior IDE , lets you find
// sections of code quicky.
#pragma mark ---------------- 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(DWord requiredVersion, Word launchFlags)
{
	DWord 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);
		
			// Pilot 1.0 will continuously relaunch this app unless we switch to 
			// another safe one.
			if (romVersion < sysMakeROMVersion(2,0,0,sysROMStageRelease,0))
				AppLaunchWithCommand(sysFileCDefaultApp, sysAppLaunchCmdNormalLaunch, NULL);
			}
		
		return (sysErrRomIncompatible);
		}

	return (0);
}


/**************************************************************
 FUNCTION:    GetObjectPtr

 DESCRIPTION: This routine returns a pointer to an object in
              the current form.

 PARAMETERS:  formId - id of the form to display

 RETURNED:    VoidPtr

 REVISION HISTORY:
**************************************************************/
static VoidPtr GetObjectPtr(Word objectID)
{
	FormPtr frmP;

	frmP = FrmGetActiveForm();
	return (FrmGetObjectPtr(frmP, FrmGetObjectIndex(frmP, objectID)));
}

/**************************************************************
FUNCTION: YSetField

DESCRIPTION:
Set a field with given text
**************************************************************/
void YSetField(FieldPtr fldP, char *text)
{
	Word textLen;

	textLen = FldGetTextLength (fldP);
	
	// If there's text in the field, delete it
	if (textLen > 0)
		FldDelete (fldP, 0, textLen);
		
	if (*text == NULL)
	{
		FldEraseField (fldP);
	}
	else
		FldInsert (fldP, text, StrLen (text));
		
	FldDrawField (fldP);
	
	return;
}
/**************************************************************
FUNCTION: YAppendField

DESCRIPTION: Append a field with given text
**************************************************************/
Boolean YAppendField(FieldPtr fldP, char *text)
{
	Word textLen;
	Word allocatedSize;
	
	textLen = FldGetTextLength (fldP);
	allocatedSize = FldGetTextAllocatedSize (fldP);
	
	if (textLen+StrLen(text) >= allocatedSize)
		return false;
		
	FldSetInsPtPosition (fldP, textLen);
	FldInsert (fldP, text, StrLen(text));
	FldDrawField (fldP);
	
	return true;
}

#pragma mark ---------------- ECALC FORM
/**************************************************************
FUNCTION: EcOperate

DESCRIPTION: 
Perform operation on parameters based on global gCalcOp.			

**************************************************************/
static FlpDouble EcOperate (FlpDouble a, FlpDouble b)
{
	switch (gCalcOp)
	{
	case add:
		return _d_add (a, b);
		
	case subtract:
		return _d_sub (a, b);
		
	case multiply:
		return _d_mul (a, b);
		
	case divide:
		return _d_div (a, b);
		
	default:
		break;
	}
	return FlpAToF ("0");
	
}
/**************************************************************
FUNCTION: EcCalculate

DESCRIPTION:
**************************************************************/
static void EcCalculate (FieldPtr currencyFldP, FieldPtr euroFldP)
{
	CharPtr activeFieldP;
	
	if (gActiveField == EcCurrencyField)
		activeFieldP =  FldGetTextPtr (currencyFldP);
	else
		activeFieldP = FldGetTextPtr (euroFldP);
	
	if (gCalcOp == nop || gCalcOp == equals)
	{
		gOperand1 = FlpAToF (activeFieldP);
	}
	else if (gCalcOp == add || gCalcOp == subtract ||
			 gCalcOp == multiply || gCalcOp == divide)
	{
		gOperand2 = FlpAToF (activeFieldP);
		gOperand1 = EcOperate (gOperand1, gOperand2);
	}
}

/**************************************************************
FUNCTION: EcAppendDigit

DESCRIPTION: Append digit to display, checking for length
**************************************************************/
static Boolean EcAppendDigit (FieldPtr fldP, char digit)
{
	Word textLen;
	CharPtr textP;
	char text[2];

	// YAppendField needs null terminated string	
	text[0] = digit;
	text[1] = NULL;
	
	if (gCalcOp == equals)
	{
		// Last operation was equals.  Clear the display for
		// a new number.
		YSetField (fldP, "");
		gCalcOp = nop;
	}			
				
	textP = FldGetTextPtr (fldP);
	textLen = FldGetTextLength (fldP);
	if (textLen >= SIZE_MANTISSA)
	{
		// see if there is a sign or a decimal
		if (StrChr (textP, '-'))
			--textLen;
			
		if (StrChr (textP, gDecimalSeparator))
			--textLen;
	}
	
	if (textLen >= SIZE_MANTISSA)
	{
		SndPlaySystemSound (sndError);
		return false;		// still too big
	}
	else
		return YAppendField (fldP, text);
}		
/**************************************************************
FUNCTION: EcAppendDecimal

DESCRIPTION: Insert decimal point in display
**************************************************************/
static void EcAppendDecimal ()
{
	char decimalSeparator[2];
	
	decimalSeparator[0] = gDecimalSeparator;
	decimalSeparator[1] = NULL;
	
	if (gActiveField == EcCurrencyField)
	{
		if (!gCurrencyHasDecimal)
    	{
    		if (gCalcOp == equals)
    		{
    			// clear display, then add decimal
    			YSetField (GetObjectPtr(gActiveField),decimalSeparator);
    			gCalcOp = nop;
    		}
    		else
    			YAppendField (GetObjectPtr(gActiveField), decimalSeparator);
    		gCurrencyHasDecimal = true;
    	}
    }
    else if (gActiveField == EcEuroField)
    {
    	if (!gEuroHasDecimal)
    	{
    		if (gCalcOp == equals)
    		{
    			// clear display, then add decimal
    			YSetField (GetObjectPtr(gActiveField), decimalSeparator);
    			gCalcOp = nop;
    		}
    		else
    			YAppendField (GetObjectPtr(gActiveField), decimalSeparator);
    		gEuroHasDecimal = true;
    	}
    }
}
/**************************************************************
FUNCTION: EcFormatDisplay

DESCRIPTION: Format the display for the calculator
**************************************************************/
static Err EcFormatResult (FlpDouble valFlp, CharPtr targetP)
{
	ULong mantissa;
	Int exponent;
	Int sign, i;
	Int decimalPos;
	CharPtr ptr, decimalPtr;
	
	Char mantissaBuffer [SIZE_MANTISSA+1];
	UInt mantissaLen;
	
	Err error;
	Boolean incExponent=false;

	ptr = targetP;
	error = FlpBase10Info (valFlp, &mantissa, &exponent, &sign);
	if (error)
		return error;

	if (mantissa == 0)
		return FlpFToA (valFlp, targetP);
	
	StrPrintF (mantissaBuffer, "%ld", mantissa);
	
	mantissaLen = StrLen (mantissaBuffer);
	decimalPos = mantissaLen + exponent;
	if (exponent < 0)
		incExponent = true;
		
	// truncate trailing zeroes and adjust buffer length	
	while (mantissaLen > decimalPos)
	{
		if (mantissaBuffer [mantissaLen-1] == '0')
		{
			--mantissaLen;
			if (incExponent)
				++exponent;
		}
		else
			break;
	}
		
	mantissaBuffer [mantissaLen] = NULL;
	
	// determina new decimal position
	decimalPos = mantissaLen + exponent;
	
	if (Abs (exponent) > SIZE_MANTISSA ||
		Abs (decimalPos) > SIZE_MANTISSA)
	{
		// result is zero or need to display
		// in scientific notation: 
		//	[-]x.yyyyyyyy e[-]zz
		// In either case, no formatting necessary. 
		// Just convert to ascii and return.
		error = FlpFToA (valFlp, targetP);
		if (gDecimalSeparator != '.')
		{
			// Localize decimal point
			decimalPtr = StrChr (targetP, '.');
			if (decimalPtr)
				*decimalPtr = gDecimalSeparator;
		}
		return error;
	}
	
	if (sign > 0)
	{
		*ptr++ = '-';
		*ptr = NULL;
	}
	
	if (decimalPos < 0)
	{
		// insert decimal and leading zeroes.
		//*ptr++ = '.';
		*ptr++ = gDecimalSeparator;
		while (decimalPos++ < 0) *ptr++ = '0';
		
		// now append the mantissa
		*ptr = NULL;
		StrCat (targetP, mantissaBuffer);
	}
	else if (decimalPos > mantissaLen)
	{
		// insert mantissa and append trailing zeroes
		char *mantissaPtr;
		
		mantissaPtr = mantissaBuffer;
		while ( (*ptr = *mantissaPtr) != NULL)
		{
			++ptr;
			++mantissaPtr;
		}
		while (exponent-- > 0) 
			*ptr++ = '0';
		*ptr = NULL;
	}
	else
	{
		// decimal occurs in middle of mantissa
		for (i=0; i<mantissaLen; ++i)
		{
			if (i == decimalPos)
				//*ptr++ = '.';
				*ptr++ = gDecimalSeparator;
				
			*ptr++ = mantissaBuffer[i];
		}
		*ptr = NULL;
	}
		
	return 0;
}
/**************************************************************
FUNCTION: EcConvertToEuro

DESCRIPTION: Convert currency to Euro
**************************************************************/
static Err EcConvertToEuro (CharPtr sourceP, CharPtr targetP, int currency)
{
	FlpDouble currencyFlp;
	FlpDouble euroFlp;
	FlpDouble rateFlp;
	Err error;
	char buffer[SIZE_DISPLAY+1];
	CharPtr decimalPtr;
	
	
	rateFlp = FlpAToF (gConversionRates[gRateSelection]);
	
	// Convert sourceP decimal to '.' if necessary
	if (gDecimalSeparator != '.')
	{
		StrNCopy (buffer, sourceP, sizeof(buffer));
		buffer[SIZE_DISPLAY] = NULL;

		decimalPtr = StrChr (buffer, gDecimalSeparator);
		if (decimalPtr)
			*decimalPtr = '.';
			
		currencyFlp = FlpAToF (buffer);
	}
	else
		currencyFlp = FlpAToF (sourceP);
		
	euroFlp = _d_div (currencyFlp, rateFlp);
	
	error = EcFormatResult (euroFlp, targetP);

	return error;	
}
/**************************************************************
FUNCTION: EcConvertToCurrency

DESCRIPTION: Convert Euro to currency
**************************************************************/
static Err EcConvertToCurrency (CharPtr sourceP, CharPtr targetP, int currency)
{
	FlpDouble currencyFlp;
	FlpDouble euroFlp;
	FlpDouble rateFlp;
	Err error;
	CharPtr decimalPtr;
	char buffer[SIZE_DISPLAY+1];
	
	rateFlp = FlpAToF (gConversionRates[gRateSelection]);
	
	// Convert sourceP decimal to '.' if necessary
	if (gDecimalSeparator != '.')
	{
		StrNCopy (buffer, sourceP, sizeof(buffer));
		buffer[SIZE_DISPLAY] = NULL;

		decimalPtr = StrChr (buffer, gDecimalSeparator);
		if (decimalPtr)
			*decimalPtr = '.';
			
		euroFlp = FlpAToF (buffer);
	}
	else
		euroFlp = FlpAToF (sourceP);
		
	currencyFlp = _d_mul (euroFlp, rateFlp);
	
	error = EcFormatResult (currencyFlp, targetP);
	
	return error;
}
/**************************************************************
FUNCTION: EcUpdateDisplay

DESCRIPTION: Update the calculator display
**************************************************************/
static void EcUpdateDisplay(FormPtr activeFormP)
{
	CharPtr currencyP, euroP;
	Char resultBuffer [SIZE_DISPLAY+1];
	Err retCode;
	FieldPtr currencyFieldP, euroFieldP;

	currencyFieldP = GetObjectPtr (EcCurrencyField);
	euroFieldP = GetObjectPtr (EcEuroField);
	
	resultBuffer[0] = NULL;
	
	if (gActiveField == EcCurrencyField)
	{
		currencyP = FldGetTextPtr (GetObjectPtr(EcCurrencyField));
		retCode = EcConvertToEuro (currencyP, resultBuffer, frf);
		YSetField (euroFieldP, resultBuffer);
		FrmSetFocus (activeFormP, FrmGetObjectIndex (activeFormP, EcCurrencyField));
		FldSetInsPtPosition (currencyFieldP, FldGetTextLength(currencyFieldP));
	}
	else
	{
		euroP = FldGetTextPtr (GetObjectPtr (EcEuroField));
		retCode = EcConvertToCurrency (euroP, resultBuffer, frf);
		YSetField (currencyFieldP, resultBuffer);
		FrmSetFocus (activeFormP, FrmGetObjectIndex (activeFormP, EcEuroField));
		FldSetInsPtPosition (euroFieldP, FldGetTextLength (euroFieldP));
	}
	
}
/**************************************************************
 FUNCTION:    EcFormInit

 DESCRIPTION: This routine initializes the EcForm form.

 PARAMETERS:  frm - pointer to the EcForm form.

 RETURNED:    nothing
**************************************************************/
static void EcFormInit(FormPtr frmP)
{
	RectangleType currencyBounds;
	RectangleType euroBounds;
	CharPtr euroP, currencyP;
	Word error;
	
	gCurrencyH = MemHandleNew (SIZE_DISPLAY+1);
	gEuroH = MemHandleNew (SIZE_DISPLAY+1);
	
	currencyP = MemHandleLock (gCurrencyH);
	StrCopy (currencyP, gCurrencyBuffer);
	MemHandleUnlock (gCurrencyH);
	
	euroP = MemHandleLock (gEuroH);
	StrCopy (euroP, gEuroBuffer);
	MemHandleUnlock (gEuroH);
	
	if (!gCurrencyH || !gEuroH)
	{
		ErrFatalDisplay ("Insufficient memory to run Ec");
	}
	
	FldSetText (GetObjectPtr (EcCurrencyField), 
				gCurrencyH, 0, SIZE_DISPLAY+1);
	FldSetText (GetObjectPtr (EcEuroField),
				gEuroH, 0, SIZE_DISPLAY+1);
	
	// 2 pixel wide border for currency display
	WinDrawLine (0,0,0,18);				// left side
	WinDrawLine (1,1,1,17);
	
	WinDrawLine (0,18,122,18);			// bottom line
	WinDrawLine (1,17,121,17);
	
	// 2 pixel wide border for euro display
	WinDrawLine (0, 20, 0, 37);		// left side
	WinDrawLine (1, 21, 1, 36);
	
	WinDrawLine (0, 38, 123, 38);	// bottom line
	WinDrawLine (0, 37, 122, 37);
}
/**************************************************************
 FUNCTION:    EcFormDoCommand

 DESCRIPTION: This routine performs the menu command specified.

 PARAMETERS:  command  - menu item id

 RETURNED:    nothing
**************************************************************/
static Boolean EcFormDoCommand(Word command)
{
	Boolean handled = false;
    FormPtr frmP;
	short menuRateSelection = -1;
		
	switch (command)
	{
	case AboutForm:
		MenuEraseStatus (0);
    	frmP = FrmInitForm (AboutForm);
		FrmDoDialog (frmP);
		FrmDeleteForm (frmP);
		handled=true;
		break;
	
	case CurrenciesAustrianschillings:
		menuRateSelection = ats;
		break;
	
	case CurrenciesBelgianfrancs:
		menuRateSelection = bef;
		break;

	case CurrenciesGermanmarks:
		menuRateSelection = dem;
		break;
		
	case CurrenciesSpanishpesetas:
		menuRateSelection = esp;
		break;
		
	case CurrenciesFinnishmarkkaa:
		menuRateSelection = fim;
		break;
		
	case CurrenciesFrenchfrancs:
		menuRateSelection = frf;
		break;
		
	case CurrenciesIrishpounds:
		menuRateSelection = iep;
		break;
		
	case CurrenciesItalianlire:
		menuRateSelection = itl;
		break;
		
	case CurrenciesLuxembourgfrancs:
		menuRateSelection = luf;
		break;
		
	case CurrenciesDutchguilders:
		menuRateSelection = nlg;
		break;
		
	case CurrenciesPortugueseescudos:
		menuRateSelection = pte;
		break;

	default:
		SndPlaySystemSound (sndError);
		break;
	}
	
	if (menuRateSelection != -1 &&
		menuRateSelection != gRateSelection)
	{
		// update the currency popup list
		gRateSelection = menuRateSelection;
   		CtlSetLabel (GetObjectPtr (EcCurrencyPopTrigger),
    				LstGetSelectionText (GetObjectPtr (EcCurrencyList),
    					 				gRateSelection));
    	LstSetSelection (GetObjectPtr(EcCurrencyList), gRateSelection);
		EcUpdateDisplay (FrmGetActiveForm());

		handled = true;
	}
	return handled;
}
/**************************************************************
 FUNCTION:    EcFormHandleEvent

 DESCRIPTION: This routine is the event handler for the 
              "EcForm" 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.
**************************************************************/
static Boolean EcFormHandleEvent(EventPtr eventP)
{
    Boolean handled = false;
    char decimalSeparator[2];
    FormPtr frmP;
    CharPtr activeBufferP, textP;
	FieldPtr currencyFldP, euroFldP;
    short selection;
    FlpDouble resultFlp;
    
	frmP = FrmGetActiveForm();

	currencyFldP = GetObjectPtr (EcCurrencyField);
	euroFldP = GetObjectPtr (EcEuroField);
	decimalSeparator[0] = gDecimalSeparator;
	decimalSeparator[1] = NULL;
	
	switch (eventP->eType) 
	{
	case ctlSelectEvent:
        switch (eventP->data.ctlSelect.controlID)
        {
        case EcClearButton:
        	YSetField (euroFldP, "");
		    YSetField (currencyFldP, "");
			gOperand1 = gOperand2 = FlpAToF ("0");
        	gCalcOp = nop;
        	
        	// Palm OS 3.0 bug?: If a field already has
        	// the focus, call to FrmSetFocus does not
        	// turn on insertion point. Work around is
        	// to force focus to another field
        	// and then set the focus back.  
        	
        	if (gActiveField == EcCurrencyField)
        	{
				FrmSetFocus (frmP, FrmGetObjectIndex (frmP, EcEuroField));        	
				FrmSetFocus (frmP, FrmGetObjectIndex (frmP, EcCurrencyField));
				FldSetInsPtPosition (currencyFldP, 0);
			}
			else
			{
				FrmSetFocus (frmP, FrmGetObjectIndex (frmP, EcCurrencyField));
				FrmSetFocus (frmP, FrmGetObjectIndex (frmP, EcEuroField));
				FldSetInsPtPosition (euroFldP, 0);
			}
        	gCurrencyHasDecimal = gEuroHasDecimal = false;
	        handled = true;
            break;
		
        case EcEqualsButton:
       		if (gActiveField == EcCurrencyField)
       		{
       			activeBufferP = gCurrencyBuffer;
       			textP = FldGetTextPtr (currencyFldP);
       		}
       		else
       		{
       			activeBufferP = gEuroBuffer; 
       			textP = FldGetTextPtr (euroFldP);
       		}
       		
       		if (gCalcOp != nop && gCalcOp != equals)       		
	        {
	        	gOperand2 = FlpAToF (textP);
       			gOperand1 = EcOperate (gOperand1, gOperand2);
       			
       			// Make calculations based on field that has focus
 				if (EcFormatResult (gOperand1, activeBufferP) != 0)
       			{
      				// probably divide by zero error
       				YSetField (currencyFldP, "Error");
       				YSetField (euroFldP, "Error");
       			}
       			else
       			{	        		
	        		YSetField (GetObjectPtr (gActiveField), activeBufferP);
	        		FrmSetFocus (frmP, FrmGetObjectIndex (frmP, gActiveField));
	        		EcUpdateDisplay (frmP);
	        	}
			}
			else
			{
				// Just in case user enters number using grafitti or keyboard
				// allow equals button to convert
				EcUpdateDisplay (frmP);
			}
				
        	gCurrencyHasDecimal = gEuroHasDecimal = false;
			gCalcOp = equals;	        	
	        handled = true;
            break;
            
        case EcDivideButton:
			EcCalculate (currencyFldP, euroFldP);
			
			YSetField (currencyFldP, "");
			YSetField (euroFldP, "");	
    		gCurrencyHasDecimal = gEuroHasDecimal = false;
   			gCalcOp = divide;
        	
	        handled = true;
            break;

        case EcMultiplyButton:
			EcCalculate (currencyFldP, euroFldP);
			
			YSetField (currencyFldP, "");
			YSetField (euroFldP, "");	
    		gCurrencyHasDecimal = gEuroHasDecimal = false;
   			gCalcOp = multiply;
        	
	        handled = true;
            break;

		case EcSubtractButton:
			EcCalculate (currencyFldP, euroFldP);
			
			YSetField (currencyFldP, "");
			YSetField (euroFldP, "");	
    		gCurrencyHasDecimal = gEuroHasDecimal = false;
   			gCalcOp = subtract;
        	
	        handled = true;
            break;
		case EcAddButton:
			EcCalculate (currencyFldP, euroFldP);
			
			YSetField (currencyFldP, "");
			YSetField (euroFldP, "");	
    		gCurrencyHasDecimal = gEuroHasDecimal = false;
   			gCalcOp = add;
        	
	        handled = true;
            break;
			
        case EcSevenButton:
        	EcAppendDigit (GetObjectPtr(gActiveField), '7');
		    EcUpdateDisplay (frmP);
	        handled = true;
            break;

        case EcEightButton:
        	EcAppendDigit (GetObjectPtr(gActiveField), '8');
		    EcUpdateDisplay (frmP);
	        handled = true;
            break;

        case EcNineButton:
        	EcAppendDigit (GetObjectPtr(gActiveField), '9');
		    EcUpdateDisplay (frmP);
	        handled = true;
            break;

        case EcFourButton:
        	EcAppendDigit (GetObjectPtr(gActiveField), '4');
		    EcUpdateDisplay (frmP);
	        handled = true;
            break;

        case EcFiveButton:
        	EcAppendDigit (GetObjectPtr(gActiveField), '5');
		    EcUpdateDisplay (frmP);
	        handled = true;
            break;

        case EcSixButton:
        	EcAppendDigit (GetObjectPtr(gActiveField), '6');
		    EcUpdateDisplay (frmP);
	        handled = true;
            break;

        case EcOneButton:
        	EcAppendDigit (GetObjectPtr(gActiveField), '1');
		    EcUpdateDisplay (frmP);
	        handled = true;
            break;

        case EcTwoButton:
        	EcAppendDigit (GetObjectPtr(gActiveField), '2');
		    EcUpdateDisplay (frmP);
	        handled = true;
            break;

        case EcThreeButton:
        	EcAppendDigit (GetObjectPtr(gActiveField), '3');
		    EcUpdateDisplay (frmP);
	        handled = true;
            break;

        case EcZeroButton:
        	EcAppendDigit (GetObjectPtr(gActiveField), '0');
		    EcUpdateDisplay (frmP);
	        handled = true;
            break;

        case EcDecimalButton:
        	EcAppendDecimal ();
	        handled = true;
            break;

        case EcCurrencyPopTrigger:
        	selection = LstPopupList (GetObjectPtr (EcCurrencyList));
        	if (selection != -1)
        	{
        		gRateSelection = selection;
        		CtlSetLabel (GetObjectPtr (EcCurrencyPopTrigger),
        					 LstGetSelectionText (GetObjectPtr (EcCurrencyList),
        					 					  selection));
        					 					  
        		EcUpdateDisplay (frmP);
        	}
        		
	        handled = true;
            break;

        default:
            break;
    }
    break;
	
	case menuEvent:
		return EcFormDoCommand(eventP->data.menu.itemID);

	case fldEnterEvent:
		if (gCalcOp != nop && gCalcOp != equals)
		{
			SndPlaySystemSound (sndError);
			handled = true;
			break;
		}
		
		switch (eventP->data.fldEnter.fieldID)
		{
		case EcCurrencyField:
			gActiveField = EcCurrencyField;
			FrmSetFocus (frmP, FrmGetObjectIndex (frmP, EcCurrencyField));
			FldSetInsPtPosition (currencyFldP, FldGetTextLength (currencyFldP));
			if (StrChr (FldGetTextPtr (currencyFldP), gDecimalSeparator))
				gCurrencyHasDecimal = true;
			else
				gCurrencyHasDecimal = false;
				
	        handled = true;
			break;
		case EcEuroField:
			gActiveField = EcEuroField;
			FrmSetFocus (frmP, FrmGetObjectIndex (frmP, EcEuroField));
			FldSetInsPtPosition (euroFldP, FldGetTextLength (euroFldP));
			if (StrChr (FldGetTextPtr (euroFldP), gDecimalSeparator))
				gEuroHasDecimal = true;
			else
				gEuroHasDecimal = false;
			
	        handled = true;
			break;
		}
		break;
		
	case frmOpenEvent:
		frmP = FrmGetActiveForm();
		EcFormInit( frmP);
		
		CtlSetLabel (GetObjectPtr (EcCurrencyPopTrigger),
					 LstGetSelectionText (GetObjectPtr (EcCurrencyList),
					 					  gRateSelection));
					 					  

		CtlSetLabel (GetObjectPtr (EcDecimalButton), decimalSeparator);
		FrmDrawForm ( frmP);
		FrmSetFocus (frmP, FrmGetObjectIndex (frmP, gActiveField));

		
		handled = true;
		break;

	case frmCloseEvent:
		// Clear the handle value in the field, otherwise OS will try
		// to free handle values when the form is closed.
		
		FldSetTextHandle (GetObjectPtr (EcCurrencyField), 0);
		FldSetTextHandle (GetObjectPtr (EcEuroField), 0);
		
		MemHandleFree (gCurrencyH);
		MemHandleFree (gEuroH);
		
		handled = false;		// allow system to finish handling
		break;
	
	case keyDownEvent:
	{
		Word chr;
		chr = eventP->data.keyDown.chr;
		
		if (chr >= '0' && chr <= '9')
			EcAppendDigit (GetObjectPtr (gActiveField), eventP->data.keyDown.chr);
		else if (chr == gDecimalSeparator)
			EcAppendDecimal();
		else
			SndPlaySystemSound (sndError);

		EcUpdateDisplay (frmP);
		handled = true;		// don't process any other strokes
		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.
**************************************************************/
static Boolean AppHandleEvent( EventPtr eventP)
{
	Word 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 EcForm:
				FrmSetEventHandler(frmP, EcFormHandleEvent);
				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)
{
	Word 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)
{
    EcPreferenceType prefs;
    Word prefsSize;


	// Read the saved preferences / saved-state information.
	prefsSize = sizeof(EcPreferenceType);
	if (PrefGetAppPreferences(appFileCreator, appPrefID, &prefs, &prefsSize, false) != 
		noPreferenceFound)
	{
		gRateSelection = prefs.rateSelection;
		gActiveField = prefs.activeField;
		gOperand1 = prefs.operand1;
		gOperand2 = prefs.operand2;
		gCalcOp = prefs.calcOp;
		gCurrencyHasDecimal = prefs.currencyHasDecimal;
		gEuroHasDecimal = prefs.euroHasDecimal;
		StrCopy (gEuroBuffer, prefs.euroBuffer);
		StrCopy (gCurrencyBuffer, prefs.currencyBuffer);
	}
	else
	{
		gRateSelection = 0;
		gActiveField = EcCurrencyField;
		gOperand1 = gOperand2 = _d_itod (0);
		gCalcOp = nop;
		gCurrencyHasDecimal = gEuroHasDecimal = false;
		gEuroBuffer[0] = gCurrencyBuffer[0] = NULL;
	}
	
	LocGetNumberSeparators ( (NumberFormatType)PrefGetPreference(prefNumberFormat),
							 &gThousandSeparator, &gDecimalSeparator);
							 
	return 0;
}
/**************************************************************
 FUNCTION:    AppStop

 DESCRIPTION: Save the current state of the application.

 PARAMETERS:  nothing

 RETURNED:    nothing

 REVISION HISTORY:
**************************************************************/
static void AppStop(void)
{
   EcPreferenceType prefs;
   
   
	// Write the saved preferences / saved-state information.
	
	prefs.round = 0;	// no rounding for now
	prefs.rateSelection = gRateSelection;
	prefs.activeField = gActiveField;
	prefs.operand1 = gOperand1;
	prefs.operand2 = gOperand2;
	prefs.calcOp = gCalcOp; 
	prefs.currencyHasDecimal = gCurrencyHasDecimal;
	prefs.euroHasDecimal = gEuroHasDecimal;
	StrCopy (prefs.euroBuffer, FldGetTextPtr (GetObjectPtr (EcEuroField)));
	StrCopy (prefs.currencyBuffer, FldGetTextPtr (GetObjectPtr (EcCurrencyField)));
	 
	// flag set to false.  This data will not be backed up
	// during a HotSync.
	PrefSetAppPreferences (appFileCreator, appPrefID, appPrefVersionNum, 
		&prefs, sizeof (prefs), false);
}
/**************************************************************
 FUNCTION:    EcPilotEc

 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
**************************************************************/
static DWord EcPilotMain(Word cmd, Ptr cmdPBP, Word launchFlags)
{
	Err error;


	error = RomVersionCompatible (ourMinVersion, launchFlags);
	if (error) return (error);


	switch (cmd)
		{
		case sysAppLaunchCmdNormalLaunch:
			error = AppStart();
			if (error) 
				return error;
				
			FrmGotoForm(EcForm);
			AppEventLoop();
			AppStop();
			break;

		default:
			break;

		}
	
	return 0;
}
/**************************************************************
 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
**************************************************************/
DWord PilotMain( Word cmd, Ptr cmdPBP, Word launchFlags)
{
    return EcPilotMain(cmd, cmdPBP, launchFlags);
}

