
#include <Common.h>
#include <System/SysAll.h>
#include <UI/UIAll.h>
#include <System/SysEvtMgr.h>
#include <stddef.h>
#include "MathLib.h"
#include "resource.h"
#include "PCalcMain.h"
#include "MainForm.h"
#include "DataBase.h"
#include "ExecProg.h"
#include "ConvDblToStr.h"
#include "ConvStrToDbl.h"
#include "EvalExpr.h"
#include "Misc.h"



/* define global variables. */
static FieldPtr      gExprFldPtr;
static ControlPtr    gArcCntlPtr;
static ControlPtr    gHypCntlPtr;
static Int           gHistCurrPos;
static RectangleType gRectAnswerField;
static Int           gMemStoreMode;

/* prototypes. */
static void    CheckParenMatch(void);
static void    CalcExpression(void);
static void    ConstListDrawFunc(UInt, RectanglePtr, CharPtr*);
static void    MemoryListDrawFunc(UInt, RectanglePtr, CharPtr*);
static void    ProgramListDrawFunc(UInt, RectanglePtr, CharPtr*);
static void    PutNumberCharToField(CharPtr);
static void    PutOperatorToField(CharPtr);
static void    PutFunctionToField(CharPtr);
static void    PutTrigFuncToField(CharPtr);
static void    DispExprHist(Int);
static Boolean CompareWord(Char* str, Char* word);
static void    GetEvalErrMessage(Int, Char*);
static void    UpdateAngNotatUI(void);
static void    InitConstMemProgListUI(void);
static void    DisplayAnswer(void);
static void    RunProgram(void);
static void    DrawAnswerRoundBox(void);


