diff options
Diffstat (limited to 'mDNSResponder/Clients/PrinterSetupWizard/ThirdPage.cpp')
-rw-r--r-- | mDNSResponder/Clients/PrinterSetupWizard/ThirdPage.cpp | 1583 |
1 files changed, 1583 insertions, 0 deletions
diff --git a/mDNSResponder/Clients/PrinterSetupWizard/ThirdPage.cpp b/mDNSResponder/Clients/PrinterSetupWizard/ThirdPage.cpp new file mode 100644 index 00000000..6caf55cb --- /dev/null +++ b/mDNSResponder/Clients/PrinterSetupWizard/ThirdPage.cpp @@ -0,0 +1,1583 @@ +/* -*- 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 <dns_sd.h> +#include <winspool.h> +#include <setupapi.h> + +// 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 ); + + // + // <rdar://problem/4826126> + // + // 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); + // + //<rdar://problem/4528853> 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<LPCTSTR>(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<LPCTSTR>(service->hostname), service->portNumber, static_cast<LPCTSTR>(q->name) ); + } + else + { + printer->portName.Format(L"LPR_%s.%d", static_cast<LPCTSTR>(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<LPCTSTR>(service->hostname), service->portNumber, static_cast<LPCTSTR>(q->name) ); + } + else + { + printer->portName.Format(L"http://%s:%d/", static_cast<LPCTSTR>(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 ) +{ + // + //<rdar://problem/4528853> 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: + // + // <http://msdn.microsoft.com/en-us/library/ms794359.aspx> + 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; + + // <rdar://problem/4030388> 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 ) + { + // <rdar://problem/4124524> 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; + } + + // <rdar://problem/4190104> 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 ) + { + // + // <rdar://problem/4496652> 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 ) + { + // + // <rdar://problem/4496652> 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); + + // + //<rdar://problem/4528853> 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; + } + + // + // <rdar://problem/3841218> + // 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<CPrinterSetupWizardSheet*>(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; + } + + // + // <rdar://problem/4580061> 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<CPrinterSetupWizardSheet*>(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<LPNMLISTVIEW>(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<LPNMLISTVIEW>(pNMHDR); + + CPrinterSetupWizardSheet * psheet; + Printer * printer; + Service * service; + + psheet = reinterpret_cast<CPrinterSetupWizardSheet*>(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<CPrinterSetupWizardSheet*>(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<CPrinterSetupWizardSheet*>(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 ); +} |