/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

    commonui.c

Abstract:

    This file contains all the functions related to preparing data for
    CPSUI.  This includes packing data items for printer property sheets and
    document property sheets.

Environment:

    Win32 subsystem, DriverUI module, user mode

Revision History:

    02/13/97 -davidx-
        Common function to handle well-known and generic printer features.

    02/10/97 -davidx-
        Consistent handling of common printer info.

    02/04/97 -davidx-
        Reorganize driver UI to separate ps and uni DLLs.

    09/12/96 -amandan-
        Created it.

--*/

#include "precomp.h"


PCOMPROPSHEETUI
PPrepareDataForCommonUI(
    IN OUT PUIDATA  pUiData,
    IN PDLGPAGE     pDlgPage
    )

/*++

Routine Description:

    Allocate memory and partially fill out the data structures required
    to call common UI routine. Once all information in pUiData are
    initialized properly, it calls PackDocumentPropertyItems() or
    PackPrinterPropertyItems() to pack the option items.

Arguments:

    pUiData - Pointer to our UIDATA structure
    pDlgPage - Pointer to dialog pages

Return Value:

    Pointer to a COMPROPSHEETUI structure, NULL if there is an error

--*/

{
    PCOMPROPSHEETUI pCompstui;
    DWORD           dwCount, dwIcon, dwOptItemCount, dwSize;
    PCOMMONINFO     pci = (PCOMMONINFO) pUiData;
    HANDLE          hHeap = pUiData->ci.hHeap;
    BOOL            (*pfnPackItemProc)(PUIDATA);
    POPTITEM        pOptItem;
    PBYTE           pUserData;

    //
    // Enumerate form names supported on the printer
    //

    dwCount = DwEnumPaperSizes(pci, NULL, NULL, NULL, NULL, UNUSED_PARAM);

    if (dwCount != GDI_ERROR && dwCount != 0)
    {
        pUiData->dwFormNames = dwCount;

        pUiData->pFormNames = HEAPALLOC(hHeap, dwCount * sizeof(WCHAR) * CCHPAPERNAME);
        pUiData->pwPapers = HEAPALLOC(hHeap, dwCount * sizeof(WORD));
        pUiData->pwPaperFeatures = HEAPALLOC(hHeap, dwCount * sizeof(WORD));
    }

    if (!pUiData->pFormNames || !pUiData->pwPapers || !pUiData->pwPaperFeatures)
        return NULL;

    (VOID) DwEnumPaperSizes(
                    pci,
                    pUiData->pFormNames,
                    pUiData->pwPapers,
                    NULL,
                    pUiData->pwPaperFeatures,
                    UNUSED_PARAM);

    #ifdef PSCRIPT

    //
    // We don't need to keep information about spooler forms
    // after this point. So dispose of it to free up memory.
    //

    MemFree(pUiData->ci.pSplForms);
    pUiData->ci.pSplForms = NULL;
    pUiData->ci.dwSplForms = 0;

    #endif

    //
    // Enumerate input bin names supported on the printer
    //

    dwCount = DwEnumBinNames(pci, NULL);

    if (dwCount != GDI_ERROR)
    {
        pUiData->dwBinNames = dwCount;
        pUiData->pBinNames = HEAPALLOC(hHeap, dwCount * sizeof(WCHAR) * CCHBINNAME);
    }

    if (! pUiData->pBinNames)
        return NULL;

    //
    // Don't need to check return here
    //

    DwEnumBinNames(pci, pUiData->pBinNames);

    //
    // Allocate memory to hold various data structures
    //

    if (! (pCompstui = HEAPALLOC(hHeap, sizeof(COMPROPSHEETUI))))
        return NULL;

    memset(pCompstui, 0, sizeof(COMPROPSHEETUI));

    //
    // Initialize COMPROPSHEETUI structure
    //

    pCompstui->cbSize = sizeof(COMPROPSHEETUI);
    pCompstui->UserData = (ULONG_PTR) pUiData;
    pCompstui->pDlgPage = pDlgPage;
    pCompstui->cDlgPage = 0;

    pCompstui->hInstCaller = ghInstance;
    pCompstui->pCallerName = _PwstrGetCallerName();
    pCompstui->pOptItemName = pUiData->ci.pDriverInfo3->pName;
    pCompstui->CallerVersion = gwDriverVersion;
    pCompstui->OptItemVersion = 0;

    dwIcon = pUiData->ci.pUIInfo->loPrinterIcon;

    if (dwIcon && (pCompstui->IconID = HLoadIconFromResourceDLL(&pUiData->ci, dwIcon)))
        pCompstui->Flags |= CPSUIF_ICONID_AS_HICON;
    else
        pCompstui->IconID = _DwGetPrinterIconID();

    if (HASPERMISSION(pUiData))
        pCompstui->Flags |= CPSUIF_UPDATE_PERMISSION;

    pCompstui->Flags |= CPSUIF_ABOUT_CALLBACK;


    pCompstui->pHelpFile = pUiData->ci.pDriverInfo3->pHelpFile;

    //
    // Call either PackDocumentPropertyItems or PackPrinterPropertyItems
    // to get the number of items and types.
    //

    pfnPackItemProc = (pUiData->iMode == MODE_DOCUMENT_STICKY) ?
                            BPackDocumentPropertyItems :
                            BPackPrinterPropertyItems;

    pUiData->dwOptItem = 0;
    pUiData->pOptItem = NULL;
    pUiData->dwOptType = 0;
    pUiData->pOptType = NULL;

    if (! pfnPackItemProc(pUiData))
    {
        ERR(("Error while packing OPTITEM's\n"));
        return NULL;
    }

    //
    // Allocate memory to hold OPTITEMs and OPTTYPEs
    //

    ASSERT(pUiData->dwOptItem > 0);
    VERBOSE(("Number of  OPTTYPE's: %d\n", pUiData->dwOptType));
    VERBOSE(("Number of OPTITEM's: %d\n", pUiData->dwOptItem));

    pUiData->pOptItem = HEAPALLOC(hHeap, sizeof(OPTITEM) * pUiData->dwOptItem);
    pUiData->pOptType = HEAPALLOC(hHeap, sizeof(OPTTYPE) * pUiData->dwOptType);
    pUserData = HEAPALLOC(hHeap, sizeof(USERDATA)* pUiData->dwOptItem);

    if (!pUiData->pOptItem || !pUiData->pOptType || !pUserData)
        return NULL;

    //
    // Initializes OPTITEM.USERDATA
    //

    pOptItem = pUiData->pOptItem;
    dwOptItemCount = pUiData->dwOptItem;
    dwSize = sizeof(USERDATA);

    while (dwOptItemCount--)
    {

        pOptItem->UserData = (ULONG_PTR)pUserData;

        SETUSERDATA_SIZE(pOptItem, dwSize);

        pUserData += sizeof(USERDATA);
        pOptItem++;

    }

    pUiData->pDrvOptItem = pUiData->pOptItem;
    pCompstui->pOptItem = pUiData->pDrvOptItem;
    pCompstui->cOptItem = (WORD) pUiData->dwOptItem;

    pUiData->dwOptItem = pUiData->dwOptType = 0;

    //
    // Call either PackDocumentPropertyItems or PackPrinterPropertyItems
    // to build the OPTITEMs list
    //

    if (! pfnPackItemProc(pUiData))
    {
        ERR(("Error while packing OPTITEM's\n"));
        return NULL;
    }

    return pCompstui;
}



