/*++ 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; }