Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

558 lines
14 KiB

  1. /****************************Module*Header***********************************\
  2. * Module Name: SCIDISP.C
  3. *
  4. * Module Descripton:
  5. *
  6. * Warnings:
  7. *
  8. * Created:
  9. *
  10. * Author:
  11. \****************************************************************************/
  12. #include "scicalc.h"
  13. #include "unifunc.h"
  14. #include "input.h"
  15. #include <tchar.h>
  16. #include <stdlib.h>
  17. extern HNUMOBJ ghnoNum;
  18. extern eNUMOBJ_FMT nFE;
  19. extern TCHAR szDec[5];
  20. extern TCHAR gszSep[5];
  21. extern UINT gnDecGrouping;
  22. extern LPTSTR gpszNum;
  23. extern BOOL gbRecord;
  24. extern BOOL gbUseSep;
  25. extern CALCINPUTOBJ gcio;
  26. /****************************************************************************\
  27. * void DisplayNum(void)
  28. *
  29. * Convert ghnoNum to a string in the current radix.
  30. *
  31. * Updates the following globals:
  32. * ghnoNum, gpszNum
  33. \****************************************************************************/
  34. //
  35. // State of calc last time DisplayNum was called
  36. //
  37. typedef struct {
  38. HNUMOBJ hnoNum;
  39. LONG nPrecision;
  40. LONG nRadix;
  41. INT nFE;
  42. INT nCalc;
  43. INT nHexMode;
  44. BOOL fIntMath;
  45. BOOL bRecord;
  46. BOOL bUseSep;
  47. } LASTDISP;
  48. LASTDISP gldPrevious = { NULL, -1, -1, -1, -1, -1, FALSE, FALSE, FALSE };
  49. #define InvalidLastDisp( pglp ) ((pglp)->hnoNum == NULL )
  50. void GroupDigits(TCHAR sep,
  51. UINT nGrouping,
  52. BOOL bIsNumNegative,
  53. PTSTR szDisplay,
  54. PTSTR szSepDisplay);
  55. void DisplayNum(void)
  56. {
  57. SetWaitCursor( TRUE );
  58. //
  59. // Only change the display if
  60. // we are in record mode -OR-
  61. // this is the first time DisplayNum has been called, -OR-
  62. // something important has changed since the last time DisplayNum was
  63. // called.
  64. //
  65. if ( gbRecord || InvalidLastDisp( &gldPrevious ) ||
  66. !NumObjIsEq( gldPrevious.hnoNum, ghnoNum ) ||
  67. gldPrevious.nPrecision != nPrecision ||
  68. gldPrevious.nRadix != nRadix ||
  69. gldPrevious.nFE != (int)nFE ||
  70. gldPrevious.nCalc != nCalc ||
  71. gldPrevious.bUseSep != gbUseSep ||
  72. gldPrevious.nHexMode != nHexMode ||
  73. gldPrevious.fIntMath != F_INTMATH() ||
  74. gldPrevious.bRecord != gbRecord )
  75. {
  76. // Assign is an expensive operation, only do when really needed
  77. if ( ghnoNum )
  78. NumObjAssign( &gldPrevious.hnoNum, ghnoNum );
  79. gldPrevious.nPrecision = nPrecision;
  80. gldPrevious.nRadix = nRadix;
  81. gldPrevious.nFE = (int)nFE;
  82. gldPrevious.nCalc = nCalc;
  83. gldPrevious.nHexMode = nHexMode;
  84. gldPrevious.fIntMath = F_INTMATH();
  85. gldPrevious.bRecord = gbRecord;
  86. gldPrevious.bUseSep = gbUseSep;
  87. if (gbRecord)
  88. {
  89. // Display the string and return.
  90. CIO_vConvertToString(&gpszNum, &gcio, nRadix);
  91. }
  92. else if (!F_INTMATH())
  93. {
  94. // Decimal conversion
  95. NumObjGetSzValue( &gpszNum, ghnoNum, nRadix, nFE );
  96. }
  97. else
  98. {
  99. // Non-decimal conversion
  100. int i;
  101. // Truncate to an integer. Do not round here.
  102. intrat( &ghnoNum );
  103. // Check the range.
  104. if ( NumObjIsLess( ghnoNum, HNO_ZERO ) )
  105. {
  106. // if negative make positive by doing a twos complement
  107. NumObjNegate( &ghnoNum );
  108. subrat( &ghnoNum, HNO_ONE );
  109. NumObjNot( &ghnoNum );
  110. }
  111. andrat( &ghnoNum, g_ahnoChopNumbers[nHexMode] );
  112. NumObjGetSzValue( &gpszNum, ghnoNum, nRadix, FMT_FLOAT );
  113. // Clobber trailing decimal point
  114. i = lstrlen( gpszNum ) - 1;
  115. if ( i >= 0 && gpszNum[i] == szDec[0] )
  116. gpszNum[i] = TEXT('\0');
  117. }
  118. // Display the string and return.
  119. if (!gbUseSep)
  120. {
  121. TCHAR szTrailSpace[256];
  122. lstrcpyn(szTrailSpace,gpszNum, 254);
  123. lstrcat(szTrailSpace,TEXT(" "));
  124. SetDisplayText(g_hwndDlg, szTrailSpace);
  125. }
  126. else
  127. {
  128. TCHAR szSepNum[256];
  129. switch(nRadix)
  130. {
  131. case 10:
  132. GroupDigits(gszSep[0],
  133. gnDecGrouping,
  134. (TEXT('-') == *gpszNum),
  135. gpszNum,
  136. szSepNum);
  137. break;
  138. case 8:
  139. GroupDigits(TEXT(' '), 0x03, FALSE, gpszNum, szSepNum);
  140. break;
  141. case 2:
  142. case 16:
  143. GroupDigits(TEXT(' '), 0x04, FALSE, gpszNum, szSepNum);
  144. break;
  145. default:
  146. lstrcpy(szSepNum,gpszNum);
  147. }
  148. lstrcat(szSepNum,TEXT(" "));
  149. SetDisplayText(g_hwndDlg, szSepNum);
  150. }
  151. }
  152. SetWaitCursor( FALSE );
  153. return;
  154. }
  155. /****************************************************************************\
  156. *
  157. * WatchDogThread
  158. *
  159. * Thread to look out for functions that take too long. If it finds one, it
  160. * prompts the user if he wants to abort the function, and asks RATPAK to
  161. * abort if he does.
  162. *
  163. * History
  164. * 26-Nov-1996 JonPa Wrote it.
  165. *
  166. \****************************************************************************/
  167. BOOL gfExiting = FALSE;
  168. HANDLE ghCalcStart = NULL;
  169. HANDLE ghCalcDone = NULL;
  170. HANDLE ghDogThread = NULL;
  171. INT_PTR TimeOutMessageBox( void );
  172. DWORD WINAPI WatchDogThread( LPVOID pvParam ) {
  173. DWORD cmsWait;
  174. INT_PTR iRet;
  175. while( !gfExiting ) {
  176. WaitForSingleObject( ghCalcStart, INFINITE );
  177. if (gfExiting)
  178. break;
  179. cmsWait = CMS_CALC_TIMEOUT;
  180. while( WaitForSingleObject( ghCalcDone, cmsWait ) == WAIT_TIMEOUT ) {
  181. // Put up the msg box
  182. MessageBeep( MB_ICONEXCLAMATION );
  183. iRet = TimeOutMessageBox();
  184. // if user wants to cancel, then stop
  185. if (gfExiting || iRet == IDYES || iRet == IDCANCEL) {
  186. NumObjAbortOperation(TRUE);
  187. break;
  188. } else {
  189. cmsWait *= 2;
  190. if (cmsWait > CMS_MAX_TIMEOUT) {
  191. cmsWait = CMS_MAX_TIMEOUT;
  192. }
  193. }
  194. }
  195. }
  196. return 42;
  197. }
  198. /****************************************************************************\
  199. *
  200. * TimeCalc
  201. *
  202. * Function to keep track of how long Calc is taking to do a calculation.
  203. * If calc takes too long (about 10 sec's), then a popup is put up asking the
  204. * user if he wants to abort the operation.
  205. *
  206. * Usage:
  207. * TimeCalc( TRUE );
  208. * do a lengthy operation
  209. * TimeCalc( FALSE );
  210. *
  211. * History
  212. * 26-Nov-1996 JonPa Wrote it.
  213. *
  214. \****************************************************************************/
  215. HWND ghwndTimeOutDlg = NULL;
  216. void TimeCalc( BOOL fStart ) {
  217. if (ghCalcStart == NULL) {
  218. ghCalcStart = CreateEvent( NULL, FALSE, FALSE, NULL );
  219. }
  220. if (ghCalcDone == NULL) {
  221. ghCalcDone = CreateEvent( NULL, TRUE, FALSE, NULL );
  222. }
  223. if (ghDogThread == NULL) {
  224. DWORD tid;
  225. ghDogThread = CreateThread( NULL, 0, WatchDogThread, NULL, 0, &tid );
  226. }
  227. if (fStart) {
  228. NumObjAbortOperation(FALSE);
  229. ResetEvent( ghCalcDone );
  230. SetEvent( ghCalcStart );
  231. } else {
  232. SetEvent( ghCalcDone );
  233. if( ghwndTimeOutDlg != NULL ) {
  234. SendMessage( ghwndTimeOutDlg, WM_COMMAND, IDRETRY, 0L );
  235. }
  236. if( NumObjWasAborted() ) {
  237. DisplayError(SCERR_ABORTED);
  238. }
  239. }
  240. }
  241. /****************************************************************************\
  242. *
  243. * KillTimeCalc
  244. *
  245. * Should be called only at the end of the program, just before exiting, to
  246. * kill the background timer thread and free its resources.
  247. *
  248. * History
  249. * 26-Nov-1996 JonPa Wrote it.
  250. *
  251. \****************************************************************************/
  252. void KillTimeCalc( void ) {
  253. gfExiting = TRUE;
  254. SetEvent( ghCalcStart );
  255. SetEvent( ghCalcDone );
  256. WaitForSingleObject( ghDogThread, CMS_MAX_TIMEOUT );
  257. CloseHandle( ghCalcStart );
  258. CloseHandle( ghCalcDone );
  259. CloseHandle( ghDogThread );
  260. }
  261. /****************************************************************************\
  262. *
  263. * TimeOutMessageBox
  264. *
  265. * Puts up a dialog that looks like a message box. If the operation returns
  266. * before the user has responded to the dialog, the dialog gets taken away.
  267. *
  268. * History
  269. * 04-Dec-1996 JonPa Wrote it.
  270. *
  271. \****************************************************************************/
  272. INT_PTR
  273. CALLBACK TimeOutDlgProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
  274. {
  275. RECT rc;
  276. int y;
  277. switch( uMsg ) {
  278. case WM_INITDIALOG:
  279. ghwndTimeOutDlg = hwndDlg;
  280. //
  281. // Move ourselves to be over the main calc window
  282. //
  283. // Find the display window so we don't cover it up.
  284. GetWindowRect(GetDlgItem(g_hwndDlg, IDC_DISPLAY), &rc );
  285. y = rc.bottom;
  286. // Get the main calc window pos
  287. GetWindowRect( g_hwndDlg, &rc );
  288. SetWindowPos( hwndDlg, 0, rc.left + 15, y + 40, 0, 0,
  289. SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE );
  290. break;
  291. case WM_COMMAND:
  292. EndDialog( hwndDlg, LOWORD(wParam) );
  293. break;
  294. default:
  295. return FALSE;
  296. }
  297. return TRUE;
  298. }
  299. INT_PTR TimeOutMessageBox( void ) {
  300. return (int)DialogBox( hInst, MAKEINTRESOURCE(IDD_TIMEOUT), NULL, TimeOutDlgProc );
  301. }
  302. /****************************************************************************\
  303. *
  304. * DigitGroupingStringToGroupingNum
  305. *
  306. * Description:
  307. * This will take the digit grouping string found in the regional applet and
  308. * represent this string as a hex value. The grouping numbers are represented
  309. * as 4 bit numbers logically shifted and or'd to together so:
  310. *
  311. * Grouping_string GroupingNum
  312. * 0;0 0x000 - no grouping
  313. * 3;0 0x003 - group every 3 digits
  314. * 3;2;0 0x023 - group 1st 3 and then every 2 digits
  315. * 4;0 0x004 - group every 4 digits
  316. * 5;3;2;0 0x235 - group 5, then 3, then every 2
  317. *
  318. * Returns: the grouping number
  319. *
  320. * History
  321. * 10-Sept-1999 KPeery - Wrote it to fix grouping on Hindi
  322. *
  323. \****************************************************************************/
  324. UINT
  325. DigitGroupingStringToGroupingNum(PTSTR szGrouping)
  326. {
  327. PTSTR p,q;
  328. UINT n, nGrouping, shift;
  329. if (NULL == szGrouping)
  330. return 0;
  331. nGrouping=0;
  332. shift=0;
  333. for(p=szGrouping; *p != TEXT('\0'); /* nothing */)
  334. {
  335. n=_tcstoul(p,&q,10);
  336. if ((n > 0) && (n < 16))
  337. {
  338. n<<=shift;
  339. shift+=4;
  340. nGrouping|=n;
  341. }
  342. if (q)
  343. p=q+1;
  344. else
  345. p++;
  346. }
  347. return nGrouping;
  348. }
  349. /****************************************************************************\
  350. *
  351. * GroupDigits
  352. *
  353. * Description:
  354. * This routine will take a grouping number and the display string and
  355. * add the separator according to the pattern indicated by the separator.
  356. *
  357. * GroupingNum
  358. * 0x000 - no grouping
  359. * 0x003 - group every 3 digits
  360. * 0x023 - group 1st 3 and then every 2 digits
  361. * 0x004 - group every 4 digits
  362. * 0x235 - group 5, then 3, then every 2
  363. *
  364. * History
  365. * 08-Sept-1998 KPeery - Wrote orignal add num separator routine
  366. * 10-Sept-1999 KPeery - Re-wrote it to do digit grouping in general
  367. *
  368. \***************************************************************************/
  369. void
  370. GroupDigits(TCHAR sep,
  371. UINT nGrouping,
  372. BOOL bIsNumNegative,
  373. PTSTR szDisplay,
  374. PTSTR szSepDisplay)
  375. {
  376. PTSTR src,dest, dec;
  377. size_t len;
  378. int nDigits, nOrgDigits, count;
  379. UINT nOrgGrouping, nCurrGrouping;
  380. if ((sep == TEXT('\0')) || (nGrouping == 0))
  381. {
  382. lstrcpy(szSepDisplay,szDisplay);
  383. return;
  384. }
  385. // find decimal point
  386. for(dec=szDisplay; (*dec != szDec[0]) && (*dec != TEXT('\0')); dec++)
  387. ; // do nothing
  388. // at this point dec should point to '\0' or '.' we will add the left
  389. // side of the number to the final string
  390. // length of left half of number
  391. len=(dec-szDisplay);
  392. // num of digits
  393. nDigits=len-(bIsNumNegative ? 1 : 0);
  394. nOrgDigits=nDigits;
  395. nOrgGrouping=nGrouping;
  396. //
  397. // ok, we must now find the adjusted len, to do that we loop
  398. // through the grouping while keeping track of where we are in the
  399. // number. When the nGrouping reaches 0 then we simply repeat the
  400. // last grouping for the rest of the digits.
  401. //
  402. nCurrGrouping=nGrouping % 0x10;
  403. for ( ; nDigits > 0; )
  404. {
  405. if ((UINT)nDigits > nCurrGrouping)
  406. {
  407. nDigits-=nCurrGrouping;
  408. len++; // add one for comma
  409. nGrouping>>=4;
  410. if (nGrouping > 0)
  411. nCurrGrouping=nGrouping % 0x10;
  412. }
  413. else
  414. nDigits-=nCurrGrouping;
  415. }
  416. //
  417. // restore the saved nDigits and grouping pattern
  418. //
  419. nDigits=nOrgDigits;
  420. nGrouping=nOrgGrouping;
  421. nCurrGrouping=nGrouping % 0x10;
  422. //
  423. // ok, now we know the length copy the digits, at the same time
  424. // repeat the grouping pattern and place the seperator appropiatly,
  425. // repeating the last grouping until we are done
  426. //
  427. src=dec-1;
  428. dest=szSepDisplay+len-1;
  429. count=0;
  430. for( ; nDigits > 0; )
  431. {
  432. *dest=*src;
  433. nDigits--;
  434. count++;
  435. if (((count % nCurrGrouping) == 0) && (nDigits > 0))
  436. {
  437. dest--;
  438. *dest=sep;
  439. count=0; // account for comma
  440. nGrouping>>=4;
  441. if (nGrouping > 0)
  442. nCurrGrouping=nGrouping % 0x10;
  443. }
  444. dest--;
  445. src--;
  446. }
  447. // now copy the minus sign if it is there
  448. if (bIsNumNegative)
  449. *szSepDisplay=*szDisplay;
  450. //
  451. // ok, now add the right (fractional) part of the number to the final
  452. // string.
  453. //
  454. dest=szSepDisplay+len;
  455. lstrcpy(dest,dec);
  456. }