/* define functions. */
Boolean MainFormHandleEvent(EventPtr event)
{
  Boolean handled = false;
  FormPtr form = FrmGetActiveForm();

  switch (event->eType) {
  case frmOpenEvent:
    FrmDrawForm(form);

    gExprFldPtr  = GetObjectPtr(fieldMainExprID);
    gArcCntlPtr  = GetObjectPtr(pushbtnMainArcID);
    gHypCntlPtr  = GetObjectPtr(pushbtnMainHypID);
    gHistCurrPos  = -1;
    gMemStoreMode = false;
    g.LastAnsValid = false;

    /* setup expression field. */
    FrmSetFocus(form, FrmGetObjectIndex(form, fieldMainExprID));
    SetFieldText(fieldMainExprID, "");  /* hang-up prevention */

    /* initialize/setup UIs. */
    UpdateScrollBar(fieldMainExprID, scrollbarMainExprID);
    UpdateAngNotatUI();
    InitConstMemProgListUI();
    DrawAnswerRoundBox();

    handled = true;
    break;

  case frmUpdateEvent:
    InitConstMemProgListUI();
    FrmEraseForm(form);
    FrmDrawForm(form);
    DrawAnswerRoundBox();
    FrmSetFocus(form, FrmGetObjectIndex(form, fieldMainExprID));
    g.LastAnsValid = false;
    handled = true;
    break;

  case menuEvent:
    switch (event->data.menu.itemID) {
    case menuitemMainPrefsID:
      gProgramMode = false;
      FrmGotoForm(formPrefID);
      break;

    case menuitemMainAboutID:
      FrmAlert(alertAboutID);
      break;

    case menuitemMainCopyID:
      if (g.LastAnsValid) {
	ClipboardAddItem(clipboardText, gLastAnsStr, StrLen(gLastAnsStr));
      } else {
	SndPlaySystemSound(sndError);
      }
      break;

    case menuitemMainProgID:
      gProgramMode = false;
      FrmGotoForm(formSelectProgID);
      break;

    case menuitemMainConstID:
      gProgramMode = false;
      FrmGotoForm(formSelectConstID);
      break;

    }
    handled = true;
    break;

  case penDownEvent:
    if (RctPtInRectangle(event->screenX, event->screenY, &gRectAnswerField)) {
      WinInvertRectangle(&gRectAnswerField, 0);
      SysTaskDelay(10);
      WinInvertRectangle(&gRectAnswerField, 0);
      if (g.LastAnsValid) {
	SndPlaySystemSound(sndClick);
	PutNumberCharToField(gLastAnsStr);
      } else {
	SndPlaySystemSound(sndError);
      }
      handled = true;
    }
    break;

  case fldEnterEvent:
    if (event->data.fldEnter.fieldID == fieldMainExprID) {
      g.FirstInput = false;
    }
    break;

  case fldChangedEvent:
    UpdateScrollBar(fieldMainExprID, scrollbarMainExprID);
    handled = true;
    break;

  case sclRepeatEvent:
    ScrollLines(event->data.sclRepeat.newValue
		- event->data.sclRepeat.value, fieldMainExprID);
    UpdateScrollBar(fieldMainExprID, scrollbarMainExprID);
    break;

  case ctlSelectEvent:
    switch(event->data.ctlSelect.controlID) {
    case buttonMain1ID:
      PutNumberCharToField("1");
      handled = true;
      break;

    case buttonMain2ID:
      PutNumberCharToField("2");
      handled = true; 
      break;

    case buttonMain3ID:
      PutNumberCharToField("3");
      handled = true;
      break;

    case buttonMain4ID:
      PutNumberCharToField("4");
      handled = true;
      break;

    case buttonMain5ID:
      PutNumberCharToField("5");
      handled = true;
      break;

    case buttonMain6ID:
      PutNumberCharToField("6");
      handled = true;
      break;

    case buttonMain7ID:
      PutNumberCharToField("7");
      handled = true;
      break;

    case buttonMain8ID:
      PutNumberCharToField("8");
      handled = true;
      break;

    case buttonMain9ID:
      PutNumberCharToField("9");
      handled = true;
      break;

    case buttonMain0ID:
      PutNumberCharToField("0");
      handled = true;
      break;

    case buttonMainPntID:
      PutNumberCharToField(".");
      handled = true;
      break;

    case buttonMainEID:
      FldInsert(gExprFldPtr, "e", 1);
      CtlSetValue(gArcCntlPtr, 0);
      CtlSetValue(gHypCntlPtr, 0);
      g.FirstInput = false;
      handled = true;
      break;

    case buttonMainPowID:
      PutOperatorToField("^");
      handled = true;
      break;

    case buttonMainAddID:
      PutOperatorToField("+");
      handled = true;
      break;

    case buttonMainSubID:
      PutOperatorToField("-");
      handled = true;
      break;

    case buttonMainMulID:
      PutOperatorToField("*");
      handled = true;
      break;

    case buttonMainDivID:
      PutOperatorToField("/");
      handled = true;
      break;

    case buttonMainLprnID:
      PutNumberCharToField("(");
      handled = true;
      break;

    case buttonMainRprnID:
      PutNumberCharToField(")");
      CheckParenMatch(); /* check the corresponding left-parenthesis. */
      handled = true;
      break;

    case buttonMainPiID:
      PutNumberCharToField("PI");
      handled = true;
      break;

    case buttonMainSinID:
      PutTrigFuncToField("sin");
      handled = true;
      break;

    case buttonMainCosID:
      PutTrigFuncToField("cos");
      handled = true;
      break;

    case buttonMainTanID:
      PutTrigFuncToField("tan");
      handled = true;
      break;

    case buttonMainLnID:
      PutFunctionToField("ln ");
      handled = true;
      break;

    case buttonMainLogID:
      PutFunctionToField("log ");
      handled = true;
      break;

    case buttonMainExpID:
      PutFunctionToField("exp ");
      handled = true;
      break;

    case buttonMainSqrtID:
      PutFunctionToField("sqrt ");
      handled = true;
      break;

    case buttonMainEntID:
      CtlSetValue(gArcCntlPtr, 0);
      CtlSetValue(gHypCntlPtr, 0);
      if (gProgramMode) {
	RunProgram();
      } else {
	CalcExpression();
      }
      handled = true;
      break;

    case buttonMainBsID:
      EvtEnqueueKey(backspaceChr, 0, 0);
      g.FirstInput = false;
      UpdateScrollBar(fieldMainExprID, scrollbarMainExprID);
      handled = true;
      break;

    case buttonMainAnsID:
      PutNumberCharToField("$ANS ");
      handled = true;
      break;

    case buttonMainClrID:
      InsPtEnable(true);
      SetFieldText(fieldMainExprID, "");
      CtlSetValue(gArcCntlPtr, 0);
      CtlSetValue(gHypCntlPtr, 0);
      g.FirstInput = false;
      UpdateScrollBar(fieldMainExprID, scrollbarMainExprID);
      handled = true;
      break;

    case buttonMainStoID:
      {
	Short sel;

	gMemStoreMode = true;
	sel = LstPopupList(GetObjectPtr(listMainMemID));
	if (g.LastAnsValid && (sel > 0)) g.Memory[sel] = g.Memory[0];
	gMemStoreMode = false;
      }
      handled = true;
      break;

    case pushbtnMainArcID:
      handled = true;
      break;

    case pushbtnMainHypID:
      handled = true;
      break;

    case pushbtnMainProgModeID:
      if (gProgramMode) {
	gProgramMode = false;
	g.LastAnsValid = false;
	SetFieldText(fieldMainAnswID, "Program terminated.");
	SndPlaySystemSound(sndError);
      } else if (gLastExecProgRec >= 0) {
	/* Run program. */
	ReadProgramCode(gLastExecProgRec, gProgramStr);
	gProgCurrPos = gProgramStr;
	RunProgram();	
      } else {
	CtlSetValue(GetObjectPtr(pushbtnMainProgModeID), 0);
	SndPlaySystemSound(sndError);
      }
      handled = true;
      break;

    case pushbtnMainAngID:
      if        (g.AngleUnit == RAD_MODE) {
	setdegree();
	g.AngleUnit = DEG_MODE;
      } else if (g.AngleUnit == DEG_MODE) {
	setradian();
	g.AngleUnit = RAD_MODE;
      }
      UpdateAngNotatUI();
      break;

    case pushbtnMainNotatID:
      if      (g.Notation == FIX_MODE) g.Notation = SCI_MODE;
      else if (g.Notation == SCI_MODE) g.Notation = FIX_MODE;
      UpdateAngNotatUI();
      if (g.LastAnsValid) DisplayAnswer();
      else SndPlaySystemSound(sndError);
      handled = true;
      break;

    case poptrigMainConstID:
      if (DmNumRecords(gConstantDB) == 0) {
	SndPlaySystemSound(sndError);
	handled = true;
      }
      break;

    case poptrigMainProgID:
      if (DmNumRecords(gProgramDB) == 0) {
	SndPlaySystemSound(sndError);
	handled = true;
      }
      break;


    }
    break;

  case popSelectEvent:
    switch (event->data.popSelect.controlID) {

    case poptrigMainConstID:
      {
	ConstDBType c;

	ReadConstRecord(event->data.popSelect.selection, &c);
	FldInsert(gExprFldPtr, c.symbol, StrLen(c.symbol));
      }
    handled = true;
    break;
    
    case poptrigMainMemID:
      if ((event->data.popSelect.selection > 0)
	  && (finite(g.Memory[event->data.popSelect.selection]))) {
	Char s[32];

	GetAnswerFormat(g.Memory[event->data.popSelect.selection],
			     s, g.DisplayDigit, 0);
	PutNumberCharToField(s);
      } else {
	SndPlaySystemSound(sndError);
      }
      handled = true;
      break;

    case poptrigMainProgID:
      /* Run program. */
      ReadProgramCode(event->data.popSelect.selection, gProgramStr);
      gLastExecProgRec = event->data.popSelect.selection;
      gProgCurrPos = gProgramStr;
      RunProgram();	
      handled = true;
      break;
    }
    break;

  case keyDownEvent:
    switch (event->data.keyDown.chr) {
    case pageUpChr:
      if (gHistCurrPos == -1) {
	gHistCurrPos = g.HistPosition;
	if (g.FirstInput) {
	  g.FirstInput = false;
	  DispExprHist(-2);
	} else {
	  StrCopy(g.HistoryStr[g.HistPosition], FldGetTextPtr(gExprFldPtr));
	  DispExprHist(-1);
	}
      } else {
	DispExprHist(-1);
      }
      handled = true;
      break;

    case pageDownChr:
      if (gHistCurrPos == -1) {
	gHistCurrPos = g.HistPosition;
	if (g.FirstInput) {
	  g.FirstInput = false;
	  DispExprHist(+1);
	} else {
	  StrCopy(g.HistoryStr[g.HistPosition], FldGetTextPtr(gExprFldPtr));
	  DispExprHist(+1);
	}
      }	else {
	  DispExprHist(+1);
      }
      handled = true;
      break;
    }
    break;

  case nilEvent:
    handled = true;
    break;
  }

  return handled;
}


