//* *****************************************************************
//*                                                                  *
//Currency Spreadv2.0a For MS Visual C++ 6.0                         *
//********************************************************************/
// By Salim Meghani.BSc(Hons) (c) 2006 
// User documentation: Use = for formulas, $ for text
// Cursor keys or scroll bars to move to cells.
// Enter numbers (accuracy to 2 dp only).
// Cell references are entered using lowercase column
// and a numeric row value.
// Scientific notation isn't supported.
/*
Attention : This version is on test with a Reverse Polish Parser
By Salim Meghani.
*/
// All debug assertion failures have been disabled.
// A note about calculations.
// The way I carry out formulae calculations are as follows:
// 1. First I breakdown a formula to its equivalent numeric formula (string).
// 2. Then, I pass the numeric formula to the reverse polish parser to
// evaluate it.
// An example.
// 1. Suppose a1=1, a2,=2, b1=a1+a2, c1=b1*2.
// Then when evaluating cells, the formula : b1=1+2
// c1=(1+2)*2
// is the breakdown in numerical form of the expression in c1.
// NB: How when I evaluate a formula within a formula, I insert brackets (you don't have to).
// recursive formulae will make the application crash!
// Numerics must also be bracketed, because --1 is an error and -(-1) is legal.
// So, --a1 with a negative in a1 won't work without adequate bracketing, i.e. -(-a1).

// Division by zero, crashes application.
// Mismatched brackets makes application crash.
// A note about the Reverse Polish Parser.
//
// It is on test and may not function properly.
// The evaluation of a formula is dependant on size of certain arrays:
// These are:
/*
char texas[65536]; This is the texas line stack.
char calif[65536]; This is the California string
char* newyk=NULL; This is the New York line.
char stak[65536]; 
char star[65536];
char previous;
char number[50];
char bop;
double num[10000];
*/
//
#define _CRT_SECURE_NO_DEPRECATE 1
#include "stdafx.h"
#include "resource.h"
#include <malloc.h>
#include <string.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <crtdbg.h>
//
#define MAX_LOADSTRING 100
#define ID_EDIT 1
#define DELIMETER 1
#define VARIABLE 2
#define NUMBER 3
// Declare Global Variables:
WNDPROC wpOrigEditProc;
HINSTANCE hInst; // current instance
TCHAR szTitle[MAX_LOADSTRING]; // The title bar text
TCHAR szWindowClass[MAX_LOADSTRING]; // The title bar text
static char szInputStr[2048];
static char szOutputStr[2048];
static char szBuffer[2048];
static char szName[256];
int input=0,
fcol=0,
frow=0,
scol=0,
srow=0,
ecol=0,
erow=0,
dcol=0,
drow=0,
column=1,
row=1,
fitem=0,
cval=0,
rval=0,
hredraw=1,
vredraw=1,
credraw=1,
dscroll=0,
scroll=0,
stxpos=1,
endxpos=8,
stypos=1,
endypos=24,
ctxpos=1,
ctypos=1,
curxpos=1,
curypos=1,
olrxpos=1,
olrypos=1,
cvalx=0,
cvaly=0,
c_input=0,
istat=0,
istat2=0,
fcat=1,left=0,right=0,up=0,down=0,
setcursor=0,
scrolled=0,
newscroll=0,
preced=1,
errflag=0;
char switc; // This is the switch
char texas[65536]; // This is the texas line's stack.
char calif[65536]; // This is the California string
char* newyk=NULL; // This is the New York line.
char star[65536]; // Used to hold a number.
char previous; // The previous operator
char bop;
double num[10000];
char frop;
double answer=0;
double ans=0;
double right1=0;
double left1=0;
double no=0;
char rgt;
char lft;
char blop;
char check;
char op;
char rop;
char lop;
int x=0;
int s=0;
int c=0;
int n=0;
int g=0;
int notrue=0;
int str=0;
int start=0;
int end=0;
int bracket=0;
int length=0;
double sol=0;
char* form3=NULL;
char* form2=NULL;
int error=0;
//
static short cxChar,cyChar,cxClient,cyClient, NUMLINES,NUMCHARS,NUMD,NUMCOLS,MAXROW;
// Define array pointers for numbers and strings and formulae.
// For example a cell is a pointer of type pointer of char.
// If you have an address for cell, and it belongs to an address of type char,
// then it is a two dimensional structure and can be traversed by setting the
// cells address in scell, the same goes for type and stype, numeruc and stype .
char* *cell;
char* *scell;
char *type;
char *stype;
double* *numeric;
double* *snumeric;
GLOBALHANDLE hGlobalMemory, hGlobalMemory2, hGlobalMemory3;
double value;
char fch=0;
char sch=0;
static short 
nVscrollPos=1,
nHscrollPos=1;
HWND hwndEdit;
// Foward declarations of functions included in this code module:
//int find_optr(int start, char *form);
void clr_str(char *source);
void current_column(char *fch, char *sch, int column);
//void find_brkt(int *error, char *form, int *start, int *end, int *brcket, int *length); 
void cleanup(char *output, char *input);
//void eval_brkt(int *error, char *form, int start, int end, int bracket, int length);
//double eval_form(int *error, char *form);
double expr_p(int *error,char *form);
int isbracket(char c);
int isstop(char c);
int isoperator(char c);
int moreops(char *c);
void parser(int *error, char *source, char *dest);
void evaluate_cell(int column, int row, char type, char *szBuffer);
void pushtexas(char switcd); // Pushes a character onto the stack.
void poptexas(int st1); // Pops a character off the stack.
void fpdel(void); // Deletes both a char from the stack and switch.
void putcalif(char switcd); // Puts a character on the California line.
void fperror(int *error); // If there is an unbalanced expression,
void pushstk(double num);
double popstk(void);
void find_brkt(int *error, char *form, int *start, int *end, int *bracket, int *length);
void eval_brkt(int *error, char *form, int start, int end, int bracket, int length);
void clr_str(char *source);
double eval_form(int *error, char *form);
double eval_parser(char* newyk, int *error);
void clear_vars(void);
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK EditSubclassProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK Input(HWND, UINT, WPARAM, LPARAM);
//
int APIENTRY WinMain(HINSTANCE hInstance,
	HINSTANCE hPrevInstance,
	LPSTR lpCmdLine,
	int nCmdShow)
{
	_CrtSetReportMode(_CRT_ASSERT,0);
//
	form2=(char *) calloc((unsigned long) 65535, sizeof(char));
// TODO: Place code here.
	MSG msg;
	HACCEL hAccelTable;
// Initialize global strings
	LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
	LoadString(hInstance, IDC_SALIMHELLO, szWindowClass, MAX_LOADSTRING);
	MyRegisterClass(hInstance);
// Perform application initialization:
	if (!InitInstance (hInstance, nCmdShow)) 
	{
		return FALSE;
	}
	hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_SALIMHELLO);
