X-Git-Url: https://git.saurik.com/apple/mdnsresponder.git/blobdiff_plain/283ee3ffafe4266617c4a2c641f6e3b35227e7e9..2682e09e0dbe18ba42fd4707b09a89e8c34f697c:/Clients/PrinterSetupWizard/ThirdPage.cpp?ds=inline diff --git a/Clients/PrinterSetupWizard/ThirdPage.cpp b/Clients/PrinterSetupWizard/ThirdPage.cpp index 76978cd..6caf55c 100644 --- a/Clients/PrinterSetupWizard/ThirdPage.cpp +++ b/Clients/PrinterSetupWizard/ThirdPage.cpp @@ -1,120 +1,56 @@ -/* +/* -*- Mode: C; tab-width: 4 -*- + * * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * 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 * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 * - * 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 + * 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. - * - * @APPLE_LICENSE_HEADER_END@ - - Change History (most recent first): - -$Log: ThirdPage.cpp,v $ -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 "tcpxcv.h" #include -#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 +// with Windows XP and below // #define kNTPrintFile L"inf\\ntprint.inf" // -// These are pre-defined names for Generic manufacturer and model +// Windows Vista ships with a set of prn*.inf files // -#define kGenericManufacturer L"Generic" -#define kGenericModel L"Generic / Text Only" +#define kVistaPrintFiles L"inf\\prn*.inf" // -// states for parsing ntprint.inf +// These are pre-defined names for Generic manufacturer and model // -enum PrinterParsingState -{ - Looking, - ParsingManufacturers, - ParsingModels, - ParsingStrings -}; +#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 @@ -122,50 +58,113 @@ enum PrinterParsingState 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); -} - -CThirdPage::~CThirdPage() -{ // - // clean up all the printer manufacturers + // load printers from ntprint.inf // - while (m_manufacturers.size()) + 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 ) { - Manufacturers::iterator iter = m_manufacturers.begin(); + CString absolute; - while (iter->second->models.size()) + absolute.Format( L"%s\\inf\\%s", windowsDirectory, findFileData.cFileName ); + err = LoadPrintDriverDefsFromFile( m_manufacturers, absolute, false ); + require_noerr( err, exit ); + + while ( FindNextFile( findHandle, &findFileData ) ) { - Models::iterator it = iter->second->models.begin(); + absolute.Format( L"%s\\inf\\%s", windowsDirectory, findFileData.cFileName ); + err = LoadPrintDriverDefsFromFile( m_manufacturers, absolute, false ); + require_noerr( err, exit ); + } - Model * model = *it; + FindClose( findHandle ); + } + else + { + ntPrint.Format(L"%s\\%s", windowsDirectory, kNTPrintFile); + err = LoadPrintDriverDefsFromFile( m_manufacturers, ntPrint, false ); + require_noerr(err, exit); + } - delete model; + // + // load printer drivers that have been installed on this machine + // + err = LoadPrintDriverDefs( m_manufacturers ); + require_noerr(err, exit); - iter->second->models.erase(it); - } + // + // load our own special generic printer defs + // + err = LoadGenericPrintDriverDefs( m_manufacturers ); + require_noerr( err, exit ); - delete iter->second; +exit: - m_manufacturers.erase(iter); + 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 +// makes sure the printer object is update with the // latest settings // // ---------------------------------------------------- @@ -179,11 +178,6 @@ CThirdPage::SelectMatch(Printer * printer, Service * service, Manufacturer * man check( manufacturer != NULL ); check( model != NULL ); - Manufacturers manufacturers; - manufacturers[manufacturer->name] = manufacturer; - - PopulateUI( manufacturers ); - // // select the manufacturer // @@ -195,21 +189,24 @@ CThirdPage::SelectMatch(Printer * printer, Service * service, Manufacturer * man if (nIndex != -1) { m_manufacturerListCtrl.SetItemState(nIndex, LVIS_SELECTED, LVIS_SELECTED); - m_manufacturerListCtrl.EnsureVisible(nIndex, FALSE); + // + // 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->name; + 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); + AutoScroll( m_modelListCtrl, nIndex ); m_modelListCtrl.SetFocus(); } @@ -217,6 +214,13 @@ CThirdPage::SelectMatch(Printer * printer, Service * service, Manufacturer * man 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 @@ -228,8 +232,11 @@ CThirdPage::SelectMatch(Printer * printer, Service * service, Manufacturer * man void CThirdPage::CopyPrinterSettings( Printer * printer, Service * service, Manufacturer * manufacturer, Model * model ) { + DWORD portNameLen; + printer->manufacturer = manufacturer->name; - printer->model = model->name; + printer->displayModelName = model->displayName; + printer->modelName = model->name; printer->driverInstalled = model->driverInstalled; printer->infFileName = model->infFileName; @@ -261,7 +268,7 @@ CThirdPage::CopyPrinterSettings( Printer * printer, Service * service, Manufactu if ( q->name.GetLength() > 0 ) { - printer->portName.Format(L"http://%s:%d/printers/%s", static_cast(service->hostname), service->portNumber, static_cast(q->name) ); + printer->portName.Format(L"http://%s:%d/%s", static_cast(service->hostname), service->portNumber, static_cast(q->name) ); } else { @@ -270,31 +277,80 @@ CThirdPage::CopyPrinterSettings( Printer * printer, Service * service, Manufactu 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 // -// 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 +// 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, @@ -306,304 +362,190 @@ CThirdPage::CopyPrinterSettings( Printer * printer, Service * service, Manufactu 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; + 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 ); - StringMap strings; - - ok = file.Open( filename, CFile::modeRead|CFile::typeText, &feError); - err = translate_errno( ok, errno_compat(), kUnknownErr ); + // 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 ); - check ( state == Looking ); - check ( iter == manufacturers.end() ); + sectionCount = SetupGetLineCount( handle, section ); + translate_errno( sectionCount != -1, GetLastError(), kUnknownErr ); + require_noerr( err, exit ); - // - // first, parse the file looking for string sections - // - while (file.ReadString(s)) + memset( &manufacturerContext, 0, sizeof( manufacturerContext ) ); + + for ( LONG i = 0; i < sectionCount; i++ ) { - // - // check for comment - // - if (s.Find(';') == 0) - { - continue; - } - - // - // check for tag - // - else if (s.Find('[') == 0) + 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 ) { - // - // handle any capitalization issues here - // - CString tag = s; - - tag.MakeLower(); - - if (tag == L"[strings]") - { - state = ParsingStrings; - } - else - { - state = Looking; - } + ok = SetupFindFirstLine( handle, section, NULL, &manufacturerContext ); + err = translate_errno( ok, GetLastError(), kUnknownErr ); + require_noerr( err, exit ); } 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; - } + ok = SetupFindNextLine( &manufacturerContext, &manufacturerContext ); + err = translate_errno( ok, GetLastError(), kUnknownErr ); + require_noerr( err, exit ); } - } - - file.Close(); - ok = file.Open( filename, CFile::modeRead|CFile::typeText, &feError); - err = translate_errno( ok, errno_compat(), kUnknownErr ); - require_noerr( err, exit ); - - state = Looking; + ok = SetupGetStringField( &manufacturerContext, 0, line, sizeof( line ), NULL ); + err = translate_errno( ok, GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + manufacturerName = line; - check ( iter == manufacturers.end() ); + ok = SetupGetLineText( &manufacturerContext, handle, NULL, NULL, line, sizeof( line ), NULL ); + err = translate_errno( ok, GetLastError(), kUnknownErr ); + require_noerr( err, exit ); - while (file.ReadString(s)) - { - // - // check for comment + // Try to find some model section name that has entries. Explanation of int file structure + // can be found at: // - if (s.Find(';') == 0) + // + Split( line, ',', modelSectionNameDecl ); + + p = modelSectionNameDecl.GetHeadPosition(); + modelSectionName = modelSectionNameDecl.GetNext( p ); + modelCount = SetupGetLineCount( handle, modelSectionName ); + baseModelName = modelSectionName; + + while ( modelCount <= 0 && p ) { - continue; + CString targetOSVersion; + + targetOSVersion = modelSectionNameDecl.GetNext( p ); + modelSectionName = baseModelName + TEXT( "." ) + targetOSVersion; + modelCount = SetupGetLineCount( handle, modelSectionName ); } - // - // check for tag - // - else if (s.Find('[') == 0) + if ( modelCount > 0 ) { - // - // handle any capitalization issues here - // - CString tag = s; + manufacturerName = NormalizeManufacturerName( manufacturerName ); - tag.MakeLower(); + iter = manufacturers.find( manufacturerName ); - if (tag == L"[manufacturer]") + if ( iter != manufacturers.end() ) { - state = ParsingManufacturers; + manufacturer = iter->second; + require_action( manufacturer, exit, err = kUnknownErr ); } 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()) + try { - state = ParsingModels; + manufacturer = new Manufacturer; } - else + catch (...) { - state = Looking; + manufacturer = NULL; } - } - } - // - // 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(); + require_action( manufacturer, exit, err = kNoMemoryErr ); - // - // why is there no consistency in inf files? - // - if (val.GetLength() == 0) - { - val = key; - } + manufacturer->name = manufacturerName; + manufacturers[ manufacturerName ] = manufacturer; + } - // - // fix the manufacturer name if necessary - // - curPos = 0; - val = val.Tokenize(L",", curPos); + memset( &modelContext, 0, sizeof( modelContext ) ); - manufacturer->name = NormalizeManufacturerName( key ); - manufacturer->tag = val; + for ( LONG j = 0; j < modelCount; j++ ) + { + CString modelName; + Model * model; - manufacturers[val] = manufacturer; + if ( j == 0 ) + { + ok = SetupFindFirstLine( handle, modelSectionName, NULL, &modelContext ); + err = translate_errno( ok, GetLastError(), kUnknownErr ); + require_noerr( err, exit ); } - break; - - case ParsingModels: + else { - 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; + SetupFindNextLine( &modelContext, &modelContext ); + err = translate_errno( ok, GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + } - name.Remove('%'); + ok = SetupGetStringField( &modelContext, 0, line, sizeof( line ), NULL ); + err = translate_errno( ok, GetLastError(), kUnknownErr ); + require_noerr( err, exit ); - it = strings.find(name); + modelName = line; - 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 (...) + if (checkForDuplicateModels == true) + { + if ( MatchModel( manufacturer, ConvertToModelName( modelName ) ) != NULL ) { - model = NULL; + continue; } + } - require_action( model, exit, err = kNoMemoryErr ); - - model->infFileName = filename; - model->name = name; - model->driverInstalled = false; - - iter->second->models.push_back(model); + // + // 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; } - break; - default: + try { - // pay no attention if we are in any other state + model = new Model; } - break; + 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: - file.Close(); + if ( handle != INVALID_HANDLE_VALUE ) + { + SetupCloseInfFile( handle ); + handle = NULL; + } - return (err); + return err; } @@ -659,7 +601,7 @@ CThirdPage::LoadPrintDriverDefs( Manufacturers & manufacturers ) // // skip over anything that doesn't have a manufacturer field. This - // fixes a bug that I noticed that occurred after I installed + // 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. // @@ -722,6 +664,7 @@ CThirdPage::LoadPrintDriverDefs( Manufacturers & manufacturers ) require_action( model, exit, err = kNoMemoryErr ); + model->displayName = info[i].pName; model->name = info[i].pName; model->driverInstalled = true; @@ -740,11 +683,140 @@ exit: 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 +// 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) @@ -781,7 +853,6 @@ CThirdPage::ConvertToManufacturerName( const CString & name ) return lower; } - // ------------------------------------------------------ // ConvertToModelName // @@ -802,7 +873,6 @@ CThirdPage::ConvertToModelName( const CString & name ) return lower; } - // ------------------------------------------------------ // NormalizeManufacturerName // @@ -827,7 +897,6 @@ CThirdPage::NormalizeManufacturerName( const CString & name ) return normalized; } - // ------------------------------------------------------- // MatchPrinter // @@ -836,29 +905,38 @@ CThirdPage::NormalizeManufacturerName( const CString & name ) // MatchManufacturer and MatchModel in turn. // -OSStatus CThirdPage::MatchPrinter(Manufacturers & manufacturers, Printer * printer, Service * service) +OSStatus CThirdPage::MatchPrinter(Manufacturers & manufacturers, Printer * printer, Service * service, bool useCUPSWorkaround) { CString normalizedProductName; - Manufacturer * manufacturer = NULL; - Model * model = NULL; - bool found = false; + Manufacturer * manufacturer = NULL; + Manufacturer * genericManufacturer = NULL; + Model * model = NULL; + Model * genericModel = NULL; + bool found = false; CString text; - OSStatus err = kNoErr; + 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 (service->usb_MFG.GetLength() > 0) + if ( q->usb_MFG.GetLength() > 0) { - manufacturer = MatchManufacturer( manufacturers, ConvertToManufacturerName ( service->usb_MFG ) ); + manufacturer = MatchManufacturer( manufacturers, ConvertToManufacturerName ( q->usb_MFG ) ); } if ( manufacturer == NULL ) { - service->product.Remove('('); - service->product.Remove(')'); + q->product.Remove('('); + q->product.Remove(')'); - manufacturer = MatchManufacturer( manufacturers, ConvertToManufacturerName ( service->product ) ); + manufacturer = MatchManufacturer( manufacturers, ConvertToManufacturerName ( q->product ) ); } // @@ -866,22 +944,57 @@ OSStatus CThirdPage::MatchPrinter(Manufacturers & manufacturers, Printer * print // if ( manufacturer != NULL ) { - if (service->usb_MDL.GetLength() > 0) + if ( q->usb_MDL.GetLength() > 0 ) { - model = MatchModel ( manufacturer, ConvertToModelName ( service->usb_MDL ) ); + model = MatchModel ( manufacturer, ConvertToModelName ( q->usb_MDL ) ); } - if ( model == NULL ) + if ( ( model == NULL ) && ( q->product.GetLength() > 0 ) ) { - service->product.Remove('('); - service->product.Remove(')'); + q->product.Remove('('); + q->product.Remove(')'); - model = MatchModel ( manufacturer, ConvertToModelName ( service->product ) ); + model = MatchModel ( manufacturer, ConvertToModelName ( q->product ) ); } if ( model != NULL ) { - SelectMatch(printer, service, manufacturer, model); + // 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; } } @@ -893,6 +1006,32 @@ OSStatus CThirdPage::MatchPrinter(Manufacturers & manufacturers, Printer * print 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 { @@ -922,9 +1061,15 @@ OSStatus CThirdPage::MatchPrinter(Manufacturers & manufacturers, Printer * print if (nIndex != -1) { m_manufacturerListCtrl.SetItemState(nIndex, LVIS_SELECTED, LVIS_SELECTED); - m_manufacturerListCtrl.EnsureVisible(nIndex, FALSE); + + // + // 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); @@ -932,7 +1077,6 @@ OSStatus CThirdPage::MatchPrinter(Manufacturers & manufacturers, Printer * print return err; } - // ------------------------------------------------------ // MatchManufacturer // @@ -969,7 +1113,6 @@ CThirdPage::MatchManufacturer( Manufacturers & manufacturers, const CString & na return NULL; } - // ------------------------------------------------------- // MatchModel // @@ -1020,7 +1163,50 @@ CThirdPage::MatchModel(Manufacturer * manufacturer, const CString & name) 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 @@ -1031,18 +1217,14 @@ CThirdPage::MatchModel(Manufacturer * manufacturer, const CString & name) OSStatus CThirdPage::OnInitPage() { - static const int bufferSize = 32768; - TCHAR windowsDirectory[bufferSize]; - CString header; - CString ntPrint; - OSStatus err; - BOOL ok; - - // Load printer icon - + CString header; + CString ntPrint; + OSStatus err = kNoErr; + + // Load printer icon check( m_printerImage == NULL ); - m_printerImage = (CStatic*) GetDlgItem( IDR_MANIFEST ); + m_printerImage = (CStatic*) GetDlgItem( 1 ); // 1 == IDR_MANIFEST check( m_printerImage ); if ( m_printerImage != NULL ) @@ -1060,43 +1242,23 @@ OSStatus CThirdPage::OnInitPage() // // - // we have to make sure that we only do this once. Typically, + // 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_manufacturerListCtrl.InsertColumn(0, header, LVCFMT_LEFT, -1 ); m_manufacturerSelected = NULL; header.LoadString(IDS_MODEL_HEADING); - m_modelListCtrl.InsertColumn(0, header, LVCFMT_LEFT, 247); + m_modelListCtrl.InsertColumn(0, header, LVCFMT_LEFT, -1 ); m_modelSelected = NULL; - // - // 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); - -exit: - return (err); } - void CThirdPage::DoDataExchange(CDataExchange* pDX) { CPropertyPage::DoDataExchange(pDX); @@ -1104,9 +1266,9 @@ void CThirdPage::DoDataExchange(CDataExchange* pDX) 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); -} + DDX_Control(pDX, IDC_PRINTER_SELECTION_TEXT, m_printerSelectionText); +} // ---------------------------------------------------------- // OnSetActive @@ -1125,15 +1287,7 @@ CThirdPage::OnSetActive() 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 ); - } + psheet->SetWizardButtons( PSWIZB_BACK ); printer = psheet->GetSelectedPrinter(); require_quiet( printer, exit ); @@ -1150,6 +1304,20 @@ CThirdPage::OnSetActive() 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 // @@ -1164,13 +1332,40 @@ CThirdPage::OnSetActive() // // and try and match the printer // - MatchPrinter( m_manufacturers, printer, service ); + + 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 @@ -1193,12 +1388,13 @@ CThirdPage::PopulateUI(Manufacturers & manufacturers) 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) @@ -1206,7 +1402,6 @@ BEGIN_MESSAGE_MAP(CThirdPage, CPropertyPage) ON_BN_CLICKED(IDC_HAVE_DISK, OnBnClickedHaveDisk) END_MESSAGE_MAP() - // CThirdPage message handlers void CThirdPage::OnLvnItemchangedManufacturer(NMHDR *pNMHDR, LRESULT *pResult) { @@ -1230,9 +1425,11 @@ void CThirdPage::OnLvnItemchangedManufacturer(NMHDR *pNMHDR, LRESULT *pResult) { Model * model = *iter; - int nItem = m_modelListCtrl.InsertItem(0, model->name); + 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); @@ -1281,7 +1478,6 @@ exit: *pResult = 0; } - void CThirdPage::OnBnClickedDefaultPrinter() { CPrinterSetupWizardSheet * psheet; @@ -1293,7 +1489,7 @@ void CThirdPage::OnBnClickedDefaultPrinter() printer = psheet->GetSelectedPrinter(); require_quiet( printer, exit ); - printer->deflt = m_defaultPrinterCtrl.GetState() ? true : false; + printer->deflt = ( m_defaultPrinterCtrl.GetCheck() == BST_CHECKED ) ? true : false; exit: @@ -1305,6 +1501,7 @@ 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); @@ -1317,19 +1514,70 @@ void CThirdPage::OnBnClickedHaveDisk() service = printer->services.front(); require_quiet( service, exit ); - if ( dlg.DoModal() == IDOK ) + for ( ;; ) { - Manufacturers manufacturers; - CString filename = dlg.GetPathName(); + if ( dlg.DoModal() == IDOK ) + { + CString filename = dlg.GetPathName(); - LoadPrintDriverDefsFromFile( manufacturers, filename, true ); + LoadPrintDriverDefsFromFile( manufacturers, filename, true ); - PopulateUI( manufacturers ); + // 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 ); + } - MatchPrinter( manufacturers, printer, service ); + 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 ); +}