static void PutNumberCharToField(CharPtr s)
{
  if (g.FirstInput) SetFieldText(fieldMainExprID, "");
  FldInsert(gExprFldPtr, s, StrLen(s));
  CtlSetValue(gArcCntlPtr, 0);
  CtlSetValue(gHypCntlPtr, 0);
  g.FirstInput = false;
}


static void PutOperatorToField(CharPtr s)
{
  if (g.FirstInput && g.LastAnsValid) {
    SetFieldText(fieldMainExprID, gLastAnsStr);
  }
  FldInsert(gExprFldPtr, s, StrLen(s));
  CtlSetValue(gArcCntlPtr, 0);
  CtlSetValue(gHypCntlPtr, 0);
  g.FirstInput = false;
}


static void PutFunctionToField(CharPtr s)
{
  CtlSetValue(gArcCntlPtr, 0);
  CtlSetValue(gHypCntlPtr, 0);
  if (g.FirstInput) SetFieldText(fieldMainExprID, "");
  FldInsert(gExprFldPtr, s, StrLen(s));
  if (g.FirstInput && g.LastAnsValid) {
    FldInsert(gExprFldPtr, " ", 1);
    FldInsert(gExprFldPtr, gLastAnsStr, StrLen(gLastAnsStr));
    CalcExpression();
  } else {
    g.FirstInput = false;
  }
}