// Main message loop:
	while (GetMessage(&msg, NULL, 0, 0)) 
	{
		if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) 
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}
	return msg.wParam;
}
//
//
// FUNCTION: MyRegisterClass()
//
// PURPOSE: Registers the window class.
//
// COMMENTS:
//
// This function and its usage is only necessary if you want this code
// to be compatible with Win32 systems prior to the 'RegisterClassEx'
// function that was added to Windows 95. It is important to call this function
// so that the application will get 'well formed' small icons associated
// with it.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;
	wcex.cbSize = sizeof(WNDCLASSEX); 
	wcex.style = CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc = (WNDPROC)WndProc;
	wcex.cbClsExtra = 0;
	wcex.cbWndExtra = 0;
	wcex.hInstance = hInstance;
	wcex.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI_SALIMHELLO);
	wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
	wcex.lpszMenuName = (LPCSTR)IDC_SALIMHELLO;
	wcex.lpszClassName = szWindowClass;
	wcex.hIconSm = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);
	return RegisterClassEx(&wcex);
}
//
// FUNCTION: InitInstance(HANDLE, int)
//
// PURPOSE: Saves instance handle and creates main window
//
// COMMENTS:
//
// In this function, we save the instance handle in a global variable and
// create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
	HWND hWnd;
	hInst = hInstance; // Store instance handle in our global variable
	hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW | WM_VSCROLL| WM_HSCROLL,
	CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
	if (!hWnd)
	{
		return FALSE;
	}
	ShowWindow(hWnd, nCmdShow);
	UpdateWindow(hWnd);
	return TRUE;
}
//
// FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
//
// PURPOSE: Processes messages for the main window.
//
// WM_COMMAND - process the application menu
// WM_PAINT - Paint the main window
// WM_DESTROY - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	HDC hdc;
	PAINTSTRUCT ps;
	RECT lprect;
	static HWND hwndButtonP;
	static HWND hwndButtonX;
	short i,a;
	short scrlval;
//
	TEXTMETRIC tm;
	unsigned long pvalue=0;
	static HWND hwndEdit;
//
	switch (message) 
	{
		case WM_COMMAND:
			wmId = LOWORD(wParam); 
			wmEvent = HIWORD(wParam); 
			// Parse the menu selections:
			switch (wmId)
			{
				case IDM_ABOUT:
					DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);
					break;
				case IDM_EXIT:
					DestroyWindow(hWnd);
					break;
				case 0:
					if (wmEvent==BN_CLICKED)
					{
						SetFocus(hWnd);
						preced=1;
						InvalidateRect(hWnd, NULL, FALSE);
					}
					break; 
				case 1:
					if (wmEvent==BN_CLICKED)
					{
						SetFocus(hWnd);
						preced=0;
						InvalidateRect(hWnd, NULL, FALSE);
					}
				break; 
				default:
					return DefWindowProc(hWnd, message, wParam, lParam);
			}
			break;
			case WM_CREATE:
				hGlobalMemory=GlobalAlloc(0, 0x9F64L);
				hGlobalMemory2=GlobalAlloc(0, 0x9F64L);
				hGlobalMemory3=GlobalAlloc(0, 0x9F64L);
				cell=(char* *)GlobalLock (hGlobalMemory);
				scell=cell;
				numeric=(double* *)GlobalLock(hGlobalMemory2);
				snumeric=numeric;
				type=(char *)GlobalLock(hGlobalMemory3);
				stype=type;
				SetScrollRange(hWnd, SB_VERT, 1,100,FALSE);
				SetScrollPos(hWnd,SB_VERT, nVscrollPos,TRUE);
				SetScrollRange(hWnd, SB_HORZ, 1,100,FALSE);
				SetScrollPos(hWnd,SB_HORZ, nHscrollPos,TRUE);
//
// Set all the cells types to Empty.
				for(i=1;i<=100;i++)
					for(a=1;a<=100;a++)
					{
						int pvalue=0;
						pvalue=((i-1)*100)+a;
						type=stype+pvalue;
						*type='E';
					}
//
				cxChar=LOWORD(GetDialogBaseUnits());
				cyChar=HIWORD(GetDialogBaseUnits());
				hwndButtonP = CreateWindow(TEXT("button"), TEXT("Calc"), 
				WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
				cxChar,4,5*cxChar,7*cyChar/4,hWnd,(HMENU) 0, 
				((LPCREATESTRUCT) lParam)->hInstance, NULL);
//
				hwndButtonX = CreateWindow(TEXT("button"), TEXT("XCal"), 
				WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
				cxChar*6,4,5*cxChar,7*cyChar/4,hWnd,(HMENU) 1, 
				((LPCREATESTRUCT) lParam)->hInstance, NULL);
//
//
//
			break;
