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