static void PutTrigFuncToField(CharPtr s)
{
  if (g.FirstInput) SetFieldText(fieldMainExprID, "");
  if (CtlGetValue(gArcCntlPtr) == true) FldInsert(gExprFldPtr, "a", 1);
  FldInsert(gExprFldPtr, s, StrLen(s));
  if (CtlGetValue(gHypCntlPtr) == true) FldInsert(gExprFldPtr, "h", 1);
  FldInsert(gExprFldPtr, " ", 1);
  CtlSetValue(gArcCntlPtr, 0);
  CtlSetValue(gHypCntlPtr, 0);
  if (g.FirstInput && g.LastAnsValid) {
    FldInsert(gExprFldPtr, " ", 1);
    FldInsert(gExprFldPtr, gLastAnsStr, StrLen(gLastAnsStr));
    CalcExpression();
  } else {
    g.FirstInput = false;
  }
}


static void DispExprHist(Int n)
{
  gHistCurrPos += n;
  if (gHistCurrPos < 0) gHistCurrPos += kNumHistory;
  if (gHistCurrPos >= kNumHistory) gHistCurrPos -= kNumHistory;

  InsPtEnable(true);
  SetFieldText(fieldMainExprID, g.HistoryStr[gHistCurrPos]);
  FldSetSelection(gExprFldPtr, 0, StrLen(g.HistoryStr[gHistCurrPos]));
}


