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
UpdateNumberIntl(void)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
LoadRegInt(LPCTSTR lpszApp,LPCTSTR lpszKey,int iDefault)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
SaveRegInt(LPCTSTR lpszApp,LPCTSTR lpszKey,int iValue)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
load_config(void)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
save_config(void)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
post_key_press(LPARAM lParam,WORD idc)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
vk2ascii(unsigned int vk)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
process_vk_key(WPARAM wParam,LPARAM lParam)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
KeyboardHookProc(int nCode,WPARAM wParam,LPARAM lParam)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
update_lcd_display(HWND hwnd)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
update_parent_display(HWND hWnd)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
build_operand(HWND hwnd,DWORD idc)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
prepare_rpn_result(calc_number_t * rpn,TCHAR * buffer,int size,int base)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
set_rpn_result(HWND hwnd,calc_number_t * rpn)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
display_rpn_result(HWND hwnd,calc_number_t * rpn)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
get_modifiers(HWND hWnd)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
convert_text2number(calc_number_t * a)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
update_menu(HWND hWnd)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
enable_allowed_controls(HWND hwnd,DWORD base)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
update_radio(HWND hwnd,unsigned int base)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
update_memory_flag(HWND hWnd,BOOL mem_flag)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
update_n_stats_items(HWND hWnd,TCHAR * buffer,size_t cbBuffer)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
clean_stat_list(void)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
delete_stat_item(int n)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
ReadConversion(const char * formula)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
DlgStatProc(HWND hWnd,UINT msg,WPARAM wp,LPARAM lp)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
idm_2_idc(int idm)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
CopyMemToClipboard(void * ptr)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
handle_copy_command(HWND hWnd)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
ReadClipboard(void)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
handle_sequence_input(HWND hwnd,sequence_t * seq)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
run_dat_sta(calc_number_t * a)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
run_mp(calc_number_t * c)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
run_mm(calc_number_t * c)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
run_ms(calc_number_t * c)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
run_mw(calc_number_t * c)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
upload_stat_number(int n)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
run_fe(calc_number_t * number)1187 static void run_fe(calc_number_t *number)
1188 {
1189 calc.sci_out = ((calc.sci_out != FALSE) ? FALSE : TRUE);
1190 }
1191
handle_context_menu(HWND hWnd,WPARAM wp,LPARAM lp)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
run_canc(calc_number_t * c)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
run_rpar(calc_number_t * c)1244 static void run_rpar(calc_number_t *c)
1245 {
1246 exec_closeparent(c);
1247 }
1248
run_lpar(calc_number_t * c)1249 static void run_lpar(calc_number_t *c)
1250 {
1251 exec_infix2postfix(c, RPN_OPERATOR_PARENT);
1252 }
1253
SubclassButtonProc(HWND hWnd,WPARAM wp,LPARAM lp)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
HotButtonProc(HWND hWnd,UINT msg,WPARAM wp,LPARAM lp)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
EnumChildProc(HWND hWnd,LPARAM lParam)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
OnSettingChange(HWND hWnd,WPARAM wParam,LPARAM lParam)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
DlgMainProc(HWND hWnd,UINT msg,WPARAM wp,LPARAM lp)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__)
WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd)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