VOID
VPackOptItemGroupHeader(
    IN OUT PUIDATA  pUiData,
    IN DWORD        dwTitleId,
    IN DWORD        dwIconId,
    IN DWORD        dwHelpIndex
    )

/*++

Routine Description:

    Fill out a OPTITEM to be used as a header for a group of items

Arguments:

    pUiData - Points to UIDATA structure
    dwTitleId - String resource ID for the item title
    dwIconId - Icon resource ID
    dwHelpIndex - Help index

Return Value:

    NONE

--*/

{
    if (pUiData->pOptItem)
    {
        pUiData->pOptItem->cbSize = sizeof(OPTITEM);
        pUiData->pOptItem->pOptType = NULL;
        pUiData->pOptItem->pName = (PWSTR)ULongToPtr(dwTitleId);
        pUiData->pOptItem->Level = TVITEM_LEVEL1;
        pUiData->pOptItem->DMPubID = DMPUB_NONE;
        pUiData->pOptItem->Sel = dwIconId;
        //pUiData->pOptItem->UserData = 0;
        pUiData->pOptItem->HelpIndex = dwHelpIndex;
        pUiData->pOptItem++;
    }

    pUiData->dwOptItem++;
}



BOOL
BPackOptItemTemplate(
    IN OUT PUIDATA  pUiData,
    IN CONST WORD   pwItemInfo[],
    IN DWORD        dwSelection,
    IN PFEATURE     pFeature
    )

/*++

Routine Description:

    Fill out an OPTITEM and an OPTTYPE structure using a template

Arguments:

    pUiData - Points to UIDATA structure
    pwItemInfo - Pointer to item template
    dwSelection - Current item selection
    pFeature - Pointer to FEATURE

Return Value:

    TRUE if successful, FALSE otherwise

Note:

    The item template is a variable size WORD array:
        0: String resource ID of the item title
        1: Item level in the tree view (TVITEM_LEVELx)
        2: Public devmode field ID (DMPUB_xxx)
        3: User data
        4: Help index
        5: Number of OPTPARAMs for this item
        6: Item type (TVOT_xxx)
        Three words for each OPTPARAM:
            Size of OPTPARAM
            String resource ID for parameter data
            Icon resource ID
        Last word must be ITEM_INFO_SIGNATURE

    Both OPTITEM and OPTTYPE structures are assumed to be zero-initialized.

--*/

{
    POPTITEM pOptItem;
    POPTPARAM pOptParam;
    WORD wOptParam;
    POPTTYPE pOptType = pUiData->pOptType;


    if ((pOptItem = pUiData->pOptItem) != NULL)
    {
        FILLOPTITEM(pOptItem,
                    pUiData->pOptType,
                    ULongToPtr(pwItemInfo[0]),
                    ULongToPtr(dwSelection),
                    (BYTE) pwItemInfo[1],
                    (BYTE) pwItemInfo[2],
                    pwItemInfo[3],
                    pwItemInfo[4]
                    );

        wOptParam = pwItemInfo[5];
        pOptParam = PFillOutOptType(pUiData->pOptType,
                                    pwItemInfo[6],
                                    wOptParam,
                                    pUiData->ci.hHeap);

        if (pOptParam == NULL)
            return FALSE;

        pwItemInfo += 7;
        while (wOptParam--)
        {
            pOptParam->cbSize = sizeof(OPTPARAM);
            pOptParam->pData = (PWSTR) *pwItemInfo++;
            pOptParam->IconID = *pwItemInfo++;
            pOptParam++;
        }

        ASSERT(*pwItemInfo == ITEM_INFO_SIGNATURE);

        if (pFeature)
        {
            SETUSERDATA_KEYWORDNAME(pUiData->ci, pOptItem, pFeature);

            #ifdef UNIDRV

            if (pUiData->ci.pUIInfo->loHelpFileName &&
                pFeature->iHelpIndex != UNUSED_ITEM )
            {
                //
                // Allocate memory for OIEXT
                //

                POIEXT  pOIExt = HEAPALLOC(pUiData->ci.hHeap, sizeof(OIEXT));

                if (pOIExt)
                {
                    pOIExt->cbSize = sizeof(OIEXT);
                    pOIExt->Flags = 0;
                    pOIExt->hInstCaller = NULL;
                    pOIExt->pHelpFile = OFFSET_TO_POINTER(pUiData->ci.pUIInfo->pubResourceData,
                                                          pUiData->ci.pUIInfo->loHelpFileName);
                    pOptItem->pOIExt = pOIExt;
                    pOptItem->HelpIndex = pFeature->iHelpIndex;
                    pOptItem->Flags |= OPTIF_HAS_POIEXT;
                }

            }
            #endif // UNIDRV
        }
        pUiData->pOptItem++;
        pUiData->pOptType++;
    }

    pUiData->dwOptItem++;
    pUiData->dwOptType++;

    return TRUE;
}



BOOL
BPackUDArrowItemTemplate(
    IN OUT PUIDATA  pUiData,
    IN CONST WORD   pwItemInfo[],
    IN DWORD        dwSelection,
    IN DWORD        dwMaxVal,
    IN PFEATURE     pFeature
    )

/*++

Routine Description:

    Pack an updown arrow item using the specified template

Arguments:

    pUiData, pwItemInfo, dwSelection - same as for BPackOptItemTemplate
    dwMaxVal - maximum value for the updown arrow item

Return Value:

    TRUE if successful, FALSE if there is an error

--*/

{
    POPTTYPE pOptType = pUiData->pOptType;

    if (! BPackOptItemTemplate(pUiData, pwItemInfo, dwSelection, pFeature))
        return FALSE;

    if (pOptType)
        pOptType->pOptParam[1].lParam = dwMaxVal;

    return TRUE;
}



POPTPARAM
PFillOutOptType(
    OUT POPTTYPE    pOptType,
    IN  DWORD       dwType,
    IN  DWORD       dwParams,
    IN  HANDLE      hHeap
    )