static void CheckParenMatch(void)
{
  short paren_level;
  short cursor_pnt = FldGetInsPtPosition(gExprFldPtr) - 1;
  char* expr_ptr   = FldGetTextPtr(gExprFldPtr);

  paren_level = 0;
  for (;;) {
    cursor_pnt--;
    if (expr_ptr[cursor_pnt] == ')') paren_level++;
    if (expr_ptr[cursor_pnt] == '(') {
      if (paren_level == 0) { /* O.K. */
	VoidHand h = (VoidHand)FldGetTextHandle(gExprFldPtr);
	if (h) {
	  //	  CharPtr s = MemHandleLock(h);  // really needed ??
	  Word pos = FldGetInsPtPosition(gExprFldPtr);
	  FldSetInsPtPosition(gExprFldPtr, cursor_pnt);
	  SysTaskDelay(35);
	  FldSetInsPtPosition(gExprFldPtr, pos);
	  //	  MemHandleUnlock(h);
	  FldDrawField(gExprFldPtr);
	}
	break; 
      }
      paren_level--;
      if (paren_level < 0) {
	SndPlaySystemSound(sndError); /* error */
	break;
      }
    }
    if (cursor_pnt == 0) {
      SndPlaySystemSound(sndError); /* error */
      break;
    }
  }
}


static void CalcExpression(void)
{
  int expr_error, expr_length;
  double result;
  char result_str[128];
  char* expr_ptr;

  /* get expression string */
  expr_ptr = FldGetTextPtr(gExprFldPtr);
  expr_length = StrLen(expr_ptr);
  FldSetSelection(gExprFldPtr, 0, 0);

  /* push the expression into history */
  StrCopy(g.HistoryStr[g.HistPosition], expr_ptr);
  if (++g.HistPosition >= kNumHistory) g.HistPosition = 0;
  gHistCurrPos = -1;

  /* evaluate the expression */
  expr_error = evaluate(&expr_ptr, &result);
  if (expr_error == 0) {
    /* successful return. */
    g.Memory[0] = result;
    DisplayAnswer();
    g.FirstInput = true;
    InsPtEnable(false);
    g.LastAnsValid = finite(result) ? true : false;

  } else {  /* error */
    GetEvalErrMessage(expr_error, result_str);
    SndPlaySystemSound(sndError);
    FldSetInsertionPoint(gExprFldPtr, expr_length - StrLen(expr_ptr));
    SetFieldText(fieldMainAnswID, result_str);
    g.LastAnsValid = false;
    g.FirstInput = false;
  }
}


Int GetAnswerFormat(double x, CharPtr s, Int d, UInt f)
{
  if ((g.Notation == SCI_MODE)
      || (fabs(x) >= 1e-308 ? fabs(log10(fabs(x))) : 0) > g.NotatBound) {
    ConvertDblToStr(x, s, EXP_FORMAT | f, d);
    return 0;
  }
  else if (g.Notation == FIX_MODE) {
    ConvertDblToStr(x, s, FIX_FORMAT | f, d);
    return 0;
  }
  return 1;
}


static void ConstListDrawFunc(UInt itemNum, RectanglePtr bounds, CharPtr* data)
{
  ConstDBType c;

  ReadConstRecord(itemNum, &c);
  WinDrawChars(c.desc, StrLen(c.desc), bounds->topLeft.x, bounds->topLeft.y);
}


