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