/*++

Routine Description:

    Fill out an OPTTYPE structure

Arguments:

    pOpttype - Pointer to OPTTYPE structure to be filled out
    wType - Value for OPTTYPE.Type field
    wParams - Number of OPTPARAM's
    hHeap - Handle to a heap from which to allocate

Return Value:

    Pointer to OPTPARAM array if successful, NULL otherwise

--*/

{
    POPTPARAM pOptParam;

    pOptType->cbSize = sizeof(OPTTYPE);
    pOptType->Count = (WORD) dwParams;
    pOptType->Type = (BYTE) dwType;

    pOptParam = HEAPALLOC(hHeap, sizeof(OPTPARAM) * dwParams);

    if (pOptParam != NULL)
        pOptType->pOptParam = pOptParam;
    else
        ERR(("Memory allocation failed\n"));

    return pOptParam;
}


BOOL
BShouldDisplayGenericFeature(
    IN PFEATURE     pFeature,
    IN BOOL         bPrinterSticky
    )

/*++

Routine Description:

    Determine whether a printer feature should be displayed
    as a generic feature

Arguments:

    pFeature - Points to a FEATURE structure
    pPrinterSticky - Whether the feature is printer-sticky or doc-sticky

Return Value:

    TRUE if the feature should be displayed as a generic feature
    FALSE if it should not be

--*/

{
    //
    // Check if the feature is specified marked as non-displayable
    // and make sure the feature type is appropriate
    //

    if ((pFeature->dwFlags & FEATURE_FLAG_NOUI) ||
        (bPrinterSticky &&
         pFeature->dwFeatureType != FEATURETYPE_PRINTERPROPERTY) ||
        (!bPrinterSticky &&
         pFeature->dwFeatureType != FEATURETYPE_DOCPROPERTY &&
         pFeature->dwFeatureType != FEATURETYPE_JOBPROPERTY))
    {
        return FALSE;
    }

    //
    // Exclude those features which are explicitly handled
    // and also those which don't have any options
    //

    return (pFeature->Options.dwCount >= MIN_OPTIONS_ALLOWED) &&
           (pFeature->dwFeatureID == GID_UNKNOWN ||
            pFeature->dwFeatureID == GID_OUTPUTBIN ||
            pFeature->dwFeatureID == GID_MEMOPTION);
}



DWORD
DwCountDisplayableGenericFeature(
    IN PUIDATA      pUiData,
    BOOL            bPrinterSticky
    )

/*++

Routine Description:

    Count the number of features which can be displayed
    as generic features

Arguments:

    pUiData - Points to UIDATA structure
    pPrinterSticky - Whether the feature is printer-sticky or doc-sticky

Return Value:

    Number of features which can be displayed as generic features

--*/

{
    PFEATURE pFeature;
    DWORD    dwFeature, dwCount = 0;

    pFeature = PGetIndexedFeature(pUiData->ci.pUIInfo, 0);
    dwFeature = pUiData->ci.pRawData->dwDocumentFeatures +
                pUiData->ci.pRawData->dwPrinterFeatures;

    if (pFeature && dwFeature)
    {
        for ( ; dwFeature--; pFeature++)
        {
            if (BShouldDisplayGenericFeature(pFeature, bPrinterSticky))
                dwCount++;
        }
    }

    return dwCount;
}



DWORD
DwGuessOptionIconID(
    PUIINFO     pUIInfo,
    PFEATURE    pFeature,
    POPTION     pOption
    )

/*++

Routine Description:

    Try to make an intelligent guess as to what icon
    to use for a generic printer feature option

Arguments:

    pUIInfo - Points to UIINFO structure
    pFeature - Points to the feature in question
    pOption - Points to the option in question

Return Value:

    Icon resource ID appropriate for the feature option

--*/

{
    DWORD   dwIconID, iRes;

    switch (pFeature->dwFeatureID)
    {
    case GID_RESOLUTION:

        iRes = max(((PRESOLUTION) pOption)->iXdpi, ((PRESOLUTION) pOption)->iYdpi);

        if (iRes <= 150)
            dwIconID = IDI_CPSUI_RES_DRAFT;
        else if (iRes <= 300)
            dwIconID = IDI_CPSUI_RES_LOW;
        else if (iRes <= 600)
            dwIconID = IDI_CPSUI_RES_MEDIUM;
        else if (iRes <= 900)
            dwIconID = IDI_CPSUI_RES_HIGH;
        else
            dwIconID = IDI_CPSUI_RES_PRESENTATION;

        break;

    case GID_DUPLEX:

        switch (((PDUPLEX) pOption)->dwDuplexID)
        {
        case DMDUP_VERTICAL:
            dwIconID = IDI_CPSUI_DUPLEX_VERT;
            break;

        case DMDUP_HORIZONTAL:
            dwIconID = IDI_CPSUI_DUPLEX_HORZ;
            break;

        default:
            dwIconID = IDI_CPSUI_DUPLEX_NONE;
            break;
        }
        break;

    case GID_ORIENTATION:

        switch (((PORIENTATION) pOption)->dwRotationAngle)
        {
        case ROTATE_270:
            dwIconID = IDI_CPSUI_LANDSCAPE;
            break;

        case ROTATE_90:
            dwIconID = IDI_CPSUI_ROT_LAND;
            break;

        default:
            dwIconID = IDI_CPSUI_PORTRAIT;
            break;
        }
        break;

    case GID_INPUTSLOT:
        dwIconID = IDI_CPSUI_PAPER_TRAY;
        break;

    case GID_PAGEPROTECTION:
        dwIconID = IDI_CPSUI_PAGE_PROTECT;
        break;

    default:
        dwIconID = IDI_CPSUI_GENERIC_OPTION;
        break;
    }

    return dwIconID;
}



BOOL
BPackItemPrinterFeature(
    PUIDATA     pUiData,
    PFEATURE    pFeature,
    DWORD       dwLevel,
    DWORD       dwPub,
    ULONG_PTR    dwUserData,
    DWORD       dwHelpIndex
    )

/*++

Routine Description:

    Pack a single printer feature item

Arguments:

    pUiData - Points to UIDATA structure
    pFeature - Points to the printer feature to be packed
    dwLevel - Treeview item level
    dwPub - DMPUB_ identifier
    dwUserData - User data to be associated with the item
    dwHelpIndex - Help index to be associated with the item

Return Value:

    TRUE if successful, FALSE if there is an error

--*/