static void MemoryListDrawFunc(UInt itemNum, RectanglePtr bounds, CharPtr* data)
{
  Char s[32];
  UInt len;

  if (itemNum == 0) {
    if (gMemStoreMode) StrPrintF(s, "*** SELECT TO STORE ***");
    else               StrPrintF(s, "*** SELECT TO PASTE ***");
    len = StrLen(s);
    WinDrawChars(s, len, bounds->topLeft.x
		 + 0.5 * (bounds->extent.x - FntCharsWidth(s, len)),
		 bounds->topLeft.y);
  } else {
    StrPrintF(s, "%d:", itemNum);
    WinDrawChars(s, StrLen(s), bounds->topLeft.x, bounds->topLeft.y);
    GetAnswerFormat(g.Memory[itemNum], s, g.DisplayDigit, g.DispAddFormat);
    len = StrLen(s);
    WinDrawChars(s, len, bounds->topLeft.x + bounds->extent.x
		 - FntCharsWidth(s, len) - 1, bounds->topLeft.y);
  }
}


static void ProgramListDrawFunc(UInt itemNum, RectanglePtr bounds, CharPtr* data)
{
  Char title[32];

  ReadProgramTitle(itemNum, title);
  WinDrawChars(title, StrLen(title), bounds->topLeft.x, bounds->topLeft.y);
}


Int GetVarBySymbol(Char** s, double* x)
{
  Int i;
  Int n = DmNumRecords(gConstantDB);
  Int RecNumMax = -1;
  Int LengthMax = -1;
  Char ValStrMax[64];
  CharPtr endptr;
  ConstDBType c;
  Char MemSymbol[kNumMemory + 1][8] = {"$ANS", "$MEM1", "$MEM2",
				       "$MEM3", "$MEM4", "$MEM5",
				       "$MEM6", "$MEM7", "$MEM8",
				       "$MEM9"};

  for (i = 0; i <= kNumMemory; i++) {
    if (CompareWord(*s, MemSymbol[i])) {
      *x = g.Memory[i];
      *s += StrLen(MemSymbol[i]);
      return 0;
    }
  }

  for (i = 0; i < n; i++) {
    ReadConstRecord(i, &c);
    if (c.symbol[0] != '$') continue;
    if ((CompareWord(*s, c.symbol)) && ((Int)StrLen(c.symbol) > LengthMax)) {
      RecNumMax = i;
      LengthMax = StrLen(c.symbol);
      StrCopy(ValStrMax, c.value);
    }
  }

  if (RecNumMax >= 0) {
    ConvStrToDbl(x, ValStrMax, &endptr);
    *s += LengthMax;
    return 0;
  } else {
    return 1;
  }
}


static Boolean CompareWord(Char* str, Char* word)
{
  UInt len = StrLen(word);

  if (StrNCompare(str, word, len) != 0) return false;
  if (*(str + len) == '_') return false;
  if ((*(str + len) >= 'a') && (*(str + len) <= 'z')) return false;
  if ((*(str + len) >= 'A') && (*(str + len) <= 'Z')) return false;

  return true;
}


static void GetEvalErrMessage(Int err, Char* p)
{
  
  if        (err == ERR_DEEP_NESTING) {
    StrCopy(p, "ERR: Nesting too deep.");
  } else if (err == ERR_PAREN_NOT_MATCH) {
    StrCopy(p, "ERR: Parens not match.");
  } else if (err == ERR_NOT_A_NUMBER) {
    StrCopy(p, "ERR: Not a number.");
  } else if (err == ERR_NULL_FOUND) {
    StrCopy(p, "ERR: Null found.");
  } else if (err == ERR_EXCESS_CHARACTERS) {
    StrCopy(p, "ERR: Excess characters.");
  } else if (err == ERR_VAR_UNDEFINED) {
    StrCopy(p, "ERR: Var. undefined.");

  } else if (err == PROGERR_UNKNOWN_COMMAND) {
    StrCopy(p, "ERR: Unknown command.");
  } else if (err == PROGERR_NO_QUOTATION) {
    StrCopy(p, "ERR: No quotation.");
  } else if (err == PROGERR_QUOTE_NOT_CLOSED) {
    StrCopy(p, "ERR: Quote not closed.");
  } else if (err == PROGERR_OUT_OF_RANGE) {
    StrCopy(p, "ERR: Out of range.");
  } else if (err == PROGERR_QUOTE_TOO_LONG) {
    StrCopy(p, "ERR: Quote too long.");
  } else {
      StrCopy(p, "ERR: Unknown error.");
  }
}


