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.

1306 lines
40 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. /* File: xbytes.cpp
  3. Description: This module implements a class that coordinates the operation
  4. between the edit control and combo box used for entering byte values.
  5. The name "XBytes" is used because the control can represent
  6. KBytes, MBytes, GBytes etc.
  7. The cooperation between edit control and combo control is required
  8. so that the user can enter a byte value in the edit control then
  9. indicate it's order (KB, MB, GB...) using a selection from the combo box.
  10. A simple external interface is provided to initially set the
  11. object's byte value then retrieve the byte value when needed. The
  12. object's client is also required to call two member functions when
  13. the parent dialog receives an EN_UPDATE notification and a CBN_SELCHANGE
  14. message. The XBytes object handles all of the value scaling
  15. internally.
  16. NOTE: I experimented with adding a spin control to the edit control.
  17. I found that without some fancy intervention, the spin control
  18. didn't support fractional values (i.e. 2.5MB). Decided to keep
  19. fractional values and ditch the spinner. I think fractional
  20. values will be more useful to disk admins.
  21. Revision History:
  22. Date Description Programmer
  23. -------- --------------------------------------------------- ----------
  24. 08/30/96 Initial creation. BrianAu
  25. 07/23/97 Added default ctor and CommonInit() function. BrianAu
  26. Also added g_ForLoadingStaticStrings instance.
  27. 11/01/01 Copied from BrianAu for use in CYS JeffJon
  28. */
  29. ///////////////////////////////////////////////////////////////////////////////
  30. #include "pch.h" // PCH
  31. #pragma hdrstop
  32. #include "resource.h"
  33. #include "xbytes.h"
  34. const TCHAR CH_NUL = TEXT('\0');
  35. const TCHAR CH_ZERO = TEXT('0');
  36. const INT MAX_EDIT_TEXT = 16; // Max chars in edit text.
  37. const INT MAX_CMB_TEXT = 10; // For "KB", "MB", "GB" etc.
  38. const INT64 MAX_VALUE = ((1i64 << 60) * 6i64); // Max is 6EB.
  39. const INT64 MIN_VALUE = 1024i64; // Min value is 1KB.
  40. const INT64 DEFAULT_VALUE = ((1i64 << 20) * 5i64); // Default value is 5MB.
  41. const LONGLONG NOLIMIT = (LONGLONG)-1;
  42. TCHAR XBytes::m_szNoLimit[]; // "No Limit" edit control text.
  43. #define ARRAYSIZE(array) sizeof(array)/sizeof(array[0])
  44. ///////////////////////////////////////////////////////////////////////////////
  45. /* Function: XBytes::XBytes
  46. Description: Constructor
  47. Arguments:
  48. hDlg - Handle to parent dialog.
  49. idCtlEdit - Control ID for edit control.
  50. idCtlCombo - Control ID for combo box control.
  51. CurrentBytes - Initial byte value.
  52. Returns: Nothing.
  53. Revision History:
  54. Date Description Programmer
  55. -------- --------------------------------------------------- ----------
  56. 08/30/96 Initial creation. BrianAu
  57. 10/15/96 Added m_MaxBytes member. BrianAu
  58. 05/29/98 Removed m_MaxBytes member. Don't want to limit BrianAu
  59. user's ability to enter a value larger than
  60. max disk space.
  61. */
  62. ///////////////////////////////////////////////////////////////////////////////
  63. XBytes::XBytes(
  64. HWND hDlg,
  65. DWORD idCtlEdit,
  66. DWORD idCtlCombo,
  67. INT64 CurrentBytes
  68. ) : m_hDlg(hDlg),
  69. m_idCtlEdit(idCtlEdit),
  70. m_idCtlCombo(idCtlCombo),
  71. m_ValueBytes(0)
  72. {
  73. CommonInit();
  74. LoadComboItems(MAXLONGLONG); // Load options into combo.
  75. CurrentBytes = min(CurrentBytes, MAX_VALUE);
  76. if (NOLIMIT != CurrentBytes)
  77. CurrentBytes = max(CurrentBytes, DEFAULT_VALUE);
  78. SetBytes(CurrentBytes); // Set "current bytes".
  79. //
  80. // Note: SetBytes() calls SetBestDisplay().
  81. //
  82. }
  83. //
  84. // This constructor is sort of a hack. Since the m_szNoLimit string
  85. // is static, and since it is initialized in
  86. // the constructor, at least one instance of XBytes must be created.
  87. // There are cases where the static function FormatByteCountForDisplay
  88. // may be useful when there is no need for an XBytes object. The
  89. // DiskQuota watchdog is just such an example. If an XBytes object
  90. // is not created, the two strings are not created and the function
  91. // doesn't work correctly. To fix this, I've defined a single global
  92. // XBytes object constructed using this default constructor. It's sole
  93. // purpose is to load these static strings. [7/23/97 - brianau]
  94. //
  95. XBytes::XBytes(
  96. VOID
  97. ) : m_hDlg(NULL),
  98. m_idCtlEdit((DWORD)-1),
  99. m_idCtlCombo((DWORD)-1),
  100. m_ValueBytes(0)
  101. {
  102. CommonInit();
  103. }
  104. ///////////////////////////////////////////////////////////////////////////////
  105. /* Function: XBytes::Initialize
  106. Description: Initializes the XBytes class object so that the default
  107. constructor can be used. This is advantageous when using
  108. the XBytes class as an embedded member of another window
  109. class.
  110. Arguments:
  111. hDlg - Handle to parent dialog.
  112. idCtlEdit - Control ID for edit control.
  113. idCtlCombo - Control ID for combo box control.
  114. CurrentBytes - Initial byte value.
  115. Returns: Nothing.
  116. Revision History:
  117. Date Description Programmer
  118. -------- --------------------------------------------------- ----------
  119. 11/01/01 Initial creation. JeffJon
  120. */
  121. ///////////////////////////////////////////////////////////////////////////////
  122. void
  123. XBytes::Initialize(
  124. HWND hDlg,
  125. DWORD idCtlEdit,
  126. DWORD idCtlCombo,
  127. INT64 CurrentBytes)
  128. {
  129. m_hDlg = hDlg;
  130. m_idCtlEdit = idCtlEdit;
  131. m_idCtlCombo = idCtlCombo;
  132. m_ValueBytes = 0;
  133. CommonInit();
  134. LoadComboItems(MAXLONGLONG); // Load options into combo.
  135. CurrentBytes = min(CurrentBytes, MAX_VALUE);
  136. if (NOLIMIT != CurrentBytes)
  137. {
  138. CurrentBytes = max(CurrentBytes, DEFAULT_VALUE);
  139. }
  140. SetBytes(CurrentBytes); // Set "current bytes".
  141. //
  142. // Note: SetBytes() calls SetBestDisplay().
  143. //
  144. }
  145. //
  146. // Initialization common to both constructors.
  147. //
  148. VOID
  149. XBytes::CommonInit(
  150. VOID
  151. )
  152. {
  153. if (NULL != m_hDlg)
  154. SendMessage(m_hDlg, m_idCtlEdit, EM_LIMITTEXT, MAX_EDIT_TEXT);
  155. LoadStaticStrings();
  156. }
  157. ///////////////////////////////////////////////////////////////////////////////
  158. /* Function: XBytes::SetBytes
  159. Description: Stores a new byte value and updates the display to the
  160. proper units (order).
  161. Arguments:
  162. ValueBytes - Value in bytes.
  163. If the value is NOLIMIT, the controls are disabled.
  164. Otherwise the controls are enabled.
  165. Returns: Nothing.
  166. Revision History:
  167. Date Description Programmer
  168. -------- --------------------------------------------------- ----------
  169. 08/30/96 Initial creation. BrianAu
  170. */
  171. ///////////////////////////////////////////////////////////////////////////////
  172. VOID
  173. XBytes::SetBytes(INT64 ValueBytes)
  174. {
  175. if (NOLIMIT != ValueBytes)
  176. ValueBytes = max(MIN_VALUE, ValueBytes);
  177. ValueBytes = min(MAX_VALUE, ValueBytes);
  178. Store(ValueBytes, e_Byte);
  179. SetBestDisplay();
  180. }
  181. ///////////////////////////////////////////////////////////////////////////////
  182. /* Function: XBytes::Enable
  183. Description: Enables/Disables the edit and combo controls.
  184. Arguments:
  185. bEnable - TRUE = Enable, FALSE = Disable.
  186. Returns: Nothing.
  187. Revision History:
  188. Date Description Programmer
  189. -------- --------------------------------------------------- ----------
  190. 10/28/96 Initial creation. BrianAu
  191. */
  192. ///////////////////////////////////////////////////////////////////////////////
  193. VOID
  194. XBytes::Enable(
  195. BOOL bEnable
  196. )
  197. {
  198. EnableWindow(GetDlgItem(m_hDlg, m_idCtlCombo), bEnable);
  199. EnableWindow(GetDlgItem(m_hDlg, m_idCtlEdit), bEnable);
  200. }
  201. ///////////////////////////////////////////////////////////////////////////////
  202. /* Function: XBytes::IsEnabled
  203. Description: Returns the "enabled" state of the edit control. As long
  204. as the client doesn't enable/disable the edit/combo controls
  205. individually, this represents the state of the control pair.
  206. By using only the SetBytes() method to control enabling/disabling,
  207. this is ensured.
  208. Arguments:
  209. bEnable - TRUE = Enable, FALSE = Disable.
  210. Returns: Nothing.
  211. Revision History:
  212. Date Description Programmer
  213. -------- --------------------------------------------------- ----------
  214. 10/28/96 Initial creation. BrianAu
  215. */
  216. ///////////////////////////////////////////////////////////////////////////////
  217. BOOL
  218. XBytes::IsEnabled(
  219. VOID
  220. )
  221. {
  222. return IsWindowEnabled(GetDlgItem(m_hDlg, m_idCtlEdit));
  223. }
  224. bool
  225. XBytes::UndoLastEdit(
  226. void
  227. )
  228. {
  229. if (SendToEditCtl(EM_CANUNDO, 0, 0))
  230. {
  231. SendToEditCtl(EM_UNDO, 0, 0);
  232. SendToEditCtl(EM_EMPTYUNDOBUFFER, 0, 0);
  233. SendToEditCtl(EM_SETSEL, SendToEditCtl(EM_LINELENGTH, 0, 0), -1);
  234. return true;
  235. }
  236. return false;
  237. }
  238. ///////////////////////////////////////////////////////////////////////////////
  239. /* Function: XBytes::OnEditNotifyUpdate
  240. Description: Must be called whenever the parent window receives a
  241. EN_UPDATE notification for the edit control. The function
  242. reads the current string in the edit control and tries to store it
  243. as a byte value. If the store operation fails, the number is invalid
  244. and an alarm is sounded.
  245. Returns:
  246. Always returns FALSE.
  247. Revision History:
  248. Date Description Programmer
  249. -------- --------------------------------------------------- ----------
  250. 08/30/96 Initial creation. BrianAu
  251. 10/15/96 Added check for too-large input. BrianAu
  252. 10/22/96 Re-organized and added ValueInRange() function. BrianAu
  253. This was to support value check/adjustment when
  254. user changes the combo-box setting (bug).
  255. 02/26/97 Added EM_CANUNDO and EM_EMPTYUNDOBUFFER. BrianAu
  256. 05/29/98 Removed ValueInRange() function and replaced with BrianAu
  257. check for negative number.
  258. */
  259. ///////////////////////////////////////////////////////////////////////////////
  260. BOOL
  261. XBytes::OnEditNotifyUpdate(
  262. )
  263. {
  264. TCHAR szEditText[MAX_PATH];
  265. bool bBeep = false;
  266. ASSERT((MAX_EDIT_TEXT < MAX_PATH));
  267. GetDlgItemText(m_hDlg, m_idCtlEdit, szEditText, ARRAYSIZE(szEditText));
  268. if (szEditText[0] != L'\0')
  269. {
  270. if (lstrlen(szEditText) > MAX_EDIT_TEXT)
  271. {
  272. szEditText[MAX_EDIT_TEXT] = TEXT('\0');
  273. SetDlgItemText(m_hDlg, m_idCtlEdit, szEditText);
  274. }
  275. if (0 != lstrcmpi(XBytes::m_szNoLimit, szEditText))
  276. {
  277. //
  278. // If text in edit control is not "No Limit", convert the text to
  279. // a number, verify that it is in range and store it.
  280. //
  281. if (Store(szEditText, (INT)GetOrderFromCombo()))
  282. {
  283. //
  284. // If number is negative, force it to the minimum.
  285. //
  286. if (0 > Fetch(NULL, e_Byte))
  287. {
  288. SetBytes(MIN_VALUE);
  289. bBeep = true;
  290. }
  291. SendToEditCtl(EM_EMPTYUNDOBUFFER, 0, 0);
  292. }
  293. else
  294. {
  295. bBeep = true;
  296. if (!UndoLastEdit())
  297. {
  298. //
  299. // Number must be too large for the selected order.
  300. // Found that this can happen when first opening the disk quota UI
  301. // after someone's set the value out of the range acceptable by
  302. // the UI. Remember, because we allow decimal values in the UI,
  303. // the UI cannot accept values quite as large as the dskquota APIs.
  304. // Beep the user and force the value to the largest acceptable
  305. // value.
  306. //
  307. SetBytes(MAX_VALUE);
  308. }
  309. }
  310. if (bBeep)
  311. {
  312. //
  313. // Sound beep for either an invalid value or an out-of-range value.
  314. //
  315. MessageBeep(MB_OK);
  316. }
  317. }
  318. }
  319. return FALSE;
  320. }
  321. BOOL
  322. XBytes::OnEditKillFocus(
  323. )
  324. {
  325. TCHAR szEditText[MAX_EDIT_TEXT];
  326. bool bBeep = false;
  327. GetDlgItemText(m_hDlg, m_idCtlEdit, szEditText, ARRAYSIZE(szEditText));
  328. if (szEditText[0] != L'\0')
  329. {
  330. if (0 != lstrcmpi(XBytes::m_szNoLimit, szEditText))
  331. {
  332. INT64 value = Fetch(NULL, e_Byte);
  333. if (MIN_VALUE > value)
  334. {
  335. SetBytes(MIN_VALUE);
  336. bBeep = true;
  337. }
  338. else if (MAX_VALUE < value)
  339. {
  340. SetBytes(MAX_VALUE);
  341. bBeep = true;
  342. }
  343. }
  344. }
  345. else
  346. {
  347. SetBytes(MIN_VALUE);
  348. bBeep = true;
  349. }
  350. if (bBeep)
  351. {
  352. MessageBeep(MB_OK);
  353. }
  354. return FALSE;
  355. }
  356. ///////////////////////////////////////////////////////////////////////////////
  357. /* Function: XBytes::OnComboNotifySelChange
  358. Description: Must be called whenever the parent window receives a
  359. CBM_SELCHANGE message for the combo box control. The function
  360. scales the stored byte value to the new units.
  361. Returns:
  362. Always returns FALSE.
  363. Revision History:
  364. Date Description Programmer
  365. -------- --------------------------------------------------- ----------
  366. 08/30/96 Initial creation. BrianAu
  367. 10/22/96 Modified to just call OnEditNotifyUpdate(). BrianAu
  368. Combo-box selection should have same value
  369. check/adjust behavior as edit control changes.
  370. */
  371. ///////////////////////////////////////////////////////////////////////////////
  372. BOOL
  373. XBytes::OnComboNotifySelChange()
  374. {
  375. TCHAR szEditText[MAX_EDIT_TEXT];
  376. bool bBeep = false;
  377. GetDlgItemText(m_hDlg, m_idCtlEdit, szEditText, ARRAYSIZE(szEditText));
  378. if (0 != lstrcmpi(XBytes::m_szNoLimit, szEditText))
  379. {
  380. //
  381. // If text in edit control is not "No Limit", convert the text to
  382. // a number, verify that it is in range and store it.
  383. //
  384. if (Store(szEditText, (INT)GetOrderFromCombo()))
  385. {
  386. //
  387. // If number is less than the minimum, force it to the minimum.
  388. //
  389. if (MIN_VALUE > Fetch(NULL, e_Byte))
  390. {
  391. SetBytes(MIN_VALUE);
  392. bBeep = true;
  393. }
  394. }
  395. else
  396. {
  397. //
  398. // Number must be too large for the selected order.
  399. // Beep the user and force the value to the largest acceptable
  400. // value.
  401. //
  402. SetBytes(MAX_VALUE);
  403. bBeep = true;
  404. }
  405. if (bBeep)
  406. {
  407. MessageBeep(MB_OK);
  408. }
  409. }
  410. return FALSE;
  411. }
  412. ///////////////////////////////////////////////////////////////////////////////
  413. /* Function: XBytes::LoadComboItems
  414. Description: Initializes the combo box with its selections.
  415. [ "KB", "MB", "GB"... "PB" ]. The function only adds options that are
  416. reasonable for the size of the drive. For example, if the drive
  417. is less than 1 GB in size, only KB and MB are displayed.
  418. Arguments:
  419. MaxBytes - Maximum bytes available on the drive (drive size).
  420. Returns: Nothing.
  421. Revision History:
  422. Date Description Programmer
  423. -------- --------------------------------------------------- ----------
  424. 08/30/96 Initial creation. BrianAu
  425. */
  426. ///////////////////////////////////////////////////////////////////////////////
  427. VOID
  428. XBytes::LoadComboItems(
  429. INT64 MaxBytes
  430. )
  431. {
  432. TCHAR szText[MAX_CMB_TEXT];
  433. INT idMsg = 0;
  434. INT order = e_Kilo;
  435. //
  436. // Find the string resource ID for the largest units possible.
  437. //
  438. // WARNING: This code assumes that the resource IDs for
  439. // IDS_ORDERKB through IDS_ORDEREB are consecutive
  440. // increasing integers. Hence the following assertions.
  441. //
  442. ASSERT((IDS_ORDERMB == IDS_ORDERKB + 1));
  443. ASSERT((IDS_ORDERGB == IDS_ORDERKB + 2));
  444. ASSERT((IDS_ORDERTB == IDS_ORDERKB + 3));
  445. ASSERT((IDS_ORDERPB == IDS_ORDERKB + 4));
  446. ASSERT((IDS_ORDEREB == IDS_ORDERKB + 5));
  447. for (idMsg = IDS_ORDERKB; idMsg < IDS_ORDEREB; idMsg++)
  448. {
  449. if ((INT64)(1i64 << (10 * order++)) > MaxBytes)
  450. {
  451. idMsg--;
  452. break;
  453. }
  454. }
  455. //
  456. // idMsg is at largest units string we'll use.
  457. // Add strings to combo box.
  458. //
  459. while(idMsg >= IDS_ORDERKB)
  460. {
  461. if (LoadString(Win::GetModuleHandle(), idMsg, szText, ARRAYSIZE(szText)))
  462. SendToCombo(CB_INSERTSTRING, 0, (LPARAM)szText);
  463. idMsg--;
  464. }
  465. }
  466. ///////////////////////////////////////////////////////////////////////////////
  467. /* Function: XBytes::SetBestDisplay
  468. Description: Displays the byte value in the highest order that will
  469. produce a whole part of 3 digits or less. That way you see
  470. "25.5" MB instead of "25500 KB".
  471. Arguments: None.
  472. Returns: Nothing.
  473. Revision History:
  474. Date Description Programmer
  475. -------- --------------------------------------------------- ----------
  476. 08/30/96 Initial creation. BrianAu
  477. */
  478. ///////////////////////////////////////////////////////////////////////////////
  479. VOID
  480. XBytes::SetBestDisplay(
  481. VOID
  482. )
  483. {
  484. INT iOrder = e_Byte;
  485. TCHAR szValue[MAX_EDIT_TEXT];
  486. //
  487. // Display NOLIMIT as 0. Edit and combo controls will be disabled
  488. // by property page code. NOLIMIT is (-1). Defined by NTFS.
  489. //
  490. if (NOLIMIT != m_ValueBytes)
  491. {
  492. //
  493. // Format the byte count for display. Leave off the KB, MB... extension.
  494. // That part will be displayed in the combo box.
  495. //
  496. FormatByteCountForDisplay(m_ValueBytes, szValue, ARRAYSIZE(szValue), &iOrder);
  497. //
  498. // If value is 0, display MB units. That's our default.
  499. //
  500. if (0 == m_ValueBytes)
  501. iOrder = e_Mega;
  502. //
  503. // Set the value string in the edit control and the order in the combo box.
  504. //
  505. SetOrderInCombo(iOrder);
  506. SetDlgItemText(m_hDlg,
  507. m_idCtlEdit,
  508. szValue);
  509. Enable(TRUE);
  510. }
  511. else
  512. {
  513. //
  514. // Set edit control to display "No Limit".
  515. //
  516. SetOrderInCombo(0); // This will cause the combo to display nothing.
  517. SetDlgItemText(m_hDlg,
  518. m_idCtlEdit,
  519. m_szNoLimit);
  520. Enable(FALSE);
  521. }
  522. }
  523. ///////////////////////////////////////////////////////////////////////////////
  524. /* Function: XBytes::Store
  525. Description: Store a value in a given order as a byte count.
  526. Arguments:
  527. Value - Byte value in order xbOrder.
  528. xbOrder - Order of number in Value.
  529. One of set { e_Byte, e_Kilo, e_Mega ... e_Exa }
  530. Returns:
  531. TRUE - Success. Always returns TRUE.
  532. Event though we're not returning anything useful, I want
  533. the return type for both Store() methods to be the same.
  534. Revision History:
  535. Date Description Programmer
  536. -------- --------------------------------------------------- ----------
  537. 08/30/96 Initial creation. BrianAu
  538. */
  539. ///////////////////////////////////////////////////////////////////////////////
  540. BOOL
  541. XBytes::Store(
  542. INT64 Value,
  543. INT xbOrder
  544. )
  545. {
  546. ASSERT((VALID_ORDER(xbOrder)));
  547. m_ValueBytes = INT64(Value) << (10 * (xbOrder - e_Byte));
  548. return TRUE;
  549. }
  550. ///////////////////////////////////////////////////////////////////////////////
  551. /* Function: XBytes::Store
  552. Description: Store a numeric string in a given order as a byte count.
  553. Arguments:
  554. pszSource - Numeric string.
  555. xbOrder - Order of number in pszSource.
  556. One of set { e_Byte, e_Kilo, e_Mega ... e_Exa }
  557. Returns:
  558. TRUE - Success.
  559. FALSE - Invalid number in string.
  560. Revision History:
  561. Date Description Programmer
  562. -------- --------------------------------------------------- ----------
  563. 08/30/96 Initial creation. BrianAu
  564. */
  565. ///////////////////////////////////////////////////////////////////////////////
  566. BOOL
  567. XBytes::Store(
  568. LPCTSTR pszSource,
  569. INT xbOrder
  570. )
  571. {
  572. TCHAR szValue[MAX_EDIT_TEXT]; // Temp buffer.
  573. TCHAR szDecimalSep[MAX_DECIMAL_SEP];
  574. LPTSTR pszValue = szValue; // Pointer into temp buffer.
  575. LPTSTR pszDec = szValue; // Pointer to decimal part of temp buffer.
  576. BOOL bResult = FALSE;
  577. INT64 WholePart = 0;
  578. INT64 FracPart = 0;
  579. DWORD xbOrderX10 = xbOrder * 10; // Saves multiple computations.
  580. ASSERT((NULL != pszSource));
  581. ASSERT((VALID_ORDER(xbOrder)));
  582. GetLocaleInfo(LOCALE_USER_DEFAULT,
  583. LOCALE_SDECIMAL,
  584. szDecimalSep,
  585. ARRAYSIZE(szDecimalSep));
  586. //
  587. // Local copy to party on.
  588. //
  589. lstrcpyn(szValue, pszSource, ARRAYSIZE(szValue));
  590. PWSTR oldLocale = _wsetlocale(LC_ALL, L"");
  591. //
  592. // Find the start of the decimal separator.
  593. //
  594. while(NULL != *pszDec && szDecimalSep[0] != *pszDec)
  595. {
  596. // This checks to make sure its a legal digit for the
  597. // locale
  598. if (!StrToInt64Ex(pszDec, STIF_DEFAULT, &WholePart) &&
  599. szDecimalSep[0] != *pszDec)
  600. {
  601. goto not_a_number;
  602. }
  603. pszDec++;
  604. }
  605. if (CH_NUL != *pszDec)
  606. {
  607. *pszDec = CH_NUL; // Terminate the whole part.
  608. //
  609. // Skip over the decimal separator character(s).
  610. // Remember, separator is localized.
  611. //
  612. LPTSTR pszDecimalSep = &szDecimalSep[1];
  613. pszDec++;
  614. while(*pszDecimalSep && *pszDec && *pszDec == *pszDecimalSep)
  615. {
  616. pszDecimalSep++;
  617. pszDec++;
  618. }
  619. }
  620. else
  621. pszDec = NULL; // No decimal pt found.
  622. //
  623. // Convert whole part to an integer.
  624. //
  625. BOOL converted = StrToInt64Ex(pszValue, STIF_DEFAULT, &WholePart);
  626. _wsetlocale(LC_ALL, oldLocale);
  627. if (!converted)
  628. goto not_a_number;
  629. //
  630. // Check to make sure the number entered will fit into a 64-bit int when
  631. // scaled up.
  632. // With the text entry field and order combo, users can specify numbers
  633. // that will overflow an __int64. Can't let this happen. Treat overflows
  634. // as invalid entry. The (-1) accounts for the largest fractional part
  635. // that the user could enter.
  636. //
  637. if (WholePart > ((MAXLONGLONG >> xbOrderX10) - 1))
  638. goto not_a_number;
  639. //
  640. // Scale whole part according to order.
  641. //
  642. WholePart *= (1i64 << xbOrderX10);
  643. //
  644. // Convert fractional part to an integer.
  645. //
  646. if (NULL != pszDec &&
  647. pszDec[0] != L'\0')
  648. {
  649. //
  650. // Trim any trailing zero's first.
  651. //
  652. LPTSTR pszZero = pszDec + lstrlen(pszDec) - 1;
  653. while(pszZero >= pszDec && CH_ZERO == *pszZero)
  654. *pszZero-- = CH_NUL;
  655. // Check to make sure all characters are valid digits
  656. // for this locale
  657. oldLocale = _wsetlocale(LC_ALL, L"");
  658. PWSTR currentDecChar = pszDec;
  659. while (currentDecChar &&
  660. currentDecChar[0] != L'\0')
  661. {
  662. if (!StrToInt64Ex(currentDecChar, STIF_DEFAULT, &FracPart))
  663. {
  664. goto not_a_number;
  665. }
  666. ++currentDecChar;
  667. }
  668. //
  669. // Convert decimal portion of string to an integer.
  670. //
  671. converted = StrToInt64Ex(pszDec, STIF_DEFAULT, &FracPart);
  672. _wsetlocale(LC_ALL, oldLocale);
  673. if (!converted)
  674. {
  675. goto not_a_number;
  676. }
  677. //
  678. // Scale fractional part according to order.
  679. //
  680. FracPart *= (1i64 << xbOrderX10);
  681. DWORD dwDivisor = 1;
  682. while(pszZero-- >= pszDec)
  683. dwDivisor *= 10;
  684. //
  685. // Round up to the nearest muliple of the divisor to prevent
  686. // undesireable truncation during integer division we do below.
  687. //
  688. DWORD dwRemainder = (DWORD)(FracPart % dwDivisor);
  689. if (0 != dwRemainder)
  690. FracPart += dwDivisor - dwRemainder;
  691. FracPart /= dwDivisor;
  692. }
  693. m_ValueBytes = WholePart + FracPart;
  694. bResult = TRUE;
  695. not_a_number:
  696. return bResult;
  697. }
  698. ///////////////////////////////////////////////////////////////////////////////
  699. /* Function: XBytes::Fetch
  700. Description: Retrieve the byte count from the object using a specified
  701. order (magnitude). i.e. For 60.5 MB, the order is e_Mega, the decimal
  702. part is 5 and the returned value is 60.
  703. Arguments:
  704. pDecimal [optional] - Address of DWORD to receive the fractional part
  705. of the byte count. May be NULL.
  706. xbOrder - Order desired for the returned value. Must be from the
  707. enumeration set { e_Byte, e_Kilo, e_Mega ... e_Exa }
  708. Returns:
  709. Returns the whole part of the byte count.
  710. Revision History:
  711. Date Description Programmer
  712. -------- --------------------------------------------------- ----------
  713. 08/30/96 Initial creation. BrianAu
  714. */
  715. ///////////////////////////////////////////////////////////////////////////////
  716. INT64
  717. XBytes::Fetch(
  718. INT64 *pDecimal,
  719. INT xbOrder
  720. )
  721. {
  722. return BytesToParts(m_ValueBytes, pDecimal, xbOrder);
  723. }
  724. ///////////////////////////////////////////////////////////////////////////////
  725. /* Function: XBytes::Fetch
  726. Description: Retrieve the byte count from the object and tell the caller
  727. what the best order is for display. The logic used for "best order"
  728. is to use the first order that results in a 3-digit number.
  729. Arguments:
  730. pDecimal - Address of DWORD to receive the fractional part of the
  731. byte count.
  732. pxbOrder - Address of integer to receive the order of the number
  733. being returned. The returned order is in the enumeration
  734. { e_Byte, e_Kilo, e_Mega ... e_Exa }
  735. Returns:
  736. Returns the whole part of the byte count.
  737. Revision History:
  738. Date Description Programmer
  739. -------- --------------------------------------------------- ----------
  740. 08/30/96 Initial creation. BrianAu
  741. */
  742. ///////////////////////////////////////////////////////////////////////////////
  743. DWORD
  744. XBytes::Fetch(
  745. LPDWORD pDecimal,
  746. INT *pxbOrder
  747. )
  748. {
  749. return BytesToParts(m_ValueBytes, pDecimal, pxbOrder);
  750. }
  751. ///////////////////////////////////////////////////////////////////////////////
  752. /* Function: XBytes::BytesToParts
  753. Description: Converts a byte value to it's whole and fractional parts
  754. for a given magnitude (order). This is a static member function
  755. that can be used outside of the context of an XBytes object.
  756. Arguments:
  757. ValueBytes - Value to convert expressed in bytes.
  758. pDecimal [optional] - Address of variable to receive the fractional
  759. part. May be NULL.
  760. xbOrder - Order that the parts are to represent.
  761. { e_Byte, e_Kilo, e_Mega ... e_Exa }
  762. Returns: Returns the whole part of the value.
  763. Revision History:
  764. Date Description Programmer
  765. -------- --------------------------------------------------- ----------
  766. 08/30/96 Initial creation. BrianAu
  767. */
  768. ///////////////////////////////////////////////////////////////////////////////
  769. INT64
  770. XBytes::BytesToParts(
  771. INT64 ValueBytes,
  772. INT64 *pDecimal,
  773. INT xbOrder
  774. )
  775. {
  776. INT64 Value = ValueBytes;
  777. UINT64 DecMask = 0; // Start with a blank mask.
  778. DWORD dwOrderDeltaX10 = 10 * (xbOrder - e_Byte);
  779. ASSERT((VALID_ORDER(xbOrder)));
  780. //
  781. // Convert the value from order e_Byte to the order requested.
  782. // Also build a mask that can extract the decimal portion
  783. // from the original byte value. The following 2 statements implement
  784. // this logic.
  785. //
  786. // for (INT i = e_Byte; i < xbOrder; i++)
  787. // {
  788. // ValueBytes >>= 10; // Divide byte value by 1024.
  789. // DecMask <<= 10; // Shift current mask bits 10 left.
  790. // DecMask |= 0x3FF; // OR in another 10 bits.
  791. // }
  792. //
  793. Value >>= dwOrderDeltaX10;
  794. DecMask = (1i64 << dwOrderDeltaX10) - 1;
  795. if (NULL != pDecimal)
  796. {
  797. //
  798. // Caller wants fractional part.
  799. // Extract fractional part from byte value and scale it to the
  800. // specified order.
  801. // Pseudocode:
  802. // x = value & mask
  803. // pct = x / (2**order) // ** = "raise to the power of".
  804. // dec = 100 * pct
  805. //
  806. *pDecimal = (INT64)(100 * (ValueBytes & DecMask)) >> (10 * xbOrder);
  807. }
  808. return Value;
  809. }
  810. double
  811. XBytes::ConvertFromBytes(
  812. INT64 ValueBytes,
  813. INT xbOrder
  814. )
  815. {
  816. return (double)ValueBytes / (double)(10 * xbOrder);
  817. }
  818. ///////////////////////////////////////////////////////////////////////////////
  819. /* Function: XBytes::BytesToParts
  820. Description: Converts a byte value to it's whole and fractional parts.
  821. Determines the maximum magnitude (order) that will display the
  822. whole part in 3 digits or less.
  823. This is a static member function that can be used outside of the
  824. context of an XBytes object.
  825. Arguments:
  826. ValueBytes - Value to convert expressed in bytes.
  827. pDecimal [optional] - Address of variable to receive the fractional
  828. part. May be NULL.
  829. pxbOrder - Address of variable to receive the determined order.
  830. { e_Byte, e_Kilo, e_Mega ... e_Exa }
  831. Returns: Returns the whole part of the value.
  832. Revision History:
  833. Date Description Programmer
  834. -------- --------------------------------------------------- ----------
  835. 08/30/96 Initial creation. BrianAu
  836. */
  837. ///////////////////////////////////////////////////////////////////////////////
  838. DWORD
  839. XBytes::BytesToParts(
  840. INT64 ValueBytes,
  841. LPDWORD pDecimal,
  842. INT *pxbOrder
  843. )
  844. {
  845. INT64 Value = 0;
  846. INT64 Decimal = 0;
  847. INT xbOrder = e_Byte;
  848. ASSERT((NULL != pDecimal));
  849. ASSERT((NULL != pxbOrder));
  850. //
  851. // Determine the best order for display.
  852. //
  853. while(xbOrder <= MAX_ORDER)
  854. {
  855. Value = BytesToParts(ValueBytes, &Decimal, xbOrder);
  856. if (Value < (INT64)1000)
  857. break;
  858. xbOrder++;
  859. }
  860. //
  861. // Return the results.
  862. //
  863. *pxbOrder = xbOrder;
  864. *pDecimal = (DWORD)Decimal; // Fetch() guarantees this cast is OK.
  865. return (DWORD)Value;
  866. }
  867. ///////////////////////////////////////////////////////////////////////////////
  868. /* Function: XBytes::FormatByteCountForDisplay
  869. Description: Given a byte count, this static method formats a character
  870. string with the 999.99XB number where "XB" is the maximum units
  871. that can display the whole part in 3 digits or less.
  872. Arguments:
  873. Bytes - Number of bytes to format.
  874. pszDest - Address of destination character buffer.
  875. cchDest - Size of destination buffer in characters.
  876. Returns: Nothing.
  877. Revision History:
  878. Date Description Programmer
  879. -------- --------------------------------------------------- ----------
  880. 08/30/96 Initial creation. BrianAu
  881. */
  882. ///////////////////////////////////////////////////////////////////////////////
  883. VOID
  884. XBytes::FormatByteCountForDisplay(
  885. INT64 Bytes,
  886. LPTSTR pszDest,
  887. UINT cchDest
  888. )
  889. {
  890. INT Order = XBytes::e_Byte;
  891. //
  892. // To avoid using a local temp buffer, the caller's buffer must be
  893. // large enough for final string. "999.99 MB" plus NUL and some pad to
  894. // allow for possible multi-char decimal separators (localized).
  895. //
  896. ASSERT((NULL != pszDest));
  897. FormatByteCountForDisplay(Bytes, pszDest, cchDest, &Order);
  898. DWORD dwLen = lstrlen(pszDest);
  899. //
  900. // Insert a space between the number and the suffix (i.e. "99 MB").
  901. // dwLen is incremented to allow for the added space.
  902. //
  903. *(pszDest + dwLen++) = TEXT(' ');
  904. //
  905. // Append the suffix.
  906. //
  907. LoadString(Win::GetModuleHandle(), IDS_ORDERKB + Order - 1, pszDest + dwLen, cchDest - dwLen);
  908. }
  909. ///////////////////////////////////////////////////////////////////////////////
  910. /* Function: XBytes::FormatByteCountForDisplay
  911. Description: Given a byte count, this static method formats a character
  912. string with the 999.99 number and returns the enumerted value
  913. representing the order in *pOrder. This function complements
  914. the one above for those callers not needing the "KB", "MB"...
  915. suffix. In particular, our combo box.
  916. Arguments:
  917. Bytes - Number of bytes to format.
  918. pszDest - Address of destination character buffer.
  919. cchDest - Size of destination buffer in characters.
  920. pOrder - Address of variable to receive the enumerated order value.
  921. Returns: Nothing.
  922. Revision History:
  923. Date Description Programmer
  924. -------- --------------------------------------------------- ----------
  925. 08/30/96 Initial creation. BrianAu
  926. */
  927. ///////////////////////////////////////////////////////////////////////////////
  928. VOID
  929. XBytes::FormatByteCountForDisplay(
  930. INT64 Bytes,
  931. LPTSTR pszDest,
  932. UINT cchDest,
  933. INT *pOrder
  934. )
  935. {
  936. ASSERT((NULL != pszDest));
  937. ASSERT((NULL != pOrder));
  938. DWORD dwWholePart = 0;
  939. DWORD dwFracPart = 0;
  940. dwWholePart = BytesToParts(Bytes, &dwFracPart, pOrder);
  941. FormatForDisplay(pszDest, cchDest, dwWholePart, dwFracPart);
  942. }
  943. ///////////////////////////////////////////////////////////////////////////////
  944. /* Function: XBytes::FormatByteCountForDisplay
  945. Description: Given a byte count, and a specified order, this static method
  946. formats a character string with the 999.99 number in the specified
  947. order.
  948. Arguments:
  949. Bytes - Number of bytes to format.
  950. pszDest - Address of destination character buffer.
  951. cchDest - Size of destination buffer in characters.
  952. Order - Order of the value in the resultant string.
  953. Returns: Nothing.
  954. Revision History:
  955. Date Description Programmer
  956. -------- --------------------------------------------------- ----------
  957. 09/30/96 Initial creation. BrianAu
  958. */
  959. ///////////////////////////////////////////////////////////////////////////////
  960. VOID
  961. XBytes::FormatByteCountForDisplay(
  962. INT64 Bytes,
  963. LPTSTR pszDest,
  964. UINT cchDest,
  965. INT Order
  966. )
  967. {
  968. LONGLONG llWholePart;
  969. LONGLONG llFracPart;
  970. ASSERT((NULL != pszDest));
  971. //
  972. // WARNING: This code assumes that the whole and fractional parts will
  973. // each be less than 2^32. I think a valid assumption for scaled
  974. // quota information.
  975. //
  976. llWholePart = BytesToParts(Bytes, &llFracPart, Order);
  977. FormatForDisplay(pszDest, cchDest, (DWORD)llWholePart, (DWORD)llFracPart);
  978. }
  979. ///////////////////////////////////////////////////////////////////////////////
  980. /* Function: XBytes::FormatForDisplay
  981. Description: Given a whole part and a fractional part, format a decimal
  982. number suitable for display in 999.99 format. If the fractional
  983. part is 0, no decimal part is included.
  984. Arguments:
  985. pszDest - Address of destination character buffer.
  986. cchDest - Size of destination buffer in characters.
  987. dwWholePart - Whole part of the number.
  988. dwFracPart - Fractional part of the number.
  989. Returns: Nothing.
  990. Revision History:
  991. Date Description Programmer
  992. -------- --------------------------------------------------- ----------
  993. 09/30/96 Initial creation. BrianAu
  994. */
  995. ///////////////////////////////////////////////////////////////////////////////
  996. VOID
  997. XBytes::FormatForDisplay(
  998. LPTSTR pszDest,
  999. UINT cchDest,
  1000. DWORD dwWholePart,
  1001. DWORD dwFracPart
  1002. )
  1003. {
  1004. ASSERT((NULL != pszDest));
  1005. TCHAR szTemp[80];
  1006. if (0 != dwFracPart)
  1007. {
  1008. TCHAR szFmt[] = TEXT("%d%s%02d");
  1009. TCHAR szDecimalSep[MAX_DECIMAL_SEP];
  1010. if ((dwFracPart >= 10) && (0 == (dwFracPart % 10)))
  1011. {
  1012. //
  1013. // Whack off the trailing zero for display.
  1014. //
  1015. dwFracPart /= 10;
  1016. szFmt[6] = TEXT('1');
  1017. }
  1018. GetLocaleInfo(LOCALE_USER_DEFAULT,
  1019. LOCALE_SDECIMAL,
  1020. szDecimalSep,
  1021. ARRAYSIZE(szDecimalSep));
  1022. wsprintf(szTemp, szFmt, dwWholePart, szDecimalSep, dwFracPart);
  1023. }
  1024. else
  1025. wsprintf(szTemp, TEXT("%d"), dwWholePart);
  1026. lstrcpyn(pszDest, szTemp, cchDest);
  1027. }
  1028. //
  1029. // Load the static strings if they haven't been loaded.
  1030. //
  1031. VOID
  1032. XBytes::LoadStaticStrings(
  1033. void
  1034. )
  1035. {
  1036. //
  1037. // Initialize the "No Limit" text string for display in the
  1038. // edit control. This is the same string used in the details list
  1039. // view columns.
  1040. //
  1041. if (TEXT('\0') == m_szNoLimit[0])
  1042. {
  1043. INT cchLoaded = LoadString(Win::GetModuleHandle(),
  1044. IDS_NO_LIMIT,
  1045. m_szNoLimit,
  1046. ARRAYSIZE(m_szNoLimit));
  1047. ASSERT((0 < cchLoaded));
  1048. }
  1049. }