{
    DWORD       dwCount, dwIndex;
    DWORD       dwFeature, dwSel;
    POPTION     pOption;
    POPTTYPE    pOptTypeHack;
    POPTPARAM   pOptParam;
    PCOMMONINFO pci;

    if (pFeature == NULL ||
        (pFeature->dwFlags & FEATURE_FLAG_NOUI) ||
        (dwCount = pFeature->Options.dwCount) < MIN_OPTIONS_ALLOWED)
        return TRUE;

    //
    // HACK: for Orientation and Duplex feature
    //  They must be of type TVOT_2STATES or TVOT_3STATES.
    //  If not, compstui will get confused.
    //

    if (dwPub == DMPUB_ORIENTATION || dwPub == DMPUB_DUPLEX)
    {
        if (dwCount != 2 && dwCount != 3)
        {
            WARNING(("Unexpected number of Orientation/Duplex options\n"));
            return TRUE;
        }

        pOptTypeHack = pUiData->pOptType;
    }
    else
        pOptTypeHack = NULL;

    pUiData->dwOptItem++;
    pUiData->dwOptType++;

    if (pUiData->pOptItem == NULL)
        return TRUE;

    //
    // Find out the current selection first
    // DCR: needs to support PICKMANY
    //

    pci = (PCOMMONINFO) pUiData;
    dwFeature = GET_INDEX_FROM_FEATURE(pci->pUIInfo, pFeature);
    dwSel = pci->pCombinedOptions[dwFeature].ubCurOptIndex;

    if (dwSel >= dwCount)
        dwSel = 0;

    //
    // If we are in this function, we must have already successfully
    // called function PFillUiData(), where the pci->hHeap is created.
    //

    ASSERT(pci->hHeap != NULL);

    //
    // Fill in the OPTITEM structure
    //

    FILLOPTITEM(pUiData->pOptItem,
                pUiData->pOptType,
                PGetReadOnlyDisplayName(pci, pFeature->loDisplayName),
                ULongToPtr(dwSel),
                dwLevel,
                dwPub,
                dwUserData,
                dwHelpIndex);

    #ifdef UNIDRV
    //
    // Supports OEM help file. If  helpfile and helpindex are defined,
    // we will use the help id specified by the GPD.  According to GPD spec,
    // zero loHelpFileName means no help file name specified.
    //

    if (pci->pUIInfo->loHelpFileName &&
        pFeature->iHelpIndex != UNUSED_ITEM )
    {
        //
        // Allocate memory for OIEXT
        //

        POIEXT  pOIExt = HEAPALLOC(pci->hHeap, sizeof(OIEXT));

        if (pOIExt)
        {
            pOIExt->cbSize = sizeof(OIEXT);
            pOIExt->Flags = 0;
            pOIExt->hInstCaller = NULL;
            pOIExt->pHelpFile = OFFSET_TO_POINTER(pci->pUIInfo->pubResourceData,
                                                  pci->pUIInfo->loHelpFileName);
            pUiData->pOptItem->pOIExt = pOIExt;
            pUiData->pOptItem->HelpIndex = pFeature->iHelpIndex;
            pUiData->pOptItem->Flags |= OPTIF_HAS_POIEXT;
        }

    }
    #endif // UNIDRV

    pOptParam = PFillOutOptType(pUiData->pOptType, TVOT_LISTBOX, dwCount, pci->hHeap);

    if (pOptParam == NULL)
        return FALSE;

    if (pOptTypeHack)
        pOptTypeHack->Type = (dwCount == 2) ? TVOT_2STATES : TVOT_3STATES;

    //
    // Get the list of options for this features
    //

    for (dwIndex=0; dwIndex < dwCount; dwIndex++, pOptParam++)
    {
        //
        // Fill in the options name
        //

        pOption = PGetIndexedOption(pci->pUIInfo, pFeature, dwIndex);
        ASSERT(pOption != NULL);

        pOptParam->cbSize = sizeof(OPTPARAM);
        pOptParam->pData = GET_OPTION_DISPLAY_NAME(pci, pOption);

        //
        // Try to figure out the appropriate icon to use
        //  If the icon comes from the resource DLL, we need to load
        //  it ourselves and give compstui an HICON. Otherwise,
        //  we try to figure out the appropriate icon resource ID.
        //

        if (pOption->loResourceIcon &&
            (pOptParam->IconID = HLoadIconFromResourceDLL(pci, pOption->loResourceIcon)))
        {
            pOptParam->Flags |= OPTPF_ICONID_AS_HICON;
        }
        else
            pOptParam->IconID = DwGuessOptionIconID(pci->pUIInfo, pFeature, pOption);
    }

    //
    // Set the Keyword name for pOptItem->UserData
    //

    SETUSERDATA_KEYWORDNAME(pUiData->ci, pUiData->pOptItem, pFeature);

    pUiData->pOptItem++;
    pUiData->pOptType++;
    return TRUE;
}



BOOL
BPackItemGenericOptions(
    IN OUT PUIDATA  pUiData
    )

/*++

Routine Description:

    Pack generic printer features items (doc-sticky or printer-sticky)

Arguments:

    pUiData - Points to UIDATA structure

Return Value:

    TRUE if successful, FALSE if there is an error.

--*/

{
    //
    // Extended push button for restoring to default feature selections
    //

    static EXTPUSH  ExtPush =
    {
        sizeof(EXTPUSH),
        EPF_NO_DOT_DOT_DOT,
        (PWSTR) IDS_RESTORE_DEFAULTS,
        NULL,
        0,
        0,
    };

    POPTITEM    pOptItem;
    DWORD       dwHelpIndex, dwIconId;
    PFEATURE    pFeatures;
    DWORD       dwFeatures;
    BOOL        bPrinterSticky;

    //
    // If there are no generic features to display, simply return success
    //

    bPrinterSticky = (pUiData->iMode == MODE_PRINTER_STICKY);

    if (DwCountDisplayableGenericFeature(pUiData, bPrinterSticky) == 0)
        return TRUE;

    //
    // Add the group header item
    //

    pOptItem = pUiData->pOptItem;

    if (bPrinterSticky)
    {
        VPackOptItemGroupHeader(
                pUiData,
                IDS_INSTALLABLE_OPTIONS,
                IDI_CPSUI_INSTALLABLE_OPTION,
                HELP_INDEX_INSTALLABLE_OPTIONS);
    }
    else
    {
        VPackOptItemGroupHeader(
                pUiData,
                IDS_PRINTER_FEATURES,
                IDI_CPSUI_PRINTER_FEATURE,
                HELP_INDEX_PRINTER_FEATURES);
    }

    if (pOptItem != NULL && !bPrinterSticky)
    {
        //
        // "Restore Defaults" button
        //

        pUiData->pFeatureHdrItem = pOptItem;
        pOptItem->Flags |= (OPTIF_EXT_IS_EXTPUSH|OPTIF_CALLBACK);
        pOptItem->pExtPush = &ExtPush;
    }

    pOptItem = pUiData->pOptItem;

    //
    // Figure out the correct help index and icon ID
    // depending on whether we're dealing with printer-sticky
    // features or document-sticky printer features
    //

    if (bPrinterSticky)
    {
        dwHelpIndex = HELP_INDEX_INSTALLABLE_OPTIONS;
        dwIconId = IDI_CPSUI_INSTALLABLE_OPTION;
    }
    else
    {
        dwHelpIndex = HELP_INDEX_PRINTER_FEATURES;
        dwIconId = IDI_CPSUI_PRINTER_FEATURE;
    }

    //
    // Go through each printer feature
    //

    pFeatures = PGetIndexedFeature(pUiData->ci.pUIInfo, 0);
    dwFeatures = pUiData->ci.pRawData->dwDocumentFeatures +
                 pUiData->ci.pRawData->dwPrinterFeatures;

    ASSERT(pFeatures != NULL);

    for ( ; dwFeatures--; pFeatures++)
    {
        //
        // Don't do anything if it's the feature has no options OR
        // If it's not a generic feature.
        //

        if (BShouldDisplayGenericFeature(pFeatures, bPrinterSticky) &&
            !BPackItemPrinterFeature(pUiData,
                                     pFeatures,
                                     TVITEM_LEVEL2,
                                     DMPUB_NONE,
                                     (ULONG_PTR) pFeatures,
                                     dwHelpIndex))
        {
            return FALSE;
        }
    }

    if (pOptItem != NULL)
    {
        pUiData->pFeatureItems = pOptItem;
        pUiData->dwFeatureItem = (DWORD)(pUiData->pOptItem - pOptItem);
    }

    return TRUE;
}



