/* -*- Mode: C; tab-width: 4 -*- * * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "stdafx.h" #include "PrinterSetupWizardApp.h" #include "PrinterSetupWizardSheet.h" #include "ThirdPage.h" #include "tcpxcv.h" #include #include #include // local variable is initialize but not referenced #pragma warning(disable:4189) // // This is the printer description file that is shipped // with Windows XP and below // #define kNTPrintFile L"inf\\ntprint.inf" // // Windows Vista ships with a set of prn*.inf files // #define kVistaPrintFiles L"inf\\prn*.inf" // // These are pre-defined names for Generic manufacturer and model // #define kGenericManufacturer L"Generic" #define kGenericText L"Generic / Text Only" #define kGenericPostscript L"Generic / Postscript" #define kGenericPCL L"Generic / PCL" #define kPDLPostscriptKey L"application/postscript" #define kPDLPCLKey L"application/vnd.hp-pcl" #define kGenericPSColorDriver L"HP Color LaserJet 4550 PS" #define kGenericPSDriver L"HP LaserJet 4050 Series PS" #define kGenericPCLColorDriver L"HP Color LaserJet 4550 PCL" #define kGenericPCLDriver L"HP LaserJet 4050 Series PCL" // CThirdPage dialog IMPLEMENT_DYNAMIC(CThirdPage, CPropertyPage) CThirdPage::CThirdPage() : CPropertyPage(CThirdPage::IDD), m_manufacturerSelected( NULL ), m_modelSelected( NULL ), m_genericPostscript( NULL ), m_genericPCL( NULL ), m_initialized(false), m_printerImage( NULL ) { static const int bufferSize = 32768; TCHAR windowsDirectory[bufferSize]; CString header; WIN32_FIND_DATA findFileData; HANDLE findHandle; CString prnFiles; CString ntPrint; OSStatus err; BOOL ok; m_psp.dwFlags &= ~(PSP_HASHELP); m_psp.dwFlags |= PSP_DEFAULT|PSP_USEHEADERTITLE|PSP_USEHEADERSUBTITLE; m_psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_INSTALL_TITLE); m_psp.pszHeaderSubTitle = MAKEINTRESOURCE(IDS_INSTALL_SUBTITLE); // // load printers from ntprint.inf // ok = GetWindowsDirectory( windowsDirectory, bufferSize ); err = translate_errno( ok, errno_compat(), kUnknownErr ); require_noerr( err, exit ); // // // // If there are no *prn.inf files, we'll assume that the information // is in ntprint.inf // prnFiles.Format( L"%s\\%s", windowsDirectory, kVistaPrintFiles ); findHandle = FindFirstFile( prnFiles, &findFileData ); if ( findHandle != INVALID_HANDLE_VALUE ) { CString absolute; absolute.Format( L"%s\\inf\\%s", windowsDirectory, findFileData.cFileName ); err = LoadPrintDriverDefsFromFile( m_manufacturers, absolute, false ); require_noerr( err, exit ); while ( FindNextFile( findHandle, &findFileData ) ) { absolute.Format( L"%s\\inf\\%s", windowsDirectory, findFileData.cFileName ); err = LoadPrintDriverDefsFromFile( m_manufacturers, absolute, false ); require_noerr( err, exit ); } FindClose( findHandle ); } else { ntPrint.Format(L"%s\\%s", windowsDirectory, kNTPrintFile); err = LoadPrintDriverDefsFromFile( m_manufacturers, ntPrint, false ); require_noerr(err, exit); } // // load printer drivers that have been installed on this machine // err = LoadPrintDriverDefs( m_manufacturers ); require_noerr(err, exit); // // load our own special generic printer defs // err = LoadGenericPrintDriverDefs( m_manufacturers ); require_noerr( err, exit ); exit: return; } void CThirdPage::FreeManufacturers( Manufacturers & manufacturers ) { for ( Manufacturers::iterator it = manufacturers.begin(); it != manufacturers.end(); it++ ) { for ( Models::iterator it2 = it->second->models.begin(); it2 != it->second->models.end(); it2++ ) { delete *it2; } delete it->second; } } CThirdPage::~CThirdPage() { FreeManufacturers( m_manufacturers ); } // ---------------------------------------------------- // SelectMatch // // SelectMatch will do all the UI work associated with // selected a manufacturer and model of printer. It also // makes sure the printer object is update with the // latest settings // // ---------------------------------------------------- void CThirdPage::SelectMatch(Printer * printer, Service * service, Manufacturer * manufacturer, Model * model) { LVFINDINFO info; int nIndex; check( printer != NULL ); check( manufacturer != NULL ); check( model != NULL ); // // select the manufacturer // info.flags = LVFI_STRING; info.psz = manufacturer->name; nIndex = m_manufacturerListCtrl.FindItem(&info); if (nIndex != -1) { m_manufacturerListCtrl.SetItemState(nIndex, LVIS_SELECTED, LVIS_SELECTED); // // mDNS: When auto-highlighting items in lists, scroll list so highlighted item is in the middle // AutoScroll(m_manufacturerListCtrl, nIndex); } // // select the model // info.flags = LVFI_STRING; info.psz = model->displayName; nIndex = m_modelListCtrl.FindItem(&info); if (nIndex != -1) { m_modelListCtrl.SetItemState(nIndex, LVIS_SELECTED, LVIS_SELECTED); AutoScroll( m_modelListCtrl, nIndex ); m_modelListCtrl.SetFocus(); } CopyPrinterSettings( printer, service, manufacturer, model ); } void CThirdPage::SelectMatch(Manufacturers & manufacturers, Printer * printer, Service * service, Manufacturer * manufacturer, Model * model) { PopulateUI( manufacturers ); SelectMatch( printer, service, manufacturer, model ); } // -------------------------------------------------------- // CopyPrinterSettings // // This function makes sure that the printer object has the // latest settings from the manufacturer and model objects // -------------------------------------------------------- void CThirdPage::CopyPrinterSettings( Printer * printer, Service * service, Manufacturer * manufacturer, Model * model ) { DWORD portNameLen; printer->manufacturer = manufacturer->name; printer->displayModelName = model->displayName; printer->modelName = model->name; printer->driverInstalled = model->driverInstalled; printer->infFileName = model->infFileName; if ( service->type == kPDLServiceType ) { printer->portName.Format(L"IP_%s.%d", static_cast(service->hostname), service->portNumber); service->protocol = L"Raw"; } else if ( service->type == kLPRServiceType ) { Queue * q = service->queues.front(); check( q ); if ( q->name.GetLength() > 0 ) { printer->portName.Format(L"LPR_%s.%d.%s", static_cast(service->hostname), service->portNumber, static_cast(q->name) ); } else { printer->portName.Format(L"LPR_%s.%d", static_cast(service->hostname), service->portNumber); } service->protocol = L"LPR"; } else if ( service->type == kIPPServiceType ) { Queue * q = service->queues.front(); check( q ); if ( q->name.GetLength() > 0 ) { printer->portName.Format(L"http://%s:%d/%s", static_cast(service->hostname), service->portNumber, static_cast(q->name) ); } else { printer->portName.Format(L"http://%s:%d/", static_cast(service->hostname), service->portNumber ); } service->protocol = L"IPP"; } // If it's not an IPP printr, truncate the portName so that it's valid if ( service->type != kIPPServiceType ) { portNameLen = printer->portName.GetLength() + 1; if ( portNameLen > MAX_PORTNAME_LEN ) { printer->portName.Delete( MAX_PORTNAME_LEN - 1, ( portNameLen - MAX_PORTNAME_LEN ) ); } } } // -------------------------------------------------------- // DefaultPrinterExists // // Checks to see if a default printer has been configured // on this machine // -------------------------------------------------------- BOOL CThirdPage::DefaultPrinterExists() { CPrintDialog dlg(FALSE); dlg.m_pd.Flags |= PD_RETURNDEFAULT; return dlg.GetDefaults(); } // -------------------------------------------------------- // AutoScroll // // Ensure selected item is in middle of list // -------------------------------------------------------- void CThirdPage::AutoScroll( CListCtrl & list, int nIndex ) { // // mDNS: When auto-highlighting items in lists, scroll list so highlighted item is in the middle // int top; int count; list.EnsureVisible( nIndex, FALSE ); top = list.GetTopIndex(); count = list.GetCountPerPage(); if ( ( nIndex == top ) || ( ( nIndex + 1 ) == ( top + count ) ) ) { CRect rect; int rows; rows = ( count / 2 ); if ( nIndex == top ) { list.GetItemRect(0, rect, LVIR_BOUNDS); list.Scroll( CPoint( 0, rows * rect.Height() * -1 ) ); } else { list.GetItemRect(0, rect, LVIR_BOUNDS); list.Scroll( CPoint( 0, rows * rect.Height() ) ); } } } // ------------------------------------------------------ // LoadPrintDriverDefsFromFile // // The only potentially opaque thing about this function is the // checkForDuplicateModels flag. The problem here is that ntprint.inf // doesn't contain duplicate models, and it has hundreds of models // listed. You wouldn't check for duplicates there. But oftentimes, // loading different windows print driver files contain multiple // entries for the same printer. You don't want the UI to display // the same printer multiple times, so in that case, you would ask // this function to check for multiple models. OSStatus CThirdPage::LoadPrintDriverDefsFromFile(Manufacturers & manufacturers, const CString & filename, bool checkForDuplicateModels ) { HINF handle = INVALID_HANDLE_VALUE; const TCHAR * section = TEXT( "Manufacturer" ); LONG sectionCount; TCHAR line[ 1000 ]; CString klass; INFCONTEXT manufacturerContext; BOOL ok; OSStatus err = 0; // Make sure we can open the file handle = SetupOpenInfFile( filename, NULL, INF_STYLE_WIN4, NULL ); translate_errno( handle != INVALID_HANDLE_VALUE, GetLastError(), kUnknownErr ); require_noerr( err, exit ); // Make sure it's a printer file ok = SetupGetLineText( NULL, handle, TEXT( "Version" ), TEXT( "Class" ), line, sizeof( line ), NULL ); translate_errno( ok, GetLastError(), kUnknownErr ); require_noerr( err, exit ); klass = line; require_action( klass == TEXT( "Printer" ), exit, err = kUnknownErr ); sectionCount = SetupGetLineCount( handle, section ); translate_errno( sectionCount != -1, GetLastError(), kUnknownErr ); require_noerr( err, exit ); memset( &manufacturerContext, 0, sizeof( manufacturerContext ) ); for ( LONG i = 0; i < sectionCount; i++ ) { Manufacturers::iterator iter; Manufacturer * manufacturer; CString manufacturerName; CString temp; CStringList modelSectionNameDecl; CString modelSectionName; CString baseModelName; CString model; INFCONTEXT modelContext; LONG modelCount; POSITION p; if ( i == 0 ) { ok = SetupFindFirstLine( handle, section, NULL, &manufacturerContext ); err = translate_errno( ok, GetLastError(), kUnknownErr ); require_noerr( err, exit ); } else { ok = SetupFindNextLine( &manufacturerContext, &manufacturerContext ); err = translate_errno( ok, GetLastError(), kUnknownErr ); require_noerr( err, exit ); } ok = SetupGetStringField( &manufacturerContext, 0, line, sizeof( line ), NULL ); err = translate_errno( ok, GetLastError(), kUnknownErr ); require_noerr( err, exit ); manufacturerName = line; ok = SetupGetLineText( &manufacturerContext, handle, NULL, NULL, line, sizeof( line ), NULL ); err = translate_errno( ok, GetLastError(), kUnknownErr ); require_noerr( err, exit ); // Try to find some model section name that has entries. Explanation of int file structure // can be found at: // // Split( line, ',', modelSectionNameDecl ); p = modelSectionNameDecl.GetHeadPosition(); modelSectionName = modelSectionNameDecl.GetNext( p ); modelCount = SetupGetLineCount( handle, modelSectionName ); baseModelName = modelSectionName; while ( modelCount <= 0 && p ) { CString targetOSVersion; targetOSVersion = modelSectionNameDecl.GetNext( p ); modelSectionName = baseModelName + TEXT( "." ) + targetOSVersion; modelCount = SetupGetLineCount( handle, modelSectionName ); } if ( modelCount > 0 ) { manufacturerName = NormalizeManufacturerName( manufacturerName ); iter = manufacturers.find( manufacturerName ); if ( iter != manufacturers.end() ) { manufacturer = iter->second; require_action( manufacturer, exit, err = kUnknownErr ); } else { try { manufacturer = new Manufacturer; } catch (...) { manufacturer = NULL; } require_action( manufacturer, exit, err = kNoMemoryErr ); manufacturer->name = manufacturerName; manufacturers[ manufacturerName ] = manufacturer; } memset( &modelContext, 0, sizeof( modelContext ) ); for ( LONG j = 0; j < modelCount; j++ ) { CString modelName; Model * model; if ( j == 0 ) { ok = SetupFindFirstLine( handle, modelSectionName, NULL, &modelContext ); err = translate_errno( ok, GetLastError(), kUnknownErr ); require_noerr( err, exit ); } else { SetupFindNextLine( &modelContext, &modelContext ); err = translate_errno( ok, GetLastError(), kUnknownErr ); require_noerr( err, exit ); } ok = SetupGetStringField( &modelContext, 0, line, sizeof( line ), NULL ); err = translate_errno( ok, GetLastError(), kUnknownErr ); require_noerr( err, exit ); modelName = line; if (checkForDuplicateModels == true) { if ( MatchModel( manufacturer, ConvertToModelName( modelName ) ) != NULL ) { continue; } } // // Stock Vista printer inf files embed guids in the model // declarations for Epson printers. Let's ignore those. // if ( modelName.Find( TEXT( "{" ), 0 ) != -1 ) { continue; } try { model = new Model; } catch (...) { model = NULL; } require_action( model, exit, err = kNoMemoryErr ); model->infFileName = filename; model->displayName = modelName; model->name = modelName; model->driverInstalled = false; manufacturer->models.push_back(model); } } } exit: if ( handle != INVALID_HANDLE_VALUE ) { SetupCloseInfFile( handle ); handle = NULL; } return err; } // ------------------------------------------------------- // LoadPrintDriverDefs // // This function is responsible for loading the print driver // definitions of all print drivers that have been installed // on this machine. // ------------------------------------------------------- OSStatus CThirdPage::LoadPrintDriverDefs( Manufacturers & manufacturers ) { BYTE * buffer = NULL; DWORD bytesReceived = 0; DWORD numPrinters = 0; OSStatus err = 0; BOOL ok; // // like a lot of win32 calls, we call this first to get the // size of the buffer we need. // EnumPrinterDrivers(NULL, L"all", 6, NULL, 0, &bytesReceived, &numPrinters); if (bytesReceived > 0) { try { buffer = new BYTE[bytesReceived]; } catch (...) { buffer = NULL; } require_action( buffer, exit, err = kNoMemoryErr ); // // this call gets the real info // ok = EnumPrinterDrivers(NULL, L"all", 6, buffer, bytesReceived, &bytesReceived, &numPrinters); err = translate_errno( ok, errno_compat(), kUnknownErr ); require_noerr( err, exit ); DRIVER_INFO_6 * info = (DRIVER_INFO_6*) buffer; for (DWORD i = 0; i < numPrinters; i++) { Manufacturer * manufacturer; Model * model; CString name; // // skip over anything that doesn't have a manufacturer field. This // fixes a bug that I noticed that occurred after I installed // ProComm. This program add a print driver with no manufacturer // that screwed up this wizard. // if (info[i].pszMfgName == NULL) { continue; } // // look for manufacturer // Manufacturers::iterator iter; // // save the name // name = NormalizeManufacturerName( info[i].pszMfgName ); iter = manufacturers.find(name); if (iter != manufacturers.end()) { manufacturer = iter->second; } else { try { manufacturer = new Manufacturer; } catch (...) { manufacturer = NULL; } require_action( manufacturer, exit, err = kNoMemoryErr ); manufacturer->name = name; manufacturers[name] = manufacturer; } // // now look to see if we have already seen this guy. this could // happen if we have already installed printers that are described // in ntprint.inf. the extant drivers will show up in EnumPrinterDrivers // but we have already loaded their info // // if ( MatchModel( manufacturer, ConvertToModelName( info[i].pName ) ) == NULL ) { try { model = new Model; } catch (...) { model = NULL; } require_action( model, exit, err = kNoMemoryErr ); model->displayName = info[i].pName; model->name = info[i].pName; model->driverInstalled = true; manufacturer->models.push_back(model); } } } exit: if (buffer != NULL) { delete [] buffer; } return err; } // ------------------------------------------------------- // LoadGenericPrintDriverDefs // // This function is responsible for loading polymorphic // generic print drivers defs. The UI will read // something like "Generic / Postscript" and we can map // that to any print driver we want. // ------------------------------------------------------- OSStatus CThirdPage::LoadGenericPrintDriverDefs( Manufacturers & manufacturers ) { Manufacturer * manufacturer; Model * model; Manufacturers::iterator iter; CString psDriverName; CString pclDriverName; OSStatus err = 0; // Generic drivers don't do color // First try and find our generic driver names iter = m_manufacturers.find(L"HP"); require_action( iter != m_manufacturers.end(), exit, err = kUnknownErr ); manufacturer = iter->second; // Look for Postscript model = manufacturer->find( kGenericPSColorDriver ); if ( !model ) { model = manufacturer->find( kGenericPSDriver ); } if ( model ) { psDriverName = model->name; } // Look for PCL model = manufacturer->find( kGenericPCLColorDriver ); if ( !model ) { model = manufacturer->find( kGenericPCLDriver ); } if ( model ) { pclDriverName = model->name; } // If we found either a generic PS driver, or a generic PCL driver, // then add them to the list if ( psDriverName.GetLength() || pclDriverName.GetLength() ) { // Try and find generic manufacturer if there is one iter = manufacturers.find(L"Generic"); if (iter != manufacturers.end()) { manufacturer = iter->second; } else { try { manufacturer = new Manufacturer; } catch (...) { manufacturer = NULL; } require_action( manufacturer, exit, err = kNoMemoryErr ); manufacturer->name = "Generic"; manufacturers[manufacturer->name] = manufacturer; } if ( psDriverName.GetLength() > 0 ) { try { m_genericPostscript = new Model; } catch (...) { m_genericPostscript = NULL; } require_action( m_genericPostscript, exit, err = kNoMemoryErr ); m_genericPostscript->displayName = kGenericPostscript; m_genericPostscript->name = psDriverName; m_genericPostscript->driverInstalled = false; manufacturer->models.push_back( m_genericPostscript ); } if ( pclDriverName.GetLength() > 0 ) { try { m_genericPCL = new Model; } catch (...) { m_genericPCL = NULL; } require_action( m_genericPCL, exit, err = kNoMemoryErr ); m_genericPCL->displayName = kGenericPCL; m_genericPCL->name = pclDriverName; m_genericPCL->driverInstalled = false; manufacturer->models.push_back( m_genericPCL ); } } exit: return err; } // ------------------------------------------------------ // ConvertToManufacturerName // // This function is responsible for tweaking the // name so that subsequent string operations won't fail because // of capitalizations/different names for the same manufacturer // (i.e. Hewlett-Packard/HP/Hewlett Packard) // CString CThirdPage::ConvertToManufacturerName( const CString & name ) { // // first we're going to convert all the characters to lower // case // CString lower = name; lower.MakeLower(); // // now we're going to check to see if the string says "hewlett-packard", // because sometimes they refer to themselves as "hewlett-packard", and // sometimes they refer to themselves as "hp". // if ( lower == L"hewlett-packard") { lower = "hp"; } // // tweak for Xerox Phaser, which doesn't announce itself // as a xerox // else if ( lower.Find( L"phaser", 0 ) != -1 ) { lower = "xerox"; } return lower; } // ------------------------------------------------------ // ConvertToModelName // // This function is responsible for ensuring that subsequent // string operations don't fail because of differing capitalization // schemes and the like // ------------------------------------------------------ CString CThirdPage::ConvertToModelName( const CString & name ) { // // convert it to lowercase // CString lower = name; lower.MakeLower(); return lower; } // ------------------------------------------------------ // NormalizeManufacturerName // // This function is responsible for tweaking the manufacturer // name so that there are no aliases for vendors // CString CThirdPage::NormalizeManufacturerName( const CString & name ) { CString normalized = name; // // now we're going to check to see if the string says "hewlett-packard", // because sometimes they refer to themselves as "hewlett-packard", and // sometimes they refer to themselves as "hp". // if ( normalized == L"Hewlett-Packard") { normalized = "HP"; } return normalized; } // ------------------------------------------------------- // MatchPrinter // // This function is responsible for matching a printer // to a list of manufacturers and models. It calls // MatchManufacturer and MatchModel in turn. // OSStatus CThirdPage::MatchPrinter(Manufacturers & manufacturers, Printer * printer, Service * service, bool useCUPSWorkaround) { CString normalizedProductName; Manufacturer * manufacturer = NULL; Manufacturer * genericManufacturer = NULL; Model * model = NULL; Model * genericModel = NULL; bool found = false; CString text; OSStatus err = kNoErr; check( printer ); check( service ); Queue * q = service->SelectedQueue(); check( q ); // // first look to see if we have a usb_MFG descriptor // if ( q->usb_MFG.GetLength() > 0) { manufacturer = MatchManufacturer( manufacturers, ConvertToManufacturerName ( q->usb_MFG ) ); } if ( manufacturer == NULL ) { q->product.Remove('('); q->product.Remove(')'); manufacturer = MatchManufacturer( manufacturers, ConvertToManufacturerName ( q->product ) ); } // // if we found the manufacturer, then start looking for the model // if ( manufacturer != NULL ) { if ( q->usb_MDL.GetLength() > 0 ) { model = MatchModel ( manufacturer, ConvertToModelName ( q->usb_MDL ) ); } if ( ( model == NULL ) && ( q->product.GetLength() > 0 ) ) { q->product.Remove('('); q->product.Remove(')'); model = MatchModel ( manufacturer, ConvertToModelName ( q->product ) ); } if ( model != NULL ) { // Offer Generic printers if printer advertises Postscript or PCL. Workaround // bug in OS X CUPS printer sharing by selecting Generic driver instead of matched printer. bool hasGenericDriver = false; if ( MatchGeneric( manufacturers, printer, service, &genericManufacturer, &genericModel ) ) { hasGenericDriver = true; } // Use "application/octet-stream" to determine if CUPS // shared queue supports raw if ( q->pdl.Find( L"application/octet-stream" ) != -1 ) { useCUPSWorkaround = false; } if ( useCUPSWorkaround && printer->isCUPSPrinter && hasGenericDriver ) { // // mDNS: Don't allow user to choose non-working driver // Manufacturers genericManufacturers; LoadGenericPrintDriverDefs( genericManufacturers ); SelectMatch( genericManufacturers, printer, service, genericManufacturer, genericModel ); FreeManufacturers( genericManufacturers ); } else { SelectMatch(manufacturers, printer, service, manufacturer, model); } found = true; } } // // display a message to the user based on whether we could match // this printer // if (found) { text.LoadString(IDS_PRINTER_MATCH_GOOD); err = kNoErr; } else if ( MatchGeneric( manufacturers, printer, service, &genericManufacturer, &genericModel ) ) { if ( printer->isCUPSPrinter ) { // // mDNS: Don't allow user to choose non-working driver // Manufacturers genericManufacturers; LoadGenericPrintDriverDefs( genericManufacturers ); SelectMatch( genericManufacturers, printer, service, genericManufacturer, genericModel ); text.LoadString(IDS_PRINTER_MATCH_GOOD); FreeManufacturers( genericManufacturers ); } else { SelectMatch( manufacturers, printer, service, genericManufacturer, genericModel ); text.LoadString(IDS_PRINTER_MATCH_MAYBE); } err = kNoErr; } else { text.LoadString(IDS_PRINTER_MATCH_BAD); // // if there was any crud in this list from before, get rid of it now // m_modelListCtrl.DeleteAllItems(); // // select the manufacturer if we found one // if (manufacturer != NULL) { LVFINDINFO info; int nIndex; // // select the manufacturer // info.flags = LVFI_STRING; info.psz = manufacturer->name; nIndex = m_manufacturerListCtrl.FindItem(&info); if (nIndex != -1) { m_manufacturerListCtrl.SetItemState(nIndex, LVIS_SELECTED, LVIS_SELECTED); // // mDNS: When auto-highlighting items in lists, scroll list so highlighted item is in the middle // AutoScroll(m_manufacturerListCtrl, nIndex); } } err = kUnknownErr; } m_printerSelectionText.SetWindowText(text); return err; } // ------------------------------------------------------ // MatchManufacturer // // This function is responsible for finding a manufacturer // object from a string name. It does a CString::Find, which // is like strstr, so it doesn't have to do an exact match // // If it can't find a match, NULL is returned // ------------------------------------------------------ Manufacturer* CThirdPage::MatchManufacturer( Manufacturers & manufacturers, const CString & name) { Manufacturers::iterator iter; for (iter = manufacturers.begin(); iter != manufacturers.end(); iter++) { // // we're going to convert all the manufacturer names to lower case, // so we match the name passed in. // CString lower = iter->second->name; lower.MakeLower(); // // now try and find the lowered string in the name passed in. // if (name.Find(lower) != -1) { return iter->second; } } return NULL; } // ------------------------------------------------------- // MatchModel // // This function is responsible for matching a model from // a name. It does a CString::Find(), which works like strstr, // so it doesn't rely on doing an exact string match. // Model* CThirdPage::MatchModel(Manufacturer * manufacturer, const CString & name) { Models::iterator iter; iter = manufacturer->models.begin(); for (iter = manufacturer->models.begin(); iter != manufacturer->models.end(); iter++) { Model * model = *iter; // // convert the model name to lower case // CString lowered = model->name; lowered.MakeLower(); if (lowered.Find( name ) != -1) { return model; } // // // try removing the first substring and search again // if ( name.Find(' ') != -1 ) { CString altered = name; altered.Delete( 0, altered.Find(' ') + 1 ); if ( lowered.Find( altered ) != -1 ) { return model; } } } return NULL; } // ------------------------------------------------------- // MatchGeneric // // This function will attempt to find a generic printer // driver for a printer that we weren't able to match // specifically // BOOL CThirdPage::MatchGeneric( Manufacturers & manufacturers, Printer * printer, Service * service, Manufacturer ** manufacturer, Model ** model ) { CString pdl; BOOL ok = FALSE; DEBUG_UNUSED( printer ); check( service ); Queue * q = service->SelectedQueue(); check( q ); Manufacturers::iterator iter = manufacturers.find( kGenericManufacturer ); require_action_quiet( iter != manufacturers.end(), exit, ok = FALSE ); *manufacturer = iter->second; pdl = q->pdl; pdl.MakeLower(); if ( m_genericPCL && ( pdl.Find( kPDLPCLKey ) != -1 ) ) { *model = m_genericPCL; ok = TRUE; } else if ( m_genericPostscript && ( pdl.Find( kPDLPostscriptKey ) != -1 ) ) { *model = m_genericPostscript; ok = TRUE; } exit: return ok; } // ----------------------------------------------------------- // OnInitPage // // This function is responsible for doing initialization that // only occurs once during a run of the wizard // OSStatus CThirdPage::OnInitPage() { CString header; CString ntPrint; OSStatus err = kNoErr; // Load printer icon check( m_printerImage == NULL ); m_printerImage = (CStatic*) GetDlgItem( 1 ); // 1 == IDR_MANIFEST check( m_printerImage ); if ( m_printerImage != NULL ) { m_printerImage->SetIcon( LoadIcon( GetNonLocalizedResources(), MAKEINTRESOURCE( IDI_PRINTER ) ) ); } // // The CTreeCtrl widget automatically sends a selection changed // message which initially we want to ignore, because the user // hasn't selected anything // // this flag gets reset in the message handler. Every subsequent // message gets handled. // // // we have to make sure that we only do this once. Typically, // we would do this in something like OnInitDialog, but we don't // have this in Wizards, because the window is a PropertySheet. // We're considered fully initialized when we receive the first // selection notice // header.LoadString(IDS_MANUFACTURER_HEADING); m_manufacturerListCtrl.InsertColumn(0, header, LVCFMT_LEFT, -1 ); m_manufacturerSelected = NULL; header.LoadString(IDS_MODEL_HEADING); m_modelListCtrl.InsertColumn(0, header, LVCFMT_LEFT, -1 ); m_modelSelected = NULL; return (err); } void CThirdPage::DoDataExchange(CDataExchange* pDX) { CPropertyPage::DoDataExchange(pDX); DDX_Control(pDX, IDC_PRINTER_MANUFACTURER, m_manufacturerListCtrl); DDX_Control(pDX, IDC_PRINTER_MODEL, m_modelListCtrl); DDX_Control(pDX, IDC_PRINTER_NAME, m_printerName); DDX_Control(pDX, IDC_DEFAULT_PRINTER, m_defaultPrinterCtrl); DDX_Control(pDX, IDC_PRINTER_SELECTION_TEXT, m_printerSelectionText); } // ---------------------------------------------------------- // OnSetActive // // This function is called by MFC after the window has been // activated. // BOOL CThirdPage::OnSetActive() { CPrinterSetupWizardSheet * psheet; Printer * printer; Service * service; psheet = reinterpret_cast(GetParent()); require_quiet( psheet, exit ); psheet->SetWizardButtons( PSWIZB_BACK ); printer = psheet->GetSelectedPrinter(); require_quiet( printer, exit ); service = printer->services.front(); require_quiet( service, exit ); // // call OnInitPage once // if (!m_initialized) { OnInitPage(); m_initialized = true; } // // mDNS: Printers added using Bonjour should be set as the default printer. // if ( DefaultPrinterExists() ) { m_defaultPrinterCtrl.SetCheck( BST_UNCHECKED ); printer->deflt = false; } else { m_defaultPrinterCtrl.SetCheck( BST_CHECKED ); printer->deflt = true; } // // update the UI with the printer name // m_printerName.SetWindowText(printer->displayName); // // populate the list controls with the manufacturers and models // from ntprint.inf // PopulateUI( m_manufacturers ); // // and try and match the printer // if ( psheet->GetLastPage() == psheet->GetPage(0) ) { MatchPrinter( m_manufacturers, printer, service, true ); if ( ( m_manufacturerSelected != NULL ) && ( m_modelSelected != NULL ) ) { GetParent()->PostMessage(PSM_SETCURSEL, 2 ); } } else { SelectMatch(printer, service, m_manufacturerSelected, m_modelSelected); } exit: return CPropertyPage::OnSetActive(); } BOOL CThirdPage::OnKillActive() { CPrinterSetupWizardSheet * psheet; psheet = reinterpret_cast(GetParent()); require_quiet( psheet, exit ); psheet->SetLastPage(this); exit: return CPropertyPage::OnKillActive(); } // ------------------------------------------------------- // PopulateUI // // This function is called to populate the list of manufacturers // OSStatus CThirdPage::PopulateUI(Manufacturers & manufacturers) { Manufacturers::iterator iter; m_manufacturerListCtrl.DeleteAllItems(); for (iter = manufacturers.begin(); iter != manufacturers.end(); iter++) { int nIndex; Manufacturer * manufacturer = iter->second; nIndex = m_manufacturerListCtrl.InsertItem(0, manufacturer->name); m_manufacturerListCtrl.SetItemData(nIndex, (DWORD_PTR) manufacturer); m_manufacturerListCtrl.SetColumnWidth( 0, LVSCW_AUTOSIZE_USEHEADER ); } return 0; } BEGIN_MESSAGE_MAP(CThirdPage, CPropertyPage) ON_NOTIFY(LVN_ITEMCHANGED, IDC_PRINTER_MANUFACTURER, OnLvnItemchangedManufacturer) ON_NOTIFY(LVN_ITEMCHANGED, IDC_PRINTER_MODEL, OnLvnItemchangedPrinterModel) ON_BN_CLICKED(IDC_DEFAULT_PRINTER, OnBnClickedDefaultPrinter) ON_BN_CLICKED(IDC_HAVE_DISK, OnBnClickedHaveDisk) END_MESSAGE_MAP() // CThirdPage message handlers void CThirdPage::OnLvnItemchangedManufacturer(NMHDR *pNMHDR, LRESULT *pResult) { LPNMLISTVIEW pNMLV = reinterpret_cast(pNMHDR); POSITION p = m_manufacturerListCtrl.GetFirstSelectedItemPosition(); int nSelected = m_manufacturerListCtrl.GetNextSelectedItem(p); if (nSelected != -1) { m_manufacturerSelected = (Manufacturer*) m_manufacturerListCtrl.GetItemData(nSelected); m_modelListCtrl.SetRedraw(FALSE); m_modelListCtrl.DeleteAllItems(); m_modelSelected = NULL; Models::iterator iter; for (iter = m_manufacturerSelected->models.begin(); iter != m_manufacturerSelected->models.end(); iter++) { Model * model = *iter; int nItem = m_modelListCtrl.InsertItem( 0, model->displayName ); m_modelListCtrl.SetItemData(nItem, (DWORD_PTR) model); m_modelListCtrl.SetColumnWidth( 0, LVSCW_AUTOSIZE_USEHEADER ); } m_modelListCtrl.SetRedraw(TRUE); } *pResult = 0; } void CThirdPage::OnLvnItemchangedPrinterModel(NMHDR *pNMHDR, LRESULT *pResult) { LPNMLISTVIEW pNMLV = reinterpret_cast(pNMHDR); CPrinterSetupWizardSheet * psheet; Printer * printer; Service * service; psheet = reinterpret_cast(GetParent()); require_quiet( psheet, exit ); printer = psheet->GetSelectedPrinter(); require_quiet( printer, exit ); service = printer->services.front(); require_quiet( service, exit ); check ( m_manufacturerSelected ); POSITION p = m_modelListCtrl.GetFirstSelectedItemPosition(); int nSelected = m_modelListCtrl.GetNextSelectedItem(p); if (nSelected != -1) { m_modelSelected = (Model*) m_modelListCtrl.GetItemData(nSelected); CopyPrinterSettings( printer, service, m_manufacturerSelected, m_modelSelected ); psheet->SetWizardButtons(PSWIZB_BACK|PSWIZB_NEXT); } else { psheet->SetWizardButtons(PSWIZB_BACK); } exit: *pResult = 0; } void CThirdPage::OnBnClickedDefaultPrinter() { CPrinterSetupWizardSheet * psheet; Printer * printer; psheet = reinterpret_cast(GetParent()); require_quiet( psheet, exit ); printer = psheet->GetSelectedPrinter(); require_quiet( printer, exit ); printer->deflt = ( m_defaultPrinterCtrl.GetCheck() == BST_CHECKED ) ? true : false; exit: return; } void CThirdPage::OnBnClickedHaveDisk() { CPrinterSetupWizardSheet * psheet; Printer * printer; Service * service; Manufacturers manufacturers; CFileDialog dlg(TRUE, NULL, NULL, OFN_HIDEREADONLY|OFN_FILEMUSTEXIST, L"Setup Information (*.inf)|*.inf||", this); psheet = reinterpret_cast(GetParent()); require_quiet( psheet, exit ); printer = psheet->GetSelectedPrinter(); require_quiet( printer, exit ); service = printer->services.front(); require_quiet( service, exit ); for ( ;; ) { if ( dlg.DoModal() == IDOK ) { CString filename = dlg.GetPathName(); LoadPrintDriverDefsFromFile( manufacturers, filename, true ); // Sanity check if ( manufacturers.size() > 0 ) { PopulateUI( manufacturers ); if ( MatchPrinter( manufacturers, printer, service, false ) != kNoErr ) { CString errorMessage; CString errorCaption; errorMessage.LoadString( IDS_NO_MATCH_INF_FILE ); errorCaption.LoadString( IDS_NO_MATCH_INF_FILE_CAPTION ); MessageBox( errorMessage, errorCaption, MB_OK ); } break; } else { CString errorMessage; CString errorCaption; errorMessage.LoadString( IDS_BAD_INF_FILE ); errorCaption.LoadString( IDS_BAD_INF_FILE_CAPTION ); MessageBox( errorMessage, errorCaption, MB_OK ); } } else { break; } } exit: FreeManufacturers( manufacturers ); return; } void CThirdPage::Split( const CString & string, TCHAR ch, CStringList & components ) { CString temp; int n; temp = string; while ( ( n = temp.Find( ch ) ) != -1 ) { components.AddTail( temp.Left( n ) ); temp = temp.Right( temp.GetLength() - ( n + 1 ) ); } components.AddTail( temp ); }