#include "calc.h" #define HTMLHELP_PATH(_pt) TEXT("%systemroot%\\Help\\calc.chm::") TEXT(_pt) #define WM_CLOSE_STATS (WM_APP+1) #define WM_HANDLE_CLIPBOARD (WM_APP+2) #define WM_INSERT_STAT (WM_APP+3) #define WM_LOAD_STAT (WM_APP+4) #define MAKE_BITMASK4(_show_b16, _show_b10, _show_b8, _show_b2) \ (((_show_b2) << 0) | \ ((_show_b8) << 1) | \ ((_show_b10) << 2) | \ ((_show_b16) << 3)) #define MAKE_BITMASK5(_transl, _is_stats, _is_ctrl, _show_b16, _show_b10, _show_b8, _show_b2) \ (((_show_b2) << 0) | \ ((_show_b8) << 1) | \ ((_show_b10) << 2) | \ ((_show_b16) << 3) | \ ((_is_ctrl) << 5) | \ ((_is_stats) << 6) | \ ((_transl) << 7)) #define KEY_IS_UP 0x80000000 #define KEY_WAS_DOWN 0x40000000 #define BITMASK_IS_ASCII 0x80 #define BITMASK_IS_STATS 0x40 #define BITMASK_IS_CTRL 0x20 #define BITMASK_HEX_MASK 0x08 #define BITMASK_DEC_MASK 0x04 #define BITMASK_OCT_MASK 0x02 #define BITMASK_BIN_MASK 0x01 #define CALC_CLR_RED 0x000000FF #define CALC_CLR_BLUE 0x00FF0000 #define CALC_CLR_PURP 0x00FF00FF typedef struct { CHAR key; // Virtual key identifier WORD idc; // IDC for posting message } key2code_t; typedef struct { WORD idc; // IDC for posting message CHAR key; // Virtual key identifier BYTE mask; // enable/disable into the various modes. INT col; // color used for drawing the text } key3code_t; #define CTRL_FLAG 0x100 #define ALT_FLAG 0x200 #define CTRL_A (0x0001+'A'-'A') #define CTRL_C (0x0001+'C'-'A') #define CTRL_D (0x0001+'D'-'A') #define CTRL_L (0x0001+'L'-'A') #define CTRL_M (0x0001+'M'-'A') #define CTRL_P (0x0001+'P'-'A') #define CTRL_R (0x0001+'R'-'A') #define CTRL_S (0x0001+'S'-'A') #define CTRL_T (0x0001+'T'-'A') #define CTRL_V (0x0001+'V'-'A') #define CTRL_Z (0x0001+'Z'-'A') static const key3code_t key2code[] = { /* CONTROL-ID Key asc sta ctl hex dec oct bin */ { IDC_BUTTON_STA, CTRL_S, MAKE_BITMASK5( 1, 0, 1, 1, 1, 1, 1), CALC_CLR_BLUE, }, { IDC_BUTTON_AVE, CTRL_A, MAKE_BITMASK5( 1, 1, 1, 1, 1, 1, 1), CALC_CLR_BLUE, }, { IDC_BUTTON_SUM, CTRL_T, MAKE_BITMASK5( 1, 1, 1, 1, 1, 1, 1), CALC_CLR_BLUE, }, { IDC_BUTTON_S, CTRL_D, MAKE_BITMASK5( 1, 1, 1, 1, 1, 1, 1), CALC_CLR_BLUE, }, { IDC_BUTTON_MS, CTRL_M, MAKE_BITMASK5( 1, 0, 1, 1, 1, 1, 1), CALC_CLR_RED, }, { IDC_BUTTON_MR, CTRL_R, MAKE_BITMASK5( 1, 0, 1, 1, 1, 1, 1), CALC_CLR_RED, }, { IDC_BUTTON_MP, CTRL_P, MAKE_BITMASK5( 1, 0, 1, 1, 1, 1, 1), CALC_CLR_RED, }, { IDC_BUTTON_MC, CTRL_L, MAKE_BITMASK5( 1, 0, 1, 1, 1, 1, 1), CALC_CLR_RED, }, { IDC_BUTTON_0, '0', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), CALC_CLR_BLUE, }, { IDC_BUTTON_1, '1', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), CALC_CLR_BLUE, }, { IDC_BUTTON_2, '2', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 0), CALC_CLR_BLUE, }, { IDC_BUTTON_3, '3', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 0), CALC_CLR_BLUE, }, { IDC_BUTTON_4, '4', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 0), CALC_CLR_BLUE, }, { IDC_BUTTON_5, '5', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 0), CALC_CLR_BLUE, }, { IDC_BUTTON_6, '6', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 0), CALC_CLR_BLUE, }, { IDC_BUTTON_7, '7', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 0), CALC_CLR_BLUE, }, { IDC_BUTTON_8, '8', MAKE_BITMASK5( 1, 0, 0, 1, 1, 0, 0), CALC_CLR_BLUE, }, { IDC_BUTTON_9, '9', MAKE_BITMASK5( 1, 0, 0, 1, 1, 0, 0), CALC_CLR_BLUE, }, { IDC_BUTTON_DOT, '.', MAKE_BITMASK5( 1, 0, 0, 0, 1, 0, 0), CALC_CLR_BLUE, }, { IDC_BUTTON_DOT, ',', MAKE_BITMASK5( 1, 0, 0, 0, 1, 0, 0), -1, }, { IDC_BUTTON_ADD, '+', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), CALC_CLR_RED, }, { IDC_BUTTON_SUB, '-', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), CALC_CLR_RED, }, { IDC_BUTTON_MULT, '*', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), CALC_CLR_RED, }, { IDC_BUTTON_DIV, '/', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), CALC_CLR_RED, }, { IDC_BUTTON_AND, '&', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), CALC_CLR_RED, }, { IDC_BUTTON_OR, '|', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), CALC_CLR_RED, }, { IDC_BUTTON_XOR, '^', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), CALC_CLR_RED, }, { IDC_BUTTON_LSH, '<', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), CALC_CLR_RED, }, { IDC_BUTTON_NOT, '~', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), CALC_CLR_RED, }, { IDC_BUTTON_INT, ';', MAKE_BITMASK5( 1, 0, 0, 0, 1, 0, 0), CALC_CLR_RED, }, { IDC_BUTTON_EQU, '=', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), CALC_CLR_RED, }, { IDC_BUTTON_A, 'A', MAKE_BITMASK5( 1, 0, 0, 1, 0, 0, 0), CALC_CLR_BLUE, }, { IDC_BUTTON_B, 'B', MAKE_BITMASK5( 1, 0, 0, 1, 0, 0, 0), CALC_CLR_BLUE, }, { IDC_BUTTON_C, 'C', MAKE_BITMASK5( 1, 0, 0, 1, 0, 0, 0), CALC_CLR_BLUE, }, { IDC_BUTTON_D, 'D', MAKE_BITMASK5( 1, 0, 0, 1, 0, 0, 0), CALC_CLR_BLUE, }, { IDC_BUTTON_E, 'E', MAKE_BITMASK5( 1, 0, 0, 1, 0, 0, 0), CALC_CLR_BLUE, }, { IDC_BUTTON_F, 'F', MAKE_BITMASK5( 1, 0, 0, 1, 0, 0, 0), CALC_CLR_BLUE, }, { IDC_CHECK_HYP, 'H', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), -1, }, { IDC_CHECK_INV, 'I', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), -1, }, { IDC_BUTTON_LOG, 'L', MAKE_BITMASK5( 1, 0, 0, 0, 1, 0, 0), CALC_CLR_PURP, }, { IDC_BUTTON_DMS, 'M', MAKE_BITMASK5( 1, 0, 0, 0, 1, 0, 0), CALC_CLR_PURP, }, { IDC_BUTTON_LN, 'N', MAKE_BITMASK5( 1, 0, 0, 0, 1, 0, 0), CALC_CLR_PURP, }, { IDC_BUTTON_PI, 'P', MAKE_BITMASK5( 1, 0, 0, 0, 1, 0, 0), CALC_CLR_BLUE, }, { IDC_BUTTON_RX, 'R', MAKE_BITMASK5( 1, 0, 0, 0, 1, 0, 0), CALC_CLR_PURP, }, { IDC_BUTTON_SIN, 'S', MAKE_BITMASK5( 1, 0, 0, 0, 1, 0, 0), CALC_CLR_PURP, }, { IDC_BUTTON_COS, 'O', MAKE_BITMASK5( 1, 0, 0, 0, 1, 0, 0), CALC_CLR_PURP, }, { IDC_BUTTON_TAN, 'T', MAKE_BITMASK5( 1, 0, 0, 0, 1, 0, 0), CALC_CLR_PURP, }, { IDC_BUTTON_FE, 'V', MAKE_BITMASK5( 1, 0, 0, 0, 1, 0, 0), CALC_CLR_PURP, }, { IDC_BUTTON_EXP, 'X', MAKE_BITMASK5( 1, 0, 0, 0, 1, 0, 0), CALC_CLR_PURP, }, { IDC_BUTTON_XeY, 'Y', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), CALC_CLR_PURP, }, { IDC_BUTTON_SQRT, '@', MAKE_BITMASK5( 1, 0, 0, 0, 1, 0, 0), CALC_CLR_BLUE, }, { IDC_BUTTON_Xe2, '@', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), CALC_CLR_PURP, }, { IDC_BUTTON_Xe3, '#', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), CALC_CLR_PURP, }, { IDC_BUTTON_NF, '!', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), CALC_CLR_PURP, }, { IDC_BUTTON_LEFTPAR, '(', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), CALC_CLR_PURP, }, { IDC_BUTTON_RIGHTPAR, ')', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), CALC_CLR_PURP, }, { IDC_BUTTON_MOD, '%', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), CALC_CLR_RED, }, { IDC_BUTTON_PERCENT, '%', MAKE_BITMASK5( 1, 0, 0, 0, 1, 0, 0), CALC_CLR_BLUE, }, /*----------------------------------------------------------------------*/ { IDC_BUTTON_DAT, VK_INSERT, MAKE_BITMASK5( 0, 1, 0, 1, 1, 1, 1), CALC_CLR_BLUE, }, { IDC_BUTTON_EQU, VK_RETURN, MAKE_BITMASK5( 0, 0, 0, 1, 1, 1, 1), CALC_CLR_RED, }, { IDC_BUTTON_CANC, VK_ESCAPE, MAKE_BITMASK5( 0, 0, 0, 1, 1, 1, 1), CALC_CLR_RED, }, { IDC_BUTTON_CE, VK_DELETE, MAKE_BITMASK5( 0, 0, 0, 1, 1, 1, 1), CALC_CLR_RED, }, { IDC_BUTTON_BACK, VK_BACK, MAKE_BITMASK5( 0, 0, 0, 1, 1, 1, 1), CALC_CLR_RED, }, { IDC_RADIO_HEX, VK_F5, MAKE_BITMASK5( 0, 0, 0, 1, 1, 1, 1), -1, }, { IDC_RADIO_DEC, VK_F6, MAKE_BITMASK5( 0, 0, 0, 1, 1, 1, 1), -1, }, { IDC_RADIO_OCT, VK_F7, MAKE_BITMASK5( 0, 0, 0, 1, 1, 1, 1), -1, }, { IDC_RADIO_BIN, VK_F8, MAKE_BITMASK5( 0, 0, 0, 1, 1, 1, 1), -1, }, { IDC_BUTTON_SIGN, VK_F9, MAKE_BITMASK5( 0, 0, 0, 1, 1, 1, 1), CALC_CLR_BLUE, }, }; static const key2code_t key2code_base16[] = { { VK_F2, IDC_RADIO_DWORD, }, { VK_F3, IDC_RADIO_WORD, }, { VK_F4, IDC_RADIO_BYTE, }, { VK_F12, IDC_RADIO_QWORD, }, }; static const key2code_t key2code_base10[] = { { VK_F2, IDC_RADIO_DEG, }, { VK_F3, IDC_RADIO_RAD, }, { VK_F4, IDC_RADIO_GRAD, }, }; static const WORD operator_codes[] = { /* CONTROL-ID operator */ (WORD)IDC_STATIC, // RPN_OPERATOR_PARENT IDC_BUTTON_PERCENT, // RPN_OPERATOR_PERCENT IDC_BUTTON_EQU, // RPN_OPERATOR_EQUAL IDC_BUTTON_OR, // RPN_OPERATOR_OR IDC_BUTTON_XOR, // RPN_OPERATOR_XOR IDC_BUTTON_AND, // RPN_OPERATOR_AND IDC_BUTTON_LSH, // RPN_OPERATOR_LSH IDC_BUTTON_ADD, // RPN_OPERATOR_ADD IDC_BUTTON_SUB, // RPN_OPERATOR_SUB IDC_BUTTON_MULT, // RPN_OPERATOR_MULT IDC_BUTTON_DIV, // RPN_OPERATOR_DIV IDC_BUTTON_MOD, // RPN_OPERATOR_MOD }; typedef void (*rpn_callback1)(calc_number_t *); typedef struct { WORD idc; BYTE range; BYTE check_nan; rpn_callback1 direct; rpn_callback1 inverse; rpn_callback1 hyperb; rpn_callback1 inv_hyp; } function_table_t; static void run_pow(calc_number_t *number); static void run_sqr(calc_number_t *number); static void run_fe(calc_number_t *number); static void run_dat_sta(calc_number_t *number); static void run_mp(calc_number_t *c); static void run_mm(calc_number_t *c); static void run_ms(calc_number_t *c); static void run_mw(calc_number_t *c); static void run_canc(calc_number_t *c); static void run_rpar(calc_number_t *c); static void run_lpar(calc_number_t *c); static const function_table_t function_table[] = { { IDC_BUTTON_SIN, MODIFIER_INV|MODIFIER_HYP, 1, rpn_sin, rpn_asin, rpn_sinh, rpn_asinh }, { IDC_BUTTON_COS, MODIFIER_INV|MODIFIER_HYP, 1, rpn_cos, rpn_acos, rpn_cosh, rpn_acosh }, { IDC_BUTTON_TAN, MODIFIER_INV|MODIFIER_HYP, 1, rpn_tan, rpn_atan, rpn_tanh, rpn_atanh }, { IDC_BUTTON_INT, MODIFIER_INV, 1, rpn_int, rpn_frac, NULL, NULL }, { IDC_BUTTON_RX, 0, 1, rpn_reci, NULL, NULL, NULL }, { IDC_BUTTON_NOT, 0, 1, rpn_not, NULL, NULL, NULL }, { IDC_BUTTON_PI, MODIFIER_INV, 0, rpn_pi, rpn_2pi, NULL, NULL }, { IDC_BUTTON_Xe2, MODIFIER_INV, 1, rpn_exp2, rpn_sqrt, NULL, NULL }, { IDC_BUTTON_Xe3, MODIFIER_INV, 1, rpn_exp3, rpn_cbrt, NULL, NULL }, { IDC_BUTTON_LN, MODIFIER_INV, 1, rpn_ln, rpn_exp, NULL, NULL }, { IDC_BUTTON_LOG, MODIFIER_INV, 1, rpn_log, rpn_exp10, NULL, NULL }, { IDC_BUTTON_NF, 0, 1, rpn_fact, NULL, NULL, NULL }, { IDC_BUTTON_AVE, 0, 0, rpn_ave, NULL, NULL, NULL }, { IDC_BUTTON_SUM, 0, 0, rpn_sum, NULL, NULL, NULL }, { IDC_BUTTON_S, MODIFIER_INV, 0, rpn_s_m1, rpn_s, NULL, NULL }, { IDC_BUTTON_XeY, MODIFIER_INV, 1, run_pow, run_sqr, NULL, NULL }, { IDC_BUTTON_SQRT, MODIFIER_INV, 1, rpn_sqrt, NULL, NULL, NULL }, { IDC_BUTTON_DMS, MODIFIER_INV, 1, rpn_dec2dms, rpn_dms2dec, NULL, NULL }, { IDC_BUTTON_FE, 0, 1, run_fe, NULL, NULL, NULL }, { IDC_BUTTON_DAT, 0, 1, run_dat_sta, NULL, NULL, NULL, }, { IDC_BUTTON_MP, MODIFIER_INV, 1, run_mp, run_mm, NULL, NULL, }, { IDC_BUTTON_MS, MODIFIER_INV, 1, run_ms, run_mw, NULL, NULL, }, { IDC_BUTTON_CANC, 0, 0, run_canc, NULL, NULL, NULL, }, { IDC_BUTTON_RIGHTPAR, 0, 1, run_rpar, NULL, NULL, NULL, }, { IDC_BUTTON_LEFTPAR, 0, 0, run_lpar, NULL, NULL, NULL, }, }; /* */ calc_t calc; static void load_config(void) { TCHAR buf[32]; DWORD tmp; /* Try to load last selected layout */ GetProfileString(TEXT("SciCalc"), TEXT("layout"), TEXT("0"), buf, SIZEOF(buf)); if (_stscanf(buf, TEXT("%ld"), &calc.layout) != 1) calc.layout = CALC_LAYOUT_STANDARD; /* Try to load last selected formatting option */ GetProfileString(TEXT("SciCalc"), TEXT("UseSep"), TEXT("0"), buf, SIZEOF(buf)); if (_stscanf(buf, TEXT("%ld"), &tmp) != 1) calc.usesep = FALSE; else calc.usesep = (tmp == 1) ? TRUE : FALSE; /* memory is empty at startup */ calc.is_memory = FALSE; /* acquire regional settings */ calc.sDecimal_len = GetProfileString(TEXT("intl"), TEXT("sDecimal"), TEXT("."), calc.sDecimal, SIZEOF(calc.sDecimal)); calc.sThousand_len = GetProfileString(TEXT("intl"), TEXT("sThousand"), TEXT(","), calc.sThousand, SIZEOF(calc.sThousand)); } static void save_config(void) { TCHAR buf[32]; _stprintf(buf, TEXT("%lu"), calc.layout); WriteProfileString(TEXT("SciCalc"), TEXT("layout"), buf); WriteProfileString(TEXT("SciCalc"), TEXT("UseSep"), (calc.usesep==TRUE) ? TEXT("1") : TEXT("0")); } static LRESULT post_key_press(LPARAM lParam, WORD idc) { HWND hCtlWnd = GetDlgItem(calc.hWnd,idc); TCHAR ClassName[64]; /* check if the key is enabled! */ if (!IsWindowEnabled(hCtlWnd)) return 1; if (!GetClassName(hCtlWnd, ClassName, SIZEOF(ClassName))) return 1; if (!_tcscmp(ClassName, TEXT("Button"))) { DWORD dwStyle = GetWindowLong(hCtlWnd, GWL_STYLE) & 0xF; /* Set states for press/release, but only for push buttons */ if (dwStyle == BS_PUSHBUTTON || dwStyle == BS_DEFPUSHBUTTON || dwStyle == BS_OWNERDRAW) { if (!(lParam & KEY_WAS_DOWN)) { PostMessage(hCtlWnd, BM_SETSTATE, 1, 0); } else if ((lParam & KEY_IS_UP)) { PostMessage(hCtlWnd, BM_SETSTATE, 0, 0); PostMessage(hCtlWnd, BM_CLICK, 0, 0); } return 1; } } /* default action: simple click event at key release */ if ((lParam & KEY_IS_UP)) { PostMessage(hCtlWnd, BM_CLICK, 0, 0); } return 1; } static int vk2ascii(unsigned int vk) { unsigned short int s; int scan; BYTE state[256]; HKL layout=GetKeyboardLayout(0); if(!GetKeyboardState(state)) return 0; scan=MapVirtualKeyEx(vk, 0, layout); s = 0; if (ToAsciiEx(vk, scan, state, &s, 0, layout)>0) { /* convert to upper case */ if (s >= 'a' && s <= 'z') s = s - 'a' + 'A'; /* add check to CTRL key */ if (vk >= 'A' && vk <= 'Z' && s >= CTRL_A && s <= CTRL_Z) s |= CTRL_FLAG; else if (GetAsyncKeyState(VK_MENU) < 0) s |= ALT_FLAG; return s; } return 0; } static int process_vk_key(WPARAM wParam, LPARAM lParam) { const key2code_t *k; unsigned int x; unsigned short int ch; ch = vk2ascii(LOWORD(wParam)); if ((lParam & KEY_IS_UP)) { /* Test for "copy" to clipboard */ if (ch == (CTRL_C|CTRL_FLAG)) { SendMessage(calc.hWnd, WM_COMMAND, IDM_EDIT_COPY, 0); return 1; } /* Test for "paste" from clipboard */ if (ch == (CTRL_V|CTRL_FLAG)) { SendMessage(calc.hWnd, WM_COMMAND, IDM_EDIT_PASTE, 0); return 1; } /* Test of help menu */ if (LOWORD(wParam) == VK_F1) { SendMessage(calc.hWnd, WM_COMMAND, IDM_HELP_HELP, 0); return 1; } } for (x=0; xkey == LOWORD(wParam)) { return post_key_press(lParam, k->idc); } k++; } while (--x); } return 0; } #ifdef USE_KEYBOARD_HOOK static LRESULT CALLBACK KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam) { if(nCode<0 || calc.is_menu_on) return CallNextHookEx(calc.hKeyboardHook,nCode,wParam,lParam); if(nCode==HC_ACTION) if (process_vk_key(wParam, lParam)) return; return CallNextHookEx(calc.hKeyboardHook,nCode,wParam,lParam); } #endif static void update_lcd_display(HWND hwnd) { /* +20 is the additional space for separator mode */ TCHAR *tmp = (TCHAR *)alloca((calc.ptr-calc.buffer+3+20)*sizeof(TCHAR)); if (calc.buffer[0] == TEXT('\0')) _tcscpy(tmp, TEXT("0")); else _tcscpy(tmp, calc.buffer); /* add final '.' in decimal mode (if it's missing) */ if (calc.base == IDC_RADIO_DEC) { if (_tcschr(tmp, TEXT('.')) == NULL) _tcscat(tmp, TEXT(".")); } /* if separator mode is on, let's add an additional space */ if (calc.usesep && !calc.sci_in && !calc.sci_out && !calc.is_nan) { /* go to the integer part of the string */ TCHAR *p = _tcschr(tmp, TEXT('.')); TCHAR *e = _tcschr(tmp, TEXT('\0')); int n=0, t; if (p == NULL) p = e; switch (calc.base) { case IDC_RADIO_HEX: case IDC_RADIO_BIN: t = 4; break; default: /* fall here for: IDC_RADIO_DEC: IDC_RADIO_OCT: */ t = 3; break; } while (--p > tmp) { if (++n == t && *(p-1) != TEXT('-')) { memmove(p+1, p, (e-p+1)*sizeof(TCHAR)); e++; *p = TEXT(' '); n = 0; } } /* if decimal mode, apply regional settings */ if (calc.base == IDC_RADIO_DEC) { TCHAR *p = tmp; TCHAR *e = _tcschr(tmp, TEXT('.')); /* searching for thousands default separator */ while (p < e) { if (*p == TEXT(' ')) { memmove(p+calc.sThousand_len, p+1, _tcslen(p)*sizeof(TCHAR)); memcpy(p, calc.sThousand, calc.sThousand_len*sizeof(TCHAR)); p += calc.sThousand_len; } else p++; } /* update decimal point too. */ memmove(p+calc.sDecimal_len, p+1, _tcslen(p)*sizeof(TCHAR)); memcpy(p, calc.sDecimal, calc.sDecimal_len*sizeof(TCHAR)); } } else { TCHAR *p = _tcschr(tmp, TEXT('.')); /* update decimal point when usesep is false */ if (p != NULL) { memmove(p+calc.sDecimal_len, p+1, _tcslen(p)*sizeof(TCHAR)); memcpy(p, calc.sDecimal, calc.sDecimal_len*sizeof(TCHAR)); } } SendDlgItemMessage(hwnd, IDC_TEXT_OUTPUT, WM_SETTEXT, (WPARAM)0, (LPARAM)tmp); } static void update_parent_display(HWND hWnd) { TCHAR str[8]; int n = eval_parent_count(); if (!n) str[0] = TEXT('\0'); else _stprintf(str,TEXT("(=%d"), n); SendDlgItemMessage(hWnd, IDC_TEXT_PARENT, WM_SETTEXT, 0, (LPARAM)str); } static void build_operand(HWND hwnd, DWORD idc) { unsigned int i = 0, n; if (idc == IDC_BUTTON_DOT) { /* if dot is the first char, it's added automatically */ if (calc.buffer == calc.ptr) { *calc.ptr++ = TEXT('0'); *calc.ptr++ = TEXT('.'); *calc.ptr = TEXT('\0'); update_lcd_display(hwnd); return; } /* if pressed dot and it's already in the string, then return */ if (_tcschr(calc.buffer, TEXT('.')) != NULL) return; } if (idc != IDC_STATIC) { while (idc != key2code[i].idc) i++; } n = calc.ptr - calc.buffer; if (idc == IDC_BUTTON_0 && n == 0) { /* no need to put the dot because it's handled by update_lcd_display() */ calc.buffer[0] = TEXT('0'); calc.buffer[1] = TEXT('\0'); update_lcd_display(hwnd); return; } switch (calc.base) { case IDC_RADIO_HEX: if (n >= 16) return; break; case IDC_RADIO_DEC: if (n >= SIZEOF(calc.buffer)-1) return; if (calc.sci_in) { if (idc != IDC_STATIC) calc.esp = (calc.esp * 10 + (key2code[i].key-'0')) % LOCAL_EXP_SIZE; if (calc.ptr == calc.buffer) _stprintf(calc.ptr, TEXT("0.e%+d"), calc.esp); else _stprintf(calc.ptr, TEXT("e%+d"), calc.esp); update_lcd_display(hwnd); return; } break; case IDC_RADIO_OCT: if (n >= 22) return; break; case IDC_RADIO_BIN: if (n >= 64) return; break; } calc.ptr += _stprintf(calc.ptr, TEXT("%C"), key2code[i].key); update_lcd_display(hwnd); } static void prepare_rpn_result(calc_number_t *rpn, TCHAR *buffer, int size, int base) { if (calc.is_nan) { rpn_zero(&calc.code); LoadString(calc.hInstance, IDS_MATH_ERROR, buffer, size); return; } prepare_rpn_result_2(rpn, buffer, size, base); } static void display_rpn_result(HWND hwnd, calc_number_t *rpn) { calc.sci_in = FALSE; prepare_rpn_result(rpn, calc.buffer, SIZEOF(calc.buffer), calc.base); calc.ptr = calc.buffer + _tcslen(calc.buffer); update_lcd_display(hwnd); calc.ptr = calc.buffer; update_parent_display(hwnd); } static int get_modifiers(HWND hwnd) { int modifiers = 0; if (SendDlgItemMessage(hwnd, IDC_CHECK_INV, BM_GETCHECK, 0, 0)) modifiers |= MODIFIER_INV; if (SendDlgItemMessage(hwnd, IDC_CHECK_HYP, BM_GETCHECK, 0, 0)) modifiers |= MODIFIER_HYP; return modifiers; } static void convert_text2number(calc_number_t *a) { /* if the screen output buffer is empty, then */ /* the operand is taken from the last input */ if (calc.buffer == calc.ptr) { /* if pushed valued is ZERO then we should grab it */ if (!_tcscmp(calc.buffer, TEXT("0.")) || !_tcscmp(calc.buffer, TEXT("0"))) /* this zero is good for both integer and decimal */ rpn_zero(a); else rpn_copy(a, &calc.code); return; } /* ZERO is the default value for all numeric bases */ rpn_zero(a); convert_text2number_2(a); } static const struct _update_check_menus { DWORD *sel; WORD idm; WORD idc; } upd[] = { { &calc.layout, IDM_VIEW_STANDARD, CALC_LAYOUT_STANDARD }, { &calc.layout, IDM_VIEW_SCIENTIFIC, CALC_LAYOUT_SCIENTIFIC }, /*-----------------------------------------*/ { &calc.base, IDM_VIEW_HEX, IDC_RADIO_HEX, }, { &calc.base, IDM_VIEW_DEC, IDC_RADIO_DEC, }, { &calc.base, IDM_VIEW_OCT, IDC_RADIO_OCT, }, { &calc.base, IDM_VIEW_BIN, IDC_RADIO_BIN, }, /*-----------------------------------------*/ { &calc.degr, IDM_VIEW_DEG, IDC_RADIO_DEG, }, { &calc.degr, IDM_VIEW_RAD, IDC_RADIO_RAD, }, { &calc.degr, IDM_VIEW_GRAD, IDC_RADIO_GRAD, }, /*-----------------------------------------*/ { &calc.size, IDM_VIEW_QWORD, IDC_RADIO_QWORD, }, { &calc.size, IDM_VIEW_DWORD, IDC_RADIO_DWORD, }, { &calc.size, IDM_VIEW_WORD, IDC_RADIO_WORD, }, { &calc.size, IDM_VIEW_BYTE, IDC_RADIO_BYTE, }, }; static void update_menu(HWND hwnd) { HMENU hMenu = GetSubMenu(GetMenu(hwnd), 1); unsigned int x; for (x=0; xnext); rpn_free(&s->num); free(s); } calc.stat = p; } static void delete_stat_item(int n) { statistic_t *p = calc.stat; statistic_t *s; if (n == 0) { calc.stat = (statistic_t *)p->next; rpn_free(&p->num); free(p); } else { s = (statistic_t *)p->next; while (--n) { p = s; s = (statistic_t *)p->next; } p->next = s->next; rpn_free(&s->num); free(s); } } static LRESULT CALLBACK DlgStatProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { TCHAR buffer[SIZEOF(calc.buffer)]; DWORD n; switch (msg) { case WM_INITDIALOG: return TRUE; case WM_COMMAND: switch (LOWORD(wp)) { case IDC_LIST_STAT: if (HIWORD(wp) == CBN_DBLCLK) SendMessage(hWnd, WM_COMMAND, (WPARAM)IDC_BUTTON_LOAD, 0); return TRUE; case IDC_BUTTON_RET: SetFocus(GetDlgItem(GetParent(hWnd), IDC_BUTTON_FOCUS)); return TRUE; case IDC_BUTTON_LOAD: n = SendDlgItemMessage(hWnd, IDC_LIST_STAT, LB_GETCURSEL, 0, 0); if (n == (DWORD)-1) return TRUE; PostMessage(GetParent(hWnd), WM_LOAD_STAT, (WPARAM)n, 0); return TRUE; case IDC_BUTTON_CD: n = SendDlgItemMessage(hWnd, IDC_LIST_STAT, LB_GETCURSEL, 0, 0); if (n == (DWORD)-1) return TRUE; SendDlgItemMessage(hWnd, IDC_LIST_STAT, LB_DELETESTRING, (WPARAM)n, 0); update_n_stats_items(hWnd, buffer); delete_stat_item(n); return TRUE; case IDC_BUTTON_CAD: SendDlgItemMessage(hWnd, IDC_LIST_STAT, LB_RESETCONTENT, 0, 0); clean_stat_list(); update_n_stats_items(hWnd, buffer); return TRUE; } break; case WM_CLOSE: clean_stat_list(); DestroyWindow(hWnd); return TRUE; case WM_DESTROY: PostMessage(GetParent(hWnd), WM_CLOSE_STATS, 0, 0); return TRUE; case WM_INSERT_STAT: prepare_rpn_result(&(((statistic_t *)lp)->num), buffer, SIZEOF(buffer), ((statistic_t *)lp)->base); SendDlgItemMessage(hWnd, IDC_LIST_STAT, LB_ADDSTRING, 0, (LPARAM)buffer); update_n_stats_items(hWnd, buffer); return TRUE; } return FALSE; } static WPARAM idm_2_idc(int idm) { int x; for (x=0; xnum); rpn_copy(&s->num, a); s->base = calc.base; s->next = NULL; if (p == NULL) calc.stat = s; else { while (p->next != NULL) p = (statistic_t *)(p->next); p->next = s; } PostMessage(calc.hStatWnd, WM_INSERT_STAT, 0, (LPARAM)s); } static void run_mp(calc_number_t *c) { run_operator(&calc.memory, &calc.memory, c, RPN_OPERATOR_ADD); update_memory_flag(calc.hWnd, TRUE); } static void run_mm(calc_number_t *c) { run_operator(&calc.memory, &calc.memory, c, RPN_OPERATOR_SUB); update_memory_flag(calc.hWnd, TRUE); } static void run_ms(calc_number_t *c) { rpn_copy(&calc.memory, c); update_memory_flag(calc.hWnd, rpn_is_zero(&calc.memory) ? FALSE : TRUE); } static void run_mw(calc_number_t *c) { calc_number_t tmp; rpn_copy(&tmp, &calc.memory); rpn_copy(&calc.memory, c); if (calc.is_memory) rpn_copy(c, &tmp); update_memory_flag(calc.hWnd, rpn_is_zero(&calc.memory) ? FALSE : TRUE); } static statistic_t *upload_stat_number(int n) { statistic_t *p = calc.stat; if (p == NULL) return p; while (n--) { p = (statistic_t *)(p->next); if (p == NULL) return p; } #ifndef ENABLE_MULTI_PRECISION if (calc.base != p->base) { if (calc.base == IDC_RADIO_DEC) calc.code.f = (double)p->num.i; else { calc.code.i = (__int64)p->num.f; apply_int_mask(&calc.code); } } else #endif rpn_copy(&calc.code, &p->num); calc.is_nan = FALSE; return p; } static void run_pow(calc_number_t *number) { exec_infix2postfix(number, RPN_OPERATOR_POW); } static void run_sqr(calc_number_t *number) { exec_infix2postfix(number, RPN_OPERATOR_SQR); } static void run_fe(calc_number_t *number) { calc.sci_out = ((calc.sci_out == TRUE) ? FALSE : TRUE); } static void handle_context_menu(HWND hWnd, WPARAM wp, LPARAM lp) { TCHAR text[64]; HMENU hMenu = CreatePopupMenu(); DWORD idm; LoadString(calc.hInstance, IDS_QUICKHELP, text, SIZEOF(text)); AppendMenu(hMenu, MF_STRING | MF_ENABLED, IDM_HELP_HELP, text); idm = (DWORD)TrackPopupMenu(hMenu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RETURNCMD | TPM_RIGHTBUTTON, LOWORD(lp), HIWORD(lp), 0, hWnd, NULL); DestroyMenu(hMenu); if (idm != 0) { HH_POPUP popup; memset(&popup, 0, sizeof(popup)); popup.cbStruct = sizeof(HH_POPUP); popup.clrForeground = 1; popup.clrBackground = -1; popup.pt.x = LOWORD(lp); popup.pt.y = HIWORD(lp); popup.rcMargins.top = -1; popup.rcMargins.bottom = -1; popup.rcMargins.left = -1; popup.rcMargins.right = -1; popup.idString = GetWindowLong((HWND)wp, GWL_ID); // HtmlHelp((HWND)wp, HTMLHELP_PATH("/popups.txt"), HH_DISPLAY_TEXT_POPUP, (DWORD_PTR)&popup); } } static void run_canc(calc_number_t *c) { flush_postfix(); rpn_zero(c); /* clear also scientific display modes */ calc.sci_out = FALSE; calc.sci_in = FALSE; /* clear state of inv and hyp flags */ SendDlgItemMessage(calc.hWnd, IDC_CHECK_INV, BM_SETCHECK, 0, 0); SendDlgItemMessage(calc.hWnd, IDC_CHECK_HYP, BM_SETCHECK, 0, 0); } static void run_rpar(calc_number_t *c) { exec_closeparent(c); } static void run_lpar(calc_number_t *c) { exec_infix2postfix(c, RPN_OPERATOR_PARENT); } static LRESULT CALLBACK SubclassButtonProc(HWND hWnd, WPARAM wp, LPARAM lp) { LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lp; DWORD dwStyle; UINT dwText; TCHAR text[64]; int dx, dy, len; SIZE size; POINT pt; if(dis->CtlType == ODT_BUTTON) { /* * little exception: 1/x has different color * in standard and scientific modes */ if (calc.layout == CALC_LAYOUT_STANDARD && IDC_BUTTON_RX == dis->CtlID) { SetTextColor(dis->hDC, CALC_CLR_BLUE); } else for (dx=0; dxCtlID) { SetTextColor(dis->hDC, key2code[dx].col); break; } } /* button text to write */ len = GetWindowText(dis->hwndItem, text, SIZEOF(text)); /* default state: unpushed & enabled */ dwStyle = 0; dwText = 0; if ((dis->itemState & ODS_DISABLED)) dwText = DSS_DISABLED; if ((dis->itemState & ODS_SELECTED)) dwStyle = DFCS_PUSHED; DrawFrameControl(dis->hDC, &dis->rcItem, DFC_BUTTON, DFCS_BUTTONPUSH | dwStyle); GetTextExtentPoint32(dis->hDC, text, len, &size); dx = ((dis->rcItem.right-dis->rcItem.left) - size.cx) >> 1; dy = ((dis->rcItem.bottom-dis->rcItem.top) - size.cy) >> 1; if ((dwStyle & DFCS_PUSHED)) { dx++; dy++; } pt.x = dis->rcItem.left + dx; pt.y = dis->rcItem.top + dy; DrawState(dis->hDC, NULL, NULL, (LPARAM)text, 0, pt.x, pt.y, size.cx, size.cy, DST_TEXT | dwText); } return 1L; } static LRESULT CALLBACK DlgMainProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { unsigned int x; switch (msg) { case WM_DRAWITEM: return SubclassButtonProc(hWnd, wp, lp); case WM_INITDIALOG: calc.hWnd=hWnd; #ifdef USE_KEYBOARD_HOOK calc.hKeyboardHook=SetWindowsHookEx( WH_KEYBOARD, KeyboardHookProc, NULL, GetCurrentThreadId() ); #endif rpn_zero(&calc.code); calc.sci_out = FALSE; calc.base = IDC_RADIO_DEC; calc.size = IDC_RADIO_QWORD; calc.degr = IDC_RADIO_DEG; calc.ptr = calc.buffer; calc.is_nan = FALSE; enable_allowed_controls(hWnd, IDC_RADIO_DEC); update_radio(hWnd, IDC_RADIO_DEC); update_menu(hWnd); display_rpn_result(hWnd, &calc.code); update_memory_flag(hWnd, calc.is_memory); /* remove keyboard focus */ SetFocus(GetDlgItem(hWnd, IDC_BUTTON_FOCUS)); /* set our calc icon */ SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM)LoadIcon(calc.hInstance, MAKEINTRESOURCE(IDI_CALC_BIG))); SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM)LoadIcon(calc.hInstance, MAKEINTRESOURCE(IDI_CALC_SMALL))); /* update text for decimal button */ SendDlgItemMessage(hWnd, IDC_BUTTON_DOT, WM_SETTEXT, (WPARAM)0, (LPARAM)calc.sDecimal); break; case WM_CTLCOLORSTATIC: if ((HWND)lp == GetDlgItem(hWnd, IDC_TEXT_OUTPUT)) return (LRESULT)GetStockObject(WHITE_BRUSH); break; case WM_HANDLE_CLIPBOARD: handle_clipboard_input(hWnd); return TRUE; case WM_COMMAND: if (HIWORD(wp) != BN_CLICKED && HIWORD(wp) != BN_DBLCLK) break; /* avoid flicker if the user selects from keyboard */ if (GetFocus() != GetDlgItem(hWnd, IDC_BUTTON_FOCUS)) SetFocus(GetDlgItem(hWnd, IDC_BUTTON_FOCUS)); switch (LOWORD(wp)) { case IDM_HELP_ABOUT: DialogBox(calc.hInstance,MAKEINTRESOURCE(IDD_DIALOG_ABOUT), hWnd, AboutDlgProc); return TRUE; case IDM_HELP_HELP: // HtmlHelp(hWnd, HTMLHELP_PATH("/general_information.htm"), HH_DISPLAY_TOPIC, (DWORD_PTR)NULL); return TRUE; case IDM_VIEW_STANDARD: calc.layout = CALC_LAYOUT_STANDARD; calc.action = IDM_VIEW_STANDARD; DestroyWindow(hWnd); save_config(); return TRUE; case IDM_VIEW_SCIENTIFIC: calc.layout = CALC_LAYOUT_SCIENTIFIC; calc.action = IDM_VIEW_SCIENTIFIC; DestroyWindow(hWnd); save_config(); return TRUE; case IDM_VIEW_CONVERSION: /* UNIMPLEMENTED */ return TRUE; case IDM_VIEW_HEX: case IDM_VIEW_DEC: case IDM_VIEW_OCT: case IDM_VIEW_BIN: case IDM_VIEW_DEG: case IDM_VIEW_RAD: case IDM_VIEW_GRAD: case IDM_VIEW_QWORD: case IDM_VIEW_DWORD: case IDM_VIEW_WORD: case IDM_VIEW_BYTE: SendMessage(hWnd, WM_COMMAND, idm_2_idc(LOWORD(wp)), 0); return TRUE; case IDM_EDIT_COPY: handle_copy_command(hWnd); return TRUE; case IDM_EDIT_PASTE: if (calc.Clipboard != NULL) break; calc.Clipboard = ReadClipboard(); if (calc.Clipboard != NULL) { calc.ClipPtr = calc.Clipboard; handle_clipboard_input(hWnd); } return TRUE; case IDM_VIEW_GROUP: calc.usesep = (calc.usesep ? FALSE : TRUE); update_menu(hWnd); update_lcd_display(hWnd); save_config(); return TRUE; case IDC_BUTTON_CE: { calc_number_t tmp; rpn_zero(&tmp); display_rpn_result(hWnd, &tmp); } return TRUE; case IDC_RADIO_DEC: case IDC_RADIO_HEX: case IDC_RADIO_OCT: case IDC_RADIO_BIN: /* GNU WINDRES is bugged so I must always force radio update */ /* (Fix for Win95/98) */ #ifdef _MSC_VER if (calc.base == LOWORD(wp)) break; #endif calc.is_nan = FALSE; update_radio(hWnd, LOWORD(wp)); return TRUE; case IDC_RADIO_DEG: case IDC_RADIO_RAD: case IDC_RADIO_GRAD: /* GNU WINDRES is bugged so I must always force radio update */ /* (Fix for Win95/98) */ #ifdef _MSC_VER if (calc.degr == LOWORD(wp)) break; #endif calc.degr = LOWORD(wp); calc.is_nan = FALSE; update_menu(hWnd); return TRUE; case IDC_RADIO_QWORD: case IDC_RADIO_DWORD: case IDC_RADIO_WORD: case IDC_RADIO_BYTE: /* GNU WINDRES is bugged so I must always force radio update */ /* (Fix for Win95/98) */ #ifdef _MSC_VER if (calc.size == LOWORD(wp)) break; #endif calc.size = LOWORD(wp); calc.is_nan = FALSE; update_menu(hWnd); /* * update the content of the display */ convert_text2number(&calc.code); apply_int_mask(&calc.code); display_rpn_result(hWnd, &calc.code); return TRUE; case IDC_BUTTON_1: case IDC_BUTTON_2: case IDC_BUTTON_3: case IDC_BUTTON_4: case IDC_BUTTON_5: case IDC_BUTTON_6: case IDC_BUTTON_7: case IDC_BUTTON_8: case IDC_BUTTON_9: case IDC_BUTTON_0: case IDC_BUTTON_DOT: case IDC_BUTTON_A: case IDC_BUTTON_B: case IDC_BUTTON_C: case IDC_BUTTON_D: case IDC_BUTTON_E: case IDC_BUTTON_F: calc.is_nan = FALSE; build_operand(hWnd, LOWORD(wp)); return TRUE; case IDC_BUTTON_PERCENT: case IDC_BUTTON_ADD: case IDC_BUTTON_SUB: case IDC_BUTTON_MULT: case IDC_BUTTON_DIV: case IDC_BUTTON_MOD: case IDC_BUTTON_AND: case IDC_BUTTON_OR: case IDC_BUTTON_XOR: case IDC_BUTTON_LSH: case IDC_BUTTON_EQU: if (calc.is_nan) break; for (x=0; x