PFEATURE
PGetFeatureFromItem(
    IN      PUIINFO  pUIInfo,
    IN OUT  POPTITEM pOptItem,
    OUT     PDWORD   pdwFeatureIndex
    )

/*++

Routine Description:

    Get the feature index for a given pOptItem

Arguments:

    pUIInfo - pointer to UIINFO
    pOptItem - pointer to item to look for feature id
    pdwFeatureIndex - pointer to contain the value of returned index

Return Value:

    Pointer to FEATURE structure associated with the item
    NULL if no such feature exists.

--*/

{
    PFEATURE pFeature = NULL;

    //
    // Get the dwFeature, which is the index into pOptionsArray
    //

    if (ISPRINTERFEATUREITEM(pOptItem->UserData))
    {
        //
        // Note: Generic Features contains pointer to feature (pFeature)
        // in pOptItem->UserData
        //

        pFeature = (PFEATURE) GETUSERDATAITEM(pOptItem->UserData);
    }
    else
    {
        DWORD   dwFeatureId;

        switch (GETUSERDATAITEM(pOptItem->UserData))
        {
        case FORMNAME_ITEM:
            dwFeatureId = GID_PAGESIZE;
            break;

        case DUPLEX_ITEM:
            dwFeatureId = GID_DUPLEX;
            break;

        case RESOLUTION_ITEM:
            dwFeatureId = GID_RESOLUTION;
            break;

        case MEDIATYPE_ITEM:
            dwFeatureId = GID_MEDIATYPE;
            break;

        case INPUTSLOT_ITEM:
            dwFeatureId = GID_INPUTSLOT;
            break;

        case FORM_TRAY_ITEM:
            dwFeatureId = GID_INPUTSLOT;
            break;

        case COLORMODE_ITEM:
            dwFeatureId = GID_COLORMODE;
            break;

        case ORIENTATION_ITEM:
            dwFeatureId = GID_ORIENTATION;
            break;

        case PAGE_PROTECT_ITEM:
            dwFeatureId = GID_PAGEPROTECTION;
            break;

        case COPIES_COLLATE_ITEM:
            dwFeatureId = GID_COLLATE;
            break;

        case HALFTONING_ITEM:
            dwFeatureId = GID_HALFTONING;
            break;

        default:
            dwFeatureId = GID_UNKNOWN;
            break;
        }

        if (dwFeatureId != GID_UNKNOWN)
            pFeature = GET_PREDEFINED_FEATURE(pUIInfo, dwFeatureId);
    }

    if (pFeature && pdwFeatureIndex)
        *pdwFeatureIndex = GET_INDEX_FROM_FEATURE(pUIInfo, pFeature);

    return pFeature;
}



VOID
VUpdateOptionsArrayWithSelection(
    IN OUT PUIDATA  pUiData,
    IN POPTITEM     pOptItem
    )

/*++

Routine Description:

    Update the options array with the current selection

Arguments:

    pUiData - Points to UIDATA structure
    pOptItem - Specifies the item whose selection has changed

Return Value:

    NONE

--*/

{
    PFEATURE pFeature;
    DWORD    dwFeatureIndex;

    //
    // Get the feature associated with the current item
    //

    pFeature = PGetFeatureFromItem(pUiData->ci.pUIInfo, pOptItem, &dwFeatureIndex);
    if (pFeature == NULL)
        return;

    if (pOptItem->Sel < 0 || pOptItem->Sel >= (LONG) pFeature->Options.dwCount)
    {
        RIP(("Invalid selection for the current item\n"));
        return;
    }

    ZeroMemory(pUiData->abEnabledOptions, sizeof(pUiData->abEnabledOptions));
    pUiData->abEnabledOptions[pOptItem->Sel] = TRUE;

    ReconstructOptionArray(pUiData->ci.pRawData,
                           pUiData->ci.pCombinedOptions,
                           MAX_COMBINED_OPTIONS,
                           dwFeatureIndex,
                           pUiData->abEnabledOptions);
}



VOID
VMarkSelectionConstrained(
    IN OUT POPTITEM pOptItem,
    IN DWORD        dwIndex,
    IN BOOL         bEnable
    )

/*++

Routine Description:

    Indicate whether a selection is constrained or not

Arguments:

    pOptItem - Pointer to the OPTITEM in question
    pOptParam - Specifies the index of the OPTPARAM in question
    bEnable - Whether the selection is constrained or not
              Enable means not constrained!

Return Value:

    NONE

Note:

    bEnable is the returned value from EnumEnabledOptions,

    bEnable is FALSE if the option is contrained by some
    other feature, selections.

    bEnable is TRUE if the options is not constrained
    by other feature, selections.

--*/

{
    POPTPARAM pOptParam;

    //
    // This function only work on certain types of OPTTYPE
    //

    ASSERT(pOptItem->pOptType->Type == TVOT_2STATES ||
           pOptItem->pOptType->Type == TVOT_3STATES ||
           pOptItem->pOptType->Type == TVOT_LISTBOX ||
           pOptItem->pOptType->Type == TVOT_COMBOBOX);

    pOptParam = pOptItem->pOptType->pOptParam + dwIndex;

    //
    // Set the constrained flag or clear it depending on the latest
    // check with EnumEnabledOptions
    //

    if (!bEnable && ! (pOptParam->Flags & CONSTRAINED_FLAG))
    {
        pOptParam->Flags |= CONSTRAINED_FLAG;
        pOptItem->Flags |= OPTIF_CHANGED;
    }
    else if (bEnable && (pOptParam->Flags & CONSTRAINED_FLAG))
    {
        pOptParam->Flags &= ~CONSTRAINED_FLAG;
        pOptItem->Flags |= OPTIF_CHANGED;
    }

    pOptParam->lParam = (LONG) bEnable;
}



