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.
1270 lines
39 KiB
1270 lines
39 KiB
///////////////////////////////////////////////////////////////////////////////
|
|
/* File: xbytes.cpp
|
|
|
|
Description: This module implements a class that coordinates the operation
|
|
between the edit control and combo box used for entering byte values.
|
|
The name "XBytes" is used because the control can represent
|
|
KBytes, MBytes, GBytes etc.
|
|
|
|
The cooperation between edit control and combo control is required
|
|
so that the user can enter a byte value in the edit control then
|
|
indicate it's order (KB, MB, GB...) using a selection from the combo box.
|
|
|
|
A simple external interface is provided to initially set the
|
|
object's byte value then retrieve the byte value when needed. The
|
|
object's client is also required to call two member functions when
|
|
the parent dialog receives an EN_UPDATE notification and a CBN_SELCHANGE
|
|
message. The XBytes object handles all of the value scaling
|
|
internally.
|
|
|
|
NOTE: I experimented with adding a spin control to the edit control.
|
|
I found that without some fancy intervention, the spin control
|
|
didn't support fractional values (i.e. 2.5MB). Decided to keep
|
|
fractional values and ditch the spinner. I think fractional
|
|
values will be more useful to disk admins.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
08/30/96 Initial creation. BrianAu
|
|
07/23/97 Added default ctor and CommonInit() function. BrianAu
|
|
Also added g_ForLoadingStaticStrings instance.
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
#include "pch.h" // PCH
|
|
#pragma hdrstop
|
|
|
|
#include "resource.h"
|
|
#include "xbytes.h"
|
|
|
|
const TCHAR CH_NUL = TEXT('\0');
|
|
const TCHAR CH_ZERO = TEXT('0');
|
|
const INT MAX_EDIT_TEXT = 16; // Max chars in edit text.
|
|
const INT MAX_CMB_TEXT = 10; // For "KB", "MB", "GB" etc.
|
|
const INT64 MAX_VALUE = ((1i64 << 60) * 6i64); // Max is 6EB.
|
|
const INT64 MIN_VALUE = 1024i64; // Min value is 1KB.
|
|
|
|
TCHAR XBytes::m_szNoLimit[]; // "No Limit" edit control text.
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: XBytes::XBytes
|
|
|
|
Description: Constructor
|
|
|
|
Arguments:
|
|
hDlg - Handle to parent dialog.
|
|
|
|
idCtlEdit - Control ID for edit control.
|
|
|
|
idCtlCombo - Control ID for combo box control.
|
|
|
|
CurrentBytes - Initial byte value.
|
|
|
|
Returns: Nothing.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
08/30/96 Initial creation. BrianAu
|
|
10/15/96 Added m_MaxBytes member. BrianAu
|
|
05/29/98 Removed m_MaxBytes member. Don't want to limit BrianAu
|
|
user's ability to enter a value larger than
|
|
max disk space.
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
XBytes::XBytes(
|
|
HWND hDlg,
|
|
DWORD idCtlEdit,
|
|
DWORD idCtlCombo,
|
|
INT64 CurrentBytes
|
|
) : m_hDlg(hDlg),
|
|
m_idCtlEdit(idCtlEdit),
|
|
m_idCtlCombo(idCtlCombo),
|
|
m_ValueBytes(0)
|
|
{
|
|
CommonInit();
|
|
|
|
LoadComboItems(MAXLONGLONG); // Load options into combo.
|
|
CurrentBytes = MIN(CurrentBytes, MAX_VALUE);
|
|
if (NOLIMIT != CurrentBytes)
|
|
CurrentBytes = MAX(CurrentBytes, MIN_VALUE);
|
|
|
|
SetBytes(CurrentBytes); // Set "current bytes".
|
|
//
|
|
// Note: SetBytes() calls SetBestDisplay().
|
|
//
|
|
}
|
|
|
|
//
|
|
// This constructor is sort of a hack. Since the m_szNoLimit string
|
|
// is static, and since it is initialized in
|
|
// the constructor, at least one instance of XBytes must be created.
|
|
// There are cases where the static function FormatByteCountForDisplay
|
|
// may be useful when there is no need for an XBytes object. The
|
|
// DiskQuota watchdog is just such an example. If an XBytes object
|
|
// is not created, the two strings are not created and the function
|
|
// doesn't work correctly. To fix this, I've defined a single global
|
|
// XBytes object constructed using this default constructor. It's sole
|
|
// purpose is to load these static strings. [7/23/97 - brianau]
|
|
//
|
|
XBytes::XBytes(
|
|
VOID
|
|
) : m_hDlg(NULL),
|
|
m_idCtlEdit((DWORD)-1),
|
|
m_idCtlCombo((DWORD)-1),
|
|
m_ValueBytes(0)
|
|
{
|
|
CommonInit();
|
|
}
|
|
|
|
//
|
|
// Initialization common to both constructors.
|
|
//
|
|
VOID
|
|
XBytes::CommonInit(
|
|
VOID
|
|
)
|
|
{
|
|
if (NULL != m_hDlg)
|
|
SendMessage(m_hDlg, m_idCtlEdit, EM_LIMITTEXT, MAX_EDIT_TEXT);
|
|
|
|
LoadStaticStrings();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: XBytes::SetBytes
|
|
|
|
Description: Stores a new byte value and updates the display to the
|
|
proper units (order).
|
|
|
|
Arguments:
|
|
ValueBytes - Value in bytes.
|
|
If the value is NOLIMIT, the controls are disabled.
|
|
Otherwise the controls are enabled.
|
|
|
|
Returns: Nothing.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
08/30/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
VOID
|
|
XBytes::SetBytes(INT64 ValueBytes)
|
|
{
|
|
if (NOLIMIT != ValueBytes)
|
|
ValueBytes = MAX(MIN_VALUE, ValueBytes);
|
|
|
|
ValueBytes = MIN(MAX_VALUE, ValueBytes);
|
|
Store(ValueBytes, e_Byte);
|
|
SetBestDisplay();
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: XBytes::Enable
|
|
|
|
Description: Enables/Disables the edit and combo controls.
|
|
|
|
Arguments:
|
|
bEnable - TRUE = Enable, FALSE = Disable.
|
|
|
|
Returns: Nothing.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
10/28/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
VOID
|
|
XBytes::Enable(
|
|
BOOL bEnable
|
|
)
|
|
{
|
|
EnableWindow(GetDlgItem(m_hDlg, m_idCtlCombo), bEnable);
|
|
EnableWindow(GetDlgItem(m_hDlg, m_idCtlEdit), bEnable);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: XBytes::IsEnabled
|
|
|
|
Description: Returns the "enabled" state of the edit control. As long
|
|
as the client doesn't enable/disable the edit/combo controls
|
|
individually, this represents the state of the control pair.
|
|
By using only the SetBytes() method to control enabling/disabling,
|
|
this is ensured.
|
|
|
|
Arguments:
|
|
bEnable - TRUE = Enable, FALSE = Disable.
|
|
|
|
Returns: Nothing.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
10/28/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
BOOL
|
|
XBytes::IsEnabled(
|
|
VOID
|
|
)
|
|
{
|
|
return IsWindowEnabled(GetDlgItem(m_hDlg, m_idCtlEdit));
|
|
}
|
|
|
|
|
|
bool
|
|
XBytes::UndoLastEdit(
|
|
void
|
|
)
|
|
{
|
|
if (SendToEditCtl(EM_CANUNDO, 0, 0))
|
|
{
|
|
SendToEditCtl(EM_UNDO, 0, 0);
|
|
SendToEditCtl(EM_EMPTYUNDOBUFFER, 0, 0);
|
|
SendToEditCtl(EM_SETSEL, SendToEditCtl(EM_LINELENGTH, 0, 0), -1);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: XBytes::OnEditNotifyUpdate
|
|
|
|
Description: Must be called whenever the parent window receives a
|
|
EN_UPDATE notification for the edit control. The function
|
|
reads the current string in the edit control and tries to store it
|
|
as a byte value. If the store operation fails, the number is invalid
|
|
and an alarm is sounded.
|
|
|
|
Arguments:
|
|
lParam - lParam argument to EN_UPDATE notification. It is unused.
|
|
|
|
Returns:
|
|
Always returns FALSE.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
08/30/96 Initial creation. BrianAu
|
|
10/15/96 Added check for too-large input. BrianAu
|
|
10/22/96 Re-organized and added ValueInRange() function. BrianAu
|
|
This was to support value check/adjustment when
|
|
user changes the combo-box setting (bug).
|
|
02/26/97 Added EM_CANUNDO and EM_EMPTYUNDOBUFFER. BrianAu
|
|
05/29/98 Removed ValueInRange() function and replaced with BrianAu
|
|
check for negative number.
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
BOOL
|
|
XBytes::OnEditNotifyUpdate(
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
TCHAR szEditText[MAX_PATH];
|
|
bool bBeep = false;
|
|
|
|
DBGASSERT((MAX_EDIT_TEXT < MAX_PATH));
|
|
|
|
GetDlgItemText(m_hDlg, m_idCtlEdit, szEditText, ARRAYSIZE(szEditText));
|
|
if (lstrlen(szEditText) > MAX_EDIT_TEXT)
|
|
{
|
|
szEditText[MAX_EDIT_TEXT] = TEXT('\0');
|
|
SetDlgItemText(m_hDlg, m_idCtlEdit, szEditText);
|
|
}
|
|
|
|
if (0 != lstrcmpi(XBytes::m_szNoLimit, szEditText))
|
|
{
|
|
//
|
|
// If text in edit control is not "No Limit", convert the text to
|
|
// a number, verify that it is in range and store it.
|
|
//
|
|
if (Store(szEditText, (INT)GetOrderFromCombo()))
|
|
{
|
|
//
|
|
// If number is negative, force it to the minimum.
|
|
//
|
|
if (0 > Fetch(NULL, e_Byte))
|
|
{
|
|
SetBytes(MIN_VALUE);
|
|
bBeep = true;
|
|
}
|
|
|
|
SendToEditCtl(EM_EMPTYUNDOBUFFER, 0, 0);
|
|
}
|
|
else
|
|
{
|
|
bBeep = true;
|
|
if (!UndoLastEdit())
|
|
{
|
|
//
|
|
// Number must be too large for the selected order.
|
|
// Found that this can happen when first opening the disk quota UI
|
|
// after someone's set the value out of the range acceptable by
|
|
// the UI. Remember, because we allow decimal values in the UI,
|
|
// the UI cannot accept values quite as large as the dskquota APIs.
|
|
// Beep the user and force the value to the largest acceptable
|
|
// value.
|
|
//
|
|
SetBytes(MAX_VALUE);
|
|
}
|
|
}
|
|
if (bBeep)
|
|
{
|
|
//
|
|
// Sound beep for either an invalid value or an out-of-range value.
|
|
//
|
|
MessageBeep(MB_OK);
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
XBytes::OnEditKillFocus(
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
TCHAR szEditText[MAX_EDIT_TEXT];
|
|
bool bBeep = false;
|
|
|
|
GetDlgItemText(m_hDlg, m_idCtlEdit, szEditText, ARRAYSIZE(szEditText));
|
|
|
|
if (0 != lstrcmpi(XBytes::m_szNoLimit, szEditText))
|
|
{
|
|
INT64 value = Fetch(NULL, e_Byte);
|
|
if (MIN_VALUE > value)
|
|
{
|
|
SetBytes(MIN_VALUE);
|
|
bBeep = true;
|
|
}
|
|
else if (MAX_VALUE < value)
|
|
{
|
|
SetBytes(MAX_VALUE);
|
|
bBeep = true;
|
|
}
|
|
if (bBeep)
|
|
{
|
|
MessageBeep(MB_OK);
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: XBytes::OnComboNotifySelChange
|
|
|
|
Description: Must be called whenever the parent window receives a
|
|
CBM_SELCHANGE message for the combo box control. The function
|
|
scales the stored byte value to the new units.
|
|
|
|
Arguments:
|
|
lParam - lParam argument to CBM_SELCHANGE message. It is unused.
|
|
|
|
Returns:
|
|
Always returns FALSE.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
08/30/96 Initial creation. BrianAu
|
|
10/22/96 Modified to just call OnEditNotifyUpdate(). BrianAu
|
|
Combo-box selection should have same value
|
|
check/adjust behavior as edit control changes.
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
BOOL
|
|
XBytes::OnComboNotifySelChange(
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
TCHAR szEditText[MAX_EDIT_TEXT];
|
|
bool bBeep = false;
|
|
|
|
GetDlgItemText(m_hDlg, m_idCtlEdit, szEditText, ARRAYSIZE(szEditText));
|
|
|
|
if (0 != lstrcmpi(XBytes::m_szNoLimit, szEditText))
|
|
{
|
|
//
|
|
// If text in edit control is not "No Limit", convert the text to
|
|
// a number, verify that it is in range and store it.
|
|
//
|
|
if (Store(szEditText, (INT)GetOrderFromCombo()))
|
|
{
|
|
//
|
|
// If number is less than the minimum, force it to the minimum.
|
|
//
|
|
if (MIN_VALUE > Fetch(NULL, e_Byte))
|
|
{
|
|
SetBytes(MIN_VALUE);
|
|
bBeep = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Number must be too large for the selected order.
|
|
// Beep the user and force the value to the largest acceptable
|
|
// value.
|
|
//
|
|
SetBytes(MAX_VALUE);
|
|
bBeep = true;
|
|
}
|
|
if (bBeep)
|
|
{
|
|
MessageBeep(MB_OK);
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: XBytes::LoadComboItems
|
|
|
|
Description: Initializes the combo box with its selections.
|
|
[ "KB", "MB", "GB"... "PB" ]. The function only adds options that are
|
|
reasonable for the size of the drive. For example, if the drive
|
|
is less than 1 GB in size, only KB and MB are displayed.
|
|
|
|
Arguments:
|
|
MaxBytes - Maximum bytes available on the drive (drive size).
|
|
|
|
Returns: Nothing.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
08/30/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
VOID
|
|
XBytes::LoadComboItems(
|
|
INT64 MaxBytes
|
|
)
|
|
{
|
|
TCHAR szText[MAX_CMB_TEXT];
|
|
INT idMsg = 0;
|
|
INT order = e_Kilo;
|
|
|
|
//
|
|
// Find the string resource ID for the largest units possible.
|
|
//
|
|
// WARNING: This code assumes that the resource IDs for
|
|
// IDS_ORDERKB through IDS_ORDEREB are consecutive
|
|
// increasing integers. Hence the following assertions.
|
|
//
|
|
DBGASSERT((IDS_ORDERMB == IDS_ORDERKB + 1));
|
|
DBGASSERT((IDS_ORDERGB == IDS_ORDERKB + 2));
|
|
DBGASSERT((IDS_ORDERTB == IDS_ORDERKB + 3));
|
|
DBGASSERT((IDS_ORDERPB == IDS_ORDERKB + 4));
|
|
DBGASSERT((IDS_ORDEREB == IDS_ORDERKB + 5));
|
|
|
|
for (idMsg = IDS_ORDERKB; idMsg < IDS_ORDEREB; idMsg++)
|
|
{
|
|
if ((INT64)(1i64 << (10 * order++)) > MaxBytes)
|
|
{
|
|
idMsg--;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// idMsg is at largest units string we'll use.
|
|
// Add strings to combo box.
|
|
//
|
|
while(idMsg >= IDS_ORDERKB)
|
|
{
|
|
if (LoadString(g_hInstDll, idMsg, szText, ARRAYSIZE(szText)))
|
|
SendToCombo(CB_INSERTSTRING, 0, (LPARAM)szText);
|
|
idMsg--;
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: XBytes::SetBestDisplay
|
|
|
|
Description: Displays the byte value in the highest order that will
|
|
produce a whole part of 3 digits or less. That way you see
|
|
"25.5" MB instead of "25500 KB".
|
|
|
|
Arguments: None.
|
|
|
|
Returns: Nothing.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
08/30/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
VOID
|
|
XBytes::SetBestDisplay(
|
|
VOID
|
|
)
|
|
{
|
|
INT iOrder = e_Byte;
|
|
TCHAR szValue[MAX_EDIT_TEXT];
|
|
|
|
//
|
|
// Display NOLIMIT as 0. Edit and combo controls will be disabled
|
|
// by property page code. NOLIMIT is (-1). Defined by NTFS.
|
|
//
|
|
if (NOLIMIT != m_ValueBytes)
|
|
{
|
|
//
|
|
// Format the byte count for display. Leave off the KB, MB... extension.
|
|
// That part will be displayed in the combo box.
|
|
//
|
|
FormatByteCountForDisplay(m_ValueBytes, szValue, ARRAYSIZE(szValue), &iOrder);
|
|
|
|
//
|
|
// If value is 0, display MB units. That's our default.
|
|
//
|
|
if (0 == m_ValueBytes)
|
|
iOrder = e_Mega;
|
|
|
|
//
|
|
// Set the value string in the edit control and the order in the combo box.
|
|
//
|
|
SetOrderInCombo(iOrder);
|
|
SetDlgItemText(m_hDlg,
|
|
m_idCtlEdit,
|
|
szValue);
|
|
|
|
Enable(TRUE);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Set edit control to display "No Limit".
|
|
//
|
|
SetOrderInCombo(0); // This will cause the combo to display nothing.
|
|
SetDlgItemText(m_hDlg,
|
|
m_idCtlEdit,
|
|
m_szNoLimit);
|
|
|
|
Enable(FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: XBytes::Store
|
|
|
|
Description: Store a value in a given order as a byte count.
|
|
|
|
Arguments:
|
|
Value - Byte value in order xbOrder.
|
|
|
|
xbOrder - Order of number in Value.
|
|
One of set { e_Byte, e_Kilo, e_Mega ... e_Exa }
|
|
|
|
Returns:
|
|
TRUE - Success. Always returns TRUE.
|
|
Event though we're not returning anything useful, I want
|
|
the return type for both Store() methods to be the same.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
08/30/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
BOOL
|
|
XBytes::Store(
|
|
INT64 Value,
|
|
INT xbOrder
|
|
)
|
|
{
|
|
DBGASSERT((VALID_ORDER(xbOrder)));
|
|
|
|
m_ValueBytes = INT64(Value) << (10 * (xbOrder - e_Byte));
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: XBytes::Store
|
|
|
|
Description: Store a numeric string in a given order as a byte count.
|
|
|
|
Arguments:
|
|
pszSource - Numeric string.
|
|
|
|
xbOrder - Order of number in pszSource.
|
|
One of set { e_Byte, e_Kilo, e_Mega ... e_Exa }
|
|
|
|
Returns:
|
|
TRUE - Success.
|
|
FALSE - Invalid number in string.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
08/30/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
BOOL
|
|
XBytes::Store(
|
|
LPCTSTR pszSource,
|
|
INT xbOrder
|
|
)
|
|
{
|
|
TCHAR szValue[MAX_EDIT_TEXT]; // Temp buffer.
|
|
TCHAR szDecimalSep[MAX_DECIMAL_SEP];
|
|
LPTSTR pszValue = szValue; // Pointer into temp buffer.
|
|
LPTSTR pszDec = szValue; // Pointer to decimal part of temp buffer.
|
|
BOOL bResult = FALSE;
|
|
UINT uMult = 1; // Digit multiplier.
|
|
INT64 WholePart = 0;
|
|
INT64 FracPart = 0;
|
|
DWORD xbOrderX10 = xbOrder * 10; // Saves multiple computations.
|
|
|
|
DBGASSERT((NULL != pszSource));
|
|
DBGASSERT((VALID_ORDER(xbOrder)));
|
|
|
|
GetLocaleInfo(LOCALE_USER_DEFAULT,
|
|
LOCALE_SDECIMAL,
|
|
szDecimalSep,
|
|
ARRAYSIZE(szDecimalSep));
|
|
|
|
//
|
|
// Local copy to party on.
|
|
//
|
|
if (FAILED(StringCchCopy(szValue, ARRAYSIZE(szValue), pszSource)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Find the start of the decimal separator.
|
|
//
|
|
while(NULL != *pszDec && szDecimalSep[0] != *pszDec)
|
|
pszDec++;
|
|
|
|
if (CH_NUL != *pszDec)
|
|
{
|
|
*pszDec = CH_NUL; // Terminate the whole part.
|
|
|
|
//
|
|
// Skip over the decimal separator character(s).
|
|
// Remember, separator is localized.
|
|
//
|
|
LPTSTR pszDecimalSep = &szDecimalSep[1];
|
|
pszDec++;
|
|
while(*pszDecimalSep && *pszDec && *pszDec == *pszDecimalSep)
|
|
{
|
|
pszDecimalSep++;
|
|
pszDec++;
|
|
}
|
|
}
|
|
else
|
|
pszDec = NULL; // No decimal pt found.
|
|
|
|
//
|
|
// Convert whole part to an integer.
|
|
//
|
|
if (!StrToInt(pszValue, &WholePart))
|
|
goto not_a_number;
|
|
|
|
//
|
|
// Check to make sure the number entered will fit into a 64-bit int when
|
|
// scaled up.
|
|
// With the text entry field and order combo, users can specify numbers
|
|
// that will overflow an __int64. Can't let this happen. Treat overflows
|
|
// as invalid entry. The (-1) accounts for the largest fractional part
|
|
// that the user could enter.
|
|
//
|
|
if (WholePart > ((MAXLONGLONG >> xbOrderX10) - 1))
|
|
goto not_a_number;
|
|
|
|
//
|
|
// Scale whole part according to order.
|
|
//
|
|
WholePart *= (1i64 << xbOrderX10);
|
|
|
|
//
|
|
// Convert fractional part to an integer.
|
|
//
|
|
if (NULL != pszDec)
|
|
{
|
|
//
|
|
// Trim any trailing zero's first.
|
|
//
|
|
LPTSTR pszZero = pszDec + lstrlen(pszDec) - 1;
|
|
while(pszZero >= pszDec && CH_ZERO == *pszZero)
|
|
*pszZero-- = CH_NUL;
|
|
|
|
//
|
|
// Convert decimal portion of string to an integer.
|
|
//
|
|
if (!StrToInt(pszDec, &FracPart))
|
|
goto not_a_number;
|
|
|
|
//
|
|
// Scale fractional part according to order.
|
|
//
|
|
FracPart *= (1i64 << xbOrderX10);
|
|
|
|
DWORD dwDivisor = 1;
|
|
while(pszZero-- >= pszDec)
|
|
dwDivisor *= 10;
|
|
|
|
//
|
|
// Round up to the nearest muliple of the divisor to prevent
|
|
// undesireable truncation during integer division we do below.
|
|
//
|
|
DWORD dwRemainder = (DWORD)(FracPart % dwDivisor);
|
|
if (0 != dwRemainder)
|
|
FracPart += dwDivisor - dwRemainder;
|
|
|
|
FracPart /= dwDivisor;
|
|
}
|
|
|
|
m_ValueBytes = WholePart + FracPart;
|
|
bResult = TRUE;
|
|
|
|
not_a_number:
|
|
|
|
return bResult;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: XBytes::Fetch
|
|
|
|
Description: Retrieve the byte count from the object using a specified
|
|
order (magnitude). i.e. For 60.5 MB, the order is e_Mega, the decimal
|
|
part is 5 and the returned value is 60.
|
|
|
|
Arguments:
|
|
pDecimal [optional] - Address of DWORD to receive the fractional part
|
|
of the byte count. May be NULL.
|
|
|
|
xbOrder - Order desired for the returned value. Must be from the
|
|
enumeration set { e_Byte, e_Kilo, e_Mega ... e_Exa }
|
|
|
|
Returns:
|
|
Returns the whole part of the byte count.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
08/30/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
INT64
|
|
XBytes::Fetch(
|
|
INT64 *pDecimal,
|
|
INT xbOrder
|
|
)
|
|
{
|
|
return BytesToParts(m_ValueBytes, pDecimal, xbOrder);
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: XBytes::Fetch
|
|
|
|
Description: Retrieve the byte count from the object and tell the caller
|
|
what the best order is for display. The logic used for "best order"
|
|
is to use the first order that results in a 3-digit number.
|
|
|
|
Arguments:
|
|
pDecimal - Address of DWORD to receive the fractional part of the
|
|
byte count.
|
|
|
|
pxbOrder - Address of integer to receive the order of the number
|
|
being returned. The returned order is in the enumeration
|
|
{ e_Byte, e_Kilo, e_Mega ... e_Exa }
|
|
|
|
Returns:
|
|
Returns the whole part of the byte count.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
08/30/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
DWORD
|
|
XBytes::Fetch(
|
|
LPDWORD pDecimal,
|
|
INT *pxbOrder
|
|
)
|
|
{
|
|
return BytesToParts(m_ValueBytes, pDecimal, pxbOrder);
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: XBytes::StrToInt
|
|
|
|
Description: Converts a string to an integer.
|
|
|
|
Arguments:
|
|
pszValue - Address of string to convert.
|
|
|
|
pIntValue - Address of INT64 variable to receive resulting number.
|
|
|
|
Returns:
|
|
TRUE - Successful conversion.
|
|
FALSE - String was not a valid integer.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
08/30/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
BOOL
|
|
XBytes::StrToInt(
|
|
LPCTSTR pszValue,
|
|
INT64 *pIntValue
|
|
)
|
|
{
|
|
INT iMult = 1;
|
|
INT64 Value = 0;
|
|
LPCTSTR pszDigit = pszValue + lstrlen(pszValue) - 1; // Start at right-most
|
|
|
|
DBGASSERT((NULL != pszValue));
|
|
DBGASSERT((NULL != pIntValue));
|
|
|
|
*pIntValue = 0;
|
|
while(pszDigit >= pszValue)
|
|
{
|
|
//
|
|
// Moving left... check each digit.
|
|
//
|
|
if (IsCharNumeric(*pszDigit))
|
|
{
|
|
//
|
|
// Valid digit. Add it's value to sum.
|
|
//
|
|
Value += iMult * (*pszDigit - CH_ZERO);
|
|
pszDigit--;
|
|
iMult *= 10;
|
|
}
|
|
else
|
|
return FALSE; // Invalid character.
|
|
}
|
|
*pIntValue = Value;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: XBytes::BytesToParts
|
|
|
|
Description: Converts a byte value to it's whole and fractional parts
|
|
for a given magnitude (order). This is a static member function
|
|
that can be used outside of the context of an XBytes object.
|
|
|
|
Arguments:
|
|
ValueBytes - Value to convert expressed in bytes.
|
|
|
|
pDecimal [optional] - Address of variable to receive the fractional
|
|
part. May be NULL.
|
|
|
|
xbOrder - Order that the parts are to represent.
|
|
{ e_Byte, e_Kilo, e_Mega ... e_Exa }
|
|
|
|
Returns: Returns the whole part of the value.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
08/30/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
INT64
|
|
XBytes::BytesToParts(
|
|
INT64 ValueBytes,
|
|
INT64 *pDecimal,
|
|
INT xbOrder
|
|
)
|
|
{
|
|
INT64 Value = ValueBytes;
|
|
UINT64 DecMask = 0; // Start with a blank mask.
|
|
DWORD dwOrderDeltaX10 = 10 * (xbOrder - e_Byte);
|
|
|
|
DBGASSERT((VALID_ORDER(xbOrder)));
|
|
|
|
//
|
|
// Convert the value from order e_Byte to the order requested.
|
|
// Also build a mask that can extract the decimal portion
|
|
// from the original byte value. The following 2 statements implement
|
|
// this logic.
|
|
//
|
|
// for (INT i = e_Byte; i < xbOrder; i++)
|
|
// {
|
|
// ValueBytes >>= 10; // Divide byte value by 1024.
|
|
// DecMask <<= 10; // Shift current mask bits 10 left.
|
|
// DecMask |= 0x3FF; // OR in another 10 bits.
|
|
// }
|
|
//
|
|
Value >>= dwOrderDeltaX10;
|
|
DecMask = (1i64 << dwOrderDeltaX10) - 1;
|
|
|
|
if (NULL != pDecimal)
|
|
{
|
|
//
|
|
// Caller wants fractional part.
|
|
// Extract fractional part from byte value and scale it to the
|
|
// specified order.
|
|
// Pseudocode:
|
|
// x = value & mask
|
|
// pct = x / (2**order) // ** = "raise to the power of".
|
|
// dec = 100 * pct
|
|
//
|
|
*pDecimal = (INT64)(100 * (ValueBytes & DecMask)) >> (10 * xbOrder);
|
|
}
|
|
|
|
return Value;
|
|
}
|
|
|
|
|
|
double
|
|
XBytes::ConvertFromBytes(
|
|
INT64 ValueBytes,
|
|
INT xbOrder
|
|
)
|
|
{
|
|
return (double)ValueBytes / (double)(10 * xbOrder);
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: XBytes::BytesToParts
|
|
|
|
Description: Converts a byte value to it's whole and fractional parts.
|
|
Determines the maximum magnitude (order) that will display the
|
|
whole part in 3 digits or less.
|
|
This is a static member function that can be used outside of the
|
|
context of an XBytes object.
|
|
|
|
Arguments:
|
|
ValueBytes - Value to convert expressed in bytes.
|
|
|
|
pDecimal [optional] - Address of variable to receive the fractional
|
|
part. May be NULL.
|
|
|
|
pxbOrder - Address of variable to receive the determined order.
|
|
{ e_Byte, e_Kilo, e_Mega ... e_Exa }
|
|
|
|
Returns: Returns the whole part of the value.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
08/30/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
DWORD
|
|
XBytes::BytesToParts(
|
|
INT64 ValueBytes,
|
|
LPDWORD pDecimal,
|
|
INT *pxbOrder
|
|
)
|
|
{
|
|
INT64 Value = 0;
|
|
INT64 Decimal = 0;
|
|
INT xbOrder = e_Byte;
|
|
|
|
DBGASSERT((NULL != pDecimal));
|
|
DBGASSERT((NULL != pxbOrder));
|
|
|
|
//
|
|
// Determine the best order for display.
|
|
//
|
|
while(xbOrder <= MAX_ORDER)
|
|
{
|
|
Value = BytesToParts(ValueBytes, &Decimal, xbOrder);
|
|
if (Value < (INT64)1000)
|
|
break;
|
|
xbOrder++;
|
|
}
|
|
|
|
//
|
|
// Return the results.
|
|
//
|
|
*pxbOrder = xbOrder;
|
|
*pDecimal = (DWORD)Decimal; // Fetch() guarantees this cast is OK.
|
|
|
|
return (DWORD)Value;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: XBytes::FormatByteCountForDisplay
|
|
|
|
Description: Given a byte count, this static method formats a character
|
|
string with the 999.99XB number where "XB" is the maximum units
|
|
that can display the whole part in 3 digits or less.
|
|
|
|
Arguments:
|
|
Bytes - Number of bytes to format.
|
|
|
|
pszDest - Address of destination character buffer.
|
|
|
|
cchDest - Size of destination buffer in characters.
|
|
|
|
Returns: Nothing.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
08/30/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
VOID
|
|
XBytes::FormatByteCountForDisplay(
|
|
INT64 Bytes,
|
|
LPTSTR pszDest,
|
|
UINT cchDest
|
|
)
|
|
{
|
|
DWORD dwWholePart = 0;
|
|
DWORD dwFracPart = 0;
|
|
INT Order = XBytes::e_Byte;
|
|
|
|
//
|
|
// To avoid using a local temp buffer, the caller's buffer must be
|
|
// large enough for final string. "999.99 MB" plus NUL and some pad to
|
|
// allow for possible multi-char decimal separators (localized).
|
|
//
|
|
DBGASSERT((NULL != pszDest));
|
|
|
|
FormatByteCountForDisplay(Bytes, pszDest, cchDest, &Order);
|
|
|
|
DWORD dwLen = lstrlen(pszDest);
|
|
//
|
|
// Insert a space between the number and the suffix (i.e. "99 MB").
|
|
// dwLen is incremented to allow for the added space.
|
|
//
|
|
*(pszDest + dwLen++) = TEXT(' ');
|
|
//
|
|
// Append the suffix.
|
|
//
|
|
LoadString(g_hInstDll, IDS_ORDERKB + Order - 1, pszDest + dwLen, cchDest - dwLen);
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: XBytes::FormatByteCountForDisplay
|
|
|
|
Description: Given a byte count, this static method formats a character
|
|
string with the 999.99 number and returns the enumerted value
|
|
representing the order in *pOrder. This function complements
|
|
the one above for those callers not needing the "KB", "MB"...
|
|
suffix. In particular, our combo box.
|
|
|
|
Arguments:
|
|
Bytes - Number of bytes to format.
|
|
|
|
pszDest - Address of destination character buffer.
|
|
|
|
cchDest - Size of destination buffer in characters.
|
|
|
|
pOrder - Address of variable to receive the enumerated order value.
|
|
|
|
Returns: Nothing.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
08/30/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
VOID
|
|
XBytes::FormatByteCountForDisplay(
|
|
INT64 Bytes,
|
|
LPTSTR pszDest,
|
|
UINT cchDest,
|
|
INT *pOrder
|
|
)
|
|
{
|
|
DBGASSERT((NULL != pszDest));
|
|
DBGASSERT((NULL != pOrder));
|
|
|
|
DWORD dwWholePart = 0;
|
|
DWORD dwFracPart = 0;
|
|
|
|
dwWholePart = BytesToParts(Bytes, &dwFracPart, pOrder);
|
|
|
|
FormatForDisplay(pszDest, cchDest, dwWholePart, dwFracPart);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: XBytes::FormatByteCountForDisplay
|
|
|
|
Description: Given a byte count, and a specified order, this static method
|
|
formats a character string with the 999.99 number in the specified
|
|
order.
|
|
|
|
Arguments:
|
|
Bytes - Number of bytes to format.
|
|
|
|
pszDest - Address of destination character buffer.
|
|
|
|
cchDest - Size of destination buffer in characters.
|
|
|
|
Order - Order of the value in the resultant string.
|
|
|
|
Returns: Nothing.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/30/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
VOID
|
|
XBytes::FormatByteCountForDisplay(
|
|
INT64 Bytes,
|
|
LPTSTR pszDest,
|
|
UINT cchDest,
|
|
INT Order
|
|
)
|
|
{
|
|
LONGLONG llWholePart;
|
|
LONGLONG llFracPart;
|
|
|
|
DBGASSERT((NULL != pszDest));
|
|
|
|
//
|
|
// WARNING: This code assumes that the whole and fractional parts will
|
|
// each be less than 2^32. I think a valid assumption for scaled
|
|
// quota information.
|
|
//
|
|
llWholePart = BytesToParts(Bytes, &llFracPart, Order);
|
|
FormatForDisplay(pszDest, cchDest, (DWORD)llWholePart, (DWORD)llFracPart);
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: XBytes::FormatForDisplay
|
|
|
|
Description: Given a whole part and a fractional part, format a decimal
|
|
number suitable for display in 999.99 format. If the fractional
|
|
part is 0, no decimal part is included.
|
|
|
|
Arguments:
|
|
pszDest - Address of destination character buffer.
|
|
|
|
cchDest - Size of destination buffer in characters.
|
|
|
|
dwWholePart - Whole part of the number.
|
|
|
|
dwFracPart - Fractional part of the number.
|
|
|
|
Returns: Nothing.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/30/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
VOID
|
|
XBytes::FormatForDisplay(
|
|
LPTSTR pszDest,
|
|
UINT cchDest,
|
|
DWORD dwWholePart,
|
|
DWORD dwFracPart
|
|
)
|
|
{
|
|
DBGASSERT((NULL != pszDest));
|
|
|
|
TCHAR szTemp[80];
|
|
|
|
if (0 != dwFracPart)
|
|
{
|
|
TCHAR szFmt[] = TEXT("%d%s%02d");
|
|
TCHAR szDecimalSep[MAX_DECIMAL_SEP];
|
|
|
|
if ((dwFracPart >= 10) && (0 == (dwFracPart % 10)))
|
|
{
|
|
//
|
|
// Whack off the trailing zero for display.
|
|
//
|
|
dwFracPart /= 10;
|
|
szFmt[6] = TEXT('1');
|
|
}
|
|
|
|
GetLocaleInfo(LOCALE_USER_DEFAULT,
|
|
LOCALE_SDECIMAL,
|
|
szDecimalSep,
|
|
ARRAYSIZE(szDecimalSep));
|
|
|
|
wnsprintf(szTemp, ARRAYSIZE(szTemp), szFmt, dwWholePart, szDecimalSep, dwFracPart);
|
|
}
|
|
else
|
|
wnsprintf(szTemp, ARRAYSIZE(szTemp), TEXT("%d"), dwWholePart);
|
|
|
|
lstrcpyn(pszDest, szTemp, cchDest);
|
|
}
|
|
|
|
//
|
|
// Load the static strings if they haven't been loaded.
|
|
//
|
|
VOID
|
|
XBytes::LoadStaticStrings(
|
|
void
|
|
)
|
|
{
|
|
//
|
|
// Initialize the "No Limit" text string for display in the
|
|
// edit control. This is the same string used in the details list
|
|
// view columns.
|
|
//
|
|
if (TEXT('\0') == m_szNoLimit[0])
|
|
{
|
|
INT cchLoaded = LoadString(g_hInstDll,
|
|
IDS_NO_LIMIT,
|
|
m_szNoLimit,
|
|
ARRAYSIZE(m_szNoLimit));
|
|
|
|
DBGASSERT((0 < cchLoaded));
|
|
}
|
|
}
|