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