VOID
VPropShowConstraints(
    IN PUIDATA  pUiData,
    IN INT      iMode
    )

/*++

Routine Description:

    Indicate which items are constrained.
    General rule - Any features that has a coresponding an applicable GID or a
    Generic Feature Item, check for constrained.  Ignore all others
    because it's not applicable.

Arguments:

    pUiData - Pointer to our UIDATA structure
    iMode   - MODE_DOCANDPRINTER_STICKY, MODE_PRINTER_STICKY
Return Value:

    NONE
--*/

{
    POPTITEM    pOptItem;
    DWORD       dwOptItem;
    DWORD       dwFeature, dwOption, dwNumOptions, dwIndex;

    #ifdef PSCRIPT

    if (iMode != MODE_PRINTER_STICKY)
    {
        VSyncRevPrintAndOutputOrder(pUiData, NULL);
    }

    #endif // PSCRIPT

    //
    // Go through all the features in the treeview
    //

    pOptItem = pUiData->pDrvOptItem;
    dwOptItem = pUiData->dwDrvOptItem;

    for ( ; dwOptItem--; pOptItem++)
    {

        if (! ISCONSTRAINABLEITEM(pOptItem->UserData) ||
          ! PGetFeatureFromItem(pUiData->ci.pUIInfo, pOptItem, &dwFeature))
        {
            continue;
        }

        //
        // Call the parser to get which options to be disable, or contrained
        // for this feature , so need to gray it out.
        //

        ZeroMemory(pUiData->abEnabledOptions, sizeof(pUiData->abEnabledOptions));

        if (! EnumEnabledOptions(pUiData->ci.pRawData,
                                 pUiData->ci.pCombinedOptions,
                                 dwFeature,
                                 pUiData->abEnabledOptions,
                                 iMode))
        {
            VERBOSE(("EnumEnabledOptions failed\n"));
        }

        //
        // Loop through all options and mark the constraint
        //

        dwNumOptions = pOptItem->pOptType->Count;

        if (GETUSERDATAITEM(pOptItem->UserData) == FORMNAME_ITEM)
        {
            for (dwIndex = 0; dwIndex < dwNumOptions; dwIndex++)
            {
                dwOption = pUiData->pwPaperFeatures[dwIndex];

                if (dwOption == OPTION_INDEX_ANY)
                    continue;

                VMarkSelectionConstrained(pOptItem,
                                          dwIndex,
                                          pUiData->abEnabledOptions[dwOption]);
            }
        }
        else if (GETUSERDATAITEM(pOptItem->UserData) == FORM_TRAY_ITEM)
        {
            if (pOptItem == pUiData->pFormTrayItems)
            {
                POPTITEM pTrayItem;
                PBOOL    pbEnable;

                //
                // Update form-to-tray assignment table items
                //

                pbEnable = pUiData->abEnabledOptions;
                pTrayItem = pUiData->pFormTrayItems;
                dwIndex = pUiData->dwFormTrayItem;

                for ( ; dwIndex--; pTrayItem++, pbEnable++)
                {
                    if (pTrayItem->Flags & OPTIF_HIDE)
                        continue;

                    if (*pbEnable && (pTrayItem->Flags & OPTIF_DISABLED))
                    {
                        pTrayItem->Flags &= ~OPTIF_DISABLED;
                        pTrayItem->Flags |= OPTIF_CHANGED;
                    }
                    else if (!*pbEnable && !(pTrayItem->Flags & OPTIF_DISABLED))
                    {
                        pTrayItem->Flags |= (OPTIF_DISABLED|OPTIF_CHANGED);
                        pTrayItem->Sel = -1;
                    }
                }
            }
        }
        else
        {
            for (dwOption=0; dwOption < dwNumOptions; dwOption++)
            {
                VMarkSelectionConstrained(pOptItem,
                                          dwOption,
                                          pUiData->abEnabledOptions[dwOption]);
            }
        }
    }
}



INT_PTR CALLBACK
BConflictsDlgProc(
    HWND    hDlg,
    UINT    uMsg,
    WPARAM  wParam,
    LPARAM  lParam
    )

/*++

Routine Description:

    Dialog procedure for handle "Conflicts" dialog

Arguments:

    hDlg - Handle to dialog window
    uMsg - Message
    wParam, lParam - Parameters

Return Value:

    TRUE or FALSE depending on whether message is processed

--*/

