Click here to Skip to main content
15,881,898 members
Articles / Desktop Programming / ATL

Visual Calc v3.0 - A new dimension for the desktop calculator

Rate me:
Please Sign up or sign in to vote.
3.62/5 (113 votes)
28 Apr 2006CPOL22 min read 348.8K   6.8K   104  
How to start programming a parser.
/* VisualCalcDlg.cpp
 *
 * Copyright (c) 2004-2006 by toxcct. All rights reserved.
 * Consult your license regarding permissions and restrictions.
 */

// VisualCalcDlg.cpp : implementation file
//

#include "stdafx.h"
#include "VisualCalc.h"
#include "VisualCalcDlg.h"


#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif


// Default constructor
CVisualCalcDlg::CVisualCalcDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CVisualCalcDlg::IDD, pParent),
	  m_strVisualCalcVersion ("3.0") {
	//{{AFX_DATA_INIT(CVisualCalcDlg)
	//}}AFX_DATA_INIT
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}


// For exchanges between controls and data members
void CVisualCalcDlg::DoDataExchange(CDataExchange* pDX) {
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CVisualCalcDlg)
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CVisualCalcDlg, CDialog)
	//{{AFX_MSG_MAP(CVisualCalcDlg)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_CALC_BTN,			OnCalculate)
	ON_BN_CLICKED(IDC_ABOUT_BTN,		OnAbout)
	ON_BN_CLICKED(IDC_HELP_BTN,			OnHelp)
	ON_BN_CLICKED(IDC_RESETANS_BTN,		OnResetAns)
	ON_BN_CLICKED(IDC_RESETVARS_BTN,	OnResetVars)
	ON_BN_CLICKED(IDC_RESETFUNCS_BTN,	OnResetFuncs)
	ON_LBN_DBLCLK(IDC_ANSLIST_LBOX,		OnDblclkAnsList)
	ON_LBN_DBLCLK(IDC_FORMULAS_LBOX,	OnDblclkFormulasList)
	ON_LBN_DBLCLK(IDC_VARNAMES_LBOX,	OnDblclkVarList)
	ON_LBN_DBLCLK(IDC_VARVALS_LBOX,		OnDblclkVarValuesList)
	ON_LBN_DBLCLK(IDC_FUNCLIST_LBOX,	OnDblclkFuncList)
	ON_BN_CLICKED(IDC_ANSLISTSTATE_CHK, OnChangeAnswersListState)
	ON_BN_CLICKED(IDC_VARLISTSTATE_CHK, OnChangeVariablesListState)
	ON_EN_CHANGE(IDC_INPUT_EDIT,		OnChangeInputEdit)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()


// Manages the system Menu
void CVisualCalcDlg::OnSysCommand(UINT nID, LPARAM lParam) {
	if ((nID & 0xFFF0) == IDM_ABOUTBOX) {
		OnAbout();
	}
	else if ((nID & 0xFFF0) == IDM_HELPBOX) {
		OnHelp();
	}
	else {
		CDialog::OnSysCommand(nID, lParam);
	}
}


// Draws the system icon of the dialog
void CVisualCalcDlg::OnPaint() {
	if (IsIconic()) {
		CPaintDC dc(this);
		SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		dc.DrawIcon(x, y, m_hIcon);
	}
	else {
		CDialog::OnPaint();
	}
}


// OnQueryDragIcon()
HCURSOR CVisualCalcDlg::OnQueryDragIcon() {
	return (HCURSOR) m_hIcon;
}


