696e3ee9dfd3f2c9f19c068cfa063ea0cae7ba67
[reactos.git] / base / applications / calc / winmain.c
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 0x000000FF
52 #define CALC_CLR_BLUE 0x00FF0000
53 #define CALC_CLR_PURP 0x00FF00FF
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 /*
237 */
238
239 calc_t calc;
240
241 static void load_config(void)
242 {
243 DWORD tmp;
244 HKEY hKey;
245
246 /* If no settings are found in the registry, then use the default options */
247 calc.layout = CALC_LAYOUT_STANDARD;
248 calc.usesep = FALSE;
249
250 /* Get the configuration based on what version of Windows that's being used */
251 if (RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("SOFTWARE\\Microsoft\\Calc"), 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
252 {
253 /* Try to load last selected layout */
254 tmp = sizeof(calc.layout);
255 if (RegQueryValueEx(hKey, TEXT("layout"), NULL, NULL, (LPBYTE)&calc.layout, &tmp) != ERROR_SUCCESS)
256 calc.layout = CALC_LAYOUT_STANDARD;
257
258 /* Try to load last selected formatting option */
259 tmp = sizeof(calc.usesep);
260 if (RegQueryValueEx(hKey, TEXT("UseSep"), NULL, NULL, (LPBYTE)&calc.usesep, &tmp) != ERROR_SUCCESS)
261 calc.usesep = FALSE;
262
263 /* close the key */
264 RegCloseKey(hKey);
265 }
266
267 /* memory is empty at startup */
268 calc.is_memory = FALSE;
269
270 /* empty these values */
271 calc.sDecimal[0] = TEXT('\0');
272 calc.sThousand[0] = TEXT('\0');
273
274 /* try to open the registry */
275 if (RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Control Panel\\International"), 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
276 {
277 /* get these values (ignore errors) */
278 tmp = sizeof(calc.sDecimal);
279 RegQueryValueEx(hKey, TEXT("sDecimal"), NULL, NULL, (LPBYTE)calc.sDecimal, &tmp);
280
281 tmp = sizeof(calc.sThousand);
282 RegQueryValueEx(hKey, TEXT("sThousand"), NULL, NULL, (LPBYTE)calc.sThousand, &tmp);
283
284 /* close the key */
285 RegCloseKey(hKey);
286 }
287 /* if something goes wrong, let's apply the defaults */
288 if (calc.sDecimal[0] == TEXT('\0'))
289 _tcscpy(calc.sDecimal, TEXT("."));
290
291 if (calc.sThousand[0] == TEXT('\0'))
292 _tcscpy(calc.sThousand, TEXT(","));
293
294 /* get the string lengths */
295 calc.sDecimal_len = _tcslen(calc.sDecimal);
296 calc.sThousand_len = _tcslen(calc.sThousand);
297 }
298
299 static void save_config(void)
300 {
301 HKEY hKey;
302 DWORD sepValue;
303
304 if (RegCreateKeyEx(HKEY_CURRENT_USER, TEXT("SOFTWARE\\Microsoft\\Calc"), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hKey, NULL) != ERROR_SUCCESS)
305 {
306 return;
307 }
308
309 sepValue = (calc.usesep) ? 1 : 0;
310
311 RegSetValueEx(hKey, TEXT("layout"), 0, REG_DWORD, (const BYTE*)&calc.layout, sizeof(calc.layout));
312 RegSetValueEx(hKey, TEXT("UseSep"), 0, REG_DWORD, (const BYTE*)&sepValue, sizeof(sepValue));
313
314 RegCloseKey(hKey);
315 }
316
317 static LRESULT post_key_press(LPARAM lParam, WORD idc)
318 {
319 HWND hCtlWnd = GetDlgItem(calc.hWnd,idc);
320 TCHAR ClassName[64];
321
322 /* check if the key is enabled! */
323 if (!IsWindowEnabled(hCtlWnd))
324 return 1;
325
326 if (!GetClassName(hCtlWnd, ClassName, SIZEOF(ClassName)))
327 return 1;
328
329 if (!_tcscmp(ClassName, TEXT("Button"))) {
330 DWORD dwStyle = GetWindowLongPtr(hCtlWnd, GWL_STYLE) & 0xF;
331
332 /* Set states for press/release, but only for push buttons */
333 if (dwStyle == BS_PUSHBUTTON || dwStyle == BS_DEFPUSHBUTTON || dwStyle == BS_OWNERDRAW) {
334 if (!(lParam & KEY_WAS_DOWN)) {
335 PostMessage(hCtlWnd, BM_SETSTATE, 1, 0);
336 } else
337 if ((lParam & KEY_IS_UP)) {
338 PostMessage(hCtlWnd, BM_SETSTATE, 0, 0);
339 PostMessage(hCtlWnd, BM_CLICK, 0, 0);
340 }
341 return 1;
342 }
343 }
344 /* default action: simple click event at key release */
345 if ((lParam & KEY_IS_UP)) {
346 PostMessage(hCtlWnd, BM_CLICK, 0, 0);
347 }
348 return 1;
349 }
350
351 static int vk2ascii(unsigned int vk)
352 {
353 unsigned short int s;
354 int scan;
355 BYTE state[256];
356 HKL layout=GetKeyboardLayout(0);
357
358 if(!GetKeyboardState(state))
359 return 0;
360
361 scan=MapVirtualKeyEx(vk, 0, layout);
362 s = 0;
363 if (ToAsciiEx(vk, scan, state, &s, 0, layout)>0) {
364 /* convert to upper case */
365 if (s >= 'a' && s <= 'z')
366 s = s - 'a' + 'A';
367 /* add check to CTRL key */
368 if (vk >= 'A' && vk <= 'Z' &&
369 s >= CTRL_A && s <= CTRL_Z)
370 s |= CTRL_FLAG;
371 else
372 if (GetAsyncKeyState(VK_MENU) < 0)
373 s |= ALT_FLAG;
374 return s;
375 }
376 return 0;
377 }
378
379 static int process_vk_key(WPARAM wParam, LPARAM lParam)
380 {
381 const key2code_t *k;
382 unsigned int x;
383 unsigned short int ch;
384
385 ch = vk2ascii(LOWORD(wParam));
386 if ((lParam & KEY_IS_UP)) {
387 /* Test for "copy" to clipboard */
388 if (ch == (CTRL_C|CTRL_FLAG)) {
389 SendMessage(calc.hWnd, WM_COMMAND, IDM_EDIT_COPY, 0);
390 return 1;
391 }
392 /* Test for "paste" from clipboard */
393 if (ch == (CTRL_V|CTRL_FLAG)) {
394 SendMessage(calc.hWnd, WM_COMMAND, IDM_EDIT_PASTE, 0);
395 return 1;
396 }
397 /* Test of help menu */
398 if (LOWORD(wParam) == VK_F1) {
399 SendMessage(calc.hWnd, WM_COMMAND, IDM_HELP_HELP, 0);
400 return 1;
401 }
402 }
403
404 for (x=0; x<SIZEOF(key2code); x++) {
405 int key = key2code[x].key;
406 if (key2code[x].mask & BITMASK_IS_CTRL)
407 key |= CTRL_FLAG;
408 if ((key == ch && (key2code[x].mask & BITMASK_IS_ASCII)) ||
409 (key == LOWORD(wParam) && !(key2code[x].mask & BITMASK_IS_ASCII))
410 ) {
411 if (GetDlgItem(calc.hWnd, key2code[x].idc) == NULL)
412 continue;
413 return post_key_press(lParam, key2code[x].idc);
414 }
415 }
416 if (calc.layout == CALC_LAYOUT_SCIENTIFIC) {
417 if (calc.base == IDC_RADIO_DEC) {
418 k = key2code_base10;
419 x = SIZEOF(key2code_base10);
420 } else {
421 k = key2code_base16;
422 x = SIZEOF(key2code_base16);
423 }
424 do {
425 if (k->key == LOWORD(wParam)) {
426 return post_key_press(lParam, k->idc);
427 }
428 k++;
429 } while (--x);
430 }
431 return 0;
432 }
433
434 #ifdef USE_KEYBOARD_HOOK
435 static LRESULT CALLBACK
436 KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam)
437 {
438 if(nCode<0 || calc.is_menu_on)
439 return CallNextHookEx(calc.hKeyboardHook,nCode,wParam,lParam);
440
441 if(nCode==HC_ACTION)
442 if (process_vk_key(wParam, lParam))
443 return;
444
445 return CallNextHookEx(calc.hKeyboardHook,nCode,wParam,lParam);
446 }
447 #endif
448
449 static void update_lcd_display(HWND hwnd)
450 {
451 /*
452 * multiply size of calc.buffer by 2 because it may
453 * happen that separator is used between each digit.
454 * Also added little additional space for dot and '\0'.
455 */
456 TCHAR *tmp = (TCHAR *)alloca(sizeof(calc.buffer)*2+2*sizeof(TCHAR));
457
458 if (calc.buffer[0] == TEXT('\0'))
459 _tcscpy(tmp, TEXT("0"));
460 else
461 _tcscpy(tmp, calc.buffer);
462 /* add final '.' in decimal mode (if it's missing) */
463 if (calc.base == IDC_RADIO_DEC) {
464 if (_tcschr(tmp, TEXT('.')) == NULL)
465 _tcscat(tmp, TEXT("."));
466 }
467 /* if separator mode is on, let's add an additional space */
468 if (calc.usesep && !calc.sci_in && !calc.sci_out && !calc.is_nan) {
469 /* go to the integer part of the string */
470 TCHAR *p = _tcschr(tmp, TEXT('.'));
471 TCHAR *e = _tcschr(tmp, TEXT('\0'));
472 int n=0, t;
473
474 if (p == NULL) p = e;
475 switch (calc.base) {
476 case IDC_RADIO_HEX:
477 case IDC_RADIO_BIN:
478 t = 4;
479 break;
480 default:
481 /* fall here for:
482 IDC_RADIO_DEC:
483 IDC_RADIO_OCT: */
484 t = 3;
485 break;
486 }
487 while (--p > tmp) {
488 if (++n == t && *(p-1) != TEXT('-')) {
489 memmove(p+1, p, (e-p+1)*sizeof(TCHAR));
490 e++;
491 *p = TEXT(' ');
492 n = 0;
493 }
494 }
495 /* if decimal mode, apply regional settings */
496 if (calc.base == IDC_RADIO_DEC) {
497 TCHAR *p = tmp;
498 TCHAR *e = _tcschr(tmp, TEXT('.'));
499
500 /* searching for thousands default separator */
501 while (p < e) {
502 if (*p == TEXT(' ')) {
503 memmove(p+calc.sThousand_len, p+1, _tcslen(p)*sizeof(TCHAR));
504 memcpy(p, calc.sThousand, calc.sThousand_len*sizeof(TCHAR));
505 p += calc.sThousand_len;
506 } else
507 p++;
508 }
509 /* update decimal point too. */
510 memmove(p+calc.sDecimal_len, p+1, _tcslen(p)*sizeof(TCHAR));
511 memcpy(p, calc.sDecimal, calc.sDecimal_len*sizeof(TCHAR));
512 }
513 } else {
514 TCHAR *p = _tcschr(tmp, TEXT('.'));
515
516 /* update decimal point when usesep is false */
517 if (p != NULL) {
518 memmove(p+calc.sDecimal_len, p+1, _tcslen(p)*sizeof(TCHAR));
519 memcpy(p, calc.sDecimal, calc.sDecimal_len*sizeof(TCHAR));
520 }
521 }
522 SendDlgItemMessage(hwnd, IDC_TEXT_OUTPUT, WM_SETTEXT, (WPARAM)0, (LPARAM)tmp);
523 }
524
525 static void update_parent_display(HWND hWnd)
526 {
527 TCHAR str[8];
528 int n = eval_parent_count();
529
530 if (!n)
531 str[0] = TEXT('\0');
532 else
533 _stprintf(str,TEXT("(=%d"), n);
534 SendDlgItemMessage(hWnd, IDC_TEXT_PARENT, WM_SETTEXT, 0, (LPARAM)str);
535 }
536
537 static void build_operand(HWND hwnd, DWORD idc)
538 {
539 unsigned int i = 0, n;
540
541 if (idc == IDC_BUTTON_DOT) {
542 /* if dot is the first char, it's added automatically */
543 if (calc.buffer == calc.ptr) {
544 *calc.ptr++ = TEXT('0');
545 *calc.ptr++ = TEXT('.');
546 *calc.ptr = TEXT('\0');
547 update_lcd_display(hwnd);
548 return;
549 }
550 /* if pressed dot and it's already in the string, then return */
551 if (_tcschr(calc.buffer, TEXT('.')) != NULL)
552 return;
553 }
554 if (idc != IDC_STATIC) {
555 while (idc != key2code[i].idc) i++;
556 }
557 n = calc.ptr - calc.buffer;
558 if (idc == IDC_BUTTON_0 && n == 0) {
559 /* no need to put the dot because it's handled by update_lcd_display() */
560 calc.buffer[0] = TEXT('0');
561 calc.buffer[1] = TEXT('\0');
562 update_lcd_display(hwnd);
563 return;
564 }
565 switch (calc.base) {
566 case IDC_RADIO_HEX:
567 if (n >= 16)
568 return;
569 break;
570 case IDC_RADIO_DEC:
571 if (n >= SIZEOF(calc.buffer)-1)
572 return;
573 if (calc.sci_in) {
574 if (idc != IDC_STATIC)
575 calc.esp = (calc.esp * 10 + (key2code[i].key-'0')) % LOCAL_EXP_SIZE;
576 if (calc.ptr == calc.buffer)
577 _stprintf(calc.ptr, TEXT("0.e%+d"), calc.esp);
578 else {
579 /* adds the dot at the end if the number has no decimal part */
580 if (!_tcschr(calc.buffer, TEXT('.')))
581 *calc.ptr++ = TEXT('.');
582 _stprintf(calc.ptr, TEXT("e%+d"), calc.esp);
583 }
584 update_lcd_display(hwnd);
585 return;
586 }
587 break;
588 case IDC_RADIO_OCT:
589 if (n >= 22)
590 return;
591 break;
592 case IDC_RADIO_BIN:
593 if (n >= 64)
594 return;
595 break;
596 }
597 calc.ptr += _stprintf(calc.ptr, TEXT("%C"), key2code[i].key);
598 update_lcd_display(hwnd);
599 }
600
601 static void prepare_rpn_result(calc_number_t *rpn, TCHAR *buffer, int size, int base)
602 {
603 if (calc.is_nan) {
604 rpn_zero(&calc.code);
605 LoadString(calc.hInstance, IDS_MATH_ERROR, buffer, size);
606 return;
607 }
608 prepare_rpn_result_2(rpn, buffer, size, base);
609 }
610
611 static void display_rpn_result(HWND hwnd, calc_number_t *rpn)
612 {
613 calc.sci_in = FALSE;
614 prepare_rpn_result(rpn, calc.buffer, SIZEOF(calc.buffer), calc.base);
615 calc.ptr = calc.buffer + _tcslen(calc.buffer);
616 update_lcd_display(hwnd);
617 calc.ptr = calc.buffer;
618 update_parent_display(hwnd);
619 }
620
621 static int get_modifiers(HWND hwnd)
622 {
623 int modifiers = 0;
624
625 if (SendDlgItemMessage(hwnd, IDC_CHECK_INV, BM_GETCHECK, 0, 0))
626 modifiers |= MODIFIER_INV;
627 if (SendDlgItemMessage(hwnd, IDC_CHECK_HYP, BM_GETCHECK, 0, 0))
628 modifiers |= MODIFIER_HYP;
629
630 return modifiers;
631 }
632
633 static void convert_text2number(calc_number_t *a)
634 {
635 /* if the screen output buffer is empty, then */
636 /* the operand is taken from the last input */
637 if (calc.buffer == calc.ptr) {
638 /* if pushed valued is ZERO then we should grab it */
639 if (!_tcscmp(calc.buffer, TEXT("0.")) ||
640 !_tcscmp(calc.buffer, TEXT("0")))
641 /* this zero is good for both integer and decimal */
642 rpn_zero(a);
643 else
644 rpn_copy(a, &calc.code);
645 return;
646 }
647 /* ZERO is the default value for all numeric bases */
648 rpn_zero(a);
649 convert_text2number_2(a);
650 }
651
652 static const struct _update_check_menus {
653 DWORD *sel;
654 WORD idm;
655 WORD idc;
656 } upd[] = {
657 { &calc.layout, IDM_VIEW_STANDARD, CALC_LAYOUT_STANDARD },
658 { &calc.layout, IDM_VIEW_SCIENTIFIC, CALC_LAYOUT_SCIENTIFIC },
659 { &calc.layout, IDM_VIEW_CONVERSION, CALC_LAYOUT_CONVERSION },
660 /*-----------------------------------------*/
661 { &calc.base, IDM_VIEW_HEX, IDC_RADIO_HEX, },
662 { &calc.base, IDM_VIEW_DEC, IDC_RADIO_DEC, },
663 { &calc.base, IDM_VIEW_OCT, IDC_RADIO_OCT, },
664 { &calc.base, IDM_VIEW_BIN, IDC_RADIO_BIN, },
665 /*-----------------------------------------*/
666 { &calc.degr, IDM_VIEW_DEG, IDC_RADIO_DEG, },
667 { &calc.degr, IDM_VIEW_RAD, IDC_RADIO_RAD, },
668 { &calc.degr, IDM_VIEW_GRAD, IDC_RADIO_GRAD, },
669 /*-----------------------------------------*/
670 { &calc.size, IDM_VIEW_QWORD, IDC_RADIO_QWORD, },
671 { &calc.size, IDM_VIEW_DWORD, IDC_RADIO_DWORD, },
672 { &calc.size, IDM_VIEW_WORD, IDC_RADIO_WORD, },
673 { &calc.size, IDM_VIEW_BYTE, IDC_RADIO_BYTE, },
674 };
675
676 static void update_menu(HWND hwnd)
677 {
678 HMENU hMenu = GetSubMenu(GetMenu(hwnd), 1);
679 unsigned int x;
680
681 /* Sets the state of the layout in the menu based on the configuration file */
682 if (calc.layout == CALC_LAYOUT_SCIENTIFIC)
683 {
684 CheckMenuRadioItem(GetMenu(hwnd),
685 IDM_VIEW_STANDARD,
686 IDM_VIEW_CONVERSION,
687 IDM_VIEW_SCIENTIFIC,
688 MF_BYCOMMAND);
689 }
690 else if (calc.layout == CALC_LAYOUT_CONVERSION)
691 {
692 CheckMenuRadioItem(GetMenu(hwnd),
693 IDM_VIEW_STANDARD,
694 IDM_VIEW_CONVERSION,
695 IDM_VIEW_CONVERSION,
696 MF_BYCOMMAND);
697 }
698 else
699 {
700 CheckMenuRadioItem(GetMenu(hwnd),
701 IDM_VIEW_STANDARD,
702 IDM_VIEW_CONVERSION,
703 IDM_VIEW_STANDARD,
704 MF_BYCOMMAND);
705 }
706
707 for (x=3; x<SIZEOF(upd); x++) {
708 if (*(upd[x].sel) != upd[x].idc) {
709 CheckMenuItem(hMenu, upd[x].idm, MF_BYCOMMAND|MF_UNCHECKED);
710 SendMessage((HWND)GetDlgItem(hwnd,upd[x].idc),BM_SETCHECK,FALSE,0L);
711 } else {
712 CheckMenuItem(hMenu, upd[x].idm, MF_BYCOMMAND|MF_CHECKED);
713 SendMessage((HWND)GetDlgItem(hwnd,upd[x].idc),BM_SETCHECK,TRUE,0L);
714 }
715 }
716 CheckMenuItem(hMenu, IDM_VIEW_GROUP, MF_BYCOMMAND|(calc.usesep ? MF_CHECKED : MF_UNCHECKED));
717 }
718
719 typedef struct {
720 WORD idc;
721 WORD mask;
722 } radio_config_t;
723
724 static const radio_config_t radio_setup[] = {
725 /* CONTROL-ID hex dec oct bin */
726 { IDC_RADIO_QWORD, MAKE_BITMASK4( 1, 0, 1, 1) },
727 { IDC_RADIO_DWORD, MAKE_BITMASK4( 1, 0, 1, 1) },
728 { IDC_RADIO_WORD, MAKE_BITMASK4( 1, 0, 1, 1) },
729 { IDC_RADIO_BYTE, MAKE_BITMASK4( 1, 0, 1, 1) },
730 { IDC_RADIO_DEG, MAKE_BITMASK4( 0, 1, 0, 0) },
731 { IDC_RADIO_RAD, MAKE_BITMASK4( 0, 1, 0, 0) },
732 { IDC_RADIO_GRAD, MAKE_BITMASK4( 0, 1, 0, 0) },
733 };
734
735 static void enable_allowed_controls(HWND hwnd, DWORD base)
736 {
737 BYTE mask;
738 int n;
739
740 switch (base) {
741 case IDC_RADIO_DEC:
742 mask = BITMASK_DEC_MASK;
743 break;
744 case IDC_RADIO_HEX:
745 mask = BITMASK_HEX_MASK;
746 break;
747 case IDC_RADIO_OCT:
748 mask = BITMASK_OCT_MASK;
749 break;
750 case IDC_RADIO_BIN:
751 mask = BITMASK_BIN_MASK;
752 break;
753 default:
754 return;
755 }
756 for (n=0; n<SIZEOF(key2code); n++) {
757 if (key2code[n].mask != 0) {
758 HWND hCtlWnd = GetDlgItem(hwnd, key2code[n].idc);
759 BOOL current;
760
761 if ((key2code[n].mask & BITMASK_IS_STATS))
762 current = IsWindow(calc.hStatWnd) ? TRUE : FALSE;
763 else
764 current = (key2code[n].mask & mask) ? TRUE : FALSE;
765 if (IsWindowEnabled(hCtlWnd) != current)
766 EnableWindow(hCtlWnd, current);
767 }
768 }
769 }
770
771 static void update_radio(HWND hwnd, unsigned int base)
772 {
773 HMENU hMenu;
774 LPCTSTR lpMenuId;
775 WORD mask;
776 int n;
777
778 switch (base) {
779 case IDC_RADIO_DEC:
780 lpMenuId = MAKEINTRESOURCE(IDR_MENU_SCIENTIFIC_1);
781 mask = BITMASK_DEC_MASK;
782 break;
783 case IDC_RADIO_HEX:
784 lpMenuId = MAKEINTRESOURCE(IDR_MENU_SCIENTIFIC_2);
785 mask = BITMASK_HEX_MASK;
786 break;
787 case IDC_RADIO_OCT:
788 lpMenuId = MAKEINTRESOURCE(IDR_MENU_SCIENTIFIC_2);
789 mask = BITMASK_OCT_MASK;
790 break;
791 case IDC_RADIO_BIN:
792 lpMenuId = MAKEINTRESOURCE(IDR_MENU_SCIENTIFIC_2);
793 mask = BITMASK_BIN_MASK;
794 break;
795 default:
796 return;
797 }
798
799 if (calc.base != base) {
800 convert_text2number(&calc.code);
801 convert_real_integer(base);
802 calc.base = base;
803 display_rpn_result(hwnd, &calc.code);
804
805 hMenu = GetMenu(hwnd);
806 DestroyMenu(hMenu);
807 hMenu = LoadMenu(calc.hInstance, lpMenuId);
808 SetMenu(hwnd, hMenu);
809 update_menu(hwnd);
810
811 for (n=0; n<SIZEOF(radio_setup); n++)
812 ShowWindow(GetDlgItem(hwnd, radio_setup[n].idc), (radio_setup[n].mask & mask) ? SW_SHOW : SW_HIDE);
813
814 enable_allowed_controls(hwnd, base);
815 }
816
817 SendDlgItemMessage(hwnd, calc.base, BM_SETCHECK, BST_CHECKED, 0);
818 if (base == IDC_RADIO_DEC)
819 SendDlgItemMessage(hwnd, calc.degr, BM_SETCHECK, BST_CHECKED, 0);
820 else
821 SendDlgItemMessage(hwnd, calc.size, BM_SETCHECK, BST_CHECKED, 0);
822 }
823
824 static void update_memory_flag(HWND hWnd, BOOL mem_flag)
825 {
826 calc.is_memory = mem_flag;
827 SendDlgItemMessage(hWnd, IDC_TEXT_MEMORY, WM_SETTEXT, 0, (LPARAM)(mem_flag ? TEXT("M") : TEXT("")));
828 }
829
830 static void update_n_stats_items(HWND hWnd, TCHAR *buffer)
831 {
832 unsigned int n = SendDlgItemMessage(hWnd, IDC_LIST_STAT, LB_GETCOUNT, 0, 0);
833
834 _stprintf(buffer, TEXT("n=%d"), n);
835 SendDlgItemMessage(hWnd, IDC_TEXT_NITEMS, WM_SETTEXT, 0, (LPARAM)buffer);
836 }
837
838 static void clean_stat_list(void)
839 {
840 statistic_t *p = calc.stat;
841
842 while (p != NULL) {
843 statistic_t *s = p;
844 p = (statistic_t *)(p->next);
845 rpn_free(&s->num);
846 free(s);
847 }
848 calc.stat = p;
849 }
850
851 static void delete_stat_item(int n)
852 {
853 statistic_t *p = calc.stat;
854 statistic_t *s;
855
856 if (n == 0) {
857 calc.stat = (statistic_t *)p->next;
858 rpn_free(&p->num);
859 free(p);
860 } else {
861 s = (statistic_t *)p->next;
862 while (--n) {
863 p = s;
864 s = (statistic_t *)p->next;
865 }
866 p->next = s->next;
867 rpn_free(&s->num);
868 free(s);
869 }
870 }
871
872 static char *ReadConversion(const char *formula)
873 {
874 int len = strlen(formula);
875 char *str = (char *)malloc(len+3);
876
877 if (str == NULL)
878 return NULL;
879
880 str[0] = '(';
881 memcpy(str+1, formula, len);
882 str[len+1] = ')';
883 str[len+2] = '\0';
884
885 _tcscpy(calc.source, (*calc.buffer == _T('\0')) ? _T("0") : calc.buffer);
886
887 /* clear display content before proceeding */
888 calc.ptr = calc.buffer;
889 calc.buffer[0] = TEXT('\0');
890
891 return str;
892 }
893
894 static INT_PTR CALLBACK DlgStatProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
895 {
896 TCHAR buffer[SIZEOF(calc.buffer)];
897 DWORD n;
898
899 switch (msg) {
900 case WM_INITDIALOG:
901 return TRUE;
902 case WM_COMMAND:
903 switch (LOWORD(wp)) {
904 case IDC_LIST_STAT:
905 if (HIWORD(wp) == CBN_DBLCLK)
906 SendMessage(hWnd, WM_COMMAND, (WPARAM)IDC_BUTTON_LOAD, 0);
907 return TRUE;
908 case IDC_BUTTON_RET:
909 SetFocus(GetDlgItem(GetParent(hWnd), IDC_BUTTON_FOCUS));
910 return TRUE;
911 case IDC_BUTTON_LOAD:
912 n = SendDlgItemMessage(hWnd, IDC_LIST_STAT, LB_GETCURSEL, 0, 0);
913 if (n == (DWORD)-1)
914 return TRUE;
915 PostMessage(GetParent(hWnd), WM_LOAD_STAT, (WPARAM)n, 0);
916 return TRUE;
917 case IDC_BUTTON_CD:
918 n = SendDlgItemMessage(hWnd, IDC_LIST_STAT, LB_GETCURSEL, 0, 0);
919 if (n == (DWORD)-1)
920 return TRUE;
921 SendDlgItemMessage(hWnd, IDC_LIST_STAT, LB_DELETESTRING, (WPARAM)n, 0);
922 update_n_stats_items(hWnd, buffer);
923 delete_stat_item(n);
924 return TRUE;
925 case IDC_BUTTON_CAD:
926 SendDlgItemMessage(hWnd, IDC_LIST_STAT, LB_RESETCONTENT, 0, 0);
927 clean_stat_list();
928 update_n_stats_items(hWnd, buffer);
929 return TRUE;
930 }
931 break;
932 case WM_CLOSE:
933 clean_stat_list();
934 DestroyWindow(hWnd);
935 return TRUE;
936 case WM_DESTROY:
937 PostMessage(GetParent(hWnd), WM_CLOSE_STATS, 0, 0);
938 return TRUE;
939 case WM_INSERT_STAT:
940 prepare_rpn_result(&(((statistic_t *)lp)->num),
941 buffer, SIZEOF(buffer),
942 ((statistic_t *)lp)->base);
943 SendDlgItemMessage(hWnd, IDC_LIST_STAT, LB_ADDSTRING, 0, (LPARAM)buffer);
944 update_n_stats_items(hWnd, buffer);
945 return TRUE;
946 }
947 return FALSE;
948 }
949
950 static BOOL idm_2_idc(int idm, WPARAM *pIdc)
951 {
952 int x;
953
954 for (x=0; x<SIZEOF(upd); x++) {
955 if (upd[x].idm == idm)
956 {
957 *pIdc = (WPARAM)(upd[x].idc);
958 return TRUE;
959 }
960 }
961 return FALSE;
962 }
963
964 static void CopyMemToClipboard(void *ptr)
965 {
966 if(OpenClipboard(NULL)) {
967 HGLOBAL clipbuffer;
968 TCHAR *buffer;
969
970 EmptyClipboard();
971 clipbuffer = GlobalAlloc(GMEM_DDESHARE, (_tcslen(ptr)+1)*sizeof(TCHAR));
972 buffer = (TCHAR *)GlobalLock(clipbuffer);
973 _tcscpy(buffer, ptr);
974 GlobalUnlock(clipbuffer);
975 #ifdef UNICODE
976 SetClipboardData(CF_UNICODETEXT,clipbuffer);
977 #else
978 SetClipboardData(CF_TEXT,clipbuffer);
979 #endif
980 CloseClipboard();
981 }
982 }
983
984 static void handle_copy_command(HWND hWnd)
985 {
986 TCHAR display[sizeof(calc.buffer)];
987
988 SendDlgItemMessage(hWnd, IDC_TEXT_OUTPUT, WM_GETTEXT, (WPARAM)SIZEOF(display), (LPARAM)display);
989 if (calc.base == IDC_RADIO_DEC && _tcschr(calc.buffer, _T('.')) == NULL)
990 display[_tcslen(display)-calc.sDecimal_len] = TEXT('\0');
991 CopyMemToClipboard(display);
992 }
993
994 static char *ReadClipboard(void)
995 {
996 char *buffer = NULL;
997
998 if (OpenClipboard(NULL)) {
999 HANDLE hData = GetClipboardData(CF_TEXT);
1000 char *fromClipboard;
1001
1002 if (hData != NULL) {
1003 fromClipboard = (char *)GlobalLock(hData);
1004 if (strlen(fromClipboard))
1005 buffer = _strupr(_strdup(fromClipboard));
1006 GlobalUnlock( hData );
1007 }
1008 CloseClipboard();
1009 }
1010 return buffer;
1011 }
1012
1013 static char *handle_sequence_input(HWND hwnd, sequence_t *seq)
1014 {
1015 char *ptr = seq->ptr;
1016 int ch, x;
1017
1018 ch = *ptr++;
1019 if (ch == '\\')
1020 PostMessage(hwnd, WM_COMMAND, (WPARAM)IDC_BUTTON_DAT, 0);
1021 else
1022 if (ch == ':') {
1023 ch = *ptr;
1024 if (ch != '\0')
1025 ptr++;
1026 switch (ch) {
1027 case 'C': PostMessage(hwnd, WM_COMMAND, (WPARAM)IDC_BUTTON_MC, 0); break;
1028 case 'E': PostMessage(hwnd, WM_COMMAND, (WPARAM)IDC_BUTTON_EXP,0); break;
1029 case 'M': PostMessage(hwnd, WM_COMMAND, (WPARAM)IDC_BUTTON_MS, 0); break;
1030 case 'P': PostMessage(hwnd, WM_COMMAND, (WPARAM)IDC_BUTTON_MP, 0); break;
1031 case 'Q': PostMessage(hwnd, WM_COMMAND, (WPARAM)IDC_BUTTON_CANC, 0); break;
1032 case 'R': PostMessage(hwnd, WM_COMMAND, (WPARAM)IDC_BUTTON_MR, 0); break;
1033 }
1034 } else
1035 if (ch == '$') {
1036 calc.ptr =
1037 _tcscpy(calc.buffer, calc.source) +
1038 _tcslen(calc.source);
1039 } else {
1040 for (x=0; x<SIZEOF(key2code); x++) {
1041 if (!(key2code[x].mask & BITMASK_IS_ASCII) ||
1042 (key2code[x].mask & BITMASK_IS_CTRL))
1043 continue;
1044 if (key2code[x].key == ch) {
1045 PostMessage(hwnd, WM_COMMAND, (WPARAM)key2code[x].idc, 0);
1046 break;
1047 }
1048 }
1049 }
1050 seq->ptr = ptr;
1051 if (*ptr != '\0')
1052 PostMessage(hwnd, seq->wm_msg, 0, 0);
1053 else {
1054 free(seq->data);
1055 seq->data = seq->ptr = ptr = NULL;
1056 }
1057 return ptr;
1058 }
1059
1060 static void run_dat_sta(calc_number_t *a)
1061 {
1062 statistic_t *s = (statistic_t *)malloc(sizeof(statistic_t));
1063 statistic_t *p = calc.stat;
1064
1065 rpn_alloc(&s->num);
1066 rpn_copy(&s->num, a);
1067 s->base = calc.base;
1068 s->next = NULL;
1069 if (p == NULL)
1070 calc.stat = s;
1071 else {
1072 while (p->next != NULL)
1073 p = (statistic_t *)(p->next);
1074 p->next = s;
1075 }
1076 PostMessage(calc.hStatWnd, WM_INSERT_STAT, 0, (LPARAM)s);
1077 }
1078
1079 static void run_mp(calc_number_t *c)
1080 {
1081 calc_node_t cn;
1082
1083 cn.number = *c;
1084 cn.base = calc.base;
1085 run_operator(&calc.memory, &calc.memory, &cn, RPN_OPERATOR_ADD);
1086 update_memory_flag(calc.hWnd, TRUE);
1087 }
1088
1089 static void run_mm(calc_number_t *c)
1090 {
1091 calc_node_t cn;
1092
1093 cn.number = *c;
1094 cn.base = calc.base;
1095 run_operator(&calc.memory, &calc.memory, &cn, RPN_OPERATOR_SUB);
1096 update_memory_flag(calc.hWnd, TRUE);
1097 }
1098
1099 static void run_ms(calc_number_t *c)
1100 {
1101 rpn_copy(&calc.memory.number, c);
1102 calc.memory.base = calc.base;
1103 update_memory_flag(calc.hWnd, rpn_is_zero(&calc.memory.number) ? FALSE : TRUE);
1104 }
1105
1106 static void run_mw(calc_number_t *c)
1107 {
1108 calc_number_t tmp;
1109
1110 rpn_copy(&tmp, &calc.memory.number);
1111 rpn_copy(&calc.memory.number, c);
1112 calc.memory.base = calc.base;
1113 if (calc.is_memory)
1114 rpn_copy(c, &tmp);
1115 update_memory_flag(calc.hWnd, rpn_is_zero(&calc.memory.number) ? FALSE : TRUE);
1116 }
1117
1118 static statistic_t *upload_stat_number(int n)
1119 {
1120 statistic_t *p = calc.stat;
1121
1122 if (p == NULL)
1123 return p;
1124
1125 while (n--) {
1126 p = (statistic_t *)(p->next);
1127 if (p == NULL)
1128 return p;
1129 }
1130
1131 #ifndef ENABLE_MULTI_PRECISION
1132 if (calc.base != p->base) {
1133 if (calc.base == IDC_RADIO_DEC)
1134 calc.code.f = (double)p->num.i;
1135 else {
1136 calc.code.i = (__int64)p->num.f;
1137 apply_int_mask(&calc.code);
1138 }
1139 } else
1140 #endif
1141 rpn_copy(&calc.code, &p->num);
1142
1143 calc.is_nan = FALSE;
1144
1145 return p;
1146 }
1147
1148 static void run_fe(calc_number_t *number)
1149 {
1150 calc.sci_out = ((calc.sci_out != FALSE) ? FALSE : TRUE);
1151 }
1152
1153 static void handle_context_menu(HWND hWnd, WPARAM wp, LPARAM lp)
1154 {
1155 TCHAR text[64];
1156 HMENU hMenu = CreatePopupMenu();
1157 BOOL idm;
1158
1159 LoadString(calc.hInstance, IDS_QUICKHELP, text, SIZEOF(text));
1160 AppendMenu(hMenu, MF_STRING | MF_ENABLED, IDM_HELP_HELP, text);
1161 idm = TrackPopupMenu( hMenu,
1162 TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RETURNCMD | TPM_RIGHTBUTTON,
1163 LOWORD(lp),
1164 HIWORD(lp),
1165 0,
1166 hWnd,
1167 NULL);
1168 DestroyMenu(hMenu);
1169 #ifndef DISABLE_HTMLHELP_SUPPORT
1170 if (idm) {
1171 HH_POPUP popup;
1172
1173 memset(&popup, 0, sizeof(popup));
1174 popup.cbStruct = sizeof(HH_POPUP);
1175 popup.clrForeground = 1;
1176 popup.clrBackground = -1;
1177 popup.pt.x = LOWORD(lp);
1178 popup.pt.y = HIWORD(lp);
1179 popup.rcMargins.top = -1;
1180 popup.rcMargins.bottom = -1;
1181 popup.rcMargins.left = -1;
1182 popup.rcMargins.right = -1;
1183 popup.idString = GetWindowLongPtr((HWND)wp, GWL_ID);
1184 calc_HtmlHelp((HWND)wp, HTMLHELP_PATH("/popups.txt"), HH_DISPLAY_TEXT_POPUP, (DWORD_PTR)&popup);
1185 }
1186 #else
1187 (void)idm;
1188 #endif
1189 }
1190
1191 static void run_canc(calc_number_t *c)
1192 {
1193 flush_postfix();
1194 rpn_zero(c);
1195 /* clear also scientific display modes */
1196 calc.sci_out = FALSE;
1197 calc.sci_in = FALSE;
1198 /* clear state of inv and hyp flags */
1199 SendDlgItemMessage(calc.hWnd, IDC_CHECK_INV, BM_SETCHECK, 0, 0);
1200 SendDlgItemMessage(calc.hWnd, IDC_CHECK_HYP, BM_SETCHECK, 0, 0);
1201 }
1202
1203 static void run_rpar(calc_number_t *c)
1204 {
1205 exec_closeparent(c);
1206 }
1207
1208 static void run_lpar(calc_number_t *c)
1209 {
1210 exec_infix2postfix(c, RPN_OPERATOR_PARENT);
1211 }
1212
1213 static LRESULT CALLBACK SubclassButtonProc(HWND hWnd, WPARAM wp, LPARAM lp)
1214 {
1215 LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lp;
1216 DWORD dwStyle;
1217 UINT dwText;
1218 TCHAR text[64];
1219 int dx, dy, len;
1220 SIZE size;
1221 POINT pt;
1222
1223 if(dis->CtlType == ODT_BUTTON) {
1224 /*
1225 * little exception: 1/x has different color
1226 * in standard and scientific modes
1227 */
1228 if ((calc.layout == CALC_LAYOUT_STANDARD ||
1229 calc.layout == CALC_LAYOUT_CONVERSION) &&
1230 IDC_BUTTON_RX == dis->CtlID) {
1231 SetTextColor(dis->hDC, CALC_CLR_BLUE);
1232 } else
1233 for (dx=0; dx<SIZEOF(key2code); dx++) {
1234 if (key2code[dx].idc == dis->CtlID) {
1235 SetTextColor(dis->hDC, key2code[dx].col);
1236 break;
1237 }
1238 }
1239 /* button text to write */
1240 len = GetWindowText(dis->hwndItem, text, SIZEOF(text));
1241 /* default state: unpushed & enabled */
1242 dwStyle = 0;
1243 dwText = 0;
1244 if ((dis->itemState & ODS_DISABLED))
1245 dwText = DSS_DISABLED;
1246 if ((dis->itemState & ODS_SELECTED))
1247 dwStyle = DFCS_PUSHED;
1248
1249 DrawFrameControl(dis->hDC, &dis->rcItem, DFC_BUTTON, DFCS_BUTTONPUSH | dwStyle);
1250 GetTextExtentPoint32(dis->hDC, text, len, &size);
1251 dx = ((dis->rcItem.right-dis->rcItem.left) - size.cx) >> 1;
1252 dy = ((dis->rcItem.bottom-dis->rcItem.top) - size.cy) >> 1;
1253 if ((dwStyle & DFCS_PUSHED)) {
1254 dx++;
1255 dy++;
1256 }
1257 pt.x = dis->rcItem.left + dx;
1258 pt.y = dis->rcItem.top + dy;
1259 DrawState(dis->hDC, NULL, NULL, (LPARAM)text, 0, pt.x, pt.y, size.cx, size.cy, DST_TEXT | dwText);
1260 }
1261 return 1L;
1262 }
1263
1264 static INT_PTR CALLBACK DlgMainProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
1265 {
1266 unsigned int x;
1267 RECT rc;
1268 HMENU hMenu;
1269
1270 switch (msg) {
1271 case WM_DRAWITEM:
1272 return SubclassButtonProc(hWnd, wp, lp);
1273
1274 case WM_INITDIALOG:
1275 #ifdef DISABLE_HTMLHELP_SUPPORT
1276 EnableMenuItem(GetMenu(hWnd), IDM_HELP_HELP, MF_BYCOMMAND | MF_GRAYED);
1277 #endif
1278 calc.hWnd=hWnd;
1279
1280 #ifdef USE_KEYBOARD_HOOK
1281 calc.hKeyboardHook=SetWindowsHookEx(
1282 WH_KEYBOARD,
1283 KeyboardHookProc,
1284 NULL,
1285 GetCurrentThreadId()
1286 );
1287 #endif
1288 rpn_zero(&calc.code);
1289 calc.sci_out = FALSE;
1290 calc.base = IDC_RADIO_DEC;
1291 calc.size = IDC_RADIO_QWORD;
1292 calc.degr = IDC_RADIO_DEG;
1293 calc.ptr = calc.buffer;
1294 calc.is_nan = FALSE;
1295 enable_allowed_controls(hWnd, IDC_RADIO_DEC);
1296 update_radio(hWnd, IDC_RADIO_DEC);
1297 update_menu(hWnd);
1298 display_rpn_result(hWnd, &calc.code);
1299 update_memory_flag(hWnd, calc.is_memory);
1300 /* remove keyboard focus */
1301 SetFocus(GetDlgItem(hWnd, IDC_BUTTON_FOCUS));
1302 /* set our calc icon */
1303 SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM)LoadIcon(calc.hInstance, MAKEINTRESOURCE(IDI_CALC_BIG)));
1304 SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM)LoadIcon(calc.hInstance, MAKEINTRESOURCE(IDI_CALC_SMALL)));
1305
1306 /* Sets the state of the option to group digits */
1307 hMenu = GetSubMenu(GetMenu(hWnd), 1);
1308 CheckMenuItem(hMenu, IDM_VIEW_GROUP, (calc.usesep ? MF_CHECKED : MF_UNCHECKED));
1309
1310 /* update text for decimal button */
1311 SendDlgItemMessage(hWnd, IDC_BUTTON_DOT, WM_SETTEXT, (WPARAM)0, (LPARAM)calc.sDecimal);
1312 /* Fill combo box for conversion */
1313 if (calc.layout == CALC_LAYOUT_CONVERSION)
1314 ConvInit(hWnd);
1315 /* Restore the window at the same position it was */
1316 if (calc.x_coord >= 0 && calc.y_coord >= 0) {
1317 int w, h, sw, sh;
1318
1319 GetWindowRect(hWnd, &rc);
1320 w = rc.right-rc.left;
1321 h = rc.bottom-rc.top;
1322 sw = GetSystemMetrics(SM_CXSCREEN);
1323 sh = GetSystemMetrics(SM_CYSCREEN);
1324 if (calc.x_coord+w > sw) calc.x_coord = sw - w;
1325 if (calc.y_coord+h > sh) calc.y_coord = sh - h;
1326 MoveWindow(hWnd, calc.x_coord, calc.y_coord, w, h, FALSE);
1327 }
1328 break;
1329 case WM_CTLCOLORSTATIC:
1330 if ((HWND)lp == GetDlgItem(hWnd, IDC_TEXT_OUTPUT))
1331 return (LRESULT)GetStockObject(WHITE_BRUSH);
1332 break;
1333 case WM_HANDLE_CLIPBOARD:
1334 handle_sequence_input(hWnd, &calc.Clipboard);
1335 return TRUE;
1336 case WM_COMMAND:
1337 /*
1338 * if selection of category is changed, we must
1339 * updatethe content of the "from/to" combo boxes.
1340 */
1341 if (wp == MAKEWPARAM(IDC_COMBO_CATEGORY, CBN_SELCHANGE)) {
1342 ConvAdjust(hWnd, SendDlgItemMessage(hWnd, IDC_COMBO_CATEGORY, CB_GETCURSEL, 0, 0));
1343 return TRUE;
1344 }
1345 if (HIWORD(wp) != BN_CLICKED && HIWORD(wp) != BN_DBLCLK)
1346 break;
1347 /* avoid flicker if the user selects from keyboard */
1348 if (GetFocus() != GetDlgItem(hWnd, IDC_BUTTON_FOCUS))
1349 SetFocus(GetDlgItem(hWnd, IDC_BUTTON_FOCUS));
1350 switch (LOWORD(wp)) {
1351 case IDM_HELP_ABOUT:
1352 {
1353 TCHAR infotitle[100];
1354 TCHAR infotext[200];
1355 LoadString(calc.hInstance, IDS_CALC_NAME, infotitle, SIZEOF(infotitle));
1356 LoadString(calc.hInstance, IDS_AUTHOR, infotext, SIZEOF(infotext));
1357 ShellAbout(hWnd, infotitle, infotext, (HICON)LoadIcon(calc.hInstance, MAKEINTRESOURCE(IDI_CALC_BIG)));
1358 return TRUE;
1359 }
1360 case IDM_HELP_HELP:
1361 #ifndef DISABLE_HTMLHELP_SUPPORT
1362 calc_HtmlHelp(hWnd, HTMLHELP_PATH("/general_information.htm"), HH_DISPLAY_TOPIC, (DWORD_PTR)NULL);
1363 #endif
1364 return TRUE;
1365 case IDM_VIEW_STANDARD:
1366 if (calc.layout != CALC_LAYOUT_STANDARD)
1367 {
1368 calc.layout = CALC_LAYOUT_STANDARD;
1369 calc.action = IDM_VIEW_STANDARD;
1370 DestroyWindow(hWnd);
1371 save_config();
1372
1373 CheckMenuRadioItem(GetMenu(hWnd),
1374 IDM_VIEW_STANDARD,
1375 IDM_VIEW_CONVERSION,
1376 IDM_VIEW_STANDARD,
1377 MF_BYCOMMAND);
1378 }
1379 return TRUE;
1380 case IDM_VIEW_SCIENTIFIC:
1381 if (calc.layout != CALC_LAYOUT_SCIENTIFIC)
1382 {
1383 calc.layout = CALC_LAYOUT_SCIENTIFIC;
1384 calc.action = IDM_VIEW_SCIENTIFIC;
1385 DestroyWindow(hWnd);
1386 save_config();
1387
1388 CheckMenuRadioItem(GetMenu(hWnd),
1389 IDM_VIEW_STANDARD,
1390 IDM_VIEW_CONVERSION,
1391 IDM_VIEW_SCIENTIFIC,
1392 MF_BYCOMMAND);
1393 }
1394 return TRUE;
1395 case IDM_VIEW_CONVERSION:
1396 if (calc.layout != CALC_LAYOUT_CONVERSION)
1397 {
1398 calc.layout = CALC_LAYOUT_CONVERSION;
1399 calc.action = IDM_VIEW_CONVERSION;
1400 DestroyWindow(hWnd);
1401 save_config();
1402
1403 CheckMenuRadioItem(GetMenu(hWnd),
1404 IDM_VIEW_STANDARD,
1405 IDM_VIEW_CONVERSION,
1406 IDM_VIEW_CONVERSION,
1407 MF_BYCOMMAND);
1408 }
1409 return TRUE;
1410 case IDM_VIEW_HEX:
1411 case IDM_VIEW_DEC:
1412 case IDM_VIEW_OCT:
1413 case IDM_VIEW_BIN:
1414 case IDM_VIEW_DEG:
1415 case IDM_VIEW_RAD:
1416 case IDM_VIEW_GRAD:
1417 case IDM_VIEW_QWORD:
1418 case IDM_VIEW_DWORD:
1419 case IDM_VIEW_WORD:
1420 case IDM_VIEW_BYTE:
1421 {
1422 WPARAM idc;
1423 if(idm_2_idc(LOWORD(wp), &idc))
1424 {
1425 SendMessage(hWnd, WM_COMMAND, idc, 0);
1426 return TRUE;
1427 }
1428 return FALSE;
1429 }
1430 case IDM_EDIT_COPY:
1431 handle_copy_command(hWnd);
1432 return TRUE;
1433 case IDM_EDIT_PASTE:
1434 if (calc.Clipboard.data != NULL)
1435 break;
1436 calc.Clipboard.data = ReadClipboard();
1437 if (calc.Clipboard.data != NULL) {
1438 /* clear the content of the display before pasting */
1439 PostMessage(hWnd, WM_COMMAND, IDC_BUTTON_CE, 0);
1440 calc.Clipboard.ptr = calc.Clipboard.data;
1441 calc.Clipboard.wm_msg = WM_HANDLE_CLIPBOARD;
1442 handle_sequence_input(hWnd, &calc.Clipboard);
1443 }
1444 return TRUE;
1445 case IDM_VIEW_GROUP:
1446 calc.usesep = (calc.usesep ? FALSE : TRUE);
1447 update_menu(hWnd);
1448 update_lcd_display(hWnd);
1449 save_config();
1450 return TRUE;
1451 case IDC_BUTTON_CONVERT:
1452 ConvExecute(hWnd);
1453 return TRUE;
1454 case IDC_BUTTON_CE: {
1455 calc_number_t tmp;
1456 rpn_zero(&tmp);
1457 display_rpn_result(hWnd, &tmp);
1458 }
1459 return TRUE;
1460 case IDC_RADIO_DEC:
1461 case IDC_RADIO_HEX:
1462 case IDC_RADIO_OCT:
1463 case IDC_RADIO_BIN:
1464 /* GNU WINDRES is bugged so I must always force radio update */
1465 /* (Fix for Win95/98) */
1466 #ifdef _MSC_VER
1467 if (calc.base == LOWORD(wp))
1468 break;
1469 #endif
1470 calc.is_nan = FALSE;
1471 update_radio(hWnd, LOWORD(wp));
1472 return TRUE;
1473 case IDC_RADIO_DEG:
1474 case IDC_RADIO_RAD:
1475 case IDC_RADIO_GRAD:
1476 /* GNU WINDRES is bugged so I must always force radio update */
1477 /* (Fix for Win95/98) */
1478 #ifdef _MSC_VER
1479 if (calc.degr == LOWORD(wp))
1480 break;
1481 #endif
1482 calc.degr = LOWORD(wp);
1483 calc.is_nan = FALSE;
1484 update_menu(hWnd);
1485 return TRUE;
1486 case IDC_RADIO_QWORD:
1487 case IDC_RADIO_DWORD:
1488 case IDC_RADIO_WORD:
1489 case IDC_RADIO_BYTE:
1490 /* GNU WINDRES is bugged so I must always force radio update */
1491 /* (Fix for Win95/98) */
1492 #ifdef _MSC_VER
1493 if (calc.size == LOWORD(wp))
1494 break;
1495 #endif
1496 calc.size = LOWORD(wp);
1497 calc.is_nan = FALSE;
1498 update_menu(hWnd);
1499 /*
1500 * update the content of the display
1501 */
1502 convert_text2number(&calc.code);
1503 apply_int_mask(&calc.code);
1504 display_rpn_result(hWnd, &calc.code);
1505 return TRUE;
1506 case IDC_BUTTON_1:
1507 case IDC_BUTTON_2:
1508 case IDC_BUTTON_3:
1509 case IDC_BUTTON_4:
1510 case IDC_BUTTON_5:
1511 case IDC_BUTTON_6:
1512 case IDC_BUTTON_7:
1513 case IDC_BUTTON_8:
1514 case IDC_BUTTON_9:
1515 case IDC_BUTTON_0:
1516 case IDC_BUTTON_DOT:
1517 case IDC_BUTTON_A:
1518 case IDC_BUTTON_B:
1519 case IDC_BUTTON_C:
1520 case IDC_BUTTON_D:
1521 case IDC_BUTTON_E:
1522 case IDC_BUTTON_F:
1523 calc.is_nan = FALSE;
1524 build_operand(hWnd, LOWORD(wp));
1525 return TRUE;
1526 case IDC_BUTTON_PERCENT:
1527 case IDC_BUTTON_ADD:
1528 case IDC_BUTTON_SUB:
1529 case IDC_BUTTON_MULT:
1530 case IDC_BUTTON_DIV:
1531 case IDC_BUTTON_MOD:
1532 case IDC_BUTTON_AND:
1533 case IDC_BUTTON_OR:
1534 case IDC_BUTTON_XOR:
1535 case IDC_BUTTON_LSH:
1536 case IDC_BUTTON_RSH:
1537 case IDC_BUTTON_EQU:
1538 case IDC_BUTTON_XeY:
1539 case IDC_BUTTON_XrY:
1540 if (calc.is_nan) break;
1541 /*
1542 * LSH and XeY buttons hold also the RSH and XrY functions with INV modifier,
1543 * but since they are two operand operators, they must be handled here.
1544 */
1545 if ((get_modifiers(hWnd) & MODIFIER_INV))
1546 {
1547 WPARAM IdcSim = IDC_STATIC;
1548
1549 switch (LOWORD(wp)) {
1550 case IDC_BUTTON_LSH: IdcSim = MAKEWPARAM(IDC_BUTTON_RSH, BN_CLICKED); break;
1551 case IDC_BUTTON_XeY: IdcSim = MAKEWPARAM(IDC_BUTTON_XrY, BN_CLICKED); break;
1552 }
1553
1554 if (IdcSim != IDC_STATIC)
1555 {
1556 PostMessage(hWnd, WM_COMMAND, IdcSim, 0);
1557 CheckDlgButton(hWnd, IDC_CHECK_INV, BST_UNCHECKED);
1558 }
1559 }
1560
1561 for (x=0; x<SIZEOF(operator_codes); x++) {
1562 if (LOWORD(wp) == operator_codes[x]) {
1563 convert_text2number(&calc.code);
1564
1565 if (calc.ptr == calc.buffer) {
1566 if (calc.last_operator != x) {
1567 if (x != RPN_OPERATOR_EQUAL)
1568 exec_change_infix();
1569 } else
1570 if (x == RPN_OPERATOR_EQUAL) {
1571 exec_infix2postfix(&calc.code, calc.prev_operator);
1572 rpn_copy(&calc.code, &calc.prev);
1573 } else
1574 break;
1575 }
1576
1577 /* if no change then quit silently, */
1578 /* without display updates */
1579 if (!exec_infix2postfix(&calc.code, x))
1580 break;
1581
1582 display_rpn_result(hWnd, &calc.code);
1583 break;
1584 }
1585 }
1586 return TRUE;
1587 case IDC_BUTTON_BACK:
1588 if (calc.sci_in) {
1589 if (calc.esp == 0) {
1590 TCHAR *ptr;
1591
1592 calc.sci_in = FALSE;
1593 ptr = _tcschr(calc.ptr, TEXT('e'));
1594 if (ptr)
1595 *ptr = TEXT('\0');
1596 update_lcd_display(hWnd);
1597 } else {
1598 calc.esp /= 10;
1599 build_operand(hWnd, IDC_STATIC);
1600 }
1601 } else
1602 if (calc.ptr != calc.buffer) {
1603 *--calc.ptr = TEXT('\0');
1604 if (!_tcscmp(calc.buffer, TEXT("-")) ||
1605 !_tcscmp(calc.buffer, TEXT("-0")) ||
1606 !_tcscmp(calc.buffer, TEXT("0"))) {
1607 calc.ptr = calc.buffer;
1608 calc.buffer[0] = TEXT('\0');
1609 }
1610 update_lcd_display(hWnd);
1611 }
1612 return TRUE;
1613 case IDC_BUTTON_MC:
1614 rpn_zero(&calc.memory.number);
1615 update_memory_flag(hWnd, FALSE);
1616 return TRUE;
1617 case IDC_BUTTON_MR:
1618 if (calc.is_memory) {
1619 calc.is_nan = FALSE;
1620 rpn_copy(&calc.code, &calc.memory.number);
1621 display_rpn_result(hWnd, &calc.code);
1622 }
1623 return TRUE;
1624 case IDC_BUTTON_EXP:
1625 if (calc.sci_in || calc.is_nan || calc.buffer == calc.ptr)
1626 break;
1627 calc.sci_in = TRUE;
1628 calc.esp = 0;
1629 build_operand(hWnd, IDC_STATIC);
1630 return TRUE;
1631 case IDC_BUTTON_SIGN:
1632 if (calc.sci_in) {
1633 calc.esp = 0-calc.esp;
1634 build_operand(hWnd, IDC_STATIC);
1635 } else {
1636 if (calc.is_nan || calc.buffer[0] == TEXT('\0'))
1637 break;
1638
1639 if (calc.buffer[0] == TEXT('-')) {
1640 /* make the number positive */
1641 memmove(calc.buffer, calc.buffer+1, sizeof(calc.buffer)-1);
1642 if (calc.buffer != calc.ptr)
1643 calc.ptr--;
1644 } else {
1645 /* if first char is '0' and no dot, it isn't valid */
1646 if (calc.buffer[0] == TEXT('0') &&
1647 calc.buffer[1] != TEXT('.'))
1648 break;
1649 /* make the number negative */
1650 memmove(calc.buffer+1, calc.buffer, sizeof(calc.buffer)-1);
1651 calc.buffer[0] = TEXT('-');
1652 if (calc.buffer != calc.ptr)
1653 calc.ptr++;
1654 }
1655 /* If the input buffer is empty, then
1656 we change also the sign of calc.code
1657 because it could be the result of a
1658 previous calculation. */
1659 if (calc.buffer == calc.ptr)
1660 rpn_sign(&calc.code);
1661 update_lcd_display(hWnd);
1662 }
1663 return TRUE;
1664 case IDC_BUTTON_RIGHTPAR:
1665 case IDC_BUTTON_LEFTPAR:
1666 case IDC_BUTTON_CANC:
1667 case IDC_BUTTON_MP:
1668 case IDC_BUTTON_DAT:
1669 case IDC_BUTTON_FE:
1670 case IDC_BUTTON_DMS:
1671 case IDC_BUTTON_SQRT:
1672 case IDC_BUTTON_S:
1673 case IDC_BUTTON_SUM:
1674 case IDC_BUTTON_AVE:
1675 case IDC_BUTTON_NF:
1676 case IDC_BUTTON_LN:
1677 case IDC_BUTTON_LOG:
1678 case IDC_BUTTON_Xe2:
1679 case IDC_BUTTON_Xe3:
1680 case IDC_BUTTON_PI:
1681 case IDC_BUTTON_NOT:
1682 case IDC_BUTTON_RX:
1683 case IDC_BUTTON_INT:
1684 case IDC_BUTTON_SIN:
1685 case IDC_BUTTON_COS:
1686 case IDC_BUTTON_TAN:
1687 case IDC_BUTTON_MS:
1688 for (x=0; x<SIZEOF(function_table); x++) {
1689 if (LOWORD(wp) == function_table[x].idc) {
1690 rpn_callback1 cb = NULL;
1691
1692 /* test if NaN state is important or not */
1693 if (calc.is_nan && function_table[x].check_nan) break;
1694 /* otherwise, it's cleared */
1695 calc.is_nan = FALSE;
1696
1697 switch (get_modifiers(hWnd) & function_table[x].range) {
1698 case 0:
1699 cb = function_table[x].direct;
1700 break;
1701 case MODIFIER_INV:
1702 cb = function_table[x].inverse;
1703 break;
1704 case MODIFIER_HYP:
1705 cb = function_table[x].hyperb;
1706 break;
1707 case MODIFIER_INV|MODIFIER_HYP:
1708 cb = function_table[x].inv_hyp;
1709 break;
1710 }
1711 if (cb != NULL) {
1712 convert_text2number(&calc.code);
1713 cb(&calc.code);
1714 display_rpn_result(hWnd, &calc.code);
1715 // if (!(function_table[x].range & NO_CHAIN))
1716 // exec_infix2postfix(&calc.code, RPN_OPERATOR_NONE);
1717 if (function_table[x].range & MODIFIER_INV)
1718 SendDlgItemMessage(hWnd, IDC_CHECK_INV, BM_SETCHECK, 0, 0);
1719 if (function_table[x].range & MODIFIER_HYP)
1720 SendDlgItemMessage(hWnd, IDC_CHECK_HYP, BM_SETCHECK, 0, 0);
1721 }
1722 }
1723 }
1724 return TRUE;
1725 case IDC_BUTTON_STA:
1726 if (IsWindow(calc.hStatWnd))
1727 break;
1728 calc.hStatWnd = CreateDialog(calc.hInstance,
1729 MAKEINTRESOURCE(IDD_DIALOG_STAT), hWnd, DlgStatProc);
1730 if (calc.hStatWnd != NULL) {
1731 enable_allowed_controls(hWnd, calc.base);
1732 SendMessage(calc.hStatWnd, WM_SETFOCUS, 0, 0);
1733 }
1734 return TRUE;
1735 }
1736 break;
1737 case WM_CLOSE_STATS:
1738 enable_allowed_controls(hWnd, calc.base);
1739 return TRUE;
1740 case WM_LOAD_STAT:
1741 if (upload_stat_number((int)LOWORD(wp)) != NULL)
1742 display_rpn_result(hWnd, &calc.code);
1743 return TRUE;
1744 case WM_START_CONV:
1745 x = LOWORD(lp);
1746 calc.Convert[x].data = ReadConversion(calc.Convert[x].data);
1747 if (calc.Convert[x].data != NULL) {
1748 calc.Convert[x].ptr = calc.Convert[x].data;
1749 PostMessage(hWnd, HIWORD(lp), 0, 0);
1750 }
1751 return TRUE;
1752 case WM_HANDLE_FROM:
1753 if (calc.is_nan)
1754 break;
1755 if (handle_sequence_input(hWnd, &calc.Convert[0]) == NULL)
1756 PostMessage(hWnd, WM_START_CONV, 0,
1757 MAKELPARAM(0x0001, WM_HANDLE_TO));
1758 return TRUE;
1759 case WM_HANDLE_TO:
1760 if (!calc.is_nan)
1761 handle_sequence_input(hWnd, &calc.Convert[1]);
1762 return TRUE;
1763 case WM_CLOSE:
1764 calc.action = IDC_STATIC;
1765 DestroyWindow(hWnd);
1766 return TRUE;
1767
1768 case WM_DESTROY:
1769 /* Get (x,y) position of the calculator */
1770 GetWindowRect(hWnd, &rc);
1771 calc.x_coord = rc.left;
1772 calc.y_coord = rc.top;
1773 #ifdef USE_KEYBOARD_HOOK
1774 UnhookWindowsHookEx(calc.hKeyboardHook);
1775 #endif
1776 PostQuitMessage(0);
1777 return TRUE;
1778 case WM_CONTEXTMENU:
1779 if ((HWND)wp != hWnd)
1780 handle_context_menu(hWnd, wp, lp);
1781 return TRUE;
1782 case WM_ENTERMENULOOP:
1783 calc.is_menu_on = TRUE;
1784 /* Check if a valid format is available in the clipboard */
1785 EnableMenuItem(GetSubMenu(GetMenu(hWnd), 0),
1786 IDM_EDIT_PASTE,
1787 MF_BYCOMMAND|
1788 (IsClipboardFormatAvailable(CF_TEXT) ?
1789 MF_ENABLED : MF_GRAYED));
1790 break;
1791 case WM_EXITMENULOOP:
1792 calc.is_menu_on = FALSE;
1793 break;
1794 }
1795 return FALSE;
1796 }
1797
1798 #if defined(__GNUC__) && !defined(__REACTOS__)
1799 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
1800 #else
1801 int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nShowCmd)
1802 #endif
1803 {
1804 MSG msg;
1805 DWORD dwLayout;
1806
1807 InitCommonControls();
1808
1809 calc.hInstance = hInstance;
1810
1811 calc.x_coord = -1;
1812 calc.y_coord = -1;
1813
1814 load_config();
1815 start_rpn_engine();
1816
1817 HtmlHelp_Start(hInstance);
1818
1819 do {
1820 /* ignore hwnd: dialogs are already visible! */
1821 if (calc.layout == CALC_LAYOUT_SCIENTIFIC)
1822 dwLayout = IDD_DIALOG_SCIENTIFIC;
1823 else if (calc.layout == CALC_LAYOUT_CONVERSION)
1824 dwLayout = IDD_DIALOG_CONVERSION;
1825 else
1826 dwLayout = IDD_DIALOG_STANDARD;
1827
1828 /* This call will always fail if UNICODE for Win9x */
1829 if (NULL == CreateDialog(hInstance, MAKEINTRESOURCE(dwLayout), NULL, DlgMainProc))
1830 break;
1831
1832 while (GetMessage(&msg, NULL, 0, 0)) {
1833 #ifndef USE_KEYBOARD_HOOK
1834 if ((msg.message == WM_KEYUP ||
1835 msg.message == WM_KEYDOWN) &&
1836 !calc.is_menu_on)
1837 process_vk_key(msg.wParam, msg.lParam);
1838 #endif
1839 TranslateMessage(&msg);
1840 DispatchMessage(&msg);
1841 }
1842 } while (calc.action != IDC_STATIC);
1843
1844 stop_rpn_engine();
1845
1846 HtmlHelp_Stop();
1847
1848 return 0;
1849 }