//
			case WM_PAINT:
				hdc=BeginPaint(hWnd, &ps);
				SelectObject(hdc,GetStockObject(SYSTEM_FIXED_FONT));
				current_column(&fch, &sch, column);
				if(column<=26)
				{
					sprintf(szBuffer,"%c ",fch);
					TextOut(hdc, cxChar*1, cyChar*3, szBuffer,2);
				}
				else
				{
					sprintf(szBuffer,"%c%c",fch,sch);
					TextOut(hdc, cxChar*1, cyChar*3, szBuffer,2);
				}
				sprintf(szBuffer,"%-3d ",row);
				if(column<=26)
					TextOut(hdc, cxChar*2, cyChar*3, szBuffer,4);
				else
					TextOut(hdc, cxChar*3, cyChar*3, szBuffer,4);
				sprintf(szBuffer,"                                                                                                                                                                                                                         ");
				TextOut(hdc,cxChar*6,cyChar*3,szBuffer,NUMCHARS-2);
				pvalue=((column-1)*100)+row;
				type=stype+pvalue;
				if (*type=='S' || *type=='F')
				{
					cell=scell+pvalue;
					sprintf(szBuffer, "%s", *cell);
					if ((strlen(szBuffer))<(NUMCHARS-10))
						NUMD=strlen(szBuffer);
					else
						NUMD=NUMCHARS-10;
					if (*type=='F')
						TextOut(hdc, cxChar*7, cyChar*3, szBuffer, NUMD-1);
					else
						TextOut(hdc, cxChar*7, cyChar*3, szBuffer, NUMD);
					if (NUMD==NUMCHARS-10)
						TextOut(hdc, cxChar*(NUMD+8), cyChar*3," OV",3);
				}
				if (*type=='N')
				{
						numeric=snumeric+pvalue;
						sprintf(szBuffer,"%f",*(*numeric));
						TextOut(hdc, cxChar*7, cyChar*3, szBuffer,strlen(szBuffer));
				}
//
				SetBkColor(hdc, RGB(200,200,200));
				SetTextColor(hdc, RGB(0,0,0));
				if (hredraw || vredraw || credraw || (dscroll==5))
					goto doitselect;
				else
				{
					cval=nHscrollPos;
					sprintf(szBuffer,"   ");
					TextOut(hdc,cxChar*1,cyChar*4,szBuffer,3);
					a=4;
//
// Display the column values.
					for(i=1;i<=NUMCOLS;i++)
					{
						sprintf(szBuffer,"      ");
						TextOut(hdc,cxChar*a,cyChar*4,szBuffer,6);
						a+=6;
						current_column(&fch, &sch, cval);
						sprintf(szBuffer,"%2c%c",fch,sch);
						TextOut(hdc,cxChar*a,cyChar*4, szBuffer,3);
						a+=3;
						cval++;
					}
					sprintf(szBuffer," ");
					TextOut(hdc,cxChar*a, cyChar*4, szBuffer,1);
					hredraw=0;
//
					cval=nVscrollPos;
// Display the row values.
					for (i=4;i<=NUMLINES;i++)
					{
						sprintf(szBuffer,"    ");
						TextOut(hdc,0, cyChar*(1+i),szBuffer,4);
						sprintf(szBuffer,"%3d",cval);
						TextOut(hdc,0,cyChar*(1+i),szBuffer,3);
						cval++;
					}
					vredraw=0;
					SetTextColor(hdc, RGB(0,0,0));
					SetBkColor(hdc, RGB(255,255,255));
					rval=nVscrollPos;
					cval=nHscrollPos;
// Display the contents of the Worksheet.
					for(i=1;i<=MAXROW;i++)
					{
						cvaly=cyChar*(4+i);
						for (a=1; a<=NUMCOLS;a++)
						{
							int pvalue=0;
							pvalue=((cval-1)*100)+rval;
							type=stype+pvalue;
							cvalx=5+((a-1)*9);
							evaluate_cell(cval,rval,(char)*type,szBuffer);
//
							TextOut(hdc, 0.25+cxChar*cvalx, cvaly,szBuffer,9);
							cval++;
						}
						cval=nHscrollPos;
						rval++;
					}
					{
						int pvalue=0;
						pvalue=((column-1)*100)+row;
						type=stype+pvalue;
						cvalx=5+((curxpos-1)*9);
						cvaly=cyChar*(4+curypos);
						SetTextColor(hdc, RGB(255,255,255));
						SetBkColor(hdc, RGB(0,0,200));
						evaluate_cell(column,row,(char)*type,szBuffer);
						TextOut(hdc, 0.25+cxChar*cvalx, cvaly, szBuffer,9);
						SetTextColor(hdc, RGB(0,0,0));
						SetBkColor(hdc, RGB(255,255,255));
						credraw=0;
					}
					goto finished;
				}
				doitselect :
				if (hredraw)
				{
					cval=nHscrollPos;
					sprintf(szBuffer,"   ");
					TextOut(hdc,cxChar*1,cyChar*4,szBuffer,3);
					a=4;
					for(i=1;i<=NUMCOLS;i++)
					{
						sprintf(szBuffer,"      ");
						TextOut(hdc,cxChar*a,cyChar*4,szBuffer,6);
						a+=6;
						current_column(&fch, &sch, cval);
						sprintf(szBuffer,"%2c%c",fch,sch);
						TextOut(hdc,cxChar*a,cyChar*4, szBuffer,3);
						a+=3;
						cval++;
					}
					sprintf(szBuffer," ");
					TextOut(hdc,cxChar*a, cyChar*4, szBuffer,1);
					hredraw=0;
				}
				if (vredraw)
				{
					cval=nVscrollPos;
					for (i=4;i<=NUMLINES;i++)
					{
						sprintf(szBuffer,"    ");
						TextOut(hdc,0, cyChar*(1+i),szBuffer,4);
						sprintf(szBuffer,"%3d",cval);
						TextOut(hdc,0,cyChar*(1+i),szBuffer,3);
						cval++;
					}
					vredraw=0;
				}
				if (dscroll==5)
				{
						SetTextColor(hdc, RGB(0,0,0));
						SetBkColor(hdc, RGB(255,255,255));
						rval=nVscrollPos;
						cval=nHscrollPos;
						for(i=1;i<=MAXROW;i++)
						{
							cvaly=cyChar*(4+i);
							for (a=1; a<=NUMCOLS;a++)
							{
								int pvalue=0;
								pvalue=((cval-1)*100)+rval;
								type=stype+pvalue;
								cvalx=5+((a-1)*9);
								evaluate_cell(cval,rval,(char)*type,szBuffer);
//
								TextOut(hdc, 0.25+cxChar*cvalx, cvaly,szBuffer,9);
								cval++;
							}
							cval=nHscrollPos;
							rval++;
						}
						dscroll=0;
				}
				if (credraw)
				{
					int pvalue=0;
					pvalue=((column-1)*100)+row;
					type=stype+pvalue;
					cvalx=5+((curxpos-1)*9);
					cvaly=cyChar*(4+curypos);
					SetTextColor(hdc, RGB(255,255,255));
					SetBkColor(hdc, RGB(0,0,200));
					evaluate_cell(column,row,(char)*type,szBuffer);
					TextOut(hdc, 0.25+cxChar*cvalx, cvaly, szBuffer,9);
					SetTextColor(hdc, RGB(0,0,0));
					SetBkColor(hdc, RGB(255,255,255));
					credraw=0;
				}
				finished:
				EndPaint(hWnd, &ps);
