///////////////////////////////////////////////////////////////////////////////
/*  File: userprop.cpp

    Description: Provides implementations for quota user property page.


    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/15/96    Initial creation.                                    BrianAu
    06/25/98    Replaced AddUserPropSheet with AddUserDialog.        BrianAu
                Now that we're getting user info from the DS
                object picker, the prop sheet idea doesn't work
                so well.  A std dialog is better.
*/
///////////////////////////////////////////////////////////////////////////////
#include "pch.h" // PCH
#pragma hdrstop

#include <lm.h>
#include "undo.h"
#include "userprop.h"
#include "userbat.h"
#include "uihelp.h"
#include "progress.h"
#include "uiutils.h"

//
// Context help IDs.
//
#pragma data_seg(".text", "CODE")
const static DWORD rgUserPropSheetHelpIDs[] =
{
    IDC_ICON_USER,               DWORD(-1),
    IDC_STATIC2,                 DWORD(-1),
    IDC_TXT_USERNAME,            IDH_TXT_USERNAME,
    IDC_TXT_SPACEUSED,           IDH_TXT_SPACEUSED,
    IDC_TXT_SPACEREMAINING,      IDH_TXT_SPACEREMAINING,
    IDC_LBL_SPACEUSED,           DWORD(-1),
    IDC_LBL_SPACEREMAINING,      DWORD(-1),
    IDC_ICON_USERSTATUS,         IDH_ICON_USERSTATUS,
    IDC_RBN_USER_NOLIMIT,        IDH_RBN_USER_NOLIMIT,
    IDC_RBN_USER_LIMIT,          IDH_RBN_USER_LIMIT,
    IDC_TXT_WARN_LEVEL,          DWORD(-1),
    IDC_EDIT_USER_LIMIT,         IDH_EDIT_USER_LIMIT,
    IDC_EDIT_USER_THRESHOLD,     IDH_EDIT_USER_THRESHOLD,
    IDC_CMB_USER_LIMIT,          IDH_CMB_USER_LIMIT,
    IDC_CMB_USER_THRESHOLD,      IDH_CMB_USER_THRESHOLD,
    0,0
};

#pragma data_seg()


//
// Messages for querying property page for icon images.
//
#define DQM_QUERY_STATUS_ICON      (WM_USER + 1)
#define DQM_QUERY_USER_ICON        (WM_USER + 2)
#define DQM_ENABLE_APPLY_BUTTON    (WM_USER + 3)

