1 /* 2 * ReactOS Calc (main program) 3 * 4 * Copyright 2007-2017, Carlo Bramini 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 */ 20 21 #include "calc.h" 22 23 #define HTMLHELP_PATH(_pt) _T("%systemroot%\\Help\\calc.chm::") _T(_pt) 24 25 #define MAKE_BITMASK4(_show_b16, _show_b10, _show_b8, _show_b2) \ 26 (((_show_b2) << 0) | \ 27 ((_show_b8) << 1) | \ 28 ((_show_b10) << 2) | \ 29 ((_show_b16) << 3)) 30 31 #define MAKE_BITMASK5(_transl, _is_stats, _is_ctrl, _show_b16, _show_b10, _show_b8, _show_b2) \ 32 (((_show_b2) << 0) | \ 33 ((_show_b8) << 1) | \ 34 ((_show_b10) << 2) | \ 35 ((_show_b16) << 3) | \ 36 ((_is_ctrl) << 5) | \ 37 ((_is_stats) << 6) | \ 38 ((_transl) << 7)) 39 40 #define KEY_IS_UP 0x80000000 41 #define KEY_WAS_DOWN 0x40000000 42 43 #define BITMASK_IS_ASCII 0x80 44 #define BITMASK_IS_STATS 0x40 45 #define BITMASK_IS_CTRL 0x20 46 #define BITMASK_HEX_MASK 0x08 47 #define BITMASK_DEC_MASK 0x04 48 #define BITMASK_OCT_MASK 0x02 49 #define BITMASK_BIN_MASK 0x01 50 51 #define CALC_CLR_RED RGB(0xFF, 0x00, 0x00) 52 #define CALC_CLR_BLUE RGB(0x00, 0x00, 0xFF) 53 #define CALC_CLR_PURP RGB(0xFF, 0x00, 0xFF) 54 55 typedef struct { 56 CHAR key; // Virtual key identifier 57 WORD idc; // IDC for posting message 58 } key2code_t; 59 60 typedef struct { 61 WORD idc; // IDC for posting message 62 CHAR key; // Virtual key identifier 63 BYTE mask; // enable/disable into the various modes. 64 COLORREF col; // color used for drawing the text 65 } key3code_t; 66 67 #define CTRL_FLAG 0x100 68 #define ALT_FLAG 0x200 69 70 #define CTRL_A (0x0001+'A'-'A') 71 #define CTRL_C (0x0001+'C'-'A') 72 #define CTRL_D (0x0001+'D'-'A') 73 #define CTRL_L (0x0001+'L'-'A') 74 #define CTRL_M (0x0001+'M'-'A') 75 #define CTRL_P (0x0001+'P'-'A') 76 #define CTRL_R (0x0001+'R'-'A') 77 #define CTRL_S (0x0001+'S'-'A') 78 #define CTRL_T (0x0001+'T'-'A') 79 #define CTRL_V (0x0001+'V'-'A') 80 #define CTRL_Z (0x0001+'Z'-'A') 81 82 static const key3code_t key2code[] = { 83 /* CONTROL-ID Key asc sta ctl hex dec oct bin */ 84 { IDC_BUTTON_STA, CTRL_S, MAKE_BITMASK5( 1, 0, 1, 1, 1, 1, 1), CALC_CLR_BLUE, }, 85 { IDC_BUTTON_AVE, CTRL_A, MAKE_BITMASK5( 1, 1, 1, 1, 1, 1, 1), CALC_CLR_BLUE, }, 86 { IDC_BUTTON_SUM, CTRL_T, MAKE_BITMASK5( 1, 1, 1, 1, 1, 1, 1), CALC_CLR_BLUE, }, 87 { IDC_BUTTON_S, CTRL_D, MAKE_BITMASK5( 1, 1, 1, 1, 1, 1, 1), CALC_CLR_BLUE, }, 88 { IDC_BUTTON_MS, CTRL_M, MAKE_BITMASK5( 1, 0, 1, 1, 1, 1, 1), CALC_CLR_RED, }, 89 { IDC_BUTTON_MR, CTRL_R, MAKE_BITMASK5( 1, 0, 1, 1, 1, 1, 1), CALC_CLR_RED, }, 90 { IDC_BUTTON_MP, CTRL_P, MAKE_BITMASK5( 1, 0, 1, 1, 1, 1, 1), CALC_CLR_RED, }, 91 { IDC_BUTTON_MC, CTRL_L, MAKE_BITMASK5( 1, 0, 1, 1, 1, 1, 1), CALC_CLR_RED, }, 92 { IDC_BUTTON_0, '0', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), CALC_CLR_BLUE, }, 93 { IDC_BUTTON_1, '1', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), CALC_CLR_BLUE, }, 94 { IDC_BUTTON_2, '2', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 0), CALC_CLR_BLUE, }, 95 { IDC_BUTTON_3, '3', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 0), CALC_CLR_BLUE, }, 96 { IDC_BUTTON_4, '4', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 0), CALC_CLR_BLUE, }, 97 { IDC_BUTTON_5, '5', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 0), CALC_CLR_BLUE, }, 98 { IDC_BUTTON_6, '6', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 0), CALC_CLR_BLUE, }, 99 { IDC_BUTTON_7, '7', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 0), CALC_CLR_BLUE, }, 100 { IDC_BUTTON_8, '8', MAKE_BITMASK5( 1, 0, 0, 1, 1, 0, 0), CALC_CLR_BLUE, }, 101 { IDC_BUTTON_9, '9', MAKE_BITMASK5( 1, 0, 0, 1, 1, 0, 0), CALC_CLR_BLUE, }, 102 { IDC_BUTTON_DOT, '.', MAKE_BITMASK5( 1, 0, 0, 0, 1, 0, 0), CALC_CLR_BLUE, }, 103 { IDC_BUTTON_DOT, ',', MAKE_BITMASK5( 1, 0, 0, 0, 1, 0, 0), -1, }, 104 { IDC_BUTTON_ADD, '+', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), CALC_CLR_RED, }, 105 { IDC_BUTTON_SUB, '-', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), CALC_CLR_RED, }, 106 { IDC_BUTTON_MULT, '*', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), CALC_CLR_RED, }, 107 { IDC_BUTTON_DIV, '/', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), CALC_CLR_RED, }, 108 { IDC_BUTTON_AND, '&', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), CALC_CLR_RED, }, 109 { IDC_BUTTON_OR, '|', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), CALC_CLR_RED, }, 110 { IDC_BUTTON_XOR, '^', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), CALC_CLR_RED, }, 111 { IDC_BUTTON_LSH, '<', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), CALC_CLR_RED, }, 112 { IDC_BUTTON_NOT, '~', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), CALC_CLR_RED, }, 113 { IDC_BUTTON_INT, ';', MAKE_BITMASK5( 1, 0, 0, 0, 1, 0, 0), CALC_CLR_RED, }, 114 { IDC_BUTTON_EQU, '=', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), CALC_CLR_RED, }, 115 { IDC_BUTTON_A, 'A', MAKE_BITMASK5( 1, 0, 0, 1, 0, 0, 0), CALC_CLR_BLUE, }, 116 { IDC_BUTTON_B, 'B', MAKE_BITMASK5( 1, 0, 0, 1, 0, 0, 0), CALC_CLR_BLUE, }, 117 { IDC_BUTTON_C, 'C', MAKE_BITMASK5( 1, 0, 0, 1, 0, 0, 0), CALC_CLR_BLUE, }, 118 { IDC_BUTTON_D, 'D', MAKE_BITMASK5( 1, 0, 0, 1, 0, 0, 0), CALC_CLR_BLUE, }, 119 { IDC_BUTTON_E, 'E', MAKE_BITMASK5( 1, 0, 0, 1, 0, 0, 0), CALC_CLR_BLUE, }, 120 { IDC_BUTTON_F, 'F', MAKE_BITMASK5( 1, 0, 0, 1, 0, 0, 0), CALC_CLR_BLUE, }, 121 { IDC_CHECK_HYP, 'H', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), -1, }, 122 { IDC_CHECK_INV, 'I', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), -1, }, 123 { IDC_BUTTON_LOG, 'L', MAKE_BITMASK5( 1, 0, 0, 0, 1, 0, 0), CALC_CLR_PURP, }, 124 { IDC_BUTTON_DMS, 'M', MAKE_BITMASK5( 1, 0, 0, 0, 1, 0, 0), CALC_CLR_PURP, }, 125 { IDC_BUTTON_LN, 'N', MAKE_BITMASK5( 1, 0, 0, 0, 1, 0, 0), CALC_CLR_PURP, }, 126 { IDC_BUTTON_PI, 'P', MAKE_BITMASK5( 1, 0, 0, 0, 1, 0, 0), CALC_CLR_BLUE, }, 127 { IDC_BUTTON_RX, 'R', MAKE_BITMASK5( 1, 0, 0, 0, 1, 0, 0), CALC_CLR_PURP, }, 128 { IDC_BUTTON_SIN, 'S', MAKE_BITMASK5( 1, 0, 0, 0, 1, 0, 0), CALC_CLR_PURP, }, 129 { IDC_BUTTON_COS, 'O', MAKE_BITMASK5( 1, 0, 0, 0, 1, 0, 0), CALC_CLR_PURP, }, 130 { IDC_BUTTON_TAN, 'T', MAKE_BITMASK5( 1, 0, 0, 0, 1, 0, 0), CALC_CLR_PURP, }, 131 { IDC_BUTTON_FE, 'V', MAKE_BITMASK5( 1, 0, 0, 0, 1, 0, 0), CALC_CLR_PURP, }, 132 { IDC_BUTTON_EXP, 'X', MAKE_BITMASK5( 1, 0, 0, 0, 1, 0, 0), CALC_CLR_PURP, }, 133 { IDC_BUTTON_XeY, 'Y', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), CALC_CLR_PURP, }, 134 { IDC_BUTTON_SQRT, '@', MAKE_BITMASK5( 1, 0, 0, 0, 1, 0, 0), CALC_CLR_BLUE, }, 135 { IDC_BUTTON_Xe2, '@', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), CALC_CLR_PURP, }, 136 { IDC_BUTTON_Xe3, '#', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), CALC_CLR_PURP, }, 137 { IDC_BUTTON_NF, '!', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), CALC_CLR_PURP, }, 138 { IDC_BUTTON_LEFTPAR, '(', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), CALC_CLR_PURP, }, 139 { IDC_BUTTON_RIGHTPAR, ')', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), CALC_CLR_PURP, }, 140 { IDC_BUTTON_MOD, '%', MAKE_BITMASK5( 1, 0, 0, 1, 1, 1, 1), CALC_CLR_RED, }, 141 { IDC_BUTTON_PERCENT, '%', MAKE_BITMASK5( 1, 0, 0, 0, 1, 0, 0), CALC_CLR_BLUE, }, 142 /*----------------------------------------------------------------------*/ 143 { IDC_BUTTON_DAT, VK_INSERT, MAKE_BITMASK5( 0, 1, 0, 1, 1, 1, 1), CALC_CLR_BLUE, }, 144 { IDC_BUTTON_EQU, VK_RETURN, MAKE_BITMASK5( 0, 0, 0, 1, 1, 1, 1), CALC_CLR_RED, }, 145 { IDC_BUTTON_CANC, VK_ESCAPE, MAKE_BITMASK5( 0, 0, 0, 1, 1, 1, 1), CALC_CLR_RED, }, 146 { IDC_BUTTON_CE, VK_DELETE, MAKE_BITMASK5( 0, 0, 0, 1, 1, 1, 1), CALC_CLR_RED, }, 147 { IDC_BUTTON_BACK, VK_BACK, MAKE_BITMASK5( 0, 0, 0, 1, 1, 1, 1), CALC_CLR_RED, }, 148 { IDC_RADIO_HEX, VK_F5, MAKE_BITMASK5( 0, 0, 0, 1, 1, 1, 1), -1, }, 149 { IDC_RADIO_DEC, VK_F6, MAKE_BITMASK5( 0, 0, 0, 1, 1, 1, 1), -1, }, 150 { IDC_RADIO_OCT, VK_F7, MAKE_BITMASK5( 0, 0, 0, 1, 1, 1, 1), -1, }, 151 { IDC_RADIO_BIN, VK_F8, MAKE_BITMASK5( 0, 0, 0, 1, 1, 1, 1), -1, }, 152 { IDC_BUTTON_SIGN, VK_F9, MAKE_BITMASK5( 0, 0, 0, 1, 1, 1, 1), CALC_CLR_BLUE, }, 153 }; 154 155 static const key2code_t key2code_base16[] = { 156 { VK_F2, IDC_RADIO_DWORD, }, 157 { VK_F3, IDC_RADIO_WORD, }, 158 { VK_F4, IDC_RADIO_BYTE, }, 159 { VK_F12, IDC_RADIO_QWORD, }, 160 }; 161 162 static const key2code_t key2code_base10[] = { 163 { VK_F2, IDC_RADIO_DEG, }, 164 { VK_F3, IDC_RADIO_RAD, }, 165 { VK_F4, IDC_RADIO_GRAD, }, 166 }; 167 168 static const WORD operator_codes[] = { 169 /* CONTROL-ID operator */ 170 (WORD)IDC_STATIC, // RPN_OPERATOR_PARENT 171 IDC_BUTTON_PERCENT, // RPN_OPERATOR_PERCENT 172 IDC_BUTTON_EQU, // RPN_OPERATOR_EQUAL 173 IDC_BUTTON_OR, // RPN_OPERATOR_OR 174 IDC_BUTTON_XOR, // RPN_OPERATOR_XOR 175 IDC_BUTTON_AND, // RPN_OPERATOR_AND 176 IDC_BUTTON_LSH, // RPN_OPERATOR_LSH 177 IDC_BUTTON_RSH, // RPN_OPERATOR_RSH 178 IDC_BUTTON_ADD, // RPN_OPERATOR_ADD 179 IDC_BUTTON_SUB, // RPN_OPERATOR_SUB 180 IDC_BUTTON_MULT, // RPN_OPERATOR_MULT 181 IDC_BUTTON_DIV, // RPN_OPERATOR_DIV 182 IDC_BUTTON_MOD, // RPN_OPERATOR_MOD 183 IDC_BUTTON_XeY, // RPN_OPERATOR_POW 184 IDC_BUTTON_XrY, // RPN_OPERATOR_SQR 185 }; 186 187 typedef void (*rpn_callback1)(calc_number_t *); 188 189 typedef struct { 190 WORD idc; 191 BYTE range; 192 BYTE check_nan; 193 rpn_callback1 direct; 194 rpn_callback1 inverse; 195 rpn_callback1 hyperb; 196 rpn_callback1 inv_hyp; 197 } function_table_t; 198 199 static void run_fe(calc_number_t *number); 200 static void run_dat_sta(calc_number_t *number); 201 static void run_mp(calc_number_t *c); 202 static void run_mm(calc_number_t *c); 203 static void run_ms(calc_number_t *c); 204 static void run_mw(calc_number_t *c); 205 static void run_canc(calc_number_t *c); 206 static void run_rpar(calc_number_t *c); 207 static void run_lpar(calc_number_t *c); 208 209 static const function_table_t function_table[] = { 210 { IDC_BUTTON_SIN, MODIFIER_INV|MODIFIER_HYP, 1, rpn_sin, rpn_asin, rpn_sinh, rpn_asinh }, 211 { IDC_BUTTON_COS, MODIFIER_INV|MODIFIER_HYP, 1, rpn_cos, rpn_acos, rpn_cosh, rpn_acosh }, 212 { IDC_BUTTON_TAN, MODIFIER_INV|MODIFIER_HYP, 1, rpn_tan, rpn_atan, rpn_tanh, rpn_atanh }, 213 { IDC_BUTTON_INT, MODIFIER_INV, 1, rpn_int, rpn_frac, NULL, NULL }, 214 { IDC_BUTTON_RX, 0, 1, rpn_reci, NULL, NULL, NULL }, 215 { IDC_BUTTON_NOT, 0, 1, rpn_not, NULL, NULL, NULL }, 216 { IDC_BUTTON_PI, MODIFIER_INV, 0, rpn_pi, rpn_2pi, NULL, NULL }, 217 { IDC_BUTTON_Xe2, MODIFIER_INV, 1, rpn_exp2, rpn_sqrt, NULL, NULL }, 218 { IDC_BUTTON_Xe3, MODIFIER_INV, 1, rpn_exp3, rpn_cbrt, NULL, NULL }, 219 { IDC_BUTTON_LN, MODIFIER_INV, 1, rpn_ln, rpn_exp, NULL, NULL }, 220 { IDC_BUTTON_LOG, MODIFIER_INV, 1, rpn_log, rpn_exp10, NULL, NULL }, 221 { IDC_BUTTON_NF, 0, 1, rpn_fact, NULL, NULL, NULL }, 222 { IDC_BUTTON_AVE, MODIFIER_INV, 0, rpn_ave, rpn_ave2, NULL, NULL }, 223 { IDC_BUTTON_SUM, MODIFIER_INV, 0, rpn_sum, rpn_sum2, NULL, NULL }, 224 { IDC_BUTTON_S, MODIFIER_INV, 0, rpn_s_m1, rpn_s, NULL, NULL }, 225 { IDC_BUTTON_SQRT, MODIFIER_INV, 1, rpn_sqrt, NULL, NULL, NULL }, 226 { IDC_BUTTON_DMS, MODIFIER_INV, 1, rpn_dec2dms, rpn_dms2dec, NULL, NULL }, 227 { IDC_BUTTON_FE, 0, 1, run_fe, NULL, NULL, NULL }, 228 { IDC_BUTTON_DAT, 0, 1, run_dat_sta, NULL, NULL, NULL, }, 229 { IDC_BUTTON_MP, MODIFIER_INV|NO_CHAIN, 1, run_mp, run_mm, NULL, NULL, }, 230 { IDC_BUTTON_MS, MODIFIER_INV|NO_CHAIN, 1, run_ms, run_mw, NULL, NULL, }, 231 { IDC_BUTTON_CANC, NO_CHAIN, 0, run_canc, NULL, NULL, NULL, }, 232 { IDC_BUTTON_RIGHTPAR, NO_CHAIN, 1, run_rpar, NULL, NULL, NULL, }, 233 { IDC_BUTTON_LEFTPAR, NO_CHAIN, 0, run_lpar, NULL, NULL, NULL, }, 234 }; 235 236 /* Sub-classing information for theming support */ 237 typedef struct{ 238 BOOL bHover; 239 WNDPROC oldProc; 240 } BTNINFO,*LPBTNINFO; 241 242 243 /* 244 * Global variable declaration 245 */ 246 247 calc_t calc; 248 249 /* Hot-state info for theming support */ 250 BTNINFO BtnInfo[255]; 251 UINT BtnCount; 252 253 static void UpdateNumberIntl(void) 254 { 255 /* Get current user defaults */ 256 if (!GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, calc.sDecimal, SIZEOF(calc.sDecimal))) 257 StringCbCopy(calc.sDecimal, sizeof(calc.sDecimal), _T(".")); 258 259 if (!GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, calc.sThousand, SIZEOF(calc.sThousand))) 260 StringCbCopy(calc.sThousand, sizeof(calc.sThousand), _T(",")); 261 262 /* get the string lengths */ 263 calc.sDecimal_len = _tcslen(calc.sDecimal); 264 calc.sThousand_len = _tcslen(calc.sThousand); 265 } 266 267 static int LoadRegInt(LPCTSTR lpszApp, LPCTSTR lpszKey, int iDefault) 268 { 269 HKEY hKey; 270 int iValue; 271 DWORD tmp; 272 273 if (RegOpenKeyEx(HKEY_CURRENT_USER, lpszApp, 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) 274 { 275 /* Try to load integer value */ 276 tmp = sizeof(int); 277 278 if (RegQueryValueEx(hKey, lpszKey, NULL, NULL, (LPBYTE)&iValue, &tmp) == ERROR_SUCCESS) 279 iDefault = iValue; 280 281 /* close the key */ 282 RegCloseKey(hKey); 283 } 284 285 return iDefault; 286 } 287 288 static void SaveRegInt(LPCTSTR lpszApp, LPCTSTR lpszKey, int iValue) 289 { 290 HKEY hKey; 291 292 if (RegCreateKeyEx(HKEY_CURRENT_USER, lpszApp, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hKey, NULL) == ERROR_SUCCESS) 293 { 294 RegSetValueEx(hKey, lpszKey, 0, REG_DWORD, (const BYTE*)&iValue, sizeof(int)); 295 296 /* close the key */ 297 RegCloseKey(hKey); 298 } 299 } 300 301 static void load_config(void) 302 { 303 OSVERSIONINFO osvi; 304 305 osvi.dwOSVersionInfoSize = sizeof(osvi); 306 GetVersionEx(&osvi); 307 308 switch (osvi.dwPlatformId) { 309 case VER_PLATFORM_WIN32s: 310 case VER_PLATFORM_WIN32_WINDOWS: 311 /* Try to load last selected layout */ 312 calc.layout = GetProfileInt(_T("SciCalc"), _T("layout"), CALC_LAYOUT_STANDARD); 313 314 /* Try to load last selected formatting option */ 315 calc.usesep = (GetProfileInt(_T("SciCalc"), _T("UseSep"), FALSE)) ? TRUE : FALSE; 316 break; 317 318 default: /* VER_PLATFORM_WIN32_NT */ 319 /* Try to load last selected layout */ 320 calc.layout = LoadRegInt(_T("SOFTWARE\\Microsoft\\Calc"), _T("layout"), CALC_LAYOUT_STANDARD); 321 322 /* Try to load last selected formatting option */ 323 calc.usesep = (LoadRegInt(_T("SOFTWARE\\Microsoft\\Calc"), _T("UseSep"), FALSE)) ? TRUE : FALSE; 324 break; 325 } 326 327 /* memory is empty at startup */ 328 calc.is_memory = FALSE; 329 330 /* Get locale info for numbers */ 331 UpdateNumberIntl(); 332 } 333 334 static void save_config(void) 335 { 336 TCHAR buf[32]; 337 OSVERSIONINFO osvi; 338 339 osvi.dwOSVersionInfoSize = sizeof(osvi); 340 GetVersionEx(&osvi); 341 342 switch (osvi.dwPlatformId) { 343 case VER_PLATFORM_WIN32s: 344 case VER_PLATFORM_WIN32_WINDOWS: 345 StringCbPrintf(buf, sizeof(buf), _T("%lu"), calc.layout); 346 WriteProfileString(_T("SciCalc"), _T("layout"), buf); 347 WriteProfileString(_T("SciCalc"), _T("UseSep"), (calc.usesep==TRUE) ? _T("1") : _T("0")); 348 break; 349 350 default: /* VER_PLATFORM_WIN32_NT */ 351 SaveRegInt(_T("SOFTWARE\\Microsoft\\Calc"), _T("layout"), calc.layout); 352 SaveRegInt(_T("SOFTWARE\\Microsoft\\Calc"), _T("UseSep"), calc.usesep); 353 break; 354 } 355 } 356 357 static LRESULT post_key_press(LPARAM lParam, WORD idc) 358 { 359 HWND hCtlWnd = GetDlgItem(calc.hWnd,idc); 360 TCHAR ClassName[64]; 361 362 /* check if the key is enabled! */ 363 if (!IsWindowEnabled(hCtlWnd)) 364 return 1; 365 366 if (!GetClassName(hCtlWnd, ClassName, SIZEOF(ClassName))) 367 return 1; 368 369 if (!_tcscmp(ClassName, WC_BUTTON)) { 370 DWORD dwStyle = GetWindowLongPtr(hCtlWnd, GWL_STYLE) & 0xF; 371 372 /* Set states for press/release, but only for push buttons */ 373 if (dwStyle == BS_PUSHBUTTON || dwStyle == BS_DEFPUSHBUTTON || dwStyle == BS_OWNERDRAW) { 374 if (!(lParam & KEY_WAS_DOWN)) { 375 PostMessage(hCtlWnd, BM_SETSTATE, 1, 0); 376 } else 377 if ((lParam & KEY_IS_UP)) { 378 PostMessage(hCtlWnd, BM_SETSTATE, 0, 0); 379 PostMessage(hCtlWnd, BM_CLICK, 0, 0); 380 } 381 return 1; 382 } 383 } 384 /* default action: simple click event at key release */ 385 if ((lParam & KEY_IS_UP)) { 386 PostMessage(hCtlWnd, BM_CLICK, 0, 0); 387 } 388 return 1; 389 } 390 391 static int vk2ascii(unsigned int vk) 392 { 393 unsigned short int s; 394 int scan; 395 BYTE state[256]; 396 HKL layout=GetKeyboardLayout(0); 397 398 if(!GetKeyboardState(state)) 399 return 0; 400 401 scan=MapVirtualKeyEx(vk, 0, layout); 402 s = 0; 403 if (ToAsciiEx(vk, scan, state, &s, 0, layout)>0) { 404 /* convert to upper case */ 405 if (s >= 'a' && s <= 'z') 406 s = s - 'a' + 'A'; 407 /* add check to CTRL key */ 408 if (vk >= 'A' && vk <= 'Z' && 409 s >= CTRL_A && s <= CTRL_Z) 410 s |= CTRL_FLAG; 411 else 412 if (GetAsyncKeyState(VK_MENU) < 0) 413 s |= ALT_FLAG; 414 return s; 415 } 416 return 0; 417 } 418 419 static int process_vk_key(WPARAM wParam, LPARAM lParam) 420 { 421 const key2code_t *k; 422 unsigned int x; 423 unsigned short int ch; 424 425 ch = vk2ascii(LOWORD(wParam)); 426 if ((lParam & KEY_IS_UP)) { 427 /* Test for "copy" to clipboard */ 428 if (ch == (CTRL_C|CTRL_FLAG)) { 429 SendMessage(calc.hWnd, WM_COMMAND, IDM_EDIT_COPY, 0); 430 return 1; 431 } 432 /* Test for "paste" from clipboard */ 433 if (ch == (CTRL_V|CTRL_FLAG)) { 434 SendMessage(calc.hWnd, WM_COMMAND, IDM_EDIT_PASTE, 0); 435 return 1; 436 } 437 /* Test of help menu */ 438 if (LOWORD(wParam) == VK_F1) { 439 SendMessage(calc.hWnd, WM_COMMAND, IDM_HELP_HELP, 0); 440 return 1; 441 } 442 } 443 444 for (x=0; x<SIZEOF(key2code); x++) { 445 int key = key2code[x].key; 446 if (key2code[x].mask & BITMASK_IS_CTRL) 447 key |= CTRL_FLAG; 448 if ((key == ch && (key2code[x].mask & BITMASK_IS_ASCII)) || 449 (key == LOWORD(wParam) && !(key2code[x].mask & BITMASK_IS_ASCII)) 450 ) { 451 if (GetDlgItem(calc.hWnd, key2code[x].idc) == NULL) 452 continue; 453 return post_key_press(lParam, key2code[x].idc); 454 } 455 } 456 if (calc.layout == CALC_LAYOUT_SCIENTIFIC) { 457 if (calc.base == IDC_RADIO_DEC) { 458 k = key2code_base10; 459 x = SIZEOF(key2code_base10); 460 } else { 461 k = key2code_base16; 462 x = SIZEOF(key2code_base16); 463 } 464 do { 465 if (k->key == LOWORD(wParam)) { 466 return post_key_press(lParam, k->idc); 467 } 468 k++; 469 } while (--x); 470 } 471 return 0; 472 } 473 474 #ifdef USE_KEYBOARD_HOOK 475 static LRESULT CALLBACK 476 KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam) 477 { 478 if(nCode<0 || calc.is_menu_on) 479 return CallNextHookEx(calc.hKeyboardHook,nCode,wParam,lParam); 480 481 if(nCode==HC_ACTION) 482 if (process_vk_key(wParam, lParam)) 483 return; 484 485 return CallNextHookEx(calc.hKeyboardHook,nCode,wParam,lParam); 486 } 487 #endif 488 489 static void update_lcd_display(HWND hwnd) 490 { 491 /* 492 * multiply size of calc.buffer by 2 because it may 493 * happen that separator is used between each digit. 494 * Also added little additional space for dot and '\0'. 495 */ 496 TCHAR tmp[MAX_CALC_SIZE * 2 + 2]; 497 498 if (calc.buffer[0] == _T('\0')) 499 StringCbCopy(tmp, sizeof(tmp), _T("0")); 500 else 501 StringCbCopy(tmp, sizeof(tmp), calc.buffer); 502 503 /* Add final '.' in decimal mode (if it's missing), but 504 * only if it's a result: no append if it prints "ERROR". 505 */ 506 if (calc.base == IDC_RADIO_DEC && !calc.is_nan) { 507 if (_tcschr(tmp, _T('.')) == NULL) 508 StringCbCat(tmp, sizeof(tmp), _T(".")); 509 } 510 /* if separator mode is on, let's add an additional space */ 511 if (calc.usesep && !calc.sci_in && !calc.sci_out && !calc.is_nan) { 512 /* go to the integer part of the string */ 513 TCHAR *p = _tcschr(tmp, _T('.')); 514 TCHAR *e = _tcschr(tmp, _T('\0')); 515 int n=0, t; 516 517 if (p == NULL) p = e; 518 switch (calc.base) { 519 case IDC_RADIO_HEX: 520 case IDC_RADIO_BIN: 521 t = 4; 522 break; 523 default: 524 /* fall here for: 525 IDC_RADIO_DEC: 526 IDC_RADIO_OCT: */ 527 t = 3; 528 break; 529 } 530 while (--p > tmp) { 531 if (++n == t && *(p-1) != _T('-')) { 532 memmove(p+1, p, (e-p+1)*sizeof(TCHAR)); 533 e++; 534 *p = _T(' '); 535 n = 0; 536 } 537 } 538 /* if decimal mode, apply regional settings */ 539 if (calc.base == IDC_RADIO_DEC) { 540 TCHAR *p = tmp; 541 TCHAR *e = _tcschr(tmp, _T('.')); 542 543 /* searching for thousands default separator */ 544 while (p < e) { 545 if (*p == _T(' ')) { 546 memmove(p+calc.sThousand_len, p+1, _tcslen(p)*sizeof(TCHAR)); 547 memcpy(p, calc.sThousand, calc.sThousand_len*sizeof(TCHAR)); 548 p += calc.sThousand_len; 549 } else 550 p++; 551 } 552 /* update decimal point too. */ 553 memmove(p+calc.sDecimal_len, p+1, _tcslen(p)*sizeof(TCHAR)); 554 memcpy(p, calc.sDecimal, calc.sDecimal_len*sizeof(TCHAR)); 555 } 556 } else { 557 TCHAR *p = _tcschr(tmp, _T('.')); 558 559 /* update decimal point when usesep is false */ 560 if (p != NULL) { 561 memmove(p+calc.sDecimal_len, p+1, _tcslen(p)*sizeof(TCHAR)); 562 memcpy(p, calc.sDecimal, calc.sDecimal_len*sizeof(TCHAR)); 563 } 564 } 565 SetDlgItemText(hwnd, IDC_TEXT_OUTPUT, tmp); 566 } 567 568 static void update_parent_display(HWND hWnd) 569 { 570 TCHAR str[8]; 571 int n = eval_parent_count(); 572 573 if (!n) 574 str[0] = _T('\0'); 575 else 576 StringCbPrintf(str, sizeof(str), _T("(=%d"), n); 577 SetDlgItemText(hWnd, IDC_TEXT_PARENT, str); 578 } 579 580 static void build_operand(HWND hwnd, DWORD idc) 581 { 582 unsigned int i = 0, n; 583 size_t cbPtr; 584 585 if (idc == IDC_BUTTON_DOT) { 586 /* if dot is the first char, it's added automatically */ 587 if (calc.buffer == calc.ptr) { 588 *calc.ptr++ = _T('0'); 589 *calc.ptr++ = _T('.'); 590 *calc.ptr = _T('\0'); 591 update_lcd_display(hwnd); 592 return; 593 } 594 /* if pressed dot and it's already in the string, then return */ 595 if (_tcschr(calc.buffer, _T('.')) != NULL) 596 return; 597 } 598 if (idc != IDC_STATIC) { 599 while (idc != key2code[i].idc) i++; 600 } 601 n = calc.ptr - calc.buffer; 602 if (idc == IDC_BUTTON_0 && n == 0) { 603 /* no need to put the dot because it's handled by update_lcd_display() */ 604 calc.buffer[0] = _T('0'); 605 calc.buffer[1] = _T('\0'); 606 update_lcd_display(hwnd); 607 return; 608 } 609 switch (calc.base) { 610 case IDC_RADIO_HEX: 611 if (n >= 16) 612 return; 613 break; 614 case IDC_RADIO_DEC: 615 if (n >= SIZEOF(calc.buffer)-1) 616 return; 617 if (calc.sci_in) { 618 if (idc != IDC_STATIC) 619 calc.esp = (calc.esp * 10 + (key2code[i].key-'0')) % LOCAL_EXP_SIZE; 620 if (calc.ptr == calc.buffer) 621 StringCbPrintf(calc.ptr, sizeof(calc.buffer), _T("0.e%+d"), calc.esp); 622 else { 623 /* adds the dot at the end if the number has no decimal part */ 624 if (!_tcschr(calc.buffer, _T('.'))) 625 *calc.ptr++ = _T('.'); 626 627 cbPtr = sizeof(calc.buffer) - ((BYTE*)calc.ptr - (BYTE*)calc.buffer); 628 StringCbPrintf(calc.ptr, cbPtr, _T("e%+d"), calc.esp); 629 } 630 update_lcd_display(hwnd); 631 return; 632 } 633 break; 634 case IDC_RADIO_OCT: 635 if (n >= 22) 636 return; 637 break; 638 case IDC_RADIO_BIN: 639 if (n >= 64) 640 return; 641 break; 642 } 643 644 cbPtr = sizeof(calc.buffer) - ((BYTE*)calc.ptr - (BYTE*)calc.buffer); 645 StringCbPrintfEx(calc.ptr, cbPtr, &calc.ptr, NULL, STRSAFE_FILL_ON_FAILURE, 646 _T("%C"), key2code[i].key); 647 648 update_lcd_display(hwnd); 649 } 650 651 static void prepare_rpn_result(calc_number_t *rpn, TCHAR *buffer, int size, int base) 652 { 653 if (calc.is_nan) { 654 rpn_zero(&calc.code); 655 LoadString(calc.hInstance, IDS_MATH_ERROR, buffer, size); 656 return; 657 } 658 prepare_rpn_result_2(rpn, buffer, size, base); 659 } 660 661 static void set_rpn_result(HWND hwnd, calc_number_t *rpn) 662 { 663 calc.sci_in = FALSE; 664 prepare_rpn_result(rpn, calc.buffer, SIZEOF(calc.buffer), calc.base); 665 calc.ptr = calc.buffer + _tcslen(calc.buffer); 666 update_lcd_display(hwnd); 667 update_parent_display(hwnd); 668 } 669 670 static void display_rpn_result(HWND hwnd, calc_number_t *rpn) 671 { 672 set_rpn_result(hwnd, rpn); 673 calc.ptr = calc.buffer; 674 } 675 676 static int get_modifiers(HWND hWnd) 677 { 678 int modifiers = 0; 679 680 if (IsDlgButtonChecked(hWnd, IDC_CHECK_INV) == BST_CHECKED) 681 modifiers |= MODIFIER_INV; 682 if (IsDlgButtonChecked(hWnd, IDC_CHECK_HYP) == BST_CHECKED) 683 modifiers |= MODIFIER_HYP; 684 685 return modifiers; 686 } 687 688 static void convert_text2number(calc_number_t *a) 689 { 690 /* if the screen output buffer is empty, then */ 691 /* the operand is taken from the last input */ 692 if (calc.buffer == calc.ptr) { 693 /* if pushed valued is ZERO then we should grab it */ 694 if (!_tcscmp(calc.buffer, _T("0.")) || 695 !_tcscmp(calc.buffer, _T("0"))) 696 /* this zero is good for both integer and decimal */ 697 rpn_zero(a); 698 else 699 rpn_copy(a, &calc.code); 700 return; 701 } 702 /* ZERO is the default value for all numeric bases */ 703 rpn_zero(a); 704 convert_text2number_2(a); 705 } 706 707 static const struct _update_check_menus { 708 DWORD *sel; 709 WORD idm; 710 WORD idc; 711 } upd[] = { 712 { &calc.layout, IDM_VIEW_STANDARD, CALC_LAYOUT_STANDARD }, 713 { &calc.layout, IDM_VIEW_SCIENTIFIC, CALC_LAYOUT_SCIENTIFIC }, 714 { &calc.layout, IDM_VIEW_CONVERSION, CALC_LAYOUT_CONVERSION }, 715 /*-----------------------------------------*/ 716 { &calc.base, IDM_VIEW_HEX, IDC_RADIO_HEX, }, 717 { &calc.base, IDM_VIEW_DEC, IDC_RADIO_DEC, }, 718 { &calc.base, IDM_VIEW_OCT, IDC_RADIO_OCT, }, 719 { &calc.base, IDM_VIEW_BIN, IDC_RADIO_BIN, }, 720 /*-----------------------------------------*/ 721 { &calc.degr, IDM_VIEW_DEG, IDC_RADIO_DEG, }, 722 { &calc.degr, IDM_VIEW_RAD, IDC_RADIO_RAD, }, 723 { &calc.degr, IDM_VIEW_GRAD, IDC_RADIO_GRAD, }, 724 /*-----------------------------------------*/ 725 { &calc.size, IDM_VIEW_QWORD, IDC_RADIO_QWORD, }, 726 { &calc.size, IDM_VIEW_DWORD, IDC_RADIO_DWORD, }, 727 { &calc.size, IDM_VIEW_WORD, IDC_RADIO_WORD, }, 728 { &calc.size, IDM_VIEW_BYTE, IDC_RADIO_BYTE, }, 729 }; 730 731 static void update_menu(HWND hWnd) 732 { 733 HMENU hMenu = GetSubMenu(GetMenu(hWnd), 1); 734 unsigned int x; 735 736 for (x=0; x<SIZEOF(upd); x++) { 737 if (*(upd[x].sel) != upd[x].idc) { 738 CheckMenuItem(hMenu, upd[x].idm, MF_BYCOMMAND|MF_UNCHECKED); 739 CheckDlgButton(hWnd, upd[x].idc, BST_UNCHECKED); 740 } else { 741 CheckMenuItem(hMenu, upd[x].idm, MF_BYCOMMAND|MF_CHECKED); 742 CheckDlgButton(hWnd, upd[x].idc, BST_CHECKED); 743 } 744 } 745 CheckMenuItem(hMenu, IDM_VIEW_GROUP, MF_BYCOMMAND|(calc.usesep ? MF_CHECKED : MF_UNCHECKED)); 746 } 747 748 typedef struct { 749 WORD idc; 750 WORD mask; 751 } radio_config_t; 752 753 static const radio_config_t radio_setup[] = { 754 /* CONTROL-ID hex dec oct bin */ 755 { IDC_RADIO_QWORD, MAKE_BITMASK4( 1, 0, 1, 1) }, 756 { IDC_RADIO_DWORD, MAKE_BITMASK4( 1, 0, 1, 1) }, 757 { IDC_RADIO_WORD, MAKE_BITMASK4( 1, 0, 1, 1) }, 758 { IDC_RADIO_BYTE, MAKE_BITMASK4( 1, 0, 1, 1) }, 759 { IDC_RADIO_DEG, MAKE_BITMASK4( 0, 1, 0, 0) }, 760 { IDC_RADIO_RAD, MAKE_BITMASK4( 0, 1, 0, 0) }, 761 { IDC_RADIO_GRAD, MAKE_BITMASK4( 0, 1, 0, 0) }, 762 }; 763 764 static void enable_allowed_controls(HWND hwnd, DWORD base) 765 { 766 BYTE mask; 767 int n; 768 769 switch (base) { 770 case IDC_RADIO_DEC: 771 mask = BITMASK_DEC_MASK; 772 break; 773 case IDC_RADIO_HEX: 774 mask = BITMASK_HEX_MASK; 775 break; 776 case IDC_RADIO_OCT: 777 mask = BITMASK_OCT_MASK; 778 break; 779 case IDC_RADIO_BIN: 780 mask = BITMASK_BIN_MASK; 781 break; 782 default: 783 return; 784 } 785 for (n=0; n<SIZEOF(key2code); n++) { 786 if (key2code[n].mask != 0) { 787 HWND hCtlWnd = GetDlgItem(hwnd, key2code[n].idc); 788 BOOL current; 789 790 if ((key2code[n].mask & BITMASK_IS_STATS)) 791 current = IsWindow(calc.hStatWnd) ? TRUE : FALSE; 792 else 793 current = (key2code[n].mask & mask) ? TRUE : FALSE; 794 if (IsWindowEnabled(hCtlWnd) != current) 795 EnableWindow(hCtlWnd, current); 796 } 797 } 798 } 799 800 static void update_radio(HWND hwnd, unsigned int base) 801 { 802 HMENU hMenu; 803 LPCTSTR lpMenuId; 804 WORD mask; 805 int n; 806 807 switch (base) { 808 case IDC_RADIO_DEC: 809 lpMenuId = MAKEINTRESOURCE(IDR_MENU_SCIENTIFIC_1); 810 mask = BITMASK_DEC_MASK; 811 break; 812 case IDC_RADIO_HEX: 813 lpMenuId = MAKEINTRESOURCE(IDR_MENU_SCIENTIFIC_2); 814 mask = BITMASK_HEX_MASK; 815 break; 816 case IDC_RADIO_OCT: 817 lpMenuId = MAKEINTRESOURCE(IDR_MENU_SCIENTIFIC_2); 818 mask = BITMASK_OCT_MASK; 819 break; 820 case IDC_RADIO_BIN: 821 lpMenuId = MAKEINTRESOURCE(IDR_MENU_SCIENTIFIC_2); 822 mask = BITMASK_BIN_MASK; 823 break; 824 default: 825 return; 826 } 827 828 if (calc.base != base) { 829 convert_text2number(&calc.code); 830 convert_real_integer(base); 831 calc.base = base; 832 display_rpn_result(hwnd, &calc.code); 833 834 hMenu = GetMenu(hwnd); 835 DestroyMenu(hMenu); 836 hMenu = LoadMenu(calc.hInstance, lpMenuId); 837 SetMenu(hwnd, hMenu); 838 update_menu(hwnd); 839 840 for (n=0; n<SIZEOF(radio_setup); n++) 841 ShowWindow(GetDlgItem(hwnd, radio_setup[n].idc), (radio_setup[n].mask & mask) ? SW_SHOW : SW_HIDE); 842 843 enable_allowed_controls(hwnd, base); 844 } 845 846 CheckRadioButton(hwnd, IDC_RADIO_HEX, IDC_RADIO_BIN, calc.base); 847 848 if (base == IDC_RADIO_DEC) 849 CheckRadioButton(hwnd, IDC_RADIO_DEG, IDC_RADIO_GRAD, calc.degr); 850 else 851 CheckRadioButton(hwnd, IDC_RADIO_QWORD, IDC_RADIO_BYTE, calc.size); 852 } 853 854 static void update_memory_flag(HWND hWnd, BOOL mem_flag) 855 { 856 calc.is_memory = mem_flag; 857 SetDlgItemText(hWnd, IDC_TEXT_MEMORY, mem_flag ? _T("M") : _T("")); 858 } 859 860 static void update_n_stats_items(HWND hWnd, TCHAR *buffer, size_t cbBuffer) 861 { 862 unsigned int n = SendDlgItemMessage(hWnd, IDC_LIST_STAT, LB_GETCOUNT, 0, 0); 863 864 StringCbPrintf(buffer, cbBuffer, _T("n=%u"), n); 865 SetDlgItemText(hWnd, IDC_TEXT_NITEMS, buffer); 866 } 867 868 static void clean_stat_list(void) 869 { 870 statistic_t *p = calc.stat; 871 872 while (p != NULL) { 873 statistic_t *s = p; 874 p = (statistic_t *)(p->next); 875 rpn_free(&s->num); 876 free(s); 877 } 878 calc.stat = p; 879 } 880 881 static void delete_stat_item(int n) 882 { 883 statistic_t *p = calc.stat; 884 statistic_t *s; 885 886 if (n == 0) { 887 calc.stat = (statistic_t *)p->next; 888 rpn_free(&p->num); 889 free(p); 890 } else { 891 s = (statistic_t *)p->next; 892 while (--n) { 893 p = s; 894 s = (statistic_t *)p->next; 895 } 896 p->next = s->next; 897 rpn_free(&s->num); 898 free(s); 899 } 900 } 901 902 static char *ReadConversion(const char *formula) 903 { 904 size_t len = strlen(formula); 905 char *str = (char *)malloc(len+3); 906 907 if (str == NULL) 908 return NULL; 909 910 str[0] = '('; 911 memcpy(str+1, formula, len); 912 str[len+1] = ')'; 913 str[len+2] = '\0'; 914 915 StringCbCopy(calc.source, sizeof(calc.source), (*calc.buffer == _T('\0')) ? _T("0") : calc.buffer); 916 917 /* clear display content before proceeding */ 918 calc.ptr = calc.buffer; 919 calc.buffer[0] = _T('\0'); 920 921 return str; 922 } 923 924 static INT_PTR CALLBACK DlgStatProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) 925 { 926 TCHAR buffer[SIZEOF(calc.buffer)]; 927 DWORD n; 928 929 switch (msg) { 930 case WM_INITDIALOG: 931 return TRUE; 932 case WM_COMMAND: 933 switch (LOWORD(wp)) { 934 case IDC_LIST_STAT: 935 if (HIWORD(wp) == CBN_DBLCLK) 936 SendMessage(hWnd, WM_COMMAND, (WPARAM)IDC_BUTTON_LOAD, 0); 937 return TRUE; 938 case IDC_BUTTON_RET: 939 SetFocus(GetDlgItem(GetParent(hWnd), IDC_BUTTON_FOCUS)); 940 return TRUE; 941 case IDC_BUTTON_LOAD: 942 n = SendDlgItemMessage(hWnd, IDC_LIST_STAT, LB_GETCURSEL, 0, 0); 943 if (n == LB_ERR) 944 return TRUE; 945 PostMessage(GetParent(hWnd), WM_LOAD_STAT, (WPARAM)n, 0); 946 return TRUE; 947 case IDC_BUTTON_CD: 948 n = SendDlgItemMessage(hWnd, IDC_LIST_STAT, LB_GETCURSEL, 0, 0); 949 if (n == LB_ERR) 950 return TRUE; 951 SendDlgItemMessage(hWnd, IDC_LIST_STAT, LB_DELETESTRING, (WPARAM)n, 0); 952 update_n_stats_items(hWnd, buffer, sizeof(buffer)); 953 delete_stat_item(n); 954 return TRUE; 955 case IDC_BUTTON_CAD: 956 SendDlgItemMessage(hWnd, IDC_LIST_STAT, LB_RESETCONTENT, 0, 0); 957 clean_stat_list(); 958 update_n_stats_items(hWnd, buffer, sizeof(buffer)); 959 return TRUE; 960 } 961 break; 962 case WM_CLOSE: 963 DestroyWindow(hWnd); 964 return TRUE; 965 case WM_DESTROY: 966 clean_stat_list(); 967 PostMessage(GetParent(hWnd), WM_CLOSE_STATS, 0, 0); 968 return TRUE; 969 case WM_INSERT_STAT: 970 prepare_rpn_result(&(((statistic_t *)lp)->num), 971 buffer, SIZEOF(buffer), 972 ((statistic_t *)lp)->base); 973 SendDlgItemMessage(hWnd, IDC_LIST_STAT, LB_ADDSTRING, 0, (LPARAM)buffer); 974 update_n_stats_items(hWnd, buffer, sizeof(buffer)); 975 return TRUE; 976 } 977 return FALSE; 978 } 979 980 static WPARAM idm_2_idc(int idm) 981 { 982 int x; 983 984 for (x=0; x<SIZEOF(upd); x++) { 985 if (upd[x].idm == idm) 986 break; 987 } 988 return (WPARAM)(upd[x].idc); 989 } 990 991 static void CopyMemToClipboard(void *ptr) 992 { 993 if(OpenClipboard(NULL)) { 994 HGLOBAL clipbuffer; 995 TCHAR *buffer; 996 size_t cbBuffer; 997 998 EmptyClipboard(); 999 cbBuffer = (_tcslen(ptr) + 1) * sizeof(TCHAR); 1000 clipbuffer = GlobalAlloc(GMEM_DDESHARE, cbBuffer); 1001 buffer = (TCHAR *)GlobalLock(clipbuffer); 1002 StringCbCopy(buffer, cbBuffer, ptr); 1003 GlobalUnlock(clipbuffer); 1004 #ifdef UNICODE 1005 SetClipboardData(CF_UNICODETEXT,clipbuffer); 1006 #else 1007 SetClipboardData(CF_TEXT,clipbuffer); 1008 #endif 1009 CloseClipboard(); 1010 } 1011 } 1012 1013 static void handle_copy_command(HWND hWnd) 1014 { 1015 TCHAR display[MAX_CALC_SIZE]; 1016 UINT n; 1017 1018 // Read current text from output display 1019 n = GetDlgItemText(hWnd, IDC_TEXT_OUTPUT, display, SIZEOF(display)); 1020 1021 // Check if result is a true number 1022 if (!calc.is_nan) 1023 { 1024 // Remove trailing decimal point if no decimal digits exist 1025 if (calc.base == IDC_RADIO_DEC && _tcschr(calc.buffer, _T('.')) == NULL) 1026 display[n - calc.sDecimal_len] = _T('\0'); 1027 } 1028 1029 CopyMemToClipboard(display); 1030 } 1031 1032 static char *ReadClipboard(void) 1033 { 1034 char *buffer = NULL; 1035 1036 if (OpenClipboard(NULL)) { 1037 HANDLE hData = GetClipboardData(CF_TEXT); 1038 char *fromClipboard; 1039 1040 if (hData != NULL) { 1041 fromClipboard = (char *)GlobalLock(hData); 1042 if (fromClipboard[0]) 1043 buffer = _strupr(_strdup(fromClipboard)); 1044 GlobalUnlock( hData ); 1045 } 1046 CloseClipboard(); 1047 } 1048 return buffer; 1049 } 1050 1051 static char *handle_sequence_input(HWND hwnd, sequence_t *seq) 1052 { 1053 char *ptr = seq->ptr; 1054 int ch, x; 1055 1056 ch = *ptr++; 1057 if (ch == '\\') 1058 PostMessage(hwnd, WM_COMMAND, (WPARAM)IDC_BUTTON_DAT, 0); 1059 else 1060 if (ch == ':') { 1061 ch = *ptr; 1062 if (ch != '\0') 1063 ptr++; 1064 switch (ch) { 1065 case 'C': PostMessage(hwnd, WM_COMMAND, (WPARAM)IDC_BUTTON_MC, 0); break; 1066 case 'E': PostMessage(hwnd, WM_COMMAND, (WPARAM)IDC_BUTTON_EXP,0); break; 1067 case 'M': PostMessage(hwnd, WM_COMMAND, (WPARAM)IDC_BUTTON_MS, 0); break; 1068 case 'P': PostMessage(hwnd, WM_COMMAND, (WPARAM)IDC_BUTTON_MP, 0); break; 1069 case 'Q': PostMessage(hwnd, WM_COMMAND, (WPARAM)IDC_BUTTON_CANC, 0); break; 1070 case 'R': PostMessage(hwnd, WM_COMMAND, (WPARAM)IDC_BUTTON_MR, 0); break; 1071 } 1072 } else 1073 if (ch == '$') { 1074 StringCbCopyEx(calc.buffer, sizeof(calc.buffer), calc.source, &calc.ptr, NULL, 1075 STRSAFE_FILL_ON_FAILURE); 1076 } else { 1077 for (x=0; x<SIZEOF(key2code); x++) { 1078 if (!(key2code[x].mask & BITMASK_IS_ASCII) || 1079 (key2code[x].mask & BITMASK_IS_CTRL)) 1080 continue; 1081 if (key2code[x].key == ch) { 1082 PostMessage(hwnd, WM_COMMAND, (WPARAM)key2code[x].idc, 0); 1083 break; 1084 } 1085 } 1086 } 1087 1088 if (*ptr != '\0') 1089 { 1090 seq->ptr = ptr; 1091 PostMessage(hwnd, seq->wm_msg, 0, 0); 1092 } else { 1093 free(seq->data); 1094 seq->data = seq->ptr = ptr = NULL; 1095 } 1096 return ptr; 1097 } 1098 1099 static void run_dat_sta(calc_number_t *a) 1100 { 1101 statistic_t *s = (statistic_t *)malloc(sizeof(statistic_t)); 1102 statistic_t *p = calc.stat; 1103 1104 rpn_alloc(&s->num); 1105 rpn_copy(&s->num, a); 1106 s->base = calc.base; 1107 s->next = NULL; 1108 if (p == NULL) 1109 calc.stat = s; 1110 else { 1111 while (p->next != NULL) 1112 p = (statistic_t *)(p->next); 1113 p->next = s; 1114 } 1115 PostMessage(calc.hStatWnd, WM_INSERT_STAT, 0, (LPARAM)s); 1116 } 1117 1118 static void run_mp(calc_number_t *c) 1119 { 1120 calc_node_t cn; 1121 1122 cn.number = *c; 1123 cn.base = calc.base; 1124 run_operator(&calc.memory, &calc.memory, &cn, RPN_OPERATOR_ADD); 1125 update_memory_flag(calc.hWnd, TRUE); 1126 } 1127 1128 static void run_mm(calc_number_t *c) 1129 { 1130 calc_node_t cn; 1131 1132 cn.number = *c; 1133 cn.base = calc.base; 1134 run_operator(&calc.memory, &calc.memory, &cn, RPN_OPERATOR_SUB); 1135 update_memory_flag(calc.hWnd, TRUE); 1136 } 1137 1138 static void run_ms(calc_number_t *c) 1139 { 1140 rpn_copy(&calc.memory.number, c); 1141 calc.memory.base = calc.base; 1142 update_memory_flag(calc.hWnd, rpn_is_zero(&calc.memory.number) ? FALSE : TRUE); 1143 } 1144 1145 static void run_mw(calc_number_t *c) 1146 { 1147 calc_number_t tmp; 1148 1149 rpn_copy(&tmp, &calc.memory.number); 1150 rpn_copy(&calc.memory.number, c); 1151 calc.memory.base = calc.base; 1152 if (calc.is_memory) 1153 rpn_copy(c, &tmp); 1154 update_memory_flag(calc.hWnd, rpn_is_zero(&calc.memory.number) ? FALSE : TRUE); 1155 } 1156 1157 static statistic_t *upload_stat_number(int n) 1158 { 1159 statistic_t *p = calc.stat; 1160 1161 if (p == NULL) 1162 return p; 1163 1164 while (n--) { 1165 p = (statistic_t *)(p->next); 1166 if (p == NULL) 1167 return p; 1168 } 1169 1170 #ifndef ENABLE_MULTI_PRECISION 1171 if (calc.base != p->base) { 1172 if (calc.base == IDC_RADIO_DEC) 1173 calc.code.f = (double)p->num.i; 1174 else { 1175 calc.code.i = (__int64)p->num.f; 1176 apply_int_mask(&calc.code); 1177 } 1178 } else 1179 #endif 1180 rpn_copy(&calc.code, &p->num); 1181 1182 calc.is_nan = FALSE; 1183 1184 return p; 1185 } 1186 1187 static void run_fe(calc_number_t *number) 1188 { 1189 calc.sci_out = ((calc.sci_out != FALSE) ? FALSE : TRUE); 1190 } 1191 1192 static void handle_context_menu(HWND hWnd, WPARAM wp, LPARAM lp) 1193 { 1194 TCHAR text[64]; 1195 HMENU hMenu = CreatePopupMenu(); 1196 BOOL idm; 1197 1198 LoadString(calc.hInstance, IDS_QUICKHELP, text, SIZEOF(text)); 1199 AppendMenu(hMenu, MF_STRING | MF_ENABLED, IDM_HELP_HELP, text); 1200 idm = TrackPopupMenu( hMenu, 1201 TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RETURNCMD | TPM_RIGHTBUTTON, 1202 LOWORD(lp), 1203 HIWORD(lp), 1204 0, 1205 hWnd, 1206 NULL); 1207 DestroyMenu(hMenu); 1208 #ifndef DISABLE_HTMLHELP_SUPPORT 1209 if (idm) { 1210 HH_POPUP popup; 1211 1212 memset(&popup, 0, sizeof(popup)); 1213 popup.cbStruct = sizeof(HH_POPUP); 1214 popup.clrForeground = 1; 1215 popup.clrBackground = -1; 1216 popup.pt.x = LOWORD(lp); 1217 popup.pt.y = HIWORD(lp); 1218 popup.rcMargins.top = -1; 1219 popup.rcMargins.bottom = -1; 1220 popup.rcMargins.left = -1; 1221 popup.rcMargins.right = -1; 1222 popup.idString = GetWindowLongPtr((HWND)wp, GWL_ID); 1223 calc_HtmlHelp((HWND)wp, HTMLHELP_PATH("/popups.txt"), HH_DISPLAY_TEXT_POPUP, (DWORD_PTR)&popup); 1224 } 1225 #else 1226 (void)idm; 1227 #endif 1228 } 1229 1230 static void run_canc(calc_number_t *c) 1231 { 1232 flush_postfix(); 1233 rpn_zero(c); 1234 1235 /* clear also scientific display modes */ 1236 calc.sci_out = FALSE; 1237 calc.sci_in = FALSE; 1238 1239 /* clear state of inv and hyp flags */ 1240 CheckDlgButton(calc.hWnd, IDC_CHECK_INV, BST_UNCHECKED); 1241 CheckDlgButton(calc.hWnd, IDC_CHECK_HYP, BST_UNCHECKED); 1242 } 1243 1244 static void run_rpar(calc_number_t *c) 1245 { 1246 exec_closeparent(c); 1247 } 1248 1249 static void run_lpar(calc_number_t *c) 1250 { 1251 exec_infix2postfix(c, RPN_OPERATOR_PARENT); 1252 } 1253 1254 static LRESULT CALLBACK SubclassButtonProc(HWND hWnd, WPARAM wp, LPARAM lp) 1255 { 1256 LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lp; 1257 UINT dwText; 1258 TCHAR text[64]; 1259 int dx, dy, len; 1260 SIZE size; 1261 POINT pt; 1262 1263 if(dis->CtlType == ODT_BUTTON) 1264 { 1265 HTHEME hTheme = NULL; 1266 LPBTNINFO lpBtnInfo; 1267 1268 if (calc_IsAppThemed() && calc_IsThemeActive()) 1269 hTheme = calc_OpenThemeData(hWnd, L"Button"); 1270 1271 if (hTheme) 1272 { 1273 int iState = 0; 1274 1275 if ((dis->itemState & ODS_DISABLED)) 1276 iState |= PBS_DISABLED; 1277 if ((dis->itemState & ODS_SELECTED)) 1278 iState |= PBS_PRESSED; 1279 1280 lpBtnInfo = (LPBTNINFO)GetWindowLongPtr(dis->hwndItem, GWLP_USERDATA); 1281 if (lpBtnInfo != NULL) 1282 { 1283 if (lpBtnInfo->bHover) 1284 iState |= PBS_HOT; 1285 } 1286 1287 if (calc_IsThemeBackgroundPartiallyTransparent(hTheme, BP_PUSHBUTTON, iState)) 1288 { 1289 calc_DrawThemeParentBackground(dis->hwndItem, dis->hDC, &dis->rcItem); 1290 } 1291 1292 // Draw the frame around the control 1293 calc_DrawThemeBackground(hTheme, dis->hDC, BP_PUSHBUTTON, iState, &dis->rcItem, NULL); 1294 1295 calc_CloseThemeData(hTheme); 1296 } else { 1297 /* default state: unpushed */ 1298 DWORD dwStyle = 0; 1299 1300 if ((dis->itemState & ODS_SELECTED)) 1301 dwStyle = DFCS_PUSHED; 1302 1303 DrawFrameControl(dis->hDC, &dis->rcItem, DFC_BUTTON, DFCS_BUTTONPUSH | dwStyle); 1304 } 1305 1306 /* button text to write */ 1307 len = GetWindowText(dis->hwndItem, text, SIZEOF(text)); 1308 1309 /* 1310 * little exception: 1/x has different color 1311 * in standard and scientific modes 1312 */ 1313 if ((calc.layout == CALC_LAYOUT_STANDARD || 1314 calc.layout == CALC_LAYOUT_CONVERSION) && 1315 IDC_BUTTON_RX == dis->CtlID) { 1316 SetTextColor(dis->hDC, CALC_CLR_BLUE); 1317 } else 1318 for (dx=0; dx<SIZEOF(key2code); dx++) { 1319 if (key2code[dx].idc == dis->CtlID) { 1320 SetTextColor(dis->hDC, key2code[dx].col); 1321 break; 1322 } 1323 } 1324 1325 /* No background, to avoid corruption of the texture */ 1326 SetBkMode(dis->hDC, TRANSPARENT); 1327 1328 /* Default state: enabled */ 1329 dwText = 0; 1330 if ((dis->itemState & ODS_DISABLED)) 1331 dwText = DSS_DISABLED; 1332 1333 /* Draw the text in the button */ 1334 GetTextExtentPoint32(dis->hDC, text, len, &size); 1335 dx = ((dis->rcItem.right-dis->rcItem.left) - size.cx) >> 1; 1336 dy = ((dis->rcItem.bottom-dis->rcItem.top) - size.cy) >> 1; 1337 if ((dis->itemState & ODS_SELECTED)) { 1338 dx++; 1339 dy++; 1340 } 1341 pt.x = dis->rcItem.left + dx; 1342 pt.y = dis->rcItem.top + dy; 1343 DrawState(dis->hDC, NULL, NULL, (LPARAM)text, 0, pt.x, pt.y, size.cx, size.cy, DST_TEXT | dwText); 1344 } 1345 return 1L; 1346 } 1347 1348 static INT_PTR CALLBACK HotButtonProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) 1349 { 1350 LPBTNINFO lpBtnInfo = (LPBTNINFO)GetWindowLongPtr(hWnd, GWLP_USERDATA); 1351 TRACKMOUSEEVENT mouse_event; 1352 1353 switch (msg) { 1354 case WM_MOUSEMOVE: 1355 mouse_event.cbSize = sizeof(TRACKMOUSEEVENT); 1356 mouse_event.dwFlags = TME_QUERY; 1357 if (!TrackMouseEvent(&mouse_event) || !(mouse_event.dwFlags & (TME_HOVER|TME_LEAVE))) 1358 { 1359 mouse_event.dwFlags = TME_HOVER|TME_LEAVE; 1360 mouse_event.hwndTrack = hWnd; 1361 mouse_event.dwHoverTime = 1; 1362 TrackMouseEvent(&mouse_event); 1363 } 1364 break; 1365 1366 case WM_MOUSEHOVER: 1367 lpBtnInfo->bHover = TRUE; 1368 InvalidateRect(hWnd, NULL, FALSE); 1369 break; 1370 1371 case WM_MOUSELEAVE: 1372 lpBtnInfo->bHover = FALSE; 1373 InvalidateRect(hWnd, NULL, FALSE); 1374 break; 1375 } 1376 1377 return CallWindowProc(lpBtnInfo->oldProc, hWnd, msg, wp, lp); 1378 } 1379 1380 static BOOL CALLBACK EnumChildProc(HWND hWnd, LPARAM lParam) 1381 { 1382 TCHAR szClass[64]; 1383 1384 if (!GetClassName(hWnd, szClass, SIZEOF(szClass))) 1385 return TRUE; 1386 1387 if (!_tcscmp(szClass, WC_BUTTON)) 1388 { 1389 int *pnCtrls = (int *)lParam; 1390 int nCtrls = *pnCtrls; 1391 1392 BtnInfo[nCtrls].oldProc = (WNDPROC)GetWindowLongPtr(hWnd, GWLP_WNDPROC); 1393 BtnInfo[nCtrls].bHover = FALSE; 1394 1395 SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)&BtnInfo[nCtrls]); 1396 SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)HotButtonProc); 1397 1398 *pnCtrls = ++nCtrls; 1399 } 1400 return TRUE; 1401 } 1402 1403 static INT_PTR CALLBACK OnSettingChange(HWND hWnd, WPARAM wParam, LPARAM lParam) 1404 { 1405 /* Check for user policy and area string valid */ 1406 if (wParam == 0 && lParam != 0) 1407 { 1408 LPTSTR lpArea = (LPTSTR)lParam; 1409 1410 /* Check if a parameter has been changed into the locale settings */ 1411 if (!_tcsicmp(lpArea, _T("intl"))) 1412 { 1413 /* Re-load locale parameters */ 1414 UpdateNumberIntl(); 1415 1416 /* Update text for decimal button */ 1417 SetDlgItemText(hWnd, IDC_BUTTON_DOT, calc.sDecimal); 1418 1419 /* Update text into the output display */ 1420 update_lcd_display(hWnd); 1421 } 1422 } 1423 return 0; 1424 } 1425 1426 static INT_PTR CALLBACK DlgMainProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) 1427 { 1428 unsigned int x; 1429 RECT rc; 1430 1431 switch (msg) { 1432 case WM_DRAWITEM: 1433 return SubclassButtonProc(hWnd, wp, lp); 1434 1435 case WM_INITDIALOG: 1436 #ifdef DISABLE_HTMLHELP_SUPPORT 1437 EnableMenuItem(GetMenu(hWnd), IDM_HELP_HELP, MF_BYCOMMAND | MF_GRAYED); 1438 #endif 1439 calc.hWnd=hWnd; 1440 /* Enumerate children and apply hover function */ 1441 BtnCount = 0; 1442 EnumChildWindows(hWnd, EnumChildProc, (LPARAM)&BtnCount); 1443 1444 #ifdef USE_KEYBOARD_HOOK 1445 calc.hKeyboardHook=SetWindowsHookEx( 1446 WH_KEYBOARD, 1447 KeyboardHookProc, 1448 NULL, 1449 GetCurrentThreadId() 1450 ); 1451 #endif 1452 rpn_zero(&calc.code); 1453 calc.sci_out = FALSE; 1454 calc.base = IDC_RADIO_DEC; 1455 calc.size = IDC_RADIO_QWORD; 1456 calc.degr = IDC_RADIO_DEG; 1457 calc.ptr = calc.buffer; 1458 calc.is_nan = FALSE; 1459 enable_allowed_controls(hWnd, IDC_RADIO_DEC); 1460 update_radio(hWnd, IDC_RADIO_DEC); 1461 update_menu(hWnd); 1462 display_rpn_result(hWnd, &calc.code); 1463 update_memory_flag(hWnd, calc.is_memory); 1464 /* remove keyboard focus */ 1465 SetFocus(GetDlgItem(hWnd, IDC_BUTTON_FOCUS)); 1466 /* set our calc icon */ 1467 SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM)calc.hBgIcon); 1468 SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM)calc.hSmIcon); 1469 /* update text for decimal button */ 1470 SetDlgItemText(hWnd, IDC_BUTTON_DOT, calc.sDecimal); 1471 /* Fill combo box for conversion */ 1472 if (calc.layout == CALC_LAYOUT_CONVERSION) 1473 ConvInit(hWnd); 1474 /* Restore the window at the same position it was */ 1475 if (calc.x_coord >= 0 && calc.y_coord >= 0) { 1476 int w, h, sw, sh; 1477 1478 GetWindowRect(hWnd, &rc); 1479 w = rc.right-rc.left; 1480 h = rc.bottom-rc.top; 1481 sw = GetSystemMetrics(SM_CXSCREEN); 1482 sh = GetSystemMetrics(SM_CYSCREEN); 1483 if (calc.x_coord+w > sw) calc.x_coord = sw - w; 1484 if (calc.y_coord+h > sh) calc.y_coord = sh - h; 1485 MoveWindow(hWnd, calc.x_coord, calc.y_coord, w, h, FALSE); 1486 } 1487 break; 1488 case WM_CTLCOLORSTATIC: 1489 if ((HWND)lp == GetDlgItem(hWnd, IDC_TEXT_OUTPUT)) 1490 return (LRESULT)GetStockObject(WHITE_BRUSH); 1491 break; 1492 case WM_HANDLE_CLIPBOARD: 1493 handle_sequence_input(hWnd, &calc.Clipboard); 1494 return TRUE; 1495 case WM_COMMAND: 1496 /* 1497 * if selection of category is changed, we must 1498 * update the content of the "from/to" combo boxes. 1499 */ 1500 if (wp == MAKEWPARAM(IDC_COMBO_CATEGORY, CBN_SELCHANGE)) { 1501 ConvAdjust(hWnd, SendDlgItemMessage(hWnd, IDC_COMBO_CATEGORY, CB_GETCURSEL, 0, 0)); 1502 return TRUE; 1503 } 1504 if (HIWORD(wp) != BN_CLICKED && HIWORD(wp) != BN_DBLCLK) 1505 break; 1506 /* avoid flicker if the user selects from keyboard */ 1507 if (GetFocus() != GetDlgItem(hWnd, IDC_BUTTON_FOCUS)) 1508 SetFocus(GetDlgItem(hWnd, IDC_BUTTON_FOCUS)); 1509 switch (LOWORD(wp)) { 1510 case IDM_HELP_ABOUT: 1511 { 1512 TCHAR infotitle[100]; 1513 TCHAR infotext[200]; 1514 LoadString(calc.hInstance, IDS_CALC_NAME, infotitle, SIZEOF(infotitle)); 1515 LoadString(calc.hInstance, IDS_AUTHOR, infotext, SIZEOF(infotext)); 1516 ShellAbout(hWnd, infotitle, infotext, calc.hBgIcon); 1517 return TRUE; 1518 } 1519 case IDM_HELP_HELP: 1520 #ifndef DISABLE_HTMLHELP_SUPPORT 1521 calc_HtmlHelp(hWnd, HTMLHELP_PATH("/general_information.htm"), HH_DISPLAY_TOPIC, (DWORD_PTR)NULL); 1522 #endif 1523 return TRUE; 1524 case IDM_VIEW_STANDARD: 1525 calc.layout = CALC_LAYOUT_STANDARD; 1526 calc.action = IDM_VIEW_STANDARD; 1527 DestroyWindow(hWnd); 1528 return TRUE; 1529 case IDM_VIEW_SCIENTIFIC: 1530 calc.layout = CALC_LAYOUT_SCIENTIFIC; 1531 calc.action = IDM_VIEW_SCIENTIFIC; 1532 DestroyWindow(hWnd); 1533 return TRUE; 1534 case IDM_VIEW_CONVERSION: 1535 calc.layout = CALC_LAYOUT_CONVERSION; 1536 calc.action = IDM_VIEW_CONVERSION; 1537 DestroyWindow(hWnd); 1538 return TRUE; 1539 case IDM_VIEW_HEX: 1540 case IDM_VIEW_DEC: 1541 case IDM_VIEW_OCT: 1542 case IDM_VIEW_BIN: 1543 case IDM_VIEW_DEG: 1544 case IDM_VIEW_RAD: 1545 case IDM_VIEW_GRAD: 1546 case IDM_VIEW_QWORD: 1547 case IDM_VIEW_DWORD: 1548 case IDM_VIEW_WORD: 1549 case IDM_VIEW_BYTE: 1550 SendMessage(hWnd, WM_COMMAND, idm_2_idc(LOWORD(wp)), 0); 1551 return TRUE; 1552 case IDM_EDIT_COPY: 1553 handle_copy_command(hWnd); 1554 return TRUE; 1555 case IDM_EDIT_PASTE: 1556 if (calc.Clipboard.data != NULL) 1557 break; 1558 calc.Clipboard.data = ReadClipboard(); 1559 if (calc.Clipboard.data != NULL) { 1560 /* clear the content of the display before pasting */ 1561 PostMessage(hWnd, WM_COMMAND, IDC_BUTTON_CE, 0); 1562 calc.Clipboard.ptr = calc.Clipboard.data; 1563 calc.Clipboard.wm_msg = WM_HANDLE_CLIPBOARD; 1564 handle_sequence_input(hWnd, &calc.Clipboard); 1565 } 1566 return TRUE; 1567 case IDM_VIEW_GROUP: 1568 calc.usesep = (calc.usesep ? FALSE : TRUE); 1569 update_menu(hWnd); 1570 update_lcd_display(hWnd); 1571 return TRUE; 1572 case IDC_BUTTON_CONVERT: 1573 ConvExecute(hWnd); 1574 return TRUE; 1575 case IDC_BUTTON_CE: { 1576 calc_number_t tmp; 1577 rpn_zero(&tmp); 1578 display_rpn_result(hWnd, &tmp); 1579 } 1580 return TRUE; 1581 case IDC_RADIO_DEC: 1582 case IDC_RADIO_HEX: 1583 case IDC_RADIO_OCT: 1584 case IDC_RADIO_BIN: 1585 /* GNU WINDRES is bugged so I must always force radio update */ 1586 /* (Fix for Win95/98) */ 1587 #ifdef _MSC_VER 1588 if (calc.base == LOWORD(wp)) 1589 break; 1590 #endif 1591 calc.is_nan = FALSE; 1592 update_radio(hWnd, LOWORD(wp)); 1593 return TRUE; 1594 case IDC_RADIO_DEG: 1595 case IDC_RADIO_RAD: 1596 case IDC_RADIO_GRAD: 1597 /* GNU WINDRES is bugged so I must always force radio update */ 1598 /* (Fix for Win95/98) */ 1599 #ifdef _MSC_VER 1600 if (calc.degr == LOWORD(wp)) 1601 break; 1602 #endif 1603 calc.degr = LOWORD(wp); 1604 calc.is_nan = FALSE; 1605 update_menu(hWnd); 1606 return TRUE; 1607 case IDC_RADIO_QWORD: 1608 case IDC_RADIO_DWORD: 1609 case IDC_RADIO_WORD: 1610 case IDC_RADIO_BYTE: 1611 /* GNU WINDRES is bugged so I must always force radio update */ 1612 /* (Fix for Win95/98) */ 1613 #ifdef _MSC_VER 1614 if (calc.size == LOWORD(wp)) 1615 break; 1616 #endif 1617 calc.size = LOWORD(wp); 1618 calc.is_nan = FALSE; 1619 update_menu(hWnd); 1620 /* 1621 * update the content of the display 1622 */ 1623 convert_text2number(&calc.code); 1624 apply_int_mask(&calc.code); 1625 display_rpn_result(hWnd, &calc.code); 1626 return TRUE; 1627 case IDC_BUTTON_1: 1628 case IDC_BUTTON_2: 1629 case IDC_BUTTON_3: 1630 case IDC_BUTTON_4: 1631 case IDC_BUTTON_5: 1632 case IDC_BUTTON_6: 1633 case IDC_BUTTON_7: 1634 case IDC_BUTTON_8: 1635 case IDC_BUTTON_9: 1636 case IDC_BUTTON_0: 1637 case IDC_BUTTON_DOT: 1638 case IDC_BUTTON_A: 1639 case IDC_BUTTON_B: 1640 case IDC_BUTTON_C: 1641 case IDC_BUTTON_D: 1642 case IDC_BUTTON_E: 1643 case IDC_BUTTON_F: 1644 if (calc.is_nan) break; 1645 build_operand(hWnd, LOWORD(wp)); 1646 return TRUE; 1647 case IDC_BUTTON_PERCENT: 1648 case IDC_BUTTON_ADD: 1649 case IDC_BUTTON_SUB: 1650 case IDC_BUTTON_MULT: 1651 case IDC_BUTTON_DIV: 1652 case IDC_BUTTON_MOD: 1653 case IDC_BUTTON_AND: 1654 case IDC_BUTTON_OR: 1655 case IDC_BUTTON_XOR: 1656 case IDC_BUTTON_LSH: 1657 case IDC_BUTTON_RSH: 1658 case IDC_BUTTON_EQU: 1659 case IDC_BUTTON_XeY: 1660 case IDC_BUTTON_XrY: 1661 if (calc.is_nan) break; 1662 /* 1663 * LSH and XeY buttons hold also the RSH and XrY functions with INV modifier, 1664 * but since they are two operand operators, they must be handled here. 1665 */ 1666 if ((get_modifiers(hWnd) & MODIFIER_INV)) 1667 { 1668 WPARAM IdcSim = IDC_STATIC; 1669 1670 switch (LOWORD(wp)) { 1671 case IDC_BUTTON_LSH: IdcSim = MAKEWPARAM(IDC_BUTTON_RSH, BN_CLICKED); break; 1672 case IDC_BUTTON_XeY: IdcSim = MAKEWPARAM(IDC_BUTTON_XrY, BN_CLICKED); break; 1673 } 1674 1675 if (IdcSim != IDC_STATIC) 1676 { 1677 PostMessage(hWnd, WM_COMMAND, IdcSim, 0); 1678 CheckDlgButton(hWnd, IDC_CHECK_INV, BST_UNCHECKED); 1679 break; 1680 } 1681 } 1682 1683 for (x=0; x<SIZEOF(operator_codes); x++) { 1684 if (LOWORD(wp) == operator_codes[x]) { 1685 convert_text2number(&calc.code); 1686 1687 if (calc.ptr == calc.buffer) { 1688 if (calc.last_operator != x) { 1689 if (x != RPN_OPERATOR_EQUAL) 1690 exec_change_infix(); 1691 } else 1692 if (x == RPN_OPERATOR_EQUAL) { 1693 exec_infix2postfix(&calc.code, calc.prev_operator); 1694 rpn_copy(&calc.code, &calc.prev); 1695 } else 1696 break; 1697 } 1698 1699 /* if no change then quit silently, */ 1700 /* without display updates */ 1701 if (!exec_infix2postfix(&calc.code, x)) 1702 break; 1703 1704 display_rpn_result(hWnd, &calc.code); 1705 break; 1706 } 1707 } 1708 return TRUE; 1709 case IDC_BUTTON_BACK: 1710 if (calc.is_nan) break; 1711 if (calc.sci_in) { 1712 if (calc.esp == 0) { 1713 TCHAR *ptr; 1714 1715 calc.sci_in = FALSE; 1716 ptr = _tcschr(calc.ptr, _T('e')); 1717 if (ptr) 1718 *ptr = _T('\0'); 1719 update_lcd_display(hWnd); 1720 } else { 1721 calc.esp /= 10; 1722 build_operand(hWnd, IDC_STATIC); 1723 } 1724 } else 1725 if (calc.ptr != calc.buffer) { 1726 *--calc.ptr = _T('\0'); 1727 if (!_tcscmp(calc.buffer, _T("-")) || 1728 !_tcscmp(calc.buffer, _T("-0")) || 1729 !_tcscmp(calc.buffer, _T("0"))) { 1730 calc.ptr = calc.buffer; 1731 calc.buffer[0] = _T('\0'); 1732 } 1733 update_lcd_display(hWnd); 1734 } 1735 return TRUE; 1736 case IDC_BUTTON_MC: 1737 rpn_zero(&calc.memory.number); 1738 update_memory_flag(hWnd, FALSE); 1739 return TRUE; 1740 case IDC_BUTTON_MR: 1741 if (calc.is_memory) { 1742 calc.is_nan = FALSE; 1743 rpn_copy(&calc.code, &calc.memory.number); 1744 display_rpn_result(hWnd, &calc.code); 1745 } 1746 return TRUE; 1747 case IDC_BUTTON_EXP: 1748 if (calc.sci_in || calc.is_nan || calc.buffer == calc.ptr) 1749 break; 1750 calc.sci_in = TRUE; 1751 calc.esp = 0; 1752 build_operand(hWnd, IDC_STATIC); 1753 return TRUE; 1754 case IDC_BUTTON_SIGN: 1755 if (calc.sci_in) { 1756 calc.esp = 0-calc.esp; 1757 build_operand(hWnd, IDC_STATIC); 1758 } else { 1759 if (calc.is_nan || calc.buffer[0] == _T('\0')) 1760 break; 1761 1762 if (calc.buffer[0] == _T('-')) { 1763 /* make the number positive */ 1764 memmove(calc.buffer, calc.buffer+1, sizeof(calc.buffer)-1); 1765 if (calc.buffer != calc.ptr) 1766 calc.ptr--; 1767 } else { 1768 /* if first char is '0' and no dot, it isn't valid */ 1769 if (calc.buffer[0] == _T('0') && 1770 calc.buffer[1] != _T('.')) 1771 break; 1772 /* make the number negative */ 1773 memmove(calc.buffer+1, calc.buffer, sizeof(calc.buffer)-1); 1774 calc.buffer[0] = _T('-'); 1775 if (calc.buffer != calc.ptr) 1776 calc.ptr++; 1777 } 1778 /* If the input buffer is empty, then 1779 we change also the sign of calc.code 1780 because it could be the result of a 1781 previous calculation. */ 1782 if (calc.buffer == calc.ptr) 1783 rpn_sign(&calc.code); 1784 update_lcd_display(hWnd); 1785 } 1786 return TRUE; 1787 case IDC_BUTTON_RIGHTPAR: 1788 case IDC_BUTTON_LEFTPAR: 1789 case IDC_BUTTON_CANC: 1790 case IDC_BUTTON_MP: 1791 case IDC_BUTTON_DAT: 1792 case IDC_BUTTON_FE: 1793 case IDC_BUTTON_DMS: 1794 case IDC_BUTTON_SQRT: 1795 case IDC_BUTTON_S: 1796 case IDC_BUTTON_SUM: 1797 case IDC_BUTTON_AVE: 1798 case IDC_BUTTON_NF: 1799 case IDC_BUTTON_LN: 1800 case IDC_BUTTON_LOG: 1801 case IDC_BUTTON_Xe2: 1802 case IDC_BUTTON_Xe3: 1803 case IDC_BUTTON_PI: 1804 case IDC_BUTTON_NOT: 1805 case IDC_BUTTON_RX: 1806 case IDC_BUTTON_INT: 1807 case IDC_BUTTON_SIN: 1808 case IDC_BUTTON_COS: 1809 case IDC_BUTTON_TAN: 1810 case IDC_BUTTON_MS: 1811 for (x=0; x<SIZEOF(function_table); x++) { 1812 if (LOWORD(wp) == function_table[x].idc) { 1813 rpn_callback1 cb = NULL; 1814 1815 /* test if NaN state is important or not */ 1816 if (calc.is_nan && function_table[x].check_nan) break; 1817 /* otherwise, it's cleared */ 1818 calc.is_nan = FALSE; 1819 1820 switch (get_modifiers(hWnd) & function_table[x].range) { 1821 case 0: 1822 cb = function_table[x].direct; 1823 break; 1824 case MODIFIER_INV: 1825 cb = function_table[x].inverse; 1826 break; 1827 case MODIFIER_HYP: 1828 cb = function_table[x].hyperb; 1829 break; 1830 case MODIFIER_INV|MODIFIER_HYP: 1831 cb = function_table[x].inv_hyp; 1832 break; 1833 } 1834 if (cb != NULL) { 1835 convert_text2number(&calc.code); 1836 cb(&calc.code); 1837 // display_rpn_result(hWnd, &calc.code); 1838 set_rpn_result(hWnd, &calc.code); 1839 1840 if ((function_table[x].range & NO_CHAIN)) 1841 calc.ptr = calc.buffer; 1842 1843 // if (!(function_table[x].range & NO_CHAIN)) 1844 // exec_infix2postfix(&calc.code, RPN_OPERATOR_NONE); 1845 if (function_table[x].range & MODIFIER_INV) 1846 CheckDlgButton(hWnd, IDC_CHECK_INV, BST_UNCHECKED); 1847 if (function_table[x].range & MODIFIER_HYP) 1848 CheckDlgButton(hWnd, IDC_CHECK_HYP, BST_UNCHECKED); 1849 } 1850 break; 1851 } 1852 } 1853 return TRUE; 1854 case IDC_BUTTON_STA: 1855 if (IsWindow(calc.hStatWnd)) 1856 break; 1857 calc.hStatWnd = CreateDialog(calc.hInstance, 1858 MAKEINTRESOURCE(IDD_DIALOG_STAT), hWnd, DlgStatProc); 1859 if (calc.hStatWnd != NULL) { 1860 enable_allowed_controls(hWnd, calc.base); 1861 SendMessage(calc.hStatWnd, WM_SETFOCUS, 0, 0); 1862 } 1863 return TRUE; 1864 } 1865 break; 1866 case WM_CLOSE_STATS: 1867 calc.hStatWnd = NULL; 1868 enable_allowed_controls(hWnd, calc.base); 1869 return TRUE; 1870 case WM_LOAD_STAT: 1871 if (upload_stat_number((int)LOWORD(wp)) != NULL) 1872 display_rpn_result(hWnd, &calc.code); 1873 return TRUE; 1874 case WM_START_CONV: 1875 x = LOWORD(lp); 1876 calc.Convert[x].data = ReadConversion(calc.Convert[x].data); 1877 if (calc.Convert[x].data != NULL) { 1878 calc.Convert[x].ptr = calc.Convert[x].data; 1879 PostMessage(hWnd, HIWORD(lp), 0, 0); 1880 } 1881 return TRUE; 1882 case WM_HANDLE_FROM: 1883 if (calc.is_nan) 1884 break; 1885 if (handle_sequence_input(hWnd, &calc.Convert[0]) == NULL) 1886 PostMessage(hWnd, WM_START_CONV, 0, 1887 MAKELPARAM(0x0001, WM_HANDLE_TO)); 1888 return TRUE; 1889 case WM_HANDLE_TO: 1890 if (!calc.is_nan) 1891 handle_sequence_input(hWnd, &calc.Convert[1]); 1892 return TRUE; 1893 case WM_CLOSE: 1894 calc.action = IDC_STATIC; 1895 DestroyWindow(hWnd); 1896 return TRUE; 1897 1898 case WM_DESTROY: 1899 /* Get (x,y) position of the calculator */ 1900 GetWindowRect(hWnd, &rc); 1901 calc.x_coord = rc.left; 1902 calc.y_coord = rc.top; 1903 #ifdef USE_KEYBOARD_HOOK 1904 UnhookWindowsHookEx(calc.hKeyboardHook); 1905 #endif 1906 PostQuitMessage(0); 1907 return TRUE; 1908 case WM_CONTEXTMENU: 1909 if ((HWND)wp != hWnd) 1910 handle_context_menu(hWnd, wp, lp); 1911 return TRUE; 1912 case WM_ENTERMENULOOP: 1913 calc.is_menu_on = TRUE; 1914 /* Check if a valid format is available in the clipboard */ 1915 EnableMenuItem(GetSubMenu(GetMenu(hWnd), 0), 1916 IDM_EDIT_PASTE, 1917 MF_BYCOMMAND| 1918 (IsClipboardFormatAvailable(CF_TEXT) ? 1919 MF_ENABLED : MF_GRAYED)); 1920 break; 1921 case WM_EXITMENULOOP: 1922 calc.is_menu_on = FALSE; 1923 break; 1924 1925 case WM_SETTINGCHANGE: 1926 return OnSettingChange(hWnd, wp, lp); 1927 1928 case WM_THEMECHANGED: 1929 InvalidateRect(hWnd, NULL, FALSE); 1930 break; 1931 } 1932 return FALSE; 1933 } 1934 1935 #if defined(__GNUC__) && !defined(__REACTOS__) 1936 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) 1937 #else 1938 int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nShowCmd) 1939 #endif 1940 { 1941 MSG msg; 1942 DWORD dwLayout; 1943 1944 /* Initialize controls for theming & manifest support */ 1945 InitCommonControls(); 1946 1947 calc.hInstance = hInstance; 1948 1949 calc.x_coord = -1; 1950 calc.y_coord = -1; 1951 1952 load_config(); 1953 start_rpn_engine(); 1954 1955 HtmlHelp_Start(hInstance); 1956 1957 Theme_Start(hInstance); 1958 1959 calc.hBgIcon = LoadImage( 1960 hInstance, 1961 MAKEINTRESOURCE(IDI_CALC), 1962 IMAGE_ICON, 1963 0, 1964 0, 1965 LR_DEFAULTSIZE | LR_SHARED); 1966 1967 calc.hSmIcon = LoadImage( 1968 hInstance, 1969 MAKEINTRESOURCE(IDI_CALC), 1970 IMAGE_ICON, 1971 GetSystemMetrics(SM_CXSMICON), 1972 GetSystemMetrics(SM_CYSMICON), 1973 LR_SHARED); 1974 1975 do { 1976 /* ignore hwnd: dialogs are already visible! */ 1977 if (calc.layout == CALC_LAYOUT_SCIENTIFIC) 1978 dwLayout = IDD_DIALOG_SCIENTIFIC; 1979 else 1980 if (calc.layout == CALC_LAYOUT_CONVERSION) 1981 dwLayout = IDD_DIALOG_CONVERSION; 1982 else 1983 dwLayout = IDD_DIALOG_STANDARD; 1984 1985 /* This call will always fail if UNICODE for Win9x */ 1986 if (NULL == CreateDialog(hInstance, MAKEINTRESOURCE(dwLayout), NULL, DlgMainProc)) 1987 break; 1988 1989 while (GetMessage(&msg, NULL, 0, 0)) { 1990 #ifndef USE_KEYBOARD_HOOK 1991 if ((msg.message == WM_KEYUP || 1992 msg.message == WM_KEYDOWN) && 1993 !calc.is_menu_on) 1994 process_vk_key(msg.wParam, msg.lParam); 1995 #endif 1996 TranslateMessage(&msg); 1997 DispatchMessage(&msg); 1998 } 1999 2000 save_config(); 2001 } while (calc.action != IDC_STATIC); 2002 2003 stop_rpn_engine(); 2004 2005 Theme_Stop(); 2006 HtmlHelp_Stop(); 2007 2008 return 0; 2009 } 2010