//
			break;
// Called when performing a scroll by a row.
			case WM_VSCROLL:
				hdc=GetDC(hWnd);
				SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));
				GetTextMetrics(hdc, &tm);
				cxChar=tm.tmAveCharWidth;
				cyChar=tm.tmHeight;
				scroll=0;
				switch (LOWORD(wParam))
				{
					case SB_LINEUP:
						ctypos--;
						if (ctypos<1)
							ctypos=1;
						else
						{
							if (ctypos<stypos)
							{
								endypos--;
								stypos--;
								curypos=1;
								nVscrollPos-=1;
								scroll=1;
								dscroll=1;
								vredraw=1;
							}
							else
								curypos--;
						}
						scrlval=+cyChar;
					break;
					case SB_LINEDOWN:
						ctypos++;
						if (ctypos>100)
							ctypos=100;
						else
						{
							if (ctypos>endypos)
							{
								endypos++;
								stypos++;
								curypos=MAXROW;
								nVscrollPos+=1;
								scroll=1;
								dscroll=2;
								vredraw=1;
							}
							else
								curypos++;
						}
						scrlval=-cyChar;
					break;
				}
				SetScrollPos(hWnd,SB_VERT,ctypos,TRUE);
				cvalx=5+((olrxpos-1)*9);
				cvaly=cyChar*(4+olrypos);
				SetTextColor(hdc, RGB(0,0,0));
				SetBkColor(hdc, RGB(255,255,255));
				{
					int pvalue=0;
					pvalue=((column-1)*100)+row;
					type=stype+pvalue;
					evaluate_cell(column,row,(char)*type,szBuffer);
					TextOut(hdc, 0.25+cxChar*cvalx, cvaly, szBuffer,9);
				}
				if (scroll)
				{
					dscroll=5;
				}
				ReleaseDC(hWnd,hdc);
				olrxpos=curxpos; olrypos=curypos;
				credraw=1;
				hredraw=1;
//
				row=ctypos; column=ctxpos;
				InvalidateRect(hWnd, NULL, FALSE);
			break;
// Called when performing a scroll by a column.
			case WM_HSCROLL:
				hdc=GetDC(hWnd);
				SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));
				GetTextMetrics(hdc, &tm);
				cxChar=tm.tmAveCharWidth;
				cyChar=tm.tmHeight;
				scroll=0;
				switch (LOWORD(wParam))
				{
					case SB_LINEUP:
						ctxpos--;
						if (ctxpos<1)
							ctxpos=1;
						else
						{
							if (ctxpos<stxpos)
							{
								endxpos--;
								stxpos--;
								curxpos=1;
								nHscrollPos-=1;
								scroll=1;
								dscroll=3;
								hredraw=1;
							}
							else
								curxpos--;
						}
						scrlval=+(0.25+cxChar*9);
					break;
					case SB_LINEDOWN:
						ctxpos++;
						if (ctxpos>100)
							ctxpos=100;
						else
						{
							if (ctxpos>endxpos)
							{
								endxpos++;
								stxpos++;
								curxpos=NUMCOLS;
								nHscrollPos+=1;
								scroll=1;
								dscroll=4;
								hredraw=1;
							}
							else
								curxpos++;
						}
						scrlval=-(0.25+cxChar*9);
					break;
				}
				SetScrollPos(hWnd,SB_HORZ,ctxpos,TRUE);
				cvalx=5+((olrxpos-1)*9);
				cvaly=cyChar*(4+olrypos);
				SetTextColor(hdc, RGB(0,0,0));
				SetBkColor(hdc, RGB(255,255,255));
				{
					int pvalue=0;
					pvalue=((column-1)*100)+row;
					type=stype+pvalue;
					evaluate_cell(column,row,(char)*type,szBuffer);
					TextOut(hdc, 0.25+cxChar*cvalx, cvaly, szBuffer,9);
				}
				if (scroll)
				{
					dscroll=5;
				}
				ReleaseDC(hWnd,hdc);
				olrxpos=curxpos; olrypos=curypos;
				credraw=1;
				vredraw=1;
//
				row=ctypos; column=ctxpos;
				InvalidateRect(hWnd, NULL, FALSE);
			break;
// Translate cursor keys to scroll messages.
			case WM_KEYDOWN:
				switch(wParam)
				{
					case VK_UP:
						SendMessage(hWnd, WM_VSCROLL, SB_LINEUP,0L);
					break;
					case VK_DOWN:
						SendMessage(hWnd, WM_VSCROLL, SB_LINEDOWN,0L);
					break;
					case VK_LEFT:
						SendMessage(hWnd, WM_HSCROLL, SB_LINEUP,0L);
					break;
					case VK_RIGHT:
						SendMessage(hWnd, WM_HSCROLL, SB_LINEDOWN,0L);
					break;
				}
			break;
