//------------------------------------------------------------------------------
// OWL Extensions (OWLEXT) Class Library
// CALCEDIT.CPP
//
// TCalcEdit Class
//
// Original code by Steve Carr (Compuserve: 100251,1571)
// Written: 13th November 1995
//
//------------------------------------------------------------------------------
#include <owlext\pch.h>
#pragma hdrstop
#include <owl/validate.h>
#include <owl/dialog.h>
#include <owlext/calcedit.h>
#include <owlext/calcedit.rh>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
namespace OwlExt {
using namespace owl;
using namespace std;
// private function
TModule* FindResourceModule(TWindow* parent, TModule* module, TResId resId, LPCTSTR type);
class TCalcValidator : public TFilterValidator {
protected:
double minValue;
double maxValue;
TCalcEdit* owner;
public:
TCalcValidator( double aMin, double aMax, TCalcEdit* anOwner ) :
TFilterValidator(_T("0-9+-."))
{
minValue = aMin;
maxValue = aMax;
owner = anOwner;
}
virtual void Error(TWindow*p=NULL) ;
virtual bool IsValid( LPCTSTR s) ;
void SetMin( double aMin )
{
minValue = aMin ;
if( maxValue < minValue )
maxValue = minValue ;
}
void SetMax( double aMax )
{
maxValue = aMax ;
if( minValue > maxValue )
minValue = maxValue ;
}
double GetMin() const { return minValue ; }
double GetMax() const { return maxValue ; }
};
void TCalcValidator::Error(TWindow*)
{
owner->FormatMessageBox(_T("Value must be between %.*f and %.*f"), _T(""), MB_ICONEXCLAMATION|MB_OK|MB_TASKMODAL,
owner->GetDecimals(), minValue, owner->GetDecimals(), maxValue);
}
bool TCalcValidator::IsValid( LPCTSTR s )
{
if(TFilterValidator::IsValid(s)){
double val = _ttof(s) ;
if(minValue == maxValue)
return true;
if( val >= minValue && val <= maxValue )
return true;
};
return false;
}
#define CALC_DIGITS 15
class TCalc : public TDialog{
public:
auto Execute() -> int override;
void Destroy(int retValue = IDCANCEL) override;
void CloseWindow(int retValue = IDCANCEL) override;
public:
enum TCalcState { CS_FIRST, CS_VALID, CS_ERROR } ; // calculator state
TCalcState CalcStatus;
_TCHAR Num[CALC_DIGITS + 1];
_TCHAR Operator;
bool Negative;
double Operand;
_TCHAR LastKey;
TStatic* display;
public:
TCalc( TWindow* aParent);
void FlashKey(_TCHAR key);
void Error()
{
CalcStatus = CS_ERROR;
_tcscpy(Num, _T("Error"));
Negative = FALSE;
}
void SetDisplay(double r);
double GetDisplay() ;
virtual void UpdateDisplay();
void CheckFirst()
{
if (CalcStatus == CS_FIRST){
CalcStatus = CS_VALID;
_tcscpy(Num, _T("0"));
Negative = false;
}
}
void InsertKey(_TCHAR key);
void CalcKey(_TCHAR key);
void Clear()
{
CalcStatus = CS_FIRST;
_tcscpy(Num, _T("0"));
Negative = false;
Operator = _T('=');
LastKey = _T(' ');
}
protected:
void SetupWindow() override;
//
// override EvCommand() defined by class TWindow
//
auto EvCommand(UINT, HWND, UINT) -> LRESULT override;
// Message response functions
//
HBRUSH EvCtlColor(HDC, HWND hWndChild, uint ctlType);
DECLARE_RESPONSE_TABLE(TCalc);
};
DEFINE_RESPONSE_TABLE1(TCalc, TDialog)
EV_WM_CTLCOLORSTATIC(EvCtlColor),
END_RESPONSE_TABLE;
TCalc::TCalc( TWindow* parent )
:
TDialog(parent, IDD_CALCDIALOG,
FindResourceModule(parent,0,IDD_CALCDIALOG,RT_DIALOG))
{
Clear();
TWindow::Attr.AccelTable = IDA_CALCULATOR;
display = new TStatic( this, ID_DISPLAY);
}
void TCalc::SetupWindow()
{
TDialog::SetupWindow();
TRect rc, rcThis = GetWindowRect(), rcParent = Parent->GetWindowRect();
rcThis.right = rcParent.left + rcThis.Width();
rcThis.bottom = rcParent.bottom + rcThis.Height();
rcThis.left = rcParent.left;
rcThis.top = rcParent.bottom;
::GetWindowRect(::GetDesktopWindow(), &rc);
if (rcThis.left < rc.left)
rcThis.Offset(rc.left-rcThis.left, 0);
if (rcThis.bottom > rc.bottom)
rcThis.Offset(0, rc.bottom-rcThis.bottom);
if (rcThis.right > rc.right)
rcThis.Offset(rc.right-rcThis.right, 0);
MoveWindow(rcThis);
}
int TCalc::Execute()
{
if (Create()){
if (!(TWindow::Attr.Style & WS_VISIBLE))
ShowWindow(SW_SHOW);
UpdateDisplay();
return GetApplication()->BeginModal(this,MB_TASKMODAL);
}
return -1;
}
void TCalc::Destroy(int retval)
{
GetApplication()->EndModal(retval);
TWindow::Destroy(retval);
}
void TCalc::CloseWindow(int retval)
{
if (CanClose()){
TransferData(tdGetData);
Destroy(retval);
}
}
LRESULT TCalc::EvCommand(UINT id, HWND hWndCtl, UINT notifyCode)
{
if (hWndCtl != 0 && notifyCode == BN_CLICKED)
CalcKey( _TCHAR(id) );
else if ( hWndCtl == 0 && notifyCode == 1 ){
FlashKey(_TCHAR(id));
CalcKey(_TCHAR(id));
}
return TDialog::EvCommand(id, hWndCtl, notifyCode) ;
}
//
// Colorize the calculator. Allows background to show through corners of
// buttons, uses yellow text on black background in the display, and sets
// the dialog background to blue.
//
HBRUSH
TCalc::EvCtlColor(HDC hDC, HWND /*hWndChild*/, uint ctlType)
{
PRECONDITION(ctlType == CTLCOLOR_STATIC); InUse(ctlType);
::SetTextColor(hDC, TColor::LtYellow);
SetBkColor(hDC, TColor::Black);
return reinterpret_cast<HBRUSH>(GetStockObject(BLACK_BRUSH));
}
void
TCalc::UpdateDisplay()
{
_TCHAR str[CALC_DIGITS + 2] ;
if (Negative)
_tcscpy(str, _T("-"));
else
str[0] = _T('\0');
_tcscat( str, Num ) ;
display->SetText( str ) ;
}
void TCalc::SetDisplay(double r)
{
_TCHAR* first;
_TCHAR* last;
int charsToCopy;
_TCHAR str[64];
r = (floor(r * 10000000L + .5)) / 10000000L;
_stprintf(str, _T("%0.10f"), r);
first = str;
Negative = FALSE;
if(str[0] == '-'){
first++;
Negative = true;
}
if (_tcslen(first) > CALC_DIGITS + 1 + 10 )
Error();
else{
last = _tcschr(first, 0);
while (last[-1] == _T('0'))
--last;
if (last[-1] == _T('.'))
--last;
charsToCopy = std::min(CALC_DIGITS + 1, int(last - first));
_tcsncpy(Num, first, charsToCopy);
Num[charsToCopy] = 0;
}
}
void TCalc::FlashKey(_TCHAR key)
{
if (key == _T('\n'))
key = _T('=') ;
HWND button = GetDlgItem( toupper(key) ) ;
if (button){
::SendMessage(button, BM_SETSTATE, 1, 0) ;
Sleep(100);
::SendMessage(button, BM_SETSTATE, 0, 0) ;
}
}
double
TCalc::GetDisplay()
{
double r = _ttof(Num);
if (Negative)
r = -r;
return r ;
}
void
TCalc::InsertKey(_TCHAR key)
{
int l = static_cast<int>(_tcslen(Num));
if (l < CALC_DIGITS){
Num[l++] = key;
Num[l] = _T('\0');
}
}
void
TCalc::CalcKey(_TCHAR key)
{
key = (_TCHAR)toupper(key);
if (CalcStatus == CS_ERROR && key != _T('C'))
key = _T(' ');
if (key >= _T('0') && key <= _T('9')){
CheckFirst();
if (!_tcscmp(Num, _T("0")))
Num[0] = _T('\0');
InsertKey(key);
}
else if (key == _T('+') || key == _T('-') || key == _T('*') ||
key == _T('/') || key == _T('=') || key == _T('%') || key == 0x0D)
{
if( ( key == 0x0D || key == _T('=') ) && key == LastKey )
CloseWindow( IDOK ) ;
if (CalcStatus == CS_VALID){
CalcStatus = CS_FIRST;
double r = GetDisplay() ;
if (key == _T('%')){
switch(Operator){
case _T('+'):
case _T('-'):
r = Operand * r / 100;
break;
case _T('*'):
case _T('/'):
r /= 100;
break;
}
}
switch(Operator){
case _T('+'):
SetDisplay(Operand + r);
break;
case _T('-'):
SetDisplay(Operand - r);
break;
case _T('*'):
SetDisplay(Operand * r);
break;
case _T('/'):
if (r == 0)
Error();
else
SetDisplay(Operand / r);
break;
}
}
Operator = key;
Operand = GetDisplay();
}
else{
switch(key){
case _T('.'):
CheckFirst();
if (!_tcschr(Num, _T('.')))
InsertKey(key);
break;
case 0x8:
CheckFirst();
if (_tcslen(Num) == 1)
_tcscpy(Num, _T("0"));
else
Num[_tcslen(Num) - 1] = _T('\0');
break;
case _T('_'):
Negative = !Negative;
break;
case _T('C'):
Clear();
break;
}
}
LastKey = key ;
UpdateDisplay();
}
TCalcEdit::TCalcEdit(TWindow* parent, int resourceId,
uint aWidth, uint aDecimals, double aMin, double aMax,
TModule* module)
:
TPopupEdit(parent, resourceId, IDB_CALCBTN, aWidth+1, module)
{
// SetText("0"); //?????????????????
SetCaption(_T("0"));
aCalc = NULL;
decimals = aDecimals ;
Validator = new TCalcValidator( aMin, aMax, this ) ;
}
TCalcEdit::TCalcEdit(TWindow* parent, int id, double aVal, int x, int y,
int w, int h, uint aWidth, uint aDecimals, double aMin, double aMax,
TModule* module )
:
TPopupEdit( parent, id, _T(""), x, y, w, h, IDB_CALCBTN, aWidth+1, module )
{
aCalc = NULL ;
decimals = aDecimals ;
Validator = new TCalcValidator( aMin, aMax, this ) ;
operator =( aVal ) ;
}
void TCalcEdit::Clicked()
{
TCalc* pCalc = new TCalc(this) ;
((TWindow*)pCalc)->GetWindowAttr().AccelTable = IDA_CALCULATOR;
GetText( pCalc->Num, CALC_DIGITS );
double r = _ttof( pCalc->Num );
pCalc->CalcStatus = TCalc::CS_VALID;
pCalc->SetDisplay( r );
if( pCalc->Execute() == IDOK )
operator =(pCalc->GetDisplay());
delete pCalc;
}
void TCalcEdit::SetMin( double d )
{
TCalcValidator *cv = (TCalcValidator*)Validator ;
if( cv )
cv->SetMin( d ) ;
}
void TCalcEdit::SetMax( double d )
{
TCalcValidator *cv = (TCalcValidator*)Validator ;
if( cv )
cv->SetMax( d ) ;
}
void TCalcEdit::operator = ( double d )
{
_TCHAR buf[64];
_stprintf( buf, _T("%.*f"), decimals, d );
SetText(buf);
}
TCalcEdit::operator double()
{
_TCHAR buf[64] ;
GetText( buf, sizeof(buf) ) ;
return _ttof(buf) ;
}
} // OwlExt namespace
//==============================================================================
↑ V557 Array overrun is possible. The value of 'strlen(Num) - 1' index could reach 4294967295.
↑ V550 An odd precise comparison: minValue == maxValue. It's probably better to use a comparison with defined precision: fabs(A - B) < Epsilon.
↑ V550 An odd precise comparison: r == 0. It's probably better to use a comparison with defined precision: fabs(A - B) < Epsilon.
↑ V829 Lifetime of the heap-allocated variable 'pCalc' is limited to the current function's scope. Consider allocating it on the stack instead.