/* * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ Change History (most recent first): $Log: ThirdPage.cpp,v $ Revision 1.20 2005/03/05 02:27:45 shersche Generic drivers don't do color Revision 1.19 2005/02/23 02:08:51 shersche If we can't match the manufacturer, and select a generic printer, then show all the manufacturers in the manufacturer pane, not just "Generic". Revision 1.18 2005/02/15 07:02:51 shersche Display different UI text when generic printer drivers are selected Revision 1.17 2005/02/08 21:45:06 shersche Default to Generic PostScript or PCL if unable to match driver Revision 1.16 2005/02/08 18:56:03 shersche Fix generated IPP url so that it doesn't add "/printers" string Revision 1.15 2005/02/01 01:44:07 shersche Load ntprint.inf at startup. This will cause the wizard to take a second or two longer to come up, but will eliminate the pause when auto-selecting the print drivers. Revision 1.14 2005/01/25 08:55:54 shersche Load icons at run-time from resource DLL Bug #: 3911084 Revision 1.13 2005/01/06 08:15:45 shersche Append queue name to end of LPR port name, correctly build port name when queue name is absent Revision 1.12 2005/01/05 01:06:12 shersche Strip the first substring off the product key if an initial match can't be found with the whole product key. Bug #: 3841218 Revision 1.11 2004/12/29 18:53:38 shersche Added support for LPR and IPP protocols as well as support for obtaining multiple text records. Reorganized and simplified codebase. Bug #: 3725106, 3737413 Revision 1.10 2004/10/11 22:55:34 shersche Use the IP port number when deriving the printer port name. Bug #: 3827624 Revision 1.9 2004/06/27 23:08:00 shersche code cleanup, make sure EnumPrintDrivers returns non-zero value, ignore comments in inf files Revision 1.8 2004/06/27 08:06:45 shersche Parse [Strings] section of inf file Revision 1.7 2004/06/26 04:00:05 shersche fix warnings compiling in debug mode Submitted by: herscher Revision 1.6 2004/06/26 03:19:57 shersche clean up warning messages Submitted by: herscher Revision 1.5 2004/06/25 05:06:02 shersche Trim whitespace from key/value pairs when parsing inf files Submitted by: herscher Revision 1.4 2004/06/25 02:44:13 shersche Tweaked code to handle Xerox Phaser printer identification Submitted by: herscher Revision 1.3 2004/06/25 02:27:58 shersche Do a CListCtrl::FindItem() before calling CListCtrl::SetItemState(). Submitted by: herscher Revision 1.2 2004/06/23 18:09:23 shersche Normalize tag names when parsing inf files. Submitted by: herscher Revision 1.1 2004/06/18 04:36:58 rpantos First checked in */ #include "stdafx.h" #include "PrinterSetupWizardApp.h" #include "PrinterSetupWizardSheet.h" #include "ThirdPage.h" #include "StdioFileEx.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 // #define kNTPrintFile L"inf\\ntprint.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" // // states for parsing ntprint.inf // enum PrinterParsingState { Looking, ParsingManufacturers, ParsingModels, ParsingStrings }; // CThirdPage dialog IMPLEMENT_DYNAMIC(CThirdPage, CPropertyPage) CThirdPage::CThirdPage() : CPropertyPage(CThirdPage::IDD), m_initialized(false), m_printerImage( NULL ) { static const int bufferSize = 32768; TCHAR windowsDirectory[bufferSize]; CString header; 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 ); 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); // // and lastly load our own special generic printer defs // err = LoadGenericPrintDriverDefs( m_manufacturers ); require_noerr( err, exit ); exit: return; } CThirdPage::~CThirdPage() { // // clean up all the printer manufacturers // while (m_manufacturers.size()) { Manufacturers::iterator iter = m_manufacturers.begin(); while (iter->second->models.size()) { Models::iterator it = iter->second->models.begin(); Model * model = *it; delete model; iter->second->models.erase(it); } delete iter->second; m_manufacturers.erase(iter); } } // ---------------------------------------------------- // 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, Manufacturers & manufacturers, Manufacturer * manufacturer, Model * model) { LVFINDINFO info; int nIndex; check( printer != NULL ); check( manufacturer != NULL ); check( model != NULL ); PopulateUI( manufacturers ); // // 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); m_manufacturerListCtrl.EnsureVisible(nIndex, FALSE); } // // 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); m_modelListCtrl.EnsureVisible(nIndex, FALSE); m_modelListCtrl.SetFocus(); } CopyPrinterSettings( 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 ) { 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"; } } // ------------------------------------------------------ // LoadPrintDriverDefsFromFile // // This function does all the heavy lifting in parsing inf // files. It is called to parse both ntprint.inf, and driver // files that might be shipped on a printer's installation // disk // // The inf file is not totally parsed. I only want to determine // the manufacturer and models that are involved. I leave it // to printui.dll to actually copy the driver files to the // right places. // // I was aiming to parse as little as I could so as not to // duplicate the parsing code that is contained in Windows. There // are no public APIs for parsing inf files. // // That part of the inf file that we're interested in has a fairly // easy format. Tags are strings that are enclosed in brackets. // We are only interested in [MANUFACTURERS] and models. // // 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 ) { PrinterParsingState state = Looking; Manufacturers::iterator iter = manufacturers.end(); CStdioFileEx file; CFileException feError; CString s; OSStatus err; BOOL ok; typedef std::map StringMap; StringMap strings; ok = file.Open( filename, CFile::modeRead|CFile::typeText, &feError); err = translate_errno( ok, errno_compat(), kUnknownErr ); require_noerr( err, exit ); check ( state == Looking ); check ( iter == manufacturers.end() ); // // first, parse the file looking for string sections // while (file.ReadString(s)) { // // check for comment // if (s.Find(';') == 0) { continue; } // // check for tag // else if (s.Find('[') == 0) { // // handle any capitalization issues here // CString tag = s; tag.MakeLower(); if (tag == L"[strings]") { state = ParsingStrings; } else { state = Looking; } } else { switch (state) { case ParsingStrings: { int curPos = 0; if (s.GetLength() > 0) { CString key = s.Tokenize(L"=",curPos); CString val = s.Tokenize(L"=",curPos); // // get rid of all delimiters // key.Trim(); val.Remove('"'); // // and store it // strings[key] = val; } } break; } } } file.Close(); ok = file.Open( filename, CFile::modeRead|CFile::typeText, &feError); err = translate_errno( ok, errno_compat(), kUnknownErr ); require_noerr( err, exit ); state = Looking; check ( iter == manufacturers.end() ); while (file.ReadString(s)) { // // check for comment // if (s.Find(';') == 0) { continue; } // // check for tag // else if (s.Find('[') == 0) { // // handle any capitalization issues here // CString tag = s; tag.MakeLower(); if (tag == L"[manufacturer]") { state = ParsingManufacturers; } else { // remove the leading and trailing delimiters // s.Remove('['); s.Remove(']'); // check to see if this is a printer entry // iter = manufacturers.find(s); if (iter != manufacturers.end()) { state = ParsingModels; } else { state = Looking; } } } // // only look at this if the line isn't empty, or // if it isn't a comment // else if ((s.GetLength() > 0) && (s.Find(';') != 0)) { switch (state) { // // if we're parsing manufacturers, then we will parse // an entry of the form key=val, where key is a delimited // string specifying a manufacturer name, and val is // a tag that is used later in the file. the key is // delimited by either '"' (quotes) or '%' (percent sign). // // the tag is used further down the file when models are // declared. this allows multiple manufacturers to exist // in a single inf file. // case ParsingManufacturers: { Manufacturer * manufacturer; int curPos = 0; CString key = s.Tokenize(L"=",curPos); CString val = s.Tokenize(L"=",curPos); try { manufacturer = new Manufacturer; } catch (...) { manufacturer = NULL; } require_action( manufacturer, exit, err = kNoMemoryErr ); // // if it's a variable, look it up // if (key.Find('%') == 0) { StringMap::iterator it; key.Remove('%'); it = strings.find(key); if (it != strings.end()) { key = it->second; } } else { key.Remove('"'); } val.TrimLeft(); val.TrimRight(); // // why is there no consistency in inf files? // if (val.GetLength() == 0) { val = key; } // // fix the manufacturer name if necessary // curPos = 0; val = val.Tokenize(L",", curPos); manufacturer->name = NormalizeManufacturerName( key ); manufacturer->tag = val; manufacturers[val] = manufacturer; } break; case ParsingModels: { check( iter != manufacturers.end() ); Model * model; int curPos = 0; CString name = s.Tokenize(L"=",curPos); CString description = s.Tokenize(L"=",curPos); if (name.Find('%') == 0) { StringMap::iterator it; name.Remove('%'); it = strings.find(name); if (it != strings.end()) { name = it->second; } } else { name.Remove('"'); } name.Trim(); description.Trim(); // // If true, see if we've seen this guy before // if (checkForDuplicateModels == true) { if ( MatchModel( iter->second, ConvertToModelName( name ) ) != NULL ) { continue; } } try { model = new Model; } catch (...) { model = NULL; } require_action( model, exit, err = kNoMemoryErr ); model->infFileName = filename; model->displayName = name; model->name = name; model->driverInstalled = false; iter->second->models.push_back(model); } break; default: { // pay no attention if we are in any other state } break; } } } exit: file.Close(); 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 = manufacturers.find(L"HP"); require_action( iter != 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) { CString normalizedProductName; Manufacturer * manufacturer = NULL; Manufacturer * genericManufacturer = NULL; Model * model = NULL; Model * genericModel = NULL; bool found = false; CString text; OSStatus err = kNoErr; // // first look to see if we have a usb_MFG descriptor // if (service->usb_MFG.GetLength() > 0) { manufacturer = MatchManufacturer( manufacturers, ConvertToManufacturerName ( service->usb_MFG ) ); } if ( manufacturer == NULL ) { service->product.Remove('('); service->product.Remove(')'); manufacturer = MatchManufacturer( manufacturers, ConvertToManufacturerName ( service->product ) ); } // // if we found the manufacturer, then start looking for the model // if ( manufacturer != NULL ) { if (service->usb_MDL.GetLength() > 0) { model = MatchModel ( manufacturer, ConvertToModelName ( service->usb_MDL ) ); } if ( ( model == NULL ) && ( service->product.GetLength() > 0 ) ) { service->product.Remove('('); service->product.Remove(')'); model = MatchModel ( manufacturer, ConvertToModelName ( service->product ) ); } if ( model != NULL ) { Manufacturers manufacturers; manufacturers[manufacturer->name] = manufacturer; SelectMatch(printer, service, manufacturers, 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); } else if ( MatchGeneric( printer, service, &genericManufacturer, &genericModel ) ) { Manufacturers * pManufacturers; Manufacturers manufacturers; text.LoadString(IDS_PRINTER_MATCH_MAYBE); if ( manufacturer ) { manufacturers[genericManufacturer->name] = genericManufacturer; manufacturers[manufacturer->name] = manufacturer; pManufacturers = &manufacturers; } else { pManufacturers = &m_manufacturers; } SelectMatch( printer, service, *pManufacturers, genericManufacturer, genericModel ); } 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); m_manufacturerListCtrl.EnsureVisible(nIndex, FALSE); } } } 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( Printer * printer, Service * service, Manufacturer ** manufacturer, Model ** model ) { CString pdl; BOOL ok = FALSE; DEBUG_UNUSED( printer ); Manufacturers::iterator iter = m_manufacturers.find( kGenericManufacturer ); require_action_quiet( iter != m_manufacturers.end(), exit, ok = FALSE ); *manufacturer = iter->second; pdl = service->pdl; pdl.MakeLower(); if ( pdl.Find( kPDLPCLKey ) != -1 ) { *model = m_genericPCL; ok = TRUE; } else if ( 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( 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, 138); m_manufacturerSelected = NULL; header.LoadString(IDS_MODEL_HEADING); m_modelListCtrl.InsertColumn(0, header, LVCFMT_LEFT, 247); 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 ); if ((m_manufacturerListCtrl.GetFirstSelectedItemPosition() != NULL) && (m_modelListCtrl.GetFirstSelectedItemPosition() != NULL)) { psheet->SetWizardButtons( PSWIZB_BACK|PSWIZB_NEXT ); } else { 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; } // // 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 // MatchPrinter( m_manufacturers, printer, service ); exit: return CPropertyPage::OnSetActive(); } // ------------------------------------------------------- // 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); } 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.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.GetState() ? true : false; exit: return; } void CThirdPage::OnBnClickedHaveDisk() { CPrinterSetupWizardSheet * psheet; Printer * printer; Service * service; 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 ); if ( dlg.DoModal() == IDOK ) { Manufacturers manufacturers; CString filename = dlg.GetPathName(); LoadPrintDriverDefsFromFile( manufacturers, filename, true ); PopulateUI( manufacturers ); MatchPrinter( manufacturers, printer, service ); } exit: return; }