///////////////////////////////////////////////////////////////////////////////
/*  Function: UserPropSheet::UserPropSheet

    Description: Constructor for a user property sheet object.
        Initializes the members that hold user quota data.

    Arguments: None.

    Returns: Nothing.

    Exceptions: OutOfMemory.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/15/96    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
UserPropSheet::UserPropSheet(
    PDISKQUOTA_CONTROL pQuotaControl,
    const CVolumeID& idVolume,
    HWND hWndParent,
    LVSelection& LVSel,
    UndoList& UndoList
    ) : m_cVolumeMaxBytes(0),
        m_pQuotaControl(pQuotaControl),
        m_UndoList(UndoList),
        m_LVSelection(LVSel),
        m_hWndParent(hWndParent),
        m_bIsDirty(FALSE),
        m_bHomogeneousSelection(TRUE),  // Assume selection is homogeneous.
        m_pxbQuotaLimit(NULL),
        m_pxbQuotaThreshold(NULL),
        m_idVolume(idVolume),
        m_strPageTitle(g_hInstDll, IDS_TITLE_GENERAL),
        m_idCtlNextFocus(-1)
{
    DBGASSERT((NULL != m_pQuotaControl));
    DBGASSERT((NULL != m_hWndParent));
    DBGTRACE((DM_UPROP, DL_HIGH, TEXT("UserPropSheet::UserPropSheet")));

    m_llQuotaUsed      = 0;
    m_llQuotaLimit     = 0;
    m_llQuotaThreshold = 0;

    DBGASSERT((0 == iICON_USER_SINGLE));
    DBGASSERT((1 == iICON_USER_MULTIPLE));
    m_hIconUser[0]     = LoadIcon(g_hInstDll, MAKEINTRESOURCE(IDI_SINGLE_USER));
    m_hIconUser[1]     = LoadIcon(g_hInstDll, MAKEINTRESOURCE(IDI_MULTI_USER));

    DBGASSERT((0 == iICON_STATUS_OK));
    DBGASSERT((1 == iICON_STATUS_OVER_THRESHOLD));
    DBGASSERT((2 == iICON_STATUS_OVER_LIMIT));
    m_hIconStatus[0]   = LoadIcon(g_hInstDll, MAKEINTRESOURCE(IDI_OKBUBBLE));
    m_hIconStatus[1]   = LoadIcon(NULL, IDI_WARNING);
    m_hIconStatus[2]   = LoadIcon(g_hInstDll, MAKEINTRESOURCE(IDI_WARNERR));
}



UserPropSheet::~UserPropSheet(
    VOID
    )
{
    DBGTRACE((DM_UPROP, DL_HIGH, TEXT("UserPropSheet::~UserPropSheet")));
    INT i = 0;

    if (NULL != m_pQuotaControl)
        m_pQuotaControl->Release();

    if (NULL != m_pxbQuotaLimit)
        delete m_pxbQuotaLimit;
    if (NULL != m_pxbQuotaThreshold)
        delete m_pxbQuotaThreshold;
}


///////////////////////////////////////////////////////////////////////////////
/*  Function: UserPropSheet::Run

    Description: Creates and runs the property sheet dialog.
        This is the only method a client needs to call once the object
        is created.

    Arguments: None.

    Returns:
        NO_ERROR
        E_FAIL      - Couldn't create property sheet.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/15/96    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
HRESULT
UserPropSheet::Run(
    VOID
    )
{
    HRESULT hResult = NO_ERROR; // Assume success.

    PROPSHEETHEADER psh;
    PROPSHEETPAGE   psp;

    ZeroMemory(&psh, sizeof(psh));
    ZeroMemory(&psp, sizeof(psp));

    //
    // Define page.
    //
    psp.dwSize          = sizeof(PROPSHEETPAGE);
    psp.dwFlags         = PSP_USEREFPARENT | PSP_USETITLE;
    psp.hInstance       = g_hInstDll;
    psp.pszTemplate     = MAKEINTRESOURCE(IDD_PROPPAGE_USERQUOTA);
    psp.pszTitle        = (LPCTSTR)m_strPageTitle;
    psp.pfnDlgProc      = DlgProc;
    psp.lParam          = (LPARAM)this;
    psp.pcRefParent     = (UINT *)& g_cRefThisDll;

    //
    // Define sheet.
    //
    psh.dwSize          = sizeof(PROPSHEETHEADER);
    psh.dwFlags         = PSH_PROPSHEETPAGE;
    psh.hwndParent      = m_hWndParent;
    psh.hInstance       = g_hInstDll;
    psh.pszIcon         = NULL;
    psh.pszCaption      = NULL;
    psh.nPages          = 1;
    psh.nStartPage      = 0;
    psh.ppsp            = (LPCPROPSHEETPAGE)&psp;

    if (0 <= PropertySheet(&psh))
        hResult = E_FAIL;

    return hResult;
}



///////////////////////////////////////////////////////////////////////////////
/*  Function: UserPropSheet::DlgProc

    Description: Static method called by windows to process messages for the
        property page dialog.  Since it's static, we have to save the "this"
        pointer in the window's USERDATA.

    Arguments: Standard WndProc-type arguments.

    Returns: Standard WndProc-type return values.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/15/96    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
INT_PTR APIENTRY
UserPropSheet::DlgProc(
    HWND hDlg,
    UINT message,
    WPARAM wParam,
    LPARAM lParam
    )
{
    INT_PTR bResult = FALSE;

    //
    // Retrieve the "this" pointer from the dialog's userdata.
    // It was placed there in OnInitDialog().
    //
    UserPropSheet *pThis = (UserPropSheet *)GetWindowLongPtr(hDlg, DWLP_USER);

    try
    {
        switch(message)
        {
            case WM_INITDIALOG:
                DBGPRINT((DM_WND, DL_MID, TEXT("DlgProc: WM_INITDIALOG")));
                bResult = OnInitDialog(hDlg, wParam, lParam);
                break;

            case WM_NOTIFY:
                DBGPRINT((DM_WND, DL_MID, TEXT("DlgProc: WM_NOTIFY")));
                bResult = pThis->OnNotify(hDlg, wParam, lParam);
                break;

            case WM_COMMAND:
                DBGPRINT((DM_WND, DL_MID, TEXT("DlgProc: WM_COMMAND")));
                bResult = pThis->OnCommand(hDlg, wParam, lParam);
                break;

            case WM_HELP:
                DBGPRINT((DM_WND, DL_MID, TEXT("DlgProc: WM_HELP")));
                bResult = pThis->OnHelp(hDlg, wParam, lParam);
                break;

            case WM_CONTEXTMENU:
                bResult = pThis->OnContextMenu((HWND)wParam, LOWORD(lParam), HIWORD(lParam));
                break;

            case WM_DESTROY:
                DBGPRINT((DM_WND, DL_MID, TEXT("DlgProc: WM_DESTROY")));
                break;

            case WM_CLOSE:
            case WM_ENDSESSION:
                DestroyWindow(hDlg);
                break;

            case DQM_ENABLE_APPLY_BUTTON:
                pThis->m_bIsDirty = TRUE;
                bResult = PropSheet_Changed(GetParent(hDlg), hDlg);
                break;

            //
            // These two icon query messages are for automated testing
            // of the UI.
            //
            case DQM_QUERY_USER_ICON:
                bResult = pThis->QueryUserIcon(hDlg);
                break;

            case DQM_QUERY_STATUS_ICON:
                bResult = pThis->QueryUserStatusIcon(hDlg);
                break;

            default:
                break;
        }

    }
    catch(CAllocException& e)
    {
        DiskQuotaMsgBox(GetDesktopWindow(),
                        IDS_OUTOFMEMORY,
                        IDS_TITLE_DISK_QUOTA,
                        MB_ICONERROR | MB_OK);
    }

    return bResult;
}



///////////////////////////////////////////////////////////////////////////////
/*  Function: UserPropSheet::OnInitDialog

    Description: Handler for WM_INITDIALOG.  Retrieves the "this" pointer from
        the PROPSHEETPAGE structure (pointed to by lParam) and saves it in
        the window's USERDATA.

    Arguments:
        hDlg - Dialog window handle.

        wParam - Handle of control to receive focus if we return FALSE.

        lParam - Pointer to PROPSHEETPAGE structure for the property page.

    Returns:
        TRUE  = Tells windows to assign focus to the control in wParam.

    Exceptions: OutOfMemory.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/15/96    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
INT_PTR
UserPropSheet::OnInitDialog(
    HWND hDlg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    HRESULT hResult = NO_ERROR;

    PROPSHEETPAGE *pPage = (PROPSHEETPAGE *)lParam;
    UserPropSheet *pThis = (UserPropSheet *)pPage->lParam;
    DWORD dwSectorsPerCluster = 0;
    DWORD dwBytesPerSector    = 0;
    DWORD dwFreeClusters      = 0;
    DWORD dwTotalClusters     = 0;

    DBGASSERT((NULL != pThis));

    //
    // Save "this" in the window's userdata.
    //
    SetWindowLongPtr(hDlg, DWLP_USER, (INT_PTR)pThis);

    //
    // Read quota info from NTFS.
    // For single selection, we cache the selected user's info.
    // For multi selection, we cache the defaults for the volume.
    // If adding a new user (count == 0), we also use the defaults for the
    // volume.
    //
    pThis->RefreshCachedQuotaInfo();

    //
    // Calculate the volume's size.
    // We'll use this to limit user threshold and quota limit entries.
    //
    if (GetDiskFreeSpace(pThis->m_idVolume.ForParsing(),
                         &dwSectorsPerCluster,
                         &dwBytesPerSector,
                         &dwFreeClusters,
                         &dwTotalClusters))
    {
        pThis->m_cVolumeMaxBytes = (LONGLONG)dwSectorsPerCluster *
                                   (LONGLONG)dwBytesPerSector *
                                   (LONGLONG)dwTotalClusters;
    }

    pThis->m_pxbQuotaLimit     = new XBytes(hDlg,
                                            IDC_EDIT_USER_LIMIT,
                                            IDC_CMB_USER_LIMIT,
                                            pThis->m_llQuotaLimit);
    pThis->m_pxbQuotaThreshold = new XBytes(hDlg,
                                            IDC_EDIT_USER_THRESHOLD,
                                            IDC_CMB_USER_THRESHOLD,
                                            pThis->m_llQuotaThreshold);

    pThis->InitializeControls(hDlg);

    return TRUE;  // Set focus to default control.
}



///////////////////////////////////////////////////////////////////////////////
/*  Function: UserPropSheet::RefreshCachedQuotaInfo

    Description: Reads the quota limit, threshold and used values from the
        property sheet's user object.  If multiple users are selected,
        only the first one is read.

    Arguments: None.

    Returns: Result of read operation.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/15/96    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
HRESULT
UserPropSheet::RefreshCachedQuotaInfo(
    VOID
    )
{
    HRESULT hResult = NO_ERROR;
    PDISKQUOTA_USER pUser = NULL;
    INT cSelectedUsers = m_LVSelection.Count();

    m_LVSelection.Retrieve(0, &pUser);

    //
    // Read quota threshold.  Multi-user selections use the volume's default.
    //
    if (1 == cSelectedUsers)
    {
        hResult = pUser->GetQuotaThreshold(&m_llQuotaThreshold);
    }
    else
    {
        hResult = m_pQuotaControl->GetDefaultQuotaThreshold(&m_llQuotaThreshold);
    }
    if (FAILED(hResult))
        goto refresh_quota_info_failed;

    //
    // Read quota limit.  Multi-user selections use the volume's default.
    //
    if (1 == cSelectedUsers)
    {
        hResult = pUser->GetQuotaLimit(&m_llQuotaLimit);
    }
    else
    {
        hResult = m_pQuotaControl->GetDefaultQuotaLimit(&m_llQuotaLimit);
    }
    if (FAILED(hResult))
        goto refresh_quota_info_failed;


    //
    // Read quota used.
    //
    if (1 == cSelectedUsers)
    {
        hResult = pUser->GetQuotaUsed(&m_llQuotaUsed);
    }
    else
    {
        m_llQuotaUsed = 0;
    }

refresh_quota_info_failed:
    return hResult;
}


///////////////////////////////////////////////////////////////////////////////
/*  Function: UserPropSheet::OnCommand

    Description: Handler for WM_COMMAND.

    Arguments:
        hDlg - Dialog window handle.

        wParam - ID of selected control and notification code.

        lParam - HWND of selected control.

    Returns:
        TRUE  = Message wasn't handled.
        FALSE = Message was handled.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/15/96    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
INT_PTR
UserPropSheet::OnCommand(
    HWND hDlg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    DWORD dwCtlId        = LOWORD(wParam);
    HWND hWndCtl         = (HWND)lParam;
    DWORD dwNotifyCode   = HIWORD(wParam);
    INT_PTR bResult      = FALSE;

    switch(dwCtlId)
    {
        case IDC_TXT_USERNAME:
            if (EN_SETFOCUS == dwNotifyCode && IDC_EDIT_USER_THRESHOLD == m_idCtlNextFocus)
            {
                //
                // Focus is being set as a result of an invalid entry
                // in the warning level field.  Force input focus to the
                // field and select the entire contents.  User can then just
                // enter a new value.
                //
                SetFocus(GetDlgItem(hDlg, IDC_EDIT_USER_THRESHOLD));
                SendDlgItemMessage(hDlg, IDC_EDIT_USER_THRESHOLD, EM_SETSEL, 0, -1);
                m_idCtlNextFocus = -1;
            }
            break;

        case IDC_RBN_USER_NOLIMIT:
            if (m_pxbQuotaThreshold->IsEnabled())
            {
                //
                // This is simple.  Just set both the limit and threshold controls
                // to "no limit".
                //
                m_pxbQuotaThreshold->SetBytes(NOLIMIT);
                m_pxbQuotaLimit->SetBytes(NOLIMIT);
                m_bIsDirty = TRUE;
            }
            break;

        case IDC_RBN_USER_LIMIT:
        {
            LONGLONG llValue;

            //
            // This handler needs some logic.  We have to handle several
            // scenarios/rules with this one.
            // 1. Single vs. Multiple selection.
            // 2. Single selection for Administrator account.
            // 3. Multi selection homogeneous/heterogenous with respect to
            //    quota limit and threshold values.
            // 4. Can't display "No Limit" in edit controls when they're active.
            // 5. Use volume defaults for new user and hetergenous multi-select.
            //
            if (!m_pxbQuotaThreshold->IsEnabled())
            {
                enum use_types { USE_CACHED, USE_VOLDEF, USE_NOLIMIT };

                INT iUseAsValue = USE_CACHED;
                INT cSelected   = m_LVSelection.Count();

                ///////////////////////////////////////////////////////////////
                // First set the quota limit controls.
                ///////////////////////////////////////////////////////////////
                if (0 == cSelected)                     // Adding new user...
                {
                    iUseAsValue = USE_VOLDEF;
                }
                else if (1 == cSelected)                // One user selected...
                {
                    PDISKQUOTA_USER pUser = NULL;
                    m_LVSelection.Retrieve(0, &pUser);
                    if (UserIsAdministrator(pUser))
                    {
                        //
                        // If user is administrator, the limit is always "No Limit".
                        // This will disable the "Limit" controls and prevent
                        // user from setting a limit on this account.
                        //
                        iUseAsValue = USE_NOLIMIT;
                    }
                    else if (NOLIMIT == m_llQuotaLimit)
                    {
                        //
                        // Account isn't Administrator AND the limit is NOLIMIT.
                        // Use the volume's default "new user" limit value.
                        //
                        iUseAsValue = USE_VOLDEF;
                    }
                }
                else if (!m_bHomogeneousSelection || NOLIMIT == m_llQuotaLimit) // Multiple user.
                {
                    //
                    // Multiple non-homogeneous users get the volume's default limit.
                    // Multiple homogeneous users get their current cached setting unless
                    // the cached setting is NOLIMIT; in which case, they get the
                    // volume's defaults.
                    //
                    iUseAsValue = USE_VOLDEF;
                }

                //
                // Set the proper quota limit value in the edit/combo box control.
                //
                llValue = 0;
                switch(iUseAsValue)
                {
                    case USE_VOLDEF:
                        m_pQuotaControl->GetDefaultQuotaLimit(&llValue);
                        //
                        // If default is NOLIMIT, display 0 MB.  We can't display an
                        // "editable" No Limit in the edit control.  Only numbers.
                        //
                        if (NOLIMIT == llValue)
                            llValue = 0;
                        break;

                    case USE_NOLIMIT:
                        llValue = NOLIMIT;
                        break;

                    case USE_CACHED:
                        llValue = m_llQuotaLimit;
                        break;
                }

                m_pxbQuotaLimit->SetBytes(llValue);


                ///////////////////////////////////////////////////////////////
                // Now the threshold controls...
                ///////////////////////////////////////////////////////////////
                llValue = 0;
                iUseAsValue       = USE_CACHED;
                if (0 == cSelected)
                {
                    iUseAsValue = USE_VOLDEF;
                }
                else if (1 == cSelected)
                {
                    if (NOLIMIT == m_llQuotaThreshold)
                    {
                        iUseAsValue = USE_VOLDEF;
                    }
                }
                else if (!m_bHomogeneousSelection || NOLIMIT == m_llQuotaThreshold)
                {
                    iUseAsValue = USE_VOLDEF;
                }

                //
                // Set the proper quota threshold value in the edit/combo box control.
                //
                switch(iUseAsValue)
                {
                    case USE_VOLDEF:
                        m_pQuotaControl->GetDefaultQuotaThreshold(&llValue);
                        //
                        // If default is NOLIMIT, display 0 MB.  We can't display an
                        // "editable" No Limit in the edit control.  Only numbers.
                        //
                        if (NOLIMIT == llValue)
                            llValue = 0;
                        break;

                    case USE_NOLIMIT:
                        llValue = NOLIMIT;
                        break;

                    case USE_CACHED:
                        llValue = m_llQuotaThreshold;
                        break;
                }

                m_pxbQuotaThreshold->SetBytes(llValue);

                m_bIsDirty = TRUE;
            }
            break;
        }

        case IDC_EDIT_USER_LIMIT:
        case IDC_EDIT_USER_THRESHOLD:
            switch(dwNotifyCode)
            {
                case EN_UPDATE:
                    DBGPRINT((DM_WND, DL_MID, TEXT("OnCommand, EN_CHANGE")));
                    bResult = OnEditNotifyUpdate(hDlg, wParam, lParam);
                    m_bIsDirty = TRUE;
                    break;

                case EN_KILLFOCUS:
                    DBGPRINT((DM_WND, DL_MID, TEXT("OnCommand, EN_KILLFOCUS")));
                    bResult = OnEditNotifyKillFocus(hDlg, wParam, lParam);
                    break;

                default:
                    break;
            }
            break;

        case IDC_CMB_USER_LIMIT:
        case IDC_CMB_USER_THRESHOLD:
            switch(dwNotifyCode)
            {
                case CBN_SELCHANGE:
                    DBGPRINT((DM_WND, DL_MID, TEXT("OnCommand, CBN_CHANGE")));
                    bResult = OnComboNotifySelChange(hDlg, wParam, lParam);
                    m_bIsDirty = TRUE;
                    break;

                default:
                    break;
            }
            break;

        default:
            bResult = TRUE;  // Didn't handle message.
            break;
    }


    if (m_bIsDirty)
        PropSheet_Changed(GetParent(hDlg), hDlg);

    return bResult;
}



///////////////////////////////////////////////////////////////////////////////
/*  Function: UserPropSheet::OnNotify

    Description: Handler for WM_NOTIFY.

    Arguments:
        hDlg - Dialog window handle.

        wParam - ID of selected control and notification code.

        lParam - HWND of selected control.

    Returns:
        TRUE  = Message wasn't handled.
        FALSE = Message was handled.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/15/96    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
INT_PTR
UserPropSheet::OnNotify(
    HWND hDlg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    INT_PTR bResult = FALSE;

    switch(((NMHDR *)lParam)->code)
    {
        case PSN_SETACTIVE:
            DBGPRINT((DM_WND, DL_MID, TEXT("OnNotify, PSN_SETACTIVE")));
            bResult = OnSheetNotifySetActive(hDlg, wParam, lParam);
            break;

        case PSN_APPLY:
            DBGPRINT((DM_WND, DL_MID, TEXT("OnNotify, PSN_APPLY")));
            bResult = OnSheetNotifyApply(hDlg, wParam, lParam);
            break;

        case PSN_KILLACTIVE:
            DBGPRINT((DM_WND, DL_MID, TEXT("OnNotify, PSN_KILLACTIVE")));
            bResult = OnSheetNotifyKillActive(hDlg, wParam, lParam);
            break;

        case PSN_RESET:
            DBGPRINT((DM_WND, DL_MID, TEXT("OnNotify, PSN_RESET")));
            //
            // Fall through.
            //
        default:
            break;
    }
    return bResult;
}




///////////////////////////////////////////////////////////////////////////////
/*  Function: UserPropSheet::OnSheetNotifySetActive

    Description: Handler for WM_NOTIFY - PSN_SETACTIVE.

    Arguments:
        hDlg - Dialog window handle.

        wParam - ID of control.

        lParam - Address of NMHDR structure.

    Returns:
        FALSE = Accept activation.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/15/96    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
INT_PTR
UserPropSheet::OnSheetNotifySetActive(
    HWND hDlg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    SetWindowLongPtr(hDlg, DWLP_MSGRESULT, 0);
    return TRUE;
}



///////////////////////////////////////////////////////////////////////////////
/*  Function: UserPropSheet::OnSheetNotifyApply

    Description: Handler for WM_NOTIFY - PSN_APPLY.

    Arguments:
        hDlg - Dialog window handle.

        wParam - ID of control.

        lParam - Address of NMHDR structure.

    Returns:
        TRUE = PSN return value set using SetWindowLong.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/15/96    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
INT_PTR
UserPropSheet::OnSheetNotifyApply(
    HWND hDlg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    HRESULT hResult  = NO_ERROR;
    LONG dwPSNReturn = PSNRET_NOERROR;

    //
    // Only apply settings if the "Apply" button is enabled indicating
    // that something has been changed.  No need to apply unchanged
    // settings when the OK button is pressed.
    //
    if (m_bIsDirty)
    {
        if (PSNRET_NOERROR == dwPSNReturn)
        {
            //
            // We need to do this because if you activate the apply button
            // with Alt-A we receive PSN_APPLY before EN_KILLFOCUS.
            //
            m_pxbQuotaThreshold->OnEditKillFocus((LPARAM)GetDlgItem(hDlg, IDC_EDIT_USER_THRESHOLD));
            m_pxbQuotaLimit->OnEditKillFocus((LPARAM)GetDlgItem(hDlg, IDC_EDIT_USER_LIMIT));

            //
            // Ensure warning threshold is not above limit.
            //
            INT64 iThreshold = m_pxbQuotaThreshold->GetBytes();
            INT64 iLimit     = m_pxbQuotaLimit->GetBytes();

            if (NOLIMIT != iLimit && iThreshold > iLimit)
            {
                TCHAR szLimit[40], szThreshold[40];
                XBytes::FormatByteCountForDisplay(iLimit, szLimit, ARRAYSIZE(szLimit));
                XBytes::FormatByteCountForDisplay(iThreshold, szThreshold, ARRAYSIZE(szThreshold));

                CString s(g_hInstDll, IDS_FMT_ERR_WARNOVERLIMIT, szThreshold, szLimit, szLimit);
                switch(DiskQuotaMsgBox(hDlg, s, IDS_TITLE_DISK_QUOTA, MB_ICONWARNING | MB_YESNO))
                {
                    case IDYES:
                        m_pxbQuotaThreshold->SetBytes(iLimit);
                        break;

                    case IDNO:
                        //
                        // This m_idCtlNextFocus hack stuff is here because I can't get
                        // the @#$%! prop sheet to return focus to the threshold control.
                        // The only way I've been able to get this to happen is to
                        // cache this ID value then on the EN_SETFOCUS generated when
                        // the page is activated, set focus to the control.
                        // Gross but it works without too much hassle. [brianau]
                        //
                        m_idCtlNextFocus = IDC_EDIT_USER_THRESHOLD;
                        dwPSNReturn = PSNRET_INVALID;
                        break;
                }
            }
        }
        if (PSNRET_NOERROR == dwPSNReturn)
        {
            hResult = ApplySettings(hDlg);
            if (FAILED(hResult))
            {
                INT idMsg   = IDS_UNKNOWN_ERROR;
                UINT uFlags = MB_OK;
                switch(hResult)
                {
                    case E_FAIL:
                        idMsg = IDS_WRITE_ERROR;
                        uFlags |= MB_ICONERROR;
                        break;

                    default:
                        switch(HRESULT_CODE(hResult))
                        {

//                      case ERROR_USER_EXISTS:
//                          idMsg = IDS_NOADD_EXISTING_USER;
//                          uFlags |= MB_ICONWARNING;
//                          break;
//
// FEATURE:  Still valid?  [brianau - 5/27/98]
//
                            case ERROR_NO_SUCH_USER:
                                idMsg = IDS_NOADD_UNKNOWN_USER;
                                uFlags |= MB_ICONWARNING;
                                break;

                            case ERROR_ACCESS_DENIED:
                                idMsg  = IDS_NO_WRITE_ACCESS;
                                uFlags |= MB_ICONWARNING;
                                break;

                            default:
                            uFlags |= MB_ICONERROR;
                            break;
                        }
                        break;
                }

                DiskQuotaMsgBox(GetDesktopWindow(),
                                idMsg,
                                IDS_TITLE_DISK_QUOTA,
                                uFlags);

                dwPSNReturn = PSNRET_INVALID;
            }
            else
            {
                m_bIsDirty = FALSE;
            }
        }
    }

    SetWindowLongPtr(hDlg, DWLP_MSGRESULT, dwPSNReturn);

    return TRUE;
}



///////////////////////////////////////////////////////////////////////////////
/*  Function: UserPropSheet::OnSheetNotifyKillActive

    Description: Handler for WM_NOTIFY - PSN_KILLACTIVE.

    Arguments:
        hDlg - Dialog window handle.

        wParam - ID of control.

        lParam - Address of NMHDR structure.

    Returns:
        TRUE  = Invalid data entered.  Don't kill page.
        FALSE = All data is valid.  Ok to kill page.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/15/96    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
INT_PTR
UserPropSheet::OnSheetNotifyKillActive(
    HWND hDlg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    BOOL bAllDataIsValid = TRUE;

    //
    // No sheet-level validation performed at this time.
    //
    SetWindowLongPtr(hDlg, DWLP_MSGRESULT, !bAllDataIsValid);

    return TRUE;
}


///////////////////////////////////////////////////////////////////////////////
/*  Function: UserPropSheet::OnHelp

    Description: Handler for WM_HELP.  Displays context sensitive help.

    Arguments:
        lParam - Pointer to a HELPINFO structure.

    Returns: TRUE;

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/17/96    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
INT_PTR
UserPropSheet::OnHelp(
    HWND hDlg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    WinHelp((HWND)((LPHELPINFO) lParam)->hItemHandle, STR_DSKQUOUI_HELPFILE,
                HELP_WM_HELP, (DWORD_PTR)(LPTSTR) rgUserPropSheetHelpIDs);
    return TRUE;
}


INT_PTR
UserPropSheet::OnContextMenu(
    HWND hwndItem,
    int xPos,
    int yPos
    )
{
    int idCtl = GetDlgCtrlID(hwndItem);
    WinHelp(hwndItem,
            UseWindowsHelp(idCtl) ? NULL : STR_DSKQUOUI_HELPFILE,
            HELP_CONTEXTMENU,
            (DWORD_PTR)((LPTSTR)rgUserPropSheetHelpIDs));

    return FALSE;
}



///////////////////////////////////////////////////////////////////////////////
/*  Function: UserPropPage::OnEditNotifyUpdate

    Description: Handler for WM_COMMAND, EN_UPDATE.
        Called whenever a character is entered in an edit control.

    Arguments:

    Returns: FALSE;

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    09/03/96    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
INT_PTR
UserPropSheet::OnEditNotifyUpdate(
    HWND hDlg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    XBytes *pxb = NULL;

    switch(LOWORD(wParam))
    {
        case IDC_EDIT_USER_LIMIT:
            pxb = m_pxbQuotaLimit;
            break;

        case IDC_EDIT_USER_THRESHOLD:
            pxb = m_pxbQuotaThreshold;
            break;

        default:
            break;
    }

    if (NULL != pxb)
        pxb->OnEditNotifyUpdate(lParam);

    return FALSE;
}


///////////////////////////////////////////////////////////////////////////////
/*  Function: UserPropSheet::OnEditNotifyKillFocus

    Description: Handler for WM_COMMAND, EN_KILLFOCUS.
        Called whenever focus leaves an edit control.
        Validates the value in the edit control and adjusts it if necessary.

    Arguments:

    Returns: FALSE;

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/17/96    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
INT_PTR
UserPropSheet::OnEditNotifyKillFocus(
    HWND hDlg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    XBytes *pxb = NULL;

    switch(LOWORD(wParam))
    {
        case IDC_EDIT_USER_LIMIT:
            pxb = m_pxbQuotaLimit;
            break;

        case IDC_EDIT_USER_THRESHOLD:
            pxb = m_pxbQuotaThreshold;
            break;

        default:
            break;
    }

    if (NULL != pxb)
        pxb->OnEditKillFocus(lParam);

    return FALSE;
}



///////////////////////////////////////////////////////////////////////////////
/*  Function: UserPropPage::OnComboNotifySelChange

    Description: Handler for WM_COMMAND, CBN_SELCHANGE.
        Called whenever the user selects the combo box.

    Arguments: Std DlgProc args.

    Returns: FALSE;

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    09/03/96    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
INT_PTR
UserPropSheet::OnComboNotifySelChange(
    HWND hDlg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    XBytes *pxb = NULL;

    switch(LOWORD(wParam))
    {
        case IDC_CMB_USER_LIMIT:
            pxb = m_pxbQuotaLimit;
            break;

        case IDC_CMB_USER_THRESHOLD:
            pxb = m_pxbQuotaThreshold;
            break;

        default:
            break;
    }
    if (NULL != pxb)
       pxb->OnComboNotifySelChange(lParam);

    return FALSE;
}




///////////////////////////////////////////////////////////////////////////////
/*  Function: UserPropSheet::ApplySettings

    Description: Applies the current settings to the user's quota information
        if they have not changed from the original settings.

    Arguments:
        hDlg - Dialog window handle.

    Returns:
        NO_ERROR            - Success.
        E_INVALIDARG        - One of the settings was invalid.
        ERROR_ACCESS_DENIED (hr) - No WRITE access to quota device.
        E_FAIL              - Any other error.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/15/96    Initial creation.                                    BrianAu
    01/24/98    Added bUndo argument.                                BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
HRESULT
UserPropSheet::ApplySettings(
    HWND hDlg,
    bool bUndo    // Default == true.
    )
{
    HRESULT hResult         = NO_ERROR;
    BOOL  bTranslated       = FALSE;
    com_autoptr<DISKQUOTA_USER> ptrUser;
    UINT cUsers             = m_LVSelection.Count();
    UINT i                  = 0;
    LONGLONG llThreshold;
    LONGLONG llLimit;
    CAutoSetRedraw autoredraw(m_hWndParent);

    if (bUndo)
        m_UndoList.Clear();  // Clear current undo list.

    //
    // Determine what threshold and limit to apply.
    //
    if (BST_CHECKED == IsDlgButtonChecked(hDlg, IDC_RBN_USER_NOLIMIT))
    {
        llThreshold = NOLIMIT;
        llLimit     = NOLIMIT;
    }
    else
    {
        llThreshold = m_pxbQuotaThreshold->GetBytes();
        llLimit     = m_pxbQuotaLimit->GetBytes();
    }

    if (cUsers > 1)
    {
        //
        // Create batch object and do batch update for multiple users.
        //
        com_autoptr<DISKQUOTA_USER_BATCH> ptrBatch;

        hResult = m_pQuotaControl->CreateUserBatch(ptrBatch.getaddr());
        if (SUCCEEDED(hResult))
        {
            for (i = 0; i < cUsers; i++)
            {
                m_LVSelection.Retrieve(i, ptrUser.getaddr());
                if (bUndo)
                {
                    //
                    // Add an entry to the undo list.
                    //
                    LONGLONG LimitUndo;
                    LONGLONG ThresholdUndo;
                    ptrUser->GetQuotaThreshold(&ThresholdUndo);
                    ptrUser->GetQuotaLimit(&LimitUndo);
                    //
                    // Use a local autoptr to ensure proper release of
                    // iface in case adding to the undo list throws an exception.
                    // On success, disown the real ptr so that the object
                    // stays with the undo list.
                    //
                    com_autoptr<DISKQUOTA_USER> ptrQuotaUser(ptrUser);
                    ptrUser->AddRef();
                    m_UndoList.Add(new UndoModify(ptrUser, ThresholdUndo, LimitUndo));
                    ptrQuotaUser.disown();
                }

                ptrUser->SetQuotaThreshold(llThreshold, FALSE);

                if (UserIsAdministrator(ptrUser) && NOLIMIT != llLimit)
                {
                    //
                    // User is the Administrator account AND
                    // We're trying to set the limit to something other than NOLIMIT.
                    // Can't set a limit on the administrator account.
                    //
                    DiskQuotaMsgBox(GetDesktopWindow(),
                                    IDS_CANT_SET_ADMIN_LIMIT,
                                    IDS_TITLE_DISK_QUOTA,
                                    MB_ICONWARNING | MB_OK);
                }
                else
                {
                    //
                    // OK to set quota limit.
                    //
                    ptrUser->SetQuotaLimit(llLimit, FALSE);
                }

                ptrBatch->Add(ptrUser);
            }

            hResult = ptrBatch->FlushToDisk();
        }
    }
    else
    {
        //
        // Do single user update or add new user.
        //
        m_LVSelection.Retrieve(0, ptrUser.getaddr());
        DBGASSERT((NULL != ptrUser.get()));

        if (bUndo)
        {
            //
            // Add an entry to the undo list.
            //
            LONGLONG LimitUndo;
            LONGLONG ThresholdUndo;
            ptrUser->GetQuotaThreshold(&ThresholdUndo);
            ptrUser->GetQuotaLimit(&LimitUndo);
            //
            // Use local autoptr to ensure proper release of iface ptr if
            // an exception is thrown.  Disown real ptr on success.
            //
            com_autoptr<DISKQUOTA_USER> ptrQuotaUser(ptrUser);
            ptrUser->AddRef();
            m_UndoList.Add(new UndoModify(ptrUser, ThresholdUndo, LimitUndo));
            ptrQuotaUser.disown();
        }

        if (llThreshold != m_llQuotaThreshold)
        {
            hResult = ptrUser->SetQuotaThreshold(llThreshold, TRUE);

            if (FAILED(hResult))
                goto apply_failed;

            m_llQuotaThreshold = llThreshold;
        }

        if (llLimit != m_llQuotaLimit)
        {
            hResult = ptrUser->SetQuotaLimit(llLimit, TRUE);
            if (FAILED(hResult))
                goto apply_failed;

            m_llQuotaLimit = llLimit;
        }

        //
        // Update the user's status icon and %used to reflect the new settings.
        //
        UpdateUserStatusIcon(hDlg,
                             m_llQuotaUsed,
                             m_llQuotaThreshold,
                             m_llQuotaLimit);

        UpdateSpaceUsed(hDlg,
                        m_llQuotaUsed,
                        m_llQuotaLimit,
                        1);
    }

    //
    // Update the listview item(s) so the user sees a visual response to
    // pressing the "Apply" button.
    //
    autoredraw.Set(false);
    for (i = 0; i < cUsers; i++)
    {
        INT iItem = 0;
        m_LVSelection.Retrieve(i, &iItem);
        ListView_Update(m_hWndParent, iItem);
    }
    autoredraw.Set(true);
    InvalidateRect(m_hWndParent, NULL, FALSE);

apply_failed:

    return hResult;
}



///////////////////////////////////////////////////////////////////////////////
/*  Function: UserPropSheet::InitializeControls

    Description: Initializes the page controls based on the user's
        quota settings.

    Arguments:
        hDlg - Dialog window handle.

    Returns:
        NO_ERROR - Always returns NO_ERROR.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/15/96    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
HRESULT
UserPropSheet::InitializeControls(
    HWND hDlg
    )
{
    PDISKQUOTA_USER pUser = NULL;
    UINT cUsers           = m_LVSelection.Count();

    if (1 == cUsers)
    {
        //
        // Initialize controls for a single user.
        //
        m_LVSelection.Retrieve(0, &pUser);

        //
        // Configure the Limit/NoLimit radio buttons.
        // Must examine the current threshold rather than the limit because of the
        // special-case for the Administrator account.  That account can have a
        // threshold value but quota limit must always be "No Limit".
        //
        CheckDlgButton(hDlg, IDC_RBN_USER_LIMIT,   NOLIMIT != m_llQuotaThreshold);
        CheckDlgButton(hDlg, IDC_RBN_USER_NOLIMIT, NOLIMIT == m_llQuotaThreshold);
        if (UserIsAdministrator(pUser))
        {
            //
            // Override initialization of Quota Limit control with "No Limit".
            //
            m_pxbQuotaLimit->SetBytes(NOLIMIT);
        }

        //
        // Note that the XBytes controls have already been set for single-user.
        // See OnInitDialog().
        //

        //
        // Configure the remaining dialog controls.
        //
        UpdateUserName(hDlg, pUser);
        UpdateSpaceUsed(hDlg, m_llQuotaUsed, m_llQuotaLimit, cUsers);
        UpdateUserStatusIcon(hDlg,
                             m_llQuotaUsed,
                             m_llQuotaThreshold,
                             m_llQuotaLimit);
    }
    else
    {
        //
        // Initialize controls for multiple users.
        //
        LONGLONG llLimit         = 0;
        LONGLONG llLastLimit     = 0;
        LONGLONG llThreshold     = 0;
        LONGLONG llLastThreshold = 0;
        LONGLONG llUsed          = 0;
        LONGLONG llTotalUsed     = 0;

        //
        // Add up the total usage by all users.
        //
        for (UINT i = 0; i < cUsers; i++)
        {
            m_LVSelection.Retrieve(i, &pUser);
            pUser->GetQuotaLimit(&llLimit);
            pUser->GetQuotaThreshold(&llThreshold);
            pUser->GetQuotaUsed(&llUsed);

            llTotalUsed += llUsed;
            if (m_bHomogeneousSelection)
            {
                //
                // Determine if at least one user has a different
                // threshold or limit. If all are the same, we can display
                // the values in the edit controls.  Otherwise, we default
                // to "No Limit".  Radio buttons don't provide an
                // indeterminate state like checkboxes.
                //
                if (i > 0 &&
                    (llLimit != llLastLimit ||
                     llThreshold != llLastThreshold))
                {
                    m_bHomogeneousSelection = FALSE;
                }
                else
                {
                    llLastLimit     = llLimit;
                    llLastThreshold = llThreshold;
                }
            }
        }

        //
        // If all selected objects have the same limit and threshold,
        // set the cached data to represent multiple-selection.
        // If any one is different, we stick with the volume's default
        // values set in RefreshCachedQuotaInfo().
        //
        if (m_bHomogeneousSelection)
        {
            m_llQuotaThreshold = llLastThreshold;
            m_llQuotaLimit     = llLastLimit;
        }
        else
        {
            //
            // Since not all selected users have the same limit/thresold,
            // the number we're displaying will be a change for at least
            // one user.  Activate the "Apply" button.
            //
            //
            PostMessage(hDlg, DQM_ENABLE_APPLY_BUTTON, 0, 0);
        }

        m_pxbQuotaThreshold->SetBytes(m_llQuotaThreshold);
        m_pxbQuotaLimit->SetBytes(m_llQuotaLimit);

        //
        // Configure the Limit/NoLimit radio buttons.
        //
        CheckDlgButton(hDlg,
                       IDC_RBN_USER_NOLIMIT,
                       NOLIMIT == m_llQuotaThreshold);
        CheckDlgButton(hDlg,
                       IDC_RBN_USER_LIMIT,
                       NOLIMIT != m_llQuotaThreshold);

        UpdateUserName(hDlg, cUsers);
        UpdateSpaceUsed(hDlg, llTotalUsed, NOLIMIT, cUsers);
        //
        // Don't display any user status icon for multi-users.
        //
    }


    //
    // If "No Limit" radio button is checked, set limit and threshold controls
    // to the "No Limit" state (disabled and displaying "No Limit" text).
    // This may override any setting we made above.
    //
    if (BST_CHECKED == IsDlgButtonChecked(hDlg, IDC_RBN_USER_NOLIMIT))
    {
        m_pxbQuotaThreshold->SetBytes(NOLIMIT);
        m_pxbQuotaLimit->SetBytes(NOLIMIT);
    }

    //
    // Set user icon.
    //
    SendMessage(GetDlgItem(hDlg, IDC_ICON_USER),
                STM_SETICON,
                (WPARAM)m_hIconUser[1 == cUsers ? iICON_USER_SINGLE :
                                                  iICON_USER_MULTIPLE],
                0);

    //
    // Force the property sheet to disable the "Apply" button.
    // The way I have set up the "Apply" enabling logic through OnCommand(),
    // merely initializing the edit controls on the page causes the Apply
    // button to become enabled.  Since the user hasn't changed anything
    // yet, it should be disabled.
    //
    m_bIsDirty = FALSE;
    PropSheet_UnChanged(GetParent(hDlg), hDlg);

    return NO_ERROR;
}





///////////////////////////////////////////////////////////////////////////////
/*  Function: UserPropSheet::QueryUserStatusIcon


    Description: This function is provided for automated testing of the UI.
        It is used by test scripts to determine which user status icon is
        currently displayed.

    Arguments:
        hDlg - Dialog handle.


    Returns: -1 = No icon displayed.
              0 = "Everything OK" icon.
              1 = Threshold exceded icon.
              2 = Limit exceded icon.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/15/96    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
INT
UserPropSheet::QueryUserStatusIcon(
    HWND hDlg
    ) const
{
    HICON hicon = (HICON)SendMessage(GetDlgItem(hDlg, IDC_ICON_USERSTATUS),
                                    STM_GETICON,
                                    0, 0);

    for (UINT i = 0; i < cSTATUS_ICONS; i++)
    {
        if (hicon == m_hIconStatus[i])
            return i;
    }
    return -1;
}


///////////////////////////////////////////////////////////////////////////////
/*  Function: UserPropSheet::QueryUserIcon


    Description: This function is provided for automated testing of the UI.
        It is used by test scripts to determine which user status icon is
        currently displayed.

    Arguments:
        hDlg - Dialog handle.


    Returns: -1 = No icon displayed.
              0 = Single-user icon.
              1 = Multi-user icon.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/15/96    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
INT
UserPropSheet::QueryUserIcon(
    HWND hDlg
    ) const
{
    HICON hicon = (HICON)SendMessage(GetDlgItem(hDlg, IDC_ICON_USER),
                                     STM_GETICON,
                                     0, 0);

    for (UINT i = 0; i < cUSER_ICONS; i++)
    {
        if (hicon == m_hIconUser[i])
            return i;
    }
    return -1;
}



///////////////////////////////////////////////////////////////////////////////
/*  Function: UserPropSheet::UpdateUserStatusIcon

    Description: Updates the quota status icon in the dialog box.  This icon
        must match the icon displayed in the listview for the selected user.

    Arguments:
        hDlg - Dialog handle.

        iUsed - Quota bytes charged to user.

        iThreshold - Quota warning threshold (bytes).

        iLimit - User's quota limit.

    Returns: Nothing.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/15/96    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
VOID
UserPropSheet::UpdateUserStatusIcon(
    HWND hDlg,
    LONGLONG iUsed,
    LONGLONG iThreshold,
    LONGLONG iLimit
    )
{
    //
    // Set the user status icon if user is exceding the
    // quota threshold or the limit.  This is the same icon that is
    // displayed in the listview status column.  This logic must
    // mirror that used in DetailsView::GetDispInfo_Image().
    //
    INT iIcon = iICON_STATUS_OK;
    if (NOLIMIT != iLimit && iUsed > iLimit)
    {
        iIcon = iICON_STATUS_OVER_LIMIT;
    }
    else if (NOLIMIT != iThreshold && iUsed > iThreshold)
    {
        iIcon = iICON_STATUS_OVER_THRESHOLD;
    }

    SendMessage(GetDlgItem(hDlg, IDC_ICON_USERSTATUS),
                STM_SETICON,
                (WPARAM)m_hIconStatus[iIcon],
                0);
}



///////////////////////////////////////////////////////////////////////////////
/*  Function: UserPropSheet::UpdateUserName

    Description: Updates the Domain\Name text with the user's domain name
        and account name strings.  This method is called for a single-user
        selection.

        Also sets the property sheet title text.

    Arguments:
        hDlg - Dialog handle.

        pUser - Address of user's IDiskQuotaUser interface.

    Returns: Nothing.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/15/96    Initial creation.                                    BrianAu
    08/05/97    Added code to set prop sheet title text.             BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
VOID
UserPropSheet::UpdateUserName(
    HWND hDlg,
    PDISKQUOTA_USER pUser
    )
{
    DBGASSERT((NULL != pUser));
    //
    // Display the user name, or some status text
    // if the name hasn't been resolved.
    //
    CString strLogonName;
    DWORD dwAccountStatus = 0;

    pUser->GetAccountStatus(&dwAccountStatus);

    if (DISKQUOTA_USER_ACCOUNT_RESOLVED == dwAccountStatus)
    {
        //
        // User account name has been resolved.  Display it.
        //
        TCHAR szLogonName[MAX_USERNAME];
        TCHAR szDisplayName[MAX_FULL_USERNAME];

        pUser->GetName(NULL, 0,
                       szLogonName, ARRAYSIZE(szLogonName),
                       szDisplayName, ARRAYSIZE(szDisplayName));

        if (TEXT('\0') != szLogonName[0])
        {
            if (TEXT('\0') != szDisplayName[0])
            {
                strLogonName.Format(g_hInstDll,
                                    IDS_FMT_DISPLAY_LOGON,
                                    szDisplayName,
                                    szLogonName);
            }
            else
            {
                strLogonName = szLogonName;
            }
        }
    }
    else
    {
        //
        // User account name has not been resolved or cannot
        // be resolved for some reason.  Display appropriate
        // status text.  This is the same text displayed in the
        // listview when the user's name has not been resolved.
        //
        INT idText = IDS_USER_ACCOUNT_UNKNOWN;

        switch(dwAccountStatus)
        {
            case DISKQUOTA_USER_ACCOUNT_UNAVAILABLE:
                idText = IDS_USER_ACCOUNT_UNAVAILABLE;
                break;

            case DISKQUOTA_USER_ACCOUNT_DELETED:
                idText = IDS_USER_ACCOUNT_DELETED;
                break;

            case DISKQUOTA_USER_ACCOUNT_INVALID:
                idText = IDS_USER_ACCOUNT_INVALID;
                break;

            case DISKQUOTA_USER_ACCOUNT_UNRESOLVED:
                idText = IDS_USER_ACCOUNT_UNRESOLVED;
                break;

            case DISKQUOTA_USER_ACCOUNT_UNKNOWN:
            default:
                break;
        }
        strLogonName.Format(g_hInstDll, idText);
    }
    SetDlgItemText(hDlg, IDC_TXT_USERNAME, strLogonName);

    //
    // Format and draw the prop sheet title string.
    //
    CString strSheetTitle(g_hInstDll,
                          IDS_TITLE_EDIT_USER,
                          (LPCTSTR)strLogonName);

    PropSheet_SetTitle(GetParent(hDlg), 0, (LPCTSTR)strSheetTitle);
}



///////////////////////////////////////////////////////////////////////////////
/*  Function: UserPropSheet::UpdateUserName

    Description: Replaces the user Domain\Name text with a message showing
        how many users are selected.   This is used for multi-user selections
        where no single user name is applicable.

        Also sets the property sheet title text.

    Arguments:
        hDlg - Dialog handle.

        cUsers - Number of users represented in the property dialog.

    Returns: Nothing.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/15/96    Initial creation.                                    BrianAu
    08/05/97    Added code to set prop sheet title text.             BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
VOID
UserPropSheet::UpdateUserName(
    HWND hDlg,
    INT cUsers
    )
{
    //
    // Hide name edit control.  Can't display names for all users.
    // Display "Multiple Quota Users." instead.
    //
    CString strText(g_hInstDll, IDS_TITLE_MULTIUSER, cUsers);
    SetDlgItemText(hDlg, IDC_TXT_USERNAME, strText);

    //
    // Set the title of the property sheet.
    //
    CString strSheetTitle(g_hInstDll, IDS_TITLE_EDIT_MULTIUSER);
    PropSheet_SetTitle(GetParent(hDlg), 0, (LPCTSTR)strSheetTitle);
}



///////////////////////////////////////////////////////////////////////////////
/*  Function: UserPropSheet::UpdateSpaceUsed

    Description: Updates the "space used" and "remaining" fields on the user
        property sheet.

    Arguments:
        hDlg - Dialog handle.

        iUsed - Quota bytes charged to user(s).

        iLimit - User's quota limit.

        cUsers - Number of users represented in the property dialog.

    Returns: Nothing.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    08/15/96    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
VOID
UserPropSheet::UpdateSpaceUsed(
    HWND hDlg,
    LONGLONG iUsed,
    LONGLONG iLimit,
    INT cUsers
    )
{
    TCHAR szText[80];

    //
    // Display - Used: 999XB (99%)
    //
    XBytes::FormatByteCountForDisplay(iUsed,
                                      szText, ARRAYSIZE(szText));
    CString strText(szText);
    if (1 == cUsers)
    {
        //
        // Only single-user page gets (99%) appended.
        // Pct quota is meaningless for multiple users.
        //
        if (0 != iLimit && NOLIMIT != iLimit)
        {
            UINT iPct = (INT)((iUsed * 100) / iLimit);

            strText.Format(g_hInstDll,
                           IDS_QUOTA_USED_SINGLEUSER,
                           szText,
                           iPct);
        }
    }

    SetDlgItemText(hDlg,
                  IDC_TXT_SPACEUSED,
                  strText);


    //
    // Display - Remaining: 999XB
    //
    strText = szText;
    if (NOLIMIT != iLimit)
    {
        LONGLONG iAmount = 0;

        if (iUsed <= iLimit)
            iAmount = iLimit - iUsed;

        XBytes::FormatByteCountForDisplay(iAmount,
                                          strText.GetBuffer(80), 80);
        strText.ReleaseBuffer();
    }
    else
    {
        //
        // Display "N/A" if limit is NOLIMIT.
        //
        strText.Format(g_hInstDll, IDS_NOT_APPLICABLE);
    }

    SetDlgItemText(hDlg,
                   IDC_TXT_SPACEREMAINING,
                   strText);
}