// Called when resizing the worksheet.
			case WM_SIZE:
				cxClient=LOWORD(lParam);
				cyClient=HIWORD(lParam);
				hdc=GetDC(hWnd);
				SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));
				GetTextMetrics(hdc, &tm);
				cxChar=tm.tmAveCharWidth;
				cyChar=tm.tmHeight;
				ReleaseDC(hWnd,hdc);
				NUMCHARS=(cxClient/cxChar);
				NUMCHARS=(NUMCHARS/9)*8;
				NUMLINES=(cyClient/cyChar)-2;
				NUMCOLS=NUMCHARS/9;
				MAXROW=NUMLINES-3;
				column=1;
				row=1;
				cval=0;
				rval=0;
				hredraw=1;
				vredraw=1;
				credraw=1;
				stxpos=1;
				endxpos=NUMCOLS;
				stypos=1;
				endypos=MAXROW;
				ctxpos=1;
				ctypos=1;
				curxpos=1;
				curypos=1;
				olrxpos=1;
				olrypos=1;
				cvalx=0;
				c_input=0;
				scroll=0;
				dscroll=5;
				cvaly=0;
				nVscrollPos=1;
				nHscrollPos=1;
				scrlval=0;
				SetScrollPos(hWnd, SB_VERT, nVscrollPos, TRUE);
				SetScrollPos(hWnd, SB_HORZ, nHscrollPos, TRUE);
			break;
// Called when inputting information.
			case WM_CHAR:
				if ((wParam!='\r') && (istat==0))
				{
					szInputStr[0]=wParam;
					szInputStr[1]='\0';
//
//
					input=1;
					istat=1;
					credraw=1;
					DialogBox(hInst, (LPCTSTR)IDD_DIALOG1, hWnd, (DLGPROC)Input);
					cleanup(szBuffer,szOutputStr);
				}
				setcursor=0;
				if (istat==1)
				{
					c_input=strlen(szBuffer);
					istat=0;
					switch(szBuffer[0])
					{
						case '=':
						case '$':
						{
							int pvalue=0;
							pvalue=((column-1)*100)+row;
							cell=scell+pvalue;
							*cell=(char *) calloc((unsigned long) strlen(szBuffer)+4, sizeof(char));
							strncpy(*cell, szBuffer+1, strlen(szBuffer));
							if (szBuffer[0]=='=') strcat(*cell, "#");
							type=stype+pvalue;
							if (szBuffer[0]=='$')
								*type='S';
							else
								*type='F';
							dscroll=5;
//
							break;
						}
						case '0':
						case '1':
						case '2':
						case '3':
						case '4':
						case '5':
						case '6':
						case '7':
						case '8':
						case '9':
						case '-':
						case '+':
						case '.':
						case '(':
						{
							int pvalue=0;
							pvalue=((column-1)*100)+row;
							if (moreops(szBuffer))
							{
								cell=scell+pvalue;
								*cell=(char *) calloc((unsigned long) strlen(szBuffer)+4, sizeof(char));
								strncpy(*cell, szBuffer+0, strlen(szBuffer));
								strcat(*cell, "#");
								type=stype+pvalue;
								*type='F';
							}
							else
							{
								numeric=snumeric+pvalue;
								*numeric=(double *) calloc ((unsigned long) 1, sizeof(double));
								*(*numeric)=atof(szBuffer);
								type=stype+pvalue;
								*type='N';
							}
//
							dscroll=5;
							break;
						}
//
					}
//
					if (up) { up=0; SendMessage(hWnd, WM_VSCROLL, SB_LINEUP,0L);}
					if (down) { down=0; SendMessage(hWnd, WM_VSCROLL, SB_LINEDOWN,0L);}
					if (left) {left=0; SendMessage(hWnd, WM_HSCROLL, SB_LINEUP,0L);}
					if (right) {right=0; SendMessage(hWnd, WM_HSCROLL, SB_LINEDOWN,0L);}
				}
//
				dscroll=5;
				credraw=1;
				InvalidateRect(hWnd, NULL, FALSE);
			break;
			case WM_DESTROY:
				GlobalUnlock(hGlobalMemory);
				GlobalFree(hGlobalMemory);
				GlobalUnlock(hGlobalMemory2);
				GlobalFree(hGlobalMemory2);
				GlobalUnlock(hGlobalMemory3);
				GlobalFree(hGlobalMemory3);
				PostQuitMessage(0);
			break;
			default:
				return DefWindowProc(hWnd, message, wParam, lParam);
		}
	return 0;
}
// Mesage handler for about box.
LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
		case WM_INITDIALOG:
			return TRUE;
		case WM_COMMAND:
			if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) 
			{
				EndDialog(hDlg, LOWORD(wParam));
				return TRUE;
			}
		break;
	}
	return FALSE;
}