{
    PDLGPARAM       pDlgParam;
    POPTITEM        pOptItem;
    PFEATURE        pFeature;
    POPTION         pOption;
    DWORD           dwFeature, dwOption;
    PCWSTR          pDisplayName;
    WCHAR           awchBuf[MAX_DISPLAY_NAME];
    PCOMMONINFO     pci;
    CONFLICTPAIR    ConflictPair;


    switch (uMsg)
    {
    case WM_INITDIALOG:

        pDlgParam = (PDLGPARAM) lParam;
        ASSERT(pDlgParam != NULL);

        SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR)pDlgParam);

        pci = (PCOMMONINFO) pDlgParam->pUiData;
        pOptItem = pDlgParam->pOptItem;

        if (GETUSERDATAITEM(pOptItem->UserData) == FORMNAME_ITEM)
            dwOption = pDlgParam->pUiData->pwPaperFeatures[pOptItem->Sel];
        else
            dwOption = pOptItem->Sel;

        //
        // Get the feature id for the current feature and selection
        //

        if (! PGetFeatureFromItem(pci->pUIInfo, pOptItem, &dwFeature))
            return FALSE;

        //
        // Get the first conflicting feature, option for the current pair
        //

        if (! EnumNewPickOneUIConflict(pci->pRawData,
                                       pci->pCombinedOptions,
                                       dwFeature,
                                       dwOption,
                                       &ConflictPair))
        {
            ERR(("No conflict found?\n"));
            return FALSE;
        }

        pFeature = PGetIndexedFeature(pci->pUIInfo, ConflictPair.dwFeatureIndex1);
        pOption = PGetIndexedOption(pci->pUIInfo, pFeature, ConflictPair.dwOptionIndex1);

        //
        // Display the current feature selection
        // Get feature name first
        //

        if (pDisplayName = PGetReadOnlyDisplayName(pci, pFeature->loDisplayName))
        {
            wcscpy(awchBuf, pDisplayName);
            wcscat(awchBuf, TEXT(" : "));
        }

        //
        // Kludgy fix for form - We show the form enumerated by
        // form database and map these logical forms to our supported physical
        // papersize.  So we have to notify the user with the logical form
        // selected and not the mapped physical form returned by EnumConflict
        //

        if (pFeature->dwFeatureID == GID_PAGESIZE)
            pDisplayName = pci->pdm->dmFormName;
        else if (pOption)
            pDisplayName = GET_OPTION_DISPLAY_NAME(pci, pOption);
        else
            pDisplayName = NULL;

        if (pDisplayName)
            wcscat(awchBuf, pDisplayName);

        SetDlgItemText(hDlg, IDC_FEATURE1, awchBuf);

        pFeature = PGetIndexedFeature(pci->pUIInfo, ConflictPair.dwFeatureIndex2);
        pOption = PGetIndexedOption(pci->pUIInfo, pFeature, ConflictPair.dwOptionIndex2);

        //
        // Display the current feature selection
        // Get feature name first
        //

        if (pDisplayName = PGetReadOnlyDisplayName(pci, pFeature->loDisplayName))
        {
            wcscpy(awchBuf, pDisplayName);
            wcscat(awchBuf, TEXT(" : "));
        }


        if (pFeature->dwFeatureID == GID_PAGESIZE)
            pDisplayName = pci->pdm->dmFormName;
        else if (pOption)
            pDisplayName = GET_OPTION_DISPLAY_NAME(pci, pOption);
        else
            pDisplayName = NULL;

        if (pDisplayName)
            wcscat(awchBuf, pDisplayName);

        SetDlgItemText(hDlg, IDC_FEATURE2, awchBuf);


        if (pDlgParam->bFinal)
        {
            //
            // If user is trying to exit the dialog
            //

            ShowWindow(GetDlgItem(hDlg, IDC_IGNORE), SW_HIDE);
            ShowWindow(GetDlgItem(hDlg, IDC_CANCEL), SW_HIDE);
            CheckRadioButton(hDlg, IDC_RESOLVE, IDC_CANCEL_FINAL, IDC_RESOLVE);
            pDlgParam->dwResult = CONFLICT_RESOLVE;

        }
        else
        {
            //
            // Hide the Resolve button
            //

            ShowWindow(GetDlgItem(hDlg, IDC_RESOLVE), SW_HIDE);
            ShowWindow(GetDlgItem(hDlg, IDC_CANCEL_FINAL), SW_HIDE);
            CheckRadioButton(hDlg, IDC_IGNORE, IDC_CANCEL, IDC_IGNORE);
            pDlgParam->dwResult = CONFLICT_IGNORE;

        }

        ShowWindow(hDlg, SW_SHOW);
        return TRUE;

    case WM_COMMAND:

        pDlgParam = (PDLGPARAM)GetWindowLongPtr(hDlg, DWLP_USER);

        switch (LOWORD(wParam))
        {
        case IDC_CANCEL:
        case IDC_CANCEL_FINAL:
            pDlgParam->dwResult = CONFLICT_CANCEL;
            break;

        case IDC_IGNORE:
            pDlgParam->dwResult = CONFLICT_IGNORE;
            break;

        case IDC_RESOLVE:
            pDlgParam->dwResult = CONFLICT_RESOLVE;
            break;

        case IDOK:
        case IDCANCEL:
            EndDialog(hDlg, LOWORD(wParam));
            return TRUE;
        }
    }

    return FALSE;
}



VOID
VUpdateOptItemList(
    IN OUT  PUIDATA     pUiData,
    IN      POPTSELECT  pOldCombinedOptions,
    IN      POPTSELECT  pNewCombinedOptions
    )

/*++

Routine Description:

    Ssync up OPTITEM list with the updated options array.

Arguments:

    pUiData - Pointer to our UIDATA structure
    pOldCombinedOptions - A copy of the pre-resolved options array,
    this should cut down the updating costs, only updated if it's changed
    pNewCombinedOptions - the current options array

Return Value:

    None

--*/

{
    DWORD       i, dwFeatures, dwDrvOptItem;
    PFEATURE    pFeature;
    PUIINFO     pUIInfo = pUiData->ci.pUIInfo;
    PCSTR       pKeywordName, pFeatureKeywordName;
    POPTITEM    pOptItem;

    if (pUiData->dwDrvOptItem == 0)
    {
        //
        // nothing to update
        //

        return;
    }

    dwFeatures = pUiData->ci.pRawData->dwDocumentFeatures +
                 pUiData->ci.pRawData->dwPrinterFeatures;

    for (i = 0; i < dwFeatures; i++)
    {
        if (pOldCombinedOptions[i].ubCurOptIndex != pNewCombinedOptions[i].ubCurOptIndex)
        {
            dwDrvOptItem = pUiData->dwDrvOptItem;
            pOptItem = pUiData->pDrvOptItem;

            pFeature = PGetIndexedFeature(pUIInfo, i);

            ASSERT(pFeature);

            while( dwDrvOptItem--)
            {
                pKeywordName = GETUSERDATAKEYWORDNAME(pOptItem->UserData);
                pFeatureKeywordName = OFFSET_TO_POINTER(pUIInfo->pubResourceData,
                                                        pFeature->loKeywordName);

                ASSERT(pFeatureKeywordName);

                if (pKeywordName && pFeatureKeywordName &&
                    (strcmp(pFeatureKeywordName, pKeywordName) == EQUAL_STRING))
                    break;

                pOptItem++;
            }

            pOptItem->Sel = pNewCombinedOptions[i].ubCurOptIndex;
            pOptItem->Flags |= OPTIF_CHANGED;


            //
            // This is necessary to ssync up the colormode changes with the color information
            //

            #ifdef UNIDRV
            if (GETUSERDATAITEM(pOptItem->UserData) == COLORMODE_ITEM)
                VSyncColorInformation(pUiData, pOptItem);
            #endif
        }
    }

    VPropShowConstraints(pUiData,
                         (pUiData->iMode == MODE_PRINTER_STICKY) ? pUiData->iMode : MODE_DOCANDPRINTER_STICKY);
}


INT
ICheckConstraintsDlg(
    IN OUT  PUIDATA     pUiData,
    IN OUT  POPTITEM    pOptItem,
    IN      DWORD       dwOptItem,
    IN      BOOL        bFinal
    )

/*++

Routine Description:

    Check if the user chose any constrained selection

Arguments:

    pUiData - Pointer to our UIDATA structure
    pOptItem - Pointer to an array of OPTITEMs
    dwOptItem - Number of items to be checked
    bFinal - Whether this is called when user tries to exit the dialog

Return Value:

    CONFLICT_NONE - no conflicts
    CONFLICT_RESOLVE - click RESOLVE to automatically resolve conflicts
    CONFLICT_CANCEL - click CANCEL to back out of changes
    CONFLICT_IGNORE - click IGNORE to ignore conflicts

--*/

