ttcalc/src/pad.cpp

508 lines
11 KiB
C++

/*
* This file is a part of TTCalc - a mathematical calculator
* and is distributed under the (new) BSD licence.
* Author: Tomasz Sowa <t.sowa@slimaczek.pl>
*/
/*
* Copyright (c) 2006-2009, Tomasz Sowa
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name Tomasz Sowa nor the names of contributors to this
* project may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <windows.h>
#include <ttmath/ttmath.h>
#include "programresources.h"
#include "resource.h"
#include "messages.h"
#include "bigtypes.h"
namespace Pad
{
HWND edit;
WNDPROC old_edit_proc;
std::string parse_string;
int precision;
HFONT font;
int base_input;
int base_output;
bool always_scientific;
int when_scientific;
int rounding;
bool remove_zeroes;
int angle_deg_rad_grad;
char decimal_point;
Languages::Country country;
ttmath::Objects * variables;
ttmath::Objects * functions;
ttmath::ErrorCode code;
bool calculated;
std::string res, res2;
#ifndef TTCALC_PORTABLE
ttmath::Parser<TTMathBig1> parser1;
ttmath::Parser<TTMathBig2> parser2;
ttmath::Parser<TTMathBig3> parser3;
#else
ttmath::Parser<TTMathBig1> parser1;
#endif
void PutChars(const char * str)
{
SendMessage(edit, EM_REPLACESEL, true, (LPARAM)str);
}
void PutChars(std::string & str)
{
PutChars(str.c_str());
}
void PutOverflowMsg()
{
PutChars(GetPrgRes()->GetLanguages()->ErrorMessage(country, ttmath::err_overflow));
PutChars("\r\n");
}
template<typename ValueType>
void PutResultFromParser(ttmath::Parser<ValueType> & matparser)
{
unsigned int i = 0;
res.clear();
res2.clear();
for(i=0 ; i<matparser.stack.size() ; ++i)
{
try
{
ValueType result = matparser.stack[i].value;
if( result.ToString(res2, base_output, always_scientific, when_scientific, rounding, remove_zeroes, decimal_point) )
res2 = GetPrgRes()->GetLanguages()->GuiMessage(country, Languages::overflow_during_printing);
}
catch(...)
{
res2 = GetPrgRes()->GetLanguages()->ErrorMessage(country, ttmath::err_internal_error);
}
res += res2;
if( i < matparser.stack.size()-1 )
res += "\r\n";
}
res += ' ';
PutChars(res);
}
void PutResult()
{
#ifndef TTCALC_PORTABLE
switch( precision )
{
case 0:
PutResultFromParser(parser1);
break;
case 1:
PutResultFromParser(parser2);
break;
default:
PutResultFromParser(parser3);
break;
}
#else
PutResultFromParser(parser1);
#endif
}
// line - index of a line -- as you see it on the edit control
// (if the text is wrapped then the line is larger)
void GetParseString(const char * buf, int line)
{
int i;
int first_line_char = SendMessage(edit, EM_LINEINDEX, line, 0);
// first_line_char takes into consideration \r\n characters
if( first_line_char < 0 )
// something wrong
return;
// looking for the line
for(i=0 ; i<first_line_char && buf[i]!=0 ; ++i);
// looking for the beginning of the line
for( ; i>0 && buf[i-1]!=10 ; --i);
// copying the whole line
for( ; buf[i]!=0 && buf[i]!='\r' && buf[i]!='\n' ; ++i )
parse_string += buf[i];
}
void GetParseString()
{
int line;
parse_string.clear();
// index of a last line to parse (-1 because the ENTER has been put)
line = SendMessage(edit, EM_LINEFROMCHAR, -1, 0) - 1;
if( line < 0 )
// something wrong
return;
HLOCAL handle = (HLOCAL)SendMessage(edit, EM_GETHANDLE, 0, 0);
if( handle == 0 )
// something wrong
return;
const char * buf = (const char*)LocalLock(handle);
if( buf )
GetParseString(buf, line);
LocalUnlock(handle);
}
template<class ValueType>
void ParseString(ttmath::Parser<ValueType> & matparser)
{
matparser.SetBase(base_input);
matparser.SetDegRadGrad(angle_deg_rad_grad);
matparser.SetFunctions(functions);
matparser.SetVariables(variables);
code = matparser.Parse(parse_string.c_str());
calculated = matparser.Calculated();
}
void SetParameters()
{
base_input = GetPrgRes()->GetBaseInput();
base_output = GetPrgRes()->GetBaseOutput();
always_scientific = GetPrgRes()->GetDisplayAlwaysScientific();
when_scientific = GetPrgRes()->GetDisplayWhenScientific();
rounding = GetPrgRes()->GetDisplayRounding();
precision = GetPrgRes()->GetPrecision();
remove_zeroes = GetPrgRes()->GetRemovingZeroes();
angle_deg_rad_grad = GetPrgRes()->GetDegRadGrad();
country = GetPrgRes()->GetLanguages()->GetCurrentLanguage();
if( GetPrgRes()->GetDecimalPoint() == 0 )
decimal_point = '.';
else
decimal_point = ',';
variables = GetPrgRes()->GetVariables();
functions = GetPrgRes()->GetFunctions();
}
void ParseString()
{
if( parse_string.empty() )
return;
SetParameters();
try
{
#ifndef TTCALC_PORTABLE
switch( precision )
{
case 0:
ParseString(parser1);
break;
case 1:
ParseString(parser2);
break;
default:
ParseString(parser3);
break;
}
#else
ParseString(parser1);
#endif
}
catch(...)
{
code = ttmath::err_internal_error;
}
if( code==ttmath::err_ok && calculated )
PutResult();
else
if( code==ttmath::err_overflow )
PutOverflowMsg();
}
LRESULT EditReturnPressed(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
DWORD sel_start, sel_end;
SendMessage(edit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
if( sel_start != sel_end )
// something is selected
return CallWindowProc(old_edit_proc, hwnd, msg, wParam, lParam);
// putting the ENTER
LRESULT res = CallWindowProc(old_edit_proc, hwnd, msg, wParam, lParam);
GetParseString();
ParseString();
return res;
}
LRESULT EditSubclass(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch( msg )
{
case WM_CHAR:
if( wParam == VK_RETURN )
{
return EditReturnPressed(hwnd, msg, wParam, lParam);
}
break;
}
return CallWindowProc(old_edit_proc, hwnd, msg, wParam, lParam);
}
LRESULT PadCreate(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
edit = CreateWindow("edit", "",
WS_VISIBLE | WS_CHILD | WS_VSCROLL | ES_MULTILINE | ES_AUTOVSCROLL,
0, 0, 100, 100, hwnd, 0, GetPrgRes()->GetInstance(), 0);
if( !edit )
return 0;
old_edit_proc = (WNDPROC)SetWindowLong(edit, GWL_WNDPROC, (LONG)EditSubclass);
// 65535 - 64KB
// we're using some kind of messages which operates only on 64KB
SendMessage(edit, EM_SETLIMITTEXT, 65535 - 5, 0);
if( font != 0 )
SendMessage(edit, WM_SETFONT, (WPARAM)font, 0);
return 0;
}
LRESULT PadMove(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
RECT r;
GetWindowRect(hwnd,&r);
GetPrgRes()->SetPadXPos( r.left );
GetPrgRes()->SetPadYPos( r.top );
return 0;
}
LRESULT PadSize(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
// client coordinates
int cx = LOWORD(lParam);
int cy = HIWORD(lParam);
int fwSizeType = int(wParam);
RECT r;
if( fwSizeType == SIZE_MAXIMIZED )
GetPrgRes()->SetPadMaximized(true);
else
if( fwSizeType == SIZE_RESTORED )
GetPrgRes()->SetPadMaximized(false);
MoveWindow(edit, 0, 0, cx, cy, true);
if( fwSizeType != SIZE_MINIMIZED && fwSizeType != SIZE_MAXIMIZED )
{
GetWindowRect(hwnd, &r);
GetPrgRes()->SetPadXSize( r.right - r.left );
GetPrgRes()->SetPadYSize( r.bottom - r.top );
}
return 0;
}
LRESULT PadClose(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
GetPrgRes()->ShowPad( false );
return 0;
}
LRESULT PadFocus(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
SetFocus(edit);
return 0;
}
void CreatePadMessagesTable(Messages<LRESULT> & messages)
{
messages.Associate(WM_CREATE, PadCreate);
messages.Associate(WM_MOVE, PadMove);
messages.Associate(WM_SIZE, PadSize);
messages.Associate(WM_CLOSE, PadClose);
messages.Associate(WM_SETFOCUS, PadFocus);
}
LRESULT CALLBACK WindowPadProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static Messages<LRESULT> messages;
bool method_exists;
LRESULT res;
if( messages.Empty() )
// initiation
CreatePadMessagesTable(messages);
res = messages.Call(msg, hwnd, msg, wParam, lParam, &method_exists);
if( !method_exists )
return DefWindowProc(hwnd, msg, wParam, lParam);
return res;
}
ATOM RegisterPadClass(const char * ttcalc_pad_class_name)
{
WNDCLASS w;
w.style = 0;
w.lpfnWndProc = WindowPadProc;
w.cbClsExtra = 0;
w.cbWndExtra = 0;
w.hInstance = GetPrgRes()->GetInstance();
w.hIcon = LoadIcon(GetPrgRes()->GetInstance(), MAKEINTRESOURCE(IDI_ICON2));
w.hCursor = 0;
w.hbrBackground = (HBRUSH)(COLOR_BACKGROUND+1);
w.lpszMenuName = 0;
w.lpszClassName = ttcalc_pad_class_name;
return RegisterClass(&w);
}
} // namespace Pad
bool CreatePadWindow()
{
using namespace Pad;
static char ttcalc_pad_class_name[] = "TTCalcPadWindow";
ATOM a = RegisterPadClass(ttcalc_pad_class_name);
if( a == 0 )
return false;
bool pad_maximized_from_file = GetPrgRes()->GetPadMaximized();
// CreateWindow() would change maximizing
font = CreateFont(0, 0, 0, 0, FW_NORMAL, false, false, false,
ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH, "Ms Shell Dlg");
HWND pad = CreateWindowEx(0, ttcalc_pad_class_name,
"Pad",
WS_OVERLAPPEDWINDOW | WS_POPUPWINDOW ,
GetPrgRes()->GetPadXPos(), GetPrgRes()->GetPadYPos(),
GetPrgRes()->GetPadXSize(), GetPrgRes()->GetPadYSize(),
0 /*GetPrgRes()->GetMainWindow()*/, 0, GetPrgRes()->GetInstance(), 0);
GetPrgRes()->SetPadWindow(pad);
GetPrgRes()->SetPadMaximized( pad_maximized_from_file );
GetPrgRes()->ShowPad( GetPrgRes()->IsPadVisible() );
return pad!=0 && edit!=0;
}
void DestroyPadWindow()
{
using namespace Pad;
DeleteObject(font);
}