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