{
    DLGPARAM    DlgParam;
    OPTSELECT   OldCombinedOptions[MAX_COMBINED_OPTIONS];


    DlgParam.pfnComPropSheet = pUiData->pfnComPropSheet;
    DlgParam.hComPropSheet = pUiData->hComPropSheet;
    DlgParam.pUiData = pUiData;
    DlgParam.bFinal = bFinal;
    DlgParam.dwResult = CONFLICT_NONE;

    for ( ; dwOptItem--; pOptItem++)
    {
        //
        // If the item is not constrainable, skip it.
        //

        if (! ISCONSTRAINABLEITEM(pOptItem->UserData))
            continue;

        //
        // If user has clicked IGNORE before, then don't bother
        // checking anymore until he tries to exit the dialog.
        //

        //if (pUiData->bIgnoreConflict && !bFinal)
        //    break;

        //
        // If there is a conflict, then display a warning message
        //

        if (IS_CONSTRAINED(pOptItem, pOptItem->Sel))
        {
            DlgParam.pOptItem = pOptItem;
            DlgParam.dwResult = CONFLICT_NONE;

            DialogBoxParam(ghInstance,
                        MAKEINTRESOURCE(IDD_CONFLICTS),
                        pUiData->hDlg,
                        (DLGPROC) BConflictsDlgProc,
                        (LPARAM) &DlgParam);

            //
            // Automatically resolve conflicts. We're being very
            // simple-minded here, i.e. picking the first selection
            // that's not constrained.
            //

            if (DlgParam.dwResult == CONFLICT_RESOLVE)
            {

                ASSERT((bFinal == TRUE));

                //
                // Save a copy the pre-resolve optionarray
                //

                CopyMemory(OldCombinedOptions,
                           pUiData->ci.pCombinedOptions,
                           MAX_COMBINED_OPTIONS * sizeof(OPTSELECT));

                //
                // Call the parsers to resolve the conflicts
                //
                // Note: If we're inside DrvDocumentPropertySheets,
                // we'll call the parser to resolve conflicts between
                // all printer features. Since all printer-sticky
                // features have higher priority than all doc-sticky
                // features, only doc-sticky option selections should
                // be affected.
                //

                ResolveUIConflicts(pUiData->ci.pRawData,
                                   pUiData->ci.pCombinedOptions,
                                   MAX_COMBINED_OPTIONS,
                                   (pUiData->iMode == MODE_PRINTER_STICKY) ?
                                        pUiData->iMode :
                                        MODE_DOCANDPRINTER_STICKY);

                //
                // Update the OPTITEM list to match the updated options array
                //

                VUpdateOptItemList(pUiData, OldCombinedOptions, pUiData->ci.pCombinedOptions);

            }
            else if (DlgParam.dwResult == CONFLICT_IGNORE)
            {
                //
                // Ignore any future conflicts until the
                // user tries to close the property sheet.
                //

                pUiData->bIgnoreConflict = TRUE;
            }

            break;
        }
    }

    return DlgParam.dwResult;
}



BOOL
BOptItemSelectionsChanged(
    IN POPTITEM pItems,
    IN DWORD    dwItems
    )

/*++

Routine Description:

    Check if any of the OPTITEM's was changed by the user

Arguments:

    pItems - Pointer to an array of OPTITEM's
    dwItems - Number of OPTITEM's

Return Value:

    TRUE if anything was changed, FALSE otherwise

--*/

{
    for ( ; dwItems--; pItems++)
    {
        if (pItems->Flags & OPTIF_CHANGEONCE)
            return TRUE;
    }

    return FALSE;
}



POPTITEM
PFindOptItem(
    IN PUIDATA  pUiData,
    IN DWORD    dwItemId
    )

/*++

Routine Description:

    Find an OPTITEM with the specified identifier

Arguments:

    pUiData - Points to UIDATA structure
    dwItemId - Specifies the interested item identifier

Return Value:

    Pointer to OPTITEM with the specified id,
    NULL if no such item is found

--*/

{
    POPTITEM    pOptItem = pUiData->pDrvOptItem;
    DWORD       dwOptItem = pUiData->dwDrvOptItem;

    for ( ; dwOptItem--; pOptItem++)
    {
        if (GETUSERDATAITEM(pOptItem->UserData) == dwItemId)
            return pOptItem;
    }

    return NULL;
}



INT
IDisplayErrorMessageBox(
    HWND    hwndParent,
    UINT    uType,
    INT     iTitleStrId,
    INT     iFormatStrId,
    ...
    )

/*++

Routine Description:

    Display an error message box

Arguments:

    hwndParent - Handle to the parent window
    uType - Type of message box to display
        if 0, the default value is MB_OK | MB_ICONERROR
    iTitleStrId - String resource ID for the message box title
    iFormatStrId - String resource ID for the message itself.
        This string can contain printf format specifications.
    ... - Optional arguments.

Return Value:

    Return value from MessageBox() call.

--*/

#define MAX_MBTITLE_LEN     128
#define MAX_MBFORMAT_LEN    512
#define MAX_MBMESSAGE_LEN   1024

{
    PWSTR   pwstrTitle, pwstrFormat, pwstrMessage;
    INT     iResult;
    va_list ap;

    pwstrTitle = pwstrFormat = pwstrMessage = NULL;

    if ((pwstrTitle = MemAllocZ(sizeof(WCHAR) * MAX_MBTITLE_LEN)) &&
        (pwstrFormat = MemAllocZ(sizeof(WCHAR) * MAX_MBFORMAT_LEN)) &&
        (pwstrMessage = MemAllocZ(sizeof(WCHAR) * MAX_MBMESSAGE_LEN)))
    {
        //
        // Load message box title and format string resources
        //

        LoadString(ghInstance, iTitleStrId, pwstrTitle, MAX_MBTITLE_LEN);
        LoadString(ghInstance, iFormatStrId, pwstrFormat, MAX_MBFORMAT_LEN);

        //
        // Compose the message string
        //

        va_start(ap, iFormatStrId);
        wvsprintf(pwstrMessage, pwstrFormat, ap);
        va_end(ap);

        //
        // Display the message box
        //

        if (uType == 0)
            uType = MB_OK | MB_ICONERROR;

        iResult = MessageBox(hwndParent, pwstrMessage, pwstrTitle, uType);
    }
    else
    {
        MessageBeep(MB_ICONERROR);
        iResult = 0;
    }

    MemFree(pwstrTitle);
    MemFree(pwstrFormat);
    MemFree(pwstrMessage);
    return iResult;
}