Leaked source code of windows server 2003
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.

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