LRESULT CALLBACK EditSubclassProc(HWND hwnd, UINT UMsg, WPARAM wParam, LPARAM lParam)
{
	if (UMsg==WM_GETDLGCODE)
	{
		return DLGC_WANTALLKEYS;
	}
	if (UMsg==WM_KEYDOWN)
	{
		if (wParam==VK_UP)
		{
			UMsg=WM_CHAR;
			setcursor=2;
			scrolled=0;
			wParam='';
		}
	}
	if (UMsg==WM_KEYDOWN)
	{
		if (wParam==VK_DOWN)
		{
			UMsg=WM_CHAR;
			setcursor=3;
			wParam='';
			scrolled=0;
		}
	}
	if (UMsg==WM_KEYDOWN)
	{
		if (wParam==VK_LEFT)
		{
			UMsg=WM_CHAR;
			setcursor=4;
			wParam='';
			scrolled=0;
		}
	}
	if (UMsg==WM_KEYDOWN)
	{
		if (wParam==VK_RIGHT && scrolled)
		{
			UMsg=WM_CHAR;
			setcursor=5;
			scrolled=0;
			wParam='';
		}
	}
//
//
	if (UMsg==WM_CHAR)
	{ 
		if (wParam=='\r')
		{
			UMsg=WM_CHAR;
			setcursor=1;
			wParam='';
			scrolled=0;
		}
	}
	return CallWindowProc(wpOrigEditProc, hwnd, UMsg, wParam, lParam);
}
//
//
LRESULT CALLBACK Input(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
		case WM_INITDIALOG:
			hwndEdit=GetDlgItem(hDlg,EDITTAB);
			wpOrigEditProc= (WNDPROC) SetWindowLong(hwndEdit, GWL_WNDPROC, (LONG) EditSubclassProc); 
			SendMessage(hwndEdit,WM_CLEAR,0,0);
			SendMessage(hwndEdit, EM_REPLACESEL,FALSE,(LPARAM) szInputStr);
			newscroll=1;
			scrolled=1; 
			return TRUE;
		break;
	}
	if (setcursor>=1)
	{
		if (setcursor==2) up=1;
		if (setcursor==3) down=1;
		if (setcursor==4) left=1;
		if (setcursor==5) right=1;
		input=0;
		setcursor=0;
		GetDlgItemText(hDlg, EDITTAB, szOutputStr, 2047);
		EndDialog(hDlg,0);
	}
//
	return FALSE;
}
//
// Workout current column lettering dependant on integer column.
void current_column(char *fch, char *sch, int column)
{
	div_t x;
	if (column<=26)
	{
		*fch=96+column;
		*sch=32;
	}
	else
	{
		x=div(column,26);
		if(x.rem)
		{
			*sch=x.rem+96;
			*fch=x.quot+96;
		}
		else
		{
			*sch=x.rem+122;
			*fch=(x.quot-1)+96;
		}
	}
}
void cleanup(char *output, char *input)
{
	int loop=0, yloop=0;
	for (loop=0;loop<=strlen(input);loop++)
	{
		if (input[loop]!='')
		{
			output[yloop]=input[loop];
			yloop++;
		}
	}
}

void clr_str(char *source)
{
	int loop;
	for (loop=0; loop<=sizeof(source);loop++)
	source[loop]=' ';
}