static void UpdateAngNotatUI(void)
{
  ControlPtr AngUnit  = GetObjectPtr(pushbtnMainAngID);
  ControlPtr Notation = GetObjectPtr(pushbtnMainNotatID);

  if      (g.AngleUnit == RAD_MODE) CtlSetLabel(AngUnit, "RAD");
  else if (g.AngleUnit == DEG_MODE) CtlSetLabel(AngUnit, "DEG");
  CtlSetValue(AngUnit, 0);

  if      (g.Notation == FIX_MODE) CtlSetLabel(Notation, "FIX");
  else if (g.Notation == SCI_MODE) CtlSetLabel(Notation, "SCI");
  CtlSetValue(Notation, 0);
}


static void InitConstMemProgListUI(void)
{
  ListPtr SciConstList = GetObjectPtr(listMainConstID);
  ListPtr MemoryList   = GetObjectPtr(listMainMemID);
  ListPtr ProgramList  = GetObjectPtr(listMainProgID);
  UInt NumOfConst;
  UInt NumOfProgram;

  NumOfConst   = DmNumRecords(gConstantDB);
  NumOfProgram = DmNumRecords(gProgramDB);

  LstSetListChoices(SciConstList, NULL, NumOfConst);
  LstSetListChoices(MemoryList,   NULL, kNumMemory + 1);
  LstSetListChoices(ProgramList,  NULL, NumOfProgram);

  LstSetDrawFunction(SciConstList, ConstListDrawFunc);
  LstSetDrawFunction(MemoryList,   MemoryListDrawFunc);
  LstSetDrawFunction(ProgramList,  ProgramListDrawFunc);

  LstSetHeight(SciConstList, NumOfConst >= 12 ? 12 : NumOfConst);
  LstSetHeight(MemoryList,   kNumMemory + 1);
  LstSetHeight(ProgramList,  NumOfProgram >= 12 ? 12 : NumOfProgram);
}


static void DisplayAnswer(void)
{
  Char s[32];

  GetAnswerFormat(g.Memory[0], s, g.DisplayDigit, g.DispAddFormat);
  SetFieldText(fieldMainAnswID, s);
  GetAnswerFormat(g.Memory[0], gLastAnsStr, g.InsertDigit, 0);
}


static void RunProgram(void)
{
  Int err;
  Char s[32];

  gProgramMode = true;
  CtlSetValue(GetObjectPtr(pushbtnMainProgModeID), 1);
  err = ExecProgram(&gProgCurrPos);

  if (err > 0) {
    /* program error */
    gProgramMode = false;
    CtlSetValue(GetObjectPtr(pushbtnMainProgModeID), 0);
    SndPlaySystemSound(sndError);
    PresetEditProgFormParam(gLastExecProgRec, false,
			    (SWord)(gProgCurrPos - gProgramStr));

    // display error code.
    GetEvalErrMessage(err, s);
    FrmCustomAlert(alertInformID, "PROGRAM ERROR:", s, "");
    FrmPopupForm(formEditProgID);

  } else if (err == 0) {
    /* successful return. */
    gProgramMode = false;
    CtlSetValue(GetObjectPtr(pushbtnMainProgModeID), 0);
  }

}


static void DrawAnswerRoundBox(void)
{
  FormPtr form = FrmGetActiveForm();

  FrmGetObjectBounds(form, FrmGetObjectIndex(form, fieldMainAnswID),
		     &gRectAnswerField);
  gRectAnswerField.topLeft.x = 1;
  gRectAnswerField.topLeft.y -= 2;
  gRectAnswerField.extent.x  = 158;
  gRectAnswerField.extent.y  += 1;
  WinDrawRectangleFrame(roundFrame, &gRectAnswerField);
}