/////////////////////////////////////////////////////////////////////////////
// CVisualCalcDlg message handlers
BOOL CVisualCalcDlg::OnInitDialog() {
	CDialog::OnInitDialog();

	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);
	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL) {
		CString strAboutMenu, strHelpMenu;
		strAboutMenu.Format(IDS_ABOUTBOX, m_strVisualCalcVersion);
		strHelpMenu.LoadString(IDS_HELPBOX);
		if ((!strAboutMenu.IsEmpty()) && (!strHelpMenu.IsEmpty())) {
			pSysMenu->AppendMenu(MF_SEPARATOR);
		}
		if ((!strAboutMenu.IsEmpty()) && (!strHelpMenu.IsEmpty())) {
			pSysMenu->AppendMenu(MF_STRING, IDM_HELPBOX, strHelpMenu);
		}
		if (!strAboutMenu.IsEmpty()) {
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}
	SetIcon(m_hIcon, TRUE);		// Set big icon
	SetIcon(m_hIcon, FALSE);	// Set small icon

	// Initializes the Controls' pointers...
	m_peInput				= (CEdit*)		GetDlgItem(IDC_INPUT_EDIT);
	m_peOutput				= (CEdit*)		GetDlgItem(IDC_OUTPUT_EDIT);
	m_plAnswers				= (CListBox*)	GetDlgItem(IDC_ANSLIST_LBOX);
	m_plFormulas			= (CListBox*)	GetDlgItem(IDC_FORMULAS_LBOX);
	m_plVariables			= (CListBox*)	GetDlgItem(IDC_VARNAMES_LBOX);
	m_plVarValues			= (CListBox*)	GetDlgItem(IDC_VARVALS_LBOX);
	m_plFunctions			= (CListBox*)	GetDlgItem(IDC_FUNCLIST_LBOX);
	m_pbResetAnswers		= (CButton*)	GetDlgItem(IDC_RESETANS_BTN);
	m_pbResetVariables		= (CButton*)	GetDlgItem(IDC_RESETVARS_BTN);
	m_pbResetFunctions		= (CButton*)	GetDlgItem(IDC_RESETFUNCS_BTN);
	m_pbAnswersListState	= (CButton*)	GetDlgItem(IDC_ANSLISTSTATE_CHK);
	m_pbVariablesListState	= (CButton*)	GetDlgItem(IDC_VARLISTSTATE_CHK);
	m_pbCalculate			= (CButton*)	GetDlgItem(IDC_CALC_BTN);
	m_pbAbout				= (CButton*)	GetDlgItem(IDC_ABOUT_BTN);
	m_pbHelp				= (CButton*)	GetDlgItem(IDC_HELP_BTN);
	m_psAnswers				= (CStatic*)	GetDlgItem(IDC_ANSWERS_STATIC);
	m_psVariables			= (CStatic*)	GetDlgItem(IDC_VARIABLES_STATIC);
	m_psFunctions			= (CStatic*)	GetDlgItem(IDC_FUNCTIONS_STATIC);
	m_psVisualCalc			= (CStatic*)	GetDlgItem(IDC_VISUALCALC_STATIC);
	m_psHowToQuit			= (CStatic*)	GetDlgItem(IDC_HOWTOQUIT_STATIC);

	// Sets the Controls' captions...
	CString strControlCaption;
	strControlCaption.LoadString(IDS_ERASE_BTN);		m_pbResetAnswers->SetWindowText(strControlCaption);
														m_pbResetVariables->SetWindowText(strControlCaption);
														m_pbResetFunctions->SetWindowText(strControlCaption);
	strControlCaption.LoadString(IDS_CALCULATE_BTN);	m_pbCalculate->SetWindowText(strControlCaption);
	strControlCaption.LoadString(IDS_ABOUT_BTN);		m_pbAbout->SetWindowText(strControlCaption);
	strControlCaption.LoadString(IDS_HELP_BTN);			m_pbHelp->SetWindowText(strControlCaption);
	strControlCaption.LoadString(IDS_ANSWERS_STC);		m_psAnswers->SetWindowText(strControlCaption);
	strControlCaption.LoadString(IDS_VARIABLES_STC);	m_psVariables->SetWindowText(strControlCaption);
	strControlCaption.LoadString(IDS_FUNCTIONS_STC);	m_psFunctions->SetWindowText(strControlCaption);
	strControlCaption.LoadString(IDS_HOWTOQUIT_STC);	m_psHowToQuit->SetWindowText(strControlCaption);
	strControlCaption.LoadString(IDS_VISUALCALC);		m_psVisualCalc->SetWindowText(strControlCaption);
														this->SetWindowText(strControlCaption + " " + m_strVisualCalcVersion);

	// Sets the icon on the switch answers lists state button
	HICON hIcon;
	hIcon = (HICON)LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_SWITCH), IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR);
	if (hIcon) {
		m_pbAnswersListState->SendMessage(BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
		m_pbVariablesListState->SendMessage(BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
	}
	// Initialize the ListBoxes...
	this->OnResetAns();
	this->OnResetVars();
	this->OnResetFuncs();
	this->m_peInput->SetFocus();

	return FALSE;
}


// Check if the text typed recalls the last answer
void CVisualCalcDlg::OnChangeInputEdit() {
	CString strInput;
	m_peInput->GetWindowText(strInput);
	if ((strInput.GetLength() == 1) && (m_plAnswers->GetCount())) {
		CString strNewInput;
		switch (strInput[0]) {
		case TV_ASSIGN:
			strNewInput += " ";
			strNewInput += strInput;
			strNewInput += " ";
			m_peInput->SetWindowText(strNewInput);
			m_peOutput->SetWindowText("Warning : Please select a variable to assign before");
			m_peInput->SetSel(0, 0);
			break;
			
		case TV_PLUS:
		case TV_MINUS:
		case TV_MUL:
		case TV_DIV:
		case TV_POW:
		case TV_MOD:
		case TV_FACT:
			strNewInput += "Ans(1) ";
			strNewInput += strInput + " ";
			m_peInput->SetWindowText(strNewInput);
			m_peInput->SetSel(strNewInput.GetLength(), strNewInput.GetLength());
		}
	}
}


// Opens the About DialogBox...
void CVisualCalcDlg::OnAbout() {
	CAboutDlg dlg(m_strVisualCalcVersion, m_Parser.GetVersion().c_str());
	dlg.DoModal();
}


// Opens the Help/Catalog DialogBox...
void CVisualCalcDlg::OnHelp() {
	CHelpSheetDlg dlg(m_strVisualCalcVersion);
	dlg.DoModal();
}


// Double-Click on the Answers ListBox
void CVisualCalcDlg::OnDblclkAnsList() {	
	CString strAnswer;
	m_plAnswers->GetText(m_plAnswers->GetCurSel(), strAnswer);
	m_peInput->ReplaceSel(strAnswer);
	m_peInput->SetFocus();
}


// Double-Click on the Formulas ListBox
void CVisualCalcDlg::OnDblclkFormulasList() {
	CString strAnswer;
	m_plFormulas->GetText(m_plFormulas->GetCurSel(), strAnswer);
	m_peInput->ReplaceSel(strAnswer);
	m_peInput->SetFocus();	
}


// Double-Click on the Variables ListBox
void CVisualCalcDlg::OnDblclkVarList() {
	CString strVariable;
	m_plVariables->GetText(m_plVariables->GetCurSel(), strVariable);
	m_peInput->ReplaceSel(strVariable);
	m_peInput->SetFocus();
}



// Double-Click on the Variables ListBox
void CVisualCalcDlg::OnDblclkVarValuesList() {
	CString strVarValue;
	m_plVarValues->GetText(m_plVarValues->GetCurSel(), strVarValue);
	m_peInput->ReplaceSel(strVarValue);
	m_peInput->SetFocus();
}


// Double-Click on the Functions ListBox
void CVisualCalcDlg::OnDblclkFuncList() {
	CString strFunction;
	m_plFunctions->GetText(m_plFunctions->GetCurSel(), strFunction);
	m_peInput->ReplaceSel(strFunction);
	m_peInput->SetFocus();
}


// Updates the content of the Answers History ListBox
void CVisualCalcDlg::UpdateAnswersList() {
	// Clears the Answers and Formulas ListBoxes entries
	int iNbAnswersToDelete = m_plAnswers->GetCount();
	for (int iCpt = 0; iCpt < iNbAnswersToDelete; iCpt++) {
		// Both ListBoxes contain the same number of lines
	   m_plAnswers->DeleteString(0);
	   m_plFormulas->DeleteString(0);
	}

	// Retrieves the new parser answers history
	const std::deque<AnswerItem> dqeAnswersHistory = m_Parser.GetAnswersHistory();
	std::deque<AnswerItem>::const_iterator iter;
	char pszHistoryEntry[100];
	for (iter = dqeAnswersHistory.begin(); iter != dqeAnswersHistory.end(); iter++) {
		::sprintf(pszHistoryEntry, "%.8f", iter->m_valResult);
		m_plAnswers->AddString(this->RemoveTrailingZeros(pszHistoryEntry));
		m_plFormulas->AddString(iter->m_strFormula.c_str());
	}
}


// Updates the content of the Variables ListBox
void CVisualCalcDlg::UpdateVariablesList() {
	// Clears the ListBox entries
	int iNbVariablesToDelete = m_plVariables->GetCount();
	for (int iCpt = 0; iCpt < iNbVariablesToDelete; iCpt++) {
	   m_plVariables->DeleteString(0);
	   m_plVarValues->DeleteString(0);
	}

	// Retrieves the new parser variables list
	const std::map<std::string, VALUES_TYPE> mapVariables = m_Parser.GetVariables();
	std::map<std::string, VALUES_TYPE>::const_iterator iter;
	for (iter = mapVariables.begin(); iter != mapVariables.end(); iter++) {
		CString strVarValue;
		strVarValue.Format("%.8f", iter->second);
		this->RemoveTrailingZeros(strVarValue);
		m_plVariables->AddString(iter->first.c_str());
		m_plVarValues->AddString(this->RemoveTrailingZeros(strVarValue));
	}
}


// Updates the content of the Functions ListBox
void CVisualCalcDlg::UpdateFunctionsList() {
	// Clears the ListBox entries
	int iNbFunctionsToDelete = m_plFunctions->GetCount();
	for (int iCpt = 0; iCpt < iNbFunctionsToDelete; iCpt++) {
	   m_plFunctions->DeleteString(0);
	}

	// Retrieves the new parser functions list
	const std::list<std::string> lstFunctions = m_Parser.GetFunctions();
	std::list<std::string>::const_iterator iter;
	for (iter = lstFunctions.begin(); iter != lstFunctions.end(); iter++) {
		m_plFunctions->AddString(iter->c_str());
	}
}


// Changes the display of the Answers List
void CVisualCalcDlg::OnChangeAnswersListState() {
	if (m_pbAnswersListState->GetCheck() == BST_CHECKED) {
		m_plAnswers->ShowWindow(SW_HIDE);
		m_plFormulas->ShowWindow(SW_SHOW);
	}
	else {
		m_plAnswers->ShowWindow(SW_SHOW);
		m_plFormulas->ShowWindow(SW_HIDE);
	}
	m_peInput->SetFocus();
}


// Changes the display of the Variables List
void CVisualCalcDlg::OnChangeVariablesListState() {
	if (m_pbVariablesListState->GetCheck() == BST_CHECKED) {
		m_plVariables->ShowWindow(SW_HIDE);
		m_plVarValues->ShowWindow(SW_SHOW);
	}
	else {
		m_plVariables->ShowWindow(SW_SHOW);
		m_plVarValues->ShowWindow(SW_HIDE);
	}
	m_peInput->SetFocus();
}


// Simple-Click on the "Erase Answers List" button
void CVisualCalcDlg::OnResetAns() {
	// Clears the Parser answers history
	m_Parser.ResetAnswersHistory();

	// Updates the answers history ListBox
	this->UpdateAnswersList();

	m_peInput->SetFocus();
}


// Simple-Click on the "Erase Variables List" button
void CVisualCalcDlg::OnResetVars() {
	// Clears the Parser variables list
	m_Parser.ResetVariables();

	// Updates the variables ListBox
	this->UpdateVariablesList();

	m_peInput->SetFocus();
}


// Simple-Click on the "Erase Functions List" button
void CVisualCalcDlg::OnResetFuncs() {
	// Clears the Parser functions list
	m_Parser.ResetFunctions();

	// Update the functions ListBox
	this->UpdateFunctionsList();

	m_peInput->SetFocus();	
}


// Removes the decimal zeros trailing on the right of the period
CString CVisualCalcDlg::RemoveTrailingZeros(const CString& strResult) {
	// We consider here that every characters in the string
	// is either a digit or a period

	// Searches a '.' in the string
	int iDotPosition = -1;
	int i = 0;
	int j = 1;
	CString strNewResult = strResult;
	for (; i < strNewResult.GetLength(); i++) {
		if (strResult[i] == '.') {
			iDotPosition = i;
			break;
		}
	}

	// Removes the zeros only if a period was found
	if (iDotPosition != -1) {
		for (i = strResult.GetLength()-1; i >= 0; i--) {
			if (strResult[i] != '0') {
				break;
			}
		}
		
		strNewResult = strNewResult.Mid(0, i + 1);
	}
	return strNewResult;
}


// Adds a thousand separation space on the left of the period
CString CVisualCalcDlg::InsertThousandSpaces(const CString& strResult) {
	// Searches a '.' in the string
	int iDotPosition = -1;
	int i = 0;
	int j = 1;
	CString strNewResult = strResult;
	for (; i < strNewResult.GetLength(); i++) {
		if (strResult[i] == '.') {
			iDotPosition = i;
			break;
		}
	}

	// If no '.' was found
	if (iDotPosition == -1) {
		iDotPosition = strNewResult.GetLength();
	}

	// Inserts a thousand space separator
	// (every 3 characters except at the very beginning of the string)
	iDotPosition--;
	for (i = iDotPosition; i >= 0; i--, j++) {
		if (!(j%3) && (i)) {
			strNewResult.Insert(i, ' ');
		}
	}

	return strNewResult;
}


///////////////////////////////////////////////////////////////////////////////
// Beginning of the Parser...
void CVisualCalcDlg::OnCalculate() {
	CString strSource, strDest;
	m_peInput->GetWindowText(strSource);

	// Initializes and calls the Parser
	try {
		VALUES_TYPE valResult = m_Parser.Evaluate((LPCTSTR)strSource);
		strDest.Format("%.8f", valResult);
		strDest = this->RemoveTrailingZeros(strDest);
		strDest = this->InsertThousandSpaces(strDest);
		if (m_Parser.HasWarning()) {
			// Adds the warning after the result
			strDest.Format("%s ; %s", strDest, m_Parser.GetWarningMsg().c_str());
		}
		m_peInput->SetSel(0, -1);
	}
	catch (CSyntaxException ex) {
		strDest.Format("Syntax error %d : %s", ex.GetExceptionNumber(), ex.GetMessage().c_str());
		m_peInput->SetSel(ex.GetErrorPos(), ex.GetErrorPos());
	}
	catch (CMathematicException ex) {
		strDest.Format("Mathematic error %d : %s", ex.GetExceptionNumber(), ex.GetMessage().c_str());
		m_peInput->SetSel(ex.GetErrorPos(), ex.GetErrorPos());
	}
	catch (CFunctionException ex) {
		strDest.Format("Function error %d : %s", ex.GetExceptionNumber(), ex.GetMessage().c_str());
		m_peInput->SetSel(ex.GetErrorPos(), ex.GetErrorPos());
	}
	catch (CParameterException ex) {
		strDest.Format("Parameter error %d : %s", ex.GetExceptionNumber(), ex.GetMessage().c_str());
		m_peInput->SetSel(ex.GetErrorPos(), ex.GetErrorPos());
	}
	catch (CVariableException ex) {
		strDest.Format("Variable error %d : %s", ex.GetExceptionNumber(), ex.GetMessage().c_str());
		m_peInput->SetSel(ex.GetErrorPos(), ex.GetErrorPos());
	}
	catch (CDomainException ex) {
		strDest.Format("Domain error %d : %s", ex.GetExceptionNumber(), ex.GetMessage().c_str());
		m_peInput->SetSel(ex.GetErrorPos(), ex.GetErrorPos());
	}
	catch (CParserException ex) {
		strDest.Format("Parser error %d : %s", ex.GetExceptionNumber(), ex.GetMessage().c_str());
		m_peInput->SetSel(ex.GetErrorPos(), ex.GetErrorPos());
	}
	catch (...) {
		strDest.Format("Unknown parser internal error");
	}

	// Prints the result...
	m_peOutput->SetWindowText(strDest);

	// Updates the listboxes' contents
	this->UpdateAnswersList();
	this->UpdateVariablesList();
	this->UpdateFunctionsList();
	
	// Give the focus back to the input EditBox
	m_peInput->SetFocus();
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Accenture Technology Solutions
France France

Toxcct is an electronics guy who felt in love with programming at the age of 10 when he discovered C to play with Texas-Instruments calculators.

Few years later, he discovered "The C++ Language" from Bjarne Stroustrup ; a true transformation in his life.

Now, toxcct is experiencing the Web by developing Siebel CRM Applications for a living. He also respects very much the Web Standards (YES, a HTML/CSS code MUST validate !), and plays around with HTML/CSS/Javascript/Ajax/PHP and such.

_____

After four years of services as a Codeproject MVP, toxcct is now taking some distance as he doesn't like how things are going on the forums. he particularly doesn't accept how some totally ignorant people got the MVP Reward by only being arrogant and insulting while replying on the technical forums.



Comments and Discussions