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