int find_optr(int start, char *form)
{
	while(form[start]!='*' && form[start]!='+' && form[start]!='/' && form[start]!='-' && form[start]!='#' && form[start]!=')')
		start++;
	return(start);
}
// Convert a formula to its raw native numeric form (including brackets).
double expr_p(int *error, char *form)
{
	char *source=NULL,
	*dest=NULL;
	int start=0,
	end=0,
	bracket=0,
	length=0;
	double answer=0;
	source=(char *) calloc((unsigned long) 32767, sizeof(char));
	dest=(char *) calloc((unsigned long) 32767, sizeof(char));
	strcpy(source, form);
	while ((fcat)&&(*error==0))
	{
		fcat=0;
		parser(error, source,dest);
		strcpy(source,dest);
		strcat(source,"#");
		free(dest);
		dest=(char *) calloc((unsigned long) 32767, sizeof(char));
	}
	fcat=1;
//
	if ((!*error) && (preced==1))
	{
		newyk=(char *) calloc((unsigned long) 32767, sizeof(char));
		length=strlen(source)-1;
		source[length]='\0';
		clr_str(newyk);
		length=0;
		strcpy(newyk,"%");
		strcat(newyk,source);
		strcat(newyk,"%\0");
		ans=eval_parser(newyk, error);
		free(newyk);
		clear_vars();
	}
	else
		ans=0;
	free(source);
	free(dest);
	if (*error==0)
		return (ans);
	else
		return (0);
}
int isbracket(char c)
{
	if (c=='('||c==')') return 1; else return 0;
}
//
int isstop(char c)
{
	if (c=='.') return 1; else return 0;
}
int isoperator(char c)
{
	switch(c)
	{
		case '*':return 1;
		case '+':return 1;
		case '-':return 1;
		case '/':return 1;
		default : return 0;
	}
}
int moreops(char *c)
{
	int loop=0;
	for (loop=0;loop<=strlen(c);loop++)
	if ((loop>0) && (c[loop]=='*' || c[loop]=='/' || c[loop]=='+' || c[loop]=='-' || c[loop]=='(' || c[loop]==')' || c[loop]=='a' 
|| c[loop]=='b' || c[loop]=='c' || c[loop]=='d' || c[loop]=='e' || c[loop]=='f' || c[loop]=='g' || c[loop]=='h'
|| c[loop]=='i' || c[loop]=='j' || c[loop]=='k' || c[loop]=='l' || c[loop]=='m' || c[loop]=='n' || c[loop]=='o'
|| c[loop]=='p' || c[loop]=='q' || c[loop]=='r' || c[loop]=='s' || c[loop]=='t' || c[loop]=='u' || c[loop]=='v'
|| c[loop]=='w' || c[loop]=='x' || c[loop]=='y' || c[loop]=='z')) return(1);

	return(0);
}
// Does the conversion from formula to a string of numbers.
void parser(int *error, char *source, char *dest)
{
	int loop=0;
	int dloop=0;
	int crloop=0;
	int alloop=0;
	int locx=0;
	int locy=0;
	int pvalue=0;
	char cref[20];
	char alpp[20];
	char num[20];
	while (source[loop]!='#')
	{
		while(isdigit(source[loop]) || isstop(source[loop]) || isoperator(source[loop]) || isbracket(source[loop]))
		{
			dest[dloop]=source[loop];
			dloop++;
			loop++;
		}
		if (source[loop]=='#') break;
		if (isalpha(source[loop]))
		{
			crloop=0;
			do
			{
				cref[crloop]=source[loop];
				crloop++; loop++;
			}	
			while (isdigit(source[loop])||isalpha(source[loop]));
		}
		crloop=0;
		alloop=0;
		while(isalpha(cref[crloop]))
		{
			alpp[alloop]=cref[crloop];
			crloop++;
			alloop++;
		}
		if (alloop==1)
			locx=alpp[0]-96;
		else
			if (alloop==2)
				locx=((alpp[0]-96)*26)+alpp[1]-96;
			else
			{
				locx=0;
				*error=1;
				return;
			}
		alloop=0;
		clr_str(alpp);
		while (isdigit(cref[crloop]))
		{
			alpp[alloop]=cref[crloop];
			crloop++;
			alloop++;
		}
		locy=atoi(alpp);
		clr_str(alpp);
		clr_str(cref);
		if ((locx<1)||(locx>100))
		{
			*error=1;
			return;
		}
		if ((locy<1)||(locy>100))
		{
			*error=1;
			return;
		}
		pvalue=((locx-1)*100)+locy;
		type=stype+pvalue;
		if (*type=='N')
		{
			dest[dloop]='(';
			dloop+=1;
			numeric=snumeric+pvalue;
			sprintf(num, "%f",*(*numeric));
			strcat(dest,num);
			dloop+=strlen(num);
			dest[dloop]=')';
			dloop+=1;
		}
		if (*type=='F')
		{
			dest[dloop]='(';
			dloop+=1;
			cell=scell+pvalue;
			strncat(dest,*cell,(strlen(*cell)-1));
			dloop+=strlen(*cell)-1;
			dest[dloop]=')';
			dloop+=1;
			fcat=1;
		}
		if (*type=='E'|| *type=='S')
		{
			*error=1;
			return;
		}
	}
}
// Evaluate and store contents of a cell in szBuffer.
void evaluate_cell(int column, int row, char type, char *szBuffer)
{
	errflag=0;
	switch(type)
	{
		case 'S':
		{
			int pvalue=0;
			pvalue=((column-1)*100)+row;
			cell=scell+pvalue;
			sprintf(szBuffer, "%9s", *cell);
			break;
		}
		case 'N':
		{
			int pvalue=0;
			pvalue=((column-1)*100)+row;
			numeric=snumeric+pvalue;
			sprintf(szBuffer, "%9.2f",*(*numeric));
			break;
		}
		case 'F':
		{
			int pvalue=0;
			pvalue=((column-1)*100)+row;
			cell=scell+pvalue;
			value=expr_p(&errflag, *cell);
			if (!errflag)
				sprintf(szBuffer, "%9.2f",value);
			else
			{
				sprintf(szBuffer, "%s"," Error   ");
			}
			break;
		}
		default:
			sprintf(szBuffer,"            ");
			break;
	}
}
//
double eval_parser(char* newyk, int *error)
{ 
	double answer=0;
	switc=newyk[x];
	if ((switc=='%') && (x==0)) // In the beginning we push the % terminator
// onto the texas stack
	{
		previous=switc;
		pushtexas(switc);
	}
	if ((previous=='%') && (((switc>='0') && (switc<='9')) || switc=='.'))
	{
		calif[c]='P';
		c++;
		calif[c]=',';
		c++;
	}
	else if (switc=='-')
	{
		calif[c]='M';
		c++;
		calif[c]=',';
		c++;
		x++;
		switc=newyk[x];
	}
	else if (switc=='+')
	{
		calif[c]='P';
		c++;
		calif[c]=',';
		c++;
		x++;
		switc=newyk[x];
	}
	do
	{
		if ((previous=='+' || previous=='-' || previous=='/' || previous=='*' || previous=='(') && (switc=='+' || switc=='-'))
		{
			if (switc=='+') 
			{
				previous='u';
				x++;
				switc=newyk[x];
			}
			else 
			{ 
				previous='u';
				putcalif('M');
				calif[c]=',';
				c++;
			}
			goto done;
		}
		else
		if (switc=='(')
		{
			calif[c]=switc;
			c++;
		}
		while (((switc>='0') && (switc<='9')) || switc=='.')
		{ 
			previous=switc;
			putcalif(switc);
			notrue=1;
		}
		if (notrue==1) goto done;
		if (switc=='M' || switc=='P')
		{ 
			putcalif(switc);
			goto done;
		}
		if ((switc=='%' || switc=='+' || switc=='-' || switc==')') && (texas[s-1]=='+' || texas[s-1]=='*' || texas[s-1]=='-' || texas[s-1]=='/'))
		{ 
			poptexas(s-1);
			goto done;
		}
		if ((switc=='*' || switc=='/') && (texas[s-1]=='*' || texas[s-1]=='/'))
		{ 
			poptexas(s-1);
			goto done;
		} 
		if (switc=='%' && texas[s-1]=='(')
		{
			fperror(error);
			break;
			goto done;
		}
		if ((switc=='+' || switc=='-' || switc=='*' || switc=='/' || switc=='(') && (texas[s-1]=='(' || texas[s-1]=='%'))
		{ 
			previous=switc;
			pushtexas(switc);
			goto done;
		}
		if ((switc=='*' || switc=='/') && (texas[s-1]=='+' || texas[s-1]=='-'))
		{ 
			previous=switc;
			pushtexas(switc);
			goto done;
		}
		if ((switc=='(') && (texas[s-1]=='+' || texas[s-1]=='*' || texas[s-1]=='-' || texas[s-1]=='/' || texas[s-1]=='('))
		{
			previous=switc;
			pushtexas(switc);
			goto done;
		}
		if (switc==')' && texas[s-1]=='%')
		{
			fperror(error);
			break;
			goto done;
		}
		if (switc==')' && texas[s-1]=='(')
		{ 
			calif[c]=switc;
			c++;
			calif[c]=',';
			c++;
			fpdel();
			goto done;
		}
		done:;
		if (notrue==1)
		{
			calif[c]=',';
			c++;
			notrue=0;
		}
		if (switc=='%' && texas[s-1]=='%')
		{ 
			putcalif('%');
			break;
		}
	}
	while (!(switc=='%' && texas[s-1]=='%'));
//
	form3=(char *) calloc ((unsigned long)32767, sizeof(char));
	find_brkt(error, calif,&start,&end,&bracket,&length);
//
	if ((bracket) && (!*error))
	{
		eval_brkt(error, calif, start, end, bracket, length);
	}
	else
	if (!*error)
	{
		strcpy(form3, calif);
	}
	if (!*error)
	{
		answer=eval_form(error, form3);
	}
//
	free(form3);
//
	return answer;
}
//
void pushtexas(char switcd)
{
	texas[s]=switcd;
	s++;
	x++;
	switc=newyk[x];
}

void poptexas(int st1)
{
	calif[c]=texas[st1];
	s--;
	c=c+1;
	calif[c]=',';
	c++;
//
}
//
void fpdel(void)
{
	x++;
	switc=newyk[x];
	s--;
}

void putcalif(char switcd)
{
	calif[c]=switcd;
	c++;
	x++;
	switc=newyk[x];
}

void fperror(int *error)
{
	*error=1;
}
void pushstk(double nu)
{
	num[s]=nu;
	s++;
}
double popstk()
{
	s--;
	return(num[s]);
}
//
void find_brkt(int *error, char *form, int *start, int *end, int *bracket, int *length)
{
	int loop,
	loop2,
	rgtbrac=0;
	loop=loop2=0;
	*length=strlen(form);
	*bracket=0;
	for (loop=0; loop<=*length; loop++)
	{
		if (form[loop]=='(')
		{
			*bracket=1;
			*start=loop;
		}
		if (form[loop]==')')
			rgtbrac=1;
//
	}
	if (*bracket)
	{
		loop2=*start+1;
		*bracket=0;
		do
		{
			if (form[loop2]==')')
			{
				*end=loop2;
				*bracket=1;
			}
			loop2++;
		}
		while ((!*bracket) && (form[loop2]!='%'));
	}
//
}
void eval_brkt(int *error, char *form, int start, int end, int bracket, int length)
{ 
	int loop=0,size=0;
	char *ecopy=NULL;
	char *nbrac=NULL;
	char *number=NULL;
//
	double answer=0;
	strcpy(form3,form);
	while ((bracket) && (!*error))
	{
		nbrac=(char *) calloc ((unsigned long)32767, sizeof(char));
		number=(char * ) calloc((unsigned long) 100, sizeof(char));
		ecopy=(char *) calloc((unsigned long)32767,sizeof(char));
//
		size=((end)-(start+1))+2;
		clr_str(nbrac);
		strncpy(nbrac, form3+(start+1), ((end)-(start+1)));
		nbrac[end-start+1]='%';
		answer = eval_form(error,nbrac);
//
		if (answer<0)
		{
			strcpy(nbrac, "M,");
			sprintf(number, "%-5.3f", -answer);
			strcat(nbrac,number);
		}	
		else
		{
			sprintf(number, "%-5.3f", answer);
			clr_str(nbrac);
			strcpy(nbrac, number);
		} 
		if (start !=0)
		{
			strncpy(ecopy, form3+0, start);
		}
		strcat(ecopy, nbrac);
		loop=end+1;
		strcat(ecopy, form3+loop);
		clr_str(form3);
		strcpy(form3, ecopy);
		strcat(form3, ",");
		free(ecopy);
		free(number);
		free(nbrac);
		if (bracket)
			find_brkt(error, form3, &start, &end, &bracket, &length);
	}
}

double eval_form(int *error, char *form)
{
	double answer =0,
	no1=0,
	no2=0;
	int loop=0,
	nega=0,
	opvalid=0,
	place=0;
	c=0; s=0;
	while(form[c]!='%')
	{
		int fm=0;
		if (form[c]=='P') c++;
		if (form[c]==',') c++;
		while(form[c]==' ') { c++; }
//
		if (form[c]=='%') break;
		while((form[c]>='0') && (form[c]<='9') || form[c]==' ' || form[c]=='M' || form[c]=='.')
		{
			while (form[c]=='M')
			{
				if (fm==1) fm=0; else fm=1;
				c++;
				if (form[c]==',') c++;
			}
			str=0;
			clr_str(star);
			while ((form[c]>='0') && (form[c]<='9') || (form[c]=='.'))
			{
				star[str]=form[c];
				str++;
				c++;
				notrue=1;
			}
			if (notrue==1)
			{
				if (fm!=1)
				{
					pushstk(atof(star));
				}
				else
				{
					pushstk(-atof(star));
					fm=0;
				}		
				notrue=0;
			}
			c++;
		}
//
		while(form[c]==0) {c++;}
		if (form[c]=='%') break;
		right1=popstk();
		left1=popstk();
		if (form[c]=='+')
			answer=left1+right1;
		else
			if (form[c]=='-')
				answer=left1-right1;
			else
				if (form[c]=='*')
					answer=left1*right1;
				else
					if (form[c]=='/')
						if (right1==0) {
							*error=1;
							answer=0;}
						else
							answer=left1/right1;
		pushstk(answer);
		c++;
	}
//
	answer=popstk();
	return(answer);
}
//
void clear_vars(void)
{
	clr_str(calif);
	clr_str(texas);
//clr_str(form3);
//clr_str(newyk);
	clr_str(star);
	switc=NULL; // This is the switch
	previous=NULL;
	bop=NULL;
	frop=NULL;
	right1=NULL;
	left1=NULL;
	no=0;
	rgt=NULL;
	lft=NULL;
	blop=NULL;
	check=NULL;
	op=NULL;
	rop=NULL;
	lop=NULL;
	x=0;
	s=0;
	c=0;
	n=0;
	g=0;
	notrue=0;
	str=0;
	start=0;
	end=0;
	bracket=0;
	length=0;
	sol=0;
}

// 