2 * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
23 Change History (most recent first):
25 $Log: ThirdPage.cpp,v $
26 Revision 1.20 2005/03/05 02:27:45 shersche
27 <rdar://problem/4030388> Generic drivers don't do color
29 Revision 1.19 2005/02/23 02:08:51 shersche
30 <rdar://problem/4012275> If we can't match the manufacturer, and select a generic printer, then show all the manufacturers in the manufacturer pane, not just "Generic".
32 Revision 1.18 2005/02/15 07:02:51 shersche
33 <rdar://problem/4003724> Display different UI text when generic printer drivers are selected
35 Revision 1.17 2005/02/08 21:45:06 shersche
36 <rdar://problem/3947490> Default to Generic PostScript or PCL if unable to match driver
38 Revision 1.16 2005/02/08 18:56:03 shersche
39 Fix generated IPP url so that it doesn't add "/printers" string
41 Revision 1.15 2005/02/01 01:44:07 shersche
42 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.
44 Revision 1.14 2005/01/25 08:55:54 shersche
45 <rdar://problem/3911084> Load icons at run-time from resource DLL
48 Revision 1.13 2005/01/06 08:15:45 shersche
49 Append queue name to end of LPR port name, correctly build port name when queue name is absent
51 Revision 1.12 2005/01/05 01:06:12 shersche
52 <rdar://problem/3841218> Strip the first substring off the product key if an initial match can't be found with the whole product key.
55 Revision 1.11 2004/12/29 18:53:38 shersche
56 <rdar://problem/3725106>
57 <rdar://problem/3737413> Added support for LPR and IPP protocols as well as support for obtaining multiple text records. Reorganized and simplified codebase.
58 Bug #: 3725106, 3737413
60 Revision 1.10 2004/10/11 22:55:34 shersche
61 <rdar://problem/3827624> Use the IP port number when deriving the printer port name.
64 Revision 1.9 2004/06/27 23:08:00 shersche
65 code cleanup, make sure EnumPrintDrivers returns non-zero value, ignore comments in inf files
67 Revision 1.8 2004/06/27 08:06:45 shersche
68 Parse [Strings] section of inf file
70 Revision 1.7 2004/06/26 04:00:05 shersche
71 fix warnings compiling in debug mode
72 Submitted by: herscher
74 Revision 1.6 2004/06/26 03:19:57 shersche
75 clean up warning messages
77 Submitted by: herscher
79 Revision 1.5 2004/06/25 05:06:02 shersche
80 Trim whitespace from key/value pairs when parsing inf files
81 Submitted by: herscher
83 Revision 1.4 2004/06/25 02:44:13 shersche
84 Tweaked code to handle Xerox Phaser printer identification
85 Submitted by: herscher
87 Revision 1.3 2004/06/25 02:27:58 shersche
88 Do a CListCtrl::FindItem() before calling CListCtrl::SetItemState().
89 Submitted by: herscher
91 Revision 1.2 2004/06/23 18:09:23 shersche
92 Normalize tag names when parsing inf files.
93 Submitted by: herscher
95 Revision 1.1 2004/06/18 04:36:58 rpantos
102 #include "PrinterSetupWizardApp.h"
103 #include "PrinterSetupWizardSheet.h"
104 #include "ThirdPage.h"
105 #include "StdioFileEx.h"
108 #include <winspool.h>
110 // local variable is initialize but not referenced
111 #pragma warning(disable:4189)
115 // This is the printer description file that is shipped
118 #define kNTPrintFile L"inf\\ntprint.inf"
121 // These are pre-defined names for Generic manufacturer and model
123 #define kGenericManufacturer L"Generic"
124 #define kGenericText L"Generic / Text Only"
125 #define kGenericPostscript L"Generic / Postscript"
126 #define kGenericPCL L"Generic / PCL"
127 #define kPDLPostscriptKey L"application/postscript"
128 #define kPDLPCLKey L"application/vnd.hp-pcl"
129 #define kGenericPSColorDriver L"HP Color LaserJet 4550 PS"
130 #define kGenericPSDriver L"HP LaserJet 4050 Series PS"
131 #define kGenericPCLColorDriver L"HP Color LaserJet 4550 PCL"
132 #define kGenericPCLDriver L"HP LaserJet 4050 Series PCL"
136 // states for parsing ntprint.inf
138 enum PrinterParsingState
141 ParsingManufacturers
,
149 IMPLEMENT_DYNAMIC(CThirdPage
, CPropertyPage
)
150 CThirdPage::CThirdPage()
151 : CPropertyPage(CThirdPage::IDD
),
152 m_initialized(false),
153 m_printerImage( NULL
)
155 static const int bufferSize
= 32768;
156 TCHAR windowsDirectory
[bufferSize
];
162 m_psp
.dwFlags
&= ~(PSP_HASHELP
);
163 m_psp
.dwFlags
|= PSP_DEFAULT
|PSP_USEHEADERTITLE
|PSP_USEHEADERSUBTITLE
;
165 m_psp
.pszHeaderTitle
= MAKEINTRESOURCE(IDS_INSTALL_TITLE
);
166 m_psp
.pszHeaderSubTitle
= MAKEINTRESOURCE(IDS_INSTALL_SUBTITLE
);
169 // load printers from ntprint.inf
171 ok
= GetWindowsDirectory( windowsDirectory
, bufferSize
);
172 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
173 require_noerr( err
, exit
);
175 ntPrint
.Format(L
"%s\\%s", windowsDirectory
, kNTPrintFile
);
176 err
= LoadPrintDriverDefsFromFile( m_manufacturers
, ntPrint
, false );
177 require_noerr(err
, exit
);
180 // load printer drivers that have been installed on this machine
182 err
= LoadPrintDriverDefs( m_manufacturers
);
183 require_noerr(err
, exit
);
186 // and lastly load our own special generic printer defs
188 err
= LoadGenericPrintDriverDefs( m_manufacturers
);
189 require_noerr( err
, exit
);
197 CThirdPage::~CThirdPage()
200 // clean up all the printer manufacturers
202 while (m_manufacturers
.size())
204 Manufacturers::iterator iter
= m_manufacturers
.begin();
206 while (iter
->second
->models
.size())
208 Models::iterator it
= iter
->second
->models
.begin();
214 iter
->second
->models
.erase(it
);
219 m_manufacturers
.erase(iter
);
224 // ----------------------------------------------------
227 // SelectMatch will do all the UI work associated with
228 // selected a manufacturer and model of printer. It also
229 // makes sure the printer object is update with the
232 // ----------------------------------------------------
234 CThirdPage::SelectMatch(Printer
* printer
, Service
* service
, Manufacturers
& manufacturers
, Manufacturer
* manufacturer
, Model
* model
)
239 check( printer
!= NULL
);
240 check( manufacturer
!= NULL
);
241 check( model
!= NULL
);
243 PopulateUI( manufacturers
);
246 // select the manufacturer
248 info
.flags
= LVFI_STRING
;
249 info
.psz
= manufacturer
->name
;
251 nIndex
= m_manufacturerListCtrl
.FindItem(&info
);
255 m_manufacturerListCtrl
.SetItemState(nIndex
, LVIS_SELECTED
, LVIS_SELECTED
);
256 m_manufacturerListCtrl
.EnsureVisible(nIndex
, FALSE
);
262 info
.flags
= LVFI_STRING
;
263 info
.psz
= model
->displayName
;
265 nIndex
= m_modelListCtrl
.FindItem(&info
);
269 m_modelListCtrl
.SetItemState(nIndex
, LVIS_SELECTED
, LVIS_SELECTED
);
270 m_modelListCtrl
.EnsureVisible(nIndex
, FALSE
);
272 m_modelListCtrl
.SetFocus();
275 CopyPrinterSettings( printer
, service
, manufacturer
, model
);
279 // --------------------------------------------------------
280 // CopyPrinterSettings
282 // This function makes sure that the printer object has the
283 // latest settings from the manufacturer and model objects
284 // --------------------------------------------------------
287 CThirdPage::CopyPrinterSettings( Printer
* printer
, Service
* service
, Manufacturer
* manufacturer
, Model
* model
)
289 printer
->manufacturer
= manufacturer
->name
;
290 printer
->displayModelName
= model
->displayName
;
291 printer
->modelName
= model
->name
;
292 printer
->driverInstalled
= model
->driverInstalled
;
293 printer
->infFileName
= model
->infFileName
;
295 if ( service
->type
== kPDLServiceType
)
297 printer
->portName
.Format(L
"IP_%s.%d", static_cast<LPCTSTR
>(service
->hostname
), service
->portNumber
);
298 service
->protocol
= L
"Raw";
300 else if ( service
->type
== kLPRServiceType
)
302 Queue
* q
= service
->queues
.front();
305 if ( q
->name
.GetLength() > 0 )
307 printer
->portName
.Format(L
"LPR_%s.%d.%s", static_cast<LPCTSTR
>(service
->hostname
), service
->portNumber
, static_cast<LPCTSTR
>(q
->name
) );
311 printer
->portName
.Format(L
"LPR_%s.%d", static_cast<LPCTSTR
>(service
->hostname
), service
->portNumber
);
314 service
->protocol
= L
"LPR";
316 else if ( service
->type
== kIPPServiceType
)
318 Queue
* q
= service
->queues
.front();
321 if ( q
->name
.GetLength() > 0 )
323 printer
->portName
.Format(L
"http://%s:%d/%s", static_cast<LPCTSTR
>(service
->hostname
), service
->portNumber
, static_cast<LPCTSTR
>(q
->name
) );
327 printer
->portName
.Format(L
"http://%s:%d/", static_cast<LPCTSTR
>(service
->hostname
), service
->portNumber
);
330 service
->protocol
= L
"IPP";
335 // ------------------------------------------------------
336 // LoadPrintDriverDefsFromFile
338 // This function does all the heavy lifting in parsing inf
339 // files. It is called to parse both ntprint.inf, and driver
340 // files that might be shipped on a printer's installation
343 // The inf file is not totally parsed. I only want to determine
344 // the manufacturer and models that are involved. I leave it
345 // to printui.dll to actually copy the driver files to the
348 // I was aiming to parse as little as I could so as not to
349 // duplicate the parsing code that is contained in Windows. There
350 // are no public APIs for parsing inf files.
352 // That part of the inf file that we're interested in has a fairly
353 // easy format. Tags are strings that are enclosed in brackets.
354 // We are only interested in [MANUFACTURERS] and models.
356 // The only potentially opaque thing about this function is the
357 // checkForDuplicateModels flag. The problem here is that ntprint.inf
358 // doesn't contain duplicate models, and it has hundreds of models
359 // listed. You wouldn't check for duplicates there. But oftentimes,
360 // loading different windows print driver files contain multiple
361 // entries for the same printer. You don't want the UI to display
362 // the same printer multiple times, so in that case, you would ask
363 // this function to check for multiple models.
366 CThirdPage::LoadPrintDriverDefsFromFile(Manufacturers
& manufacturers
, const CString
& filename
, bool checkForDuplicateModels
)
368 PrinterParsingState state
= Looking
;
369 Manufacturers::iterator iter
= manufacturers
.end();
371 CFileException feError
;
376 typedef std::map
<CString
, CString
> StringMap
;
380 ok
= file
.Open( filename
, CFile::modeRead
|CFile::typeText
, &feError
);
381 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
382 require_noerr( err
, exit
);
384 check ( state
== Looking
);
385 check ( iter
== manufacturers
.end() );
388 // first, parse the file looking for string sections
390 while (file
.ReadString(s
))
395 if (s
.Find(';') == 0)
403 else if (s
.Find('[') == 0)
406 // handle any capitalization issues here
412 if (tag
== L
"[strings]")
414 state
= ParsingStrings
;
429 if (s
.GetLength() > 0)
431 CString key
= s
.Tokenize(L
"=",curPos
);
432 CString val
= s
.Tokenize(L
"=",curPos
);
435 // get rid of all delimiters
453 ok
= file
.Open( filename
, CFile::modeRead
|CFile::typeText
, &feError
);
454 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
455 require_noerr( err
, exit
);
459 check ( iter
== manufacturers
.end() );
461 while (file
.ReadString(s
))
466 if (s
.Find(';') == 0)
474 else if (s
.Find('[') == 0)
477 // handle any capitalization issues here
483 if (tag
== L
"[manufacturer]")
485 state
= ParsingManufacturers
;
489 // remove the leading and trailing delimiters
494 // check to see if this is a printer entry
496 iter
= manufacturers
.find(s
);
498 if (iter
!= manufacturers
.end())
500 state
= ParsingModels
;
509 // only look at this if the line isn't empty, or
510 // if it isn't a comment
512 else if ((s
.GetLength() > 0) && (s
.Find(';') != 0))
517 // if we're parsing manufacturers, then we will parse
518 // an entry of the form key=val, where key is a delimited
519 // string specifying a manufacturer name, and val is
520 // a tag that is used later in the file. the key is
521 // delimited by either '"' (quotes) or '%' (percent sign).
523 // the tag is used further down the file when models are
524 // declared. this allows multiple manufacturers to exist
525 // in a single inf file.
527 case ParsingManufacturers
:
529 Manufacturer
* manufacturer
;
532 CString key
= s
.Tokenize(L
"=",curPos
);
533 CString val
= s
.Tokenize(L
"=",curPos
);
537 manufacturer
= new Manufacturer
;
544 require_action( manufacturer
, exit
, err
= kNoMemoryErr
);
547 // if it's a variable, look it up
549 if (key
.Find('%') == 0)
551 StringMap::iterator it
;
555 it
= strings
.find(key
);
557 if (it
!= strings
.end())
571 // why is there no consistency in inf files?
573 if (val
.GetLength() == 0)
579 // fix the manufacturer name if necessary
582 val
= val
.Tokenize(L
",", curPos
);
584 manufacturer
->name
= NormalizeManufacturerName( key
);
585 manufacturer
->tag
= val
;
587 manufacturers
[val
] = manufacturer
;
593 check( iter
!= manufacturers
.end() );
598 CString name
= s
.Tokenize(L
"=",curPos
);
599 CString description
= s
.Tokenize(L
"=",curPos
);
601 if (name
.Find('%') == 0)
603 StringMap::iterator it
;
607 it
= strings
.find(name
);
609 if (it
!= strings
.end())
623 // If true, see if we've seen this guy before
625 if (checkForDuplicateModels
== true)
627 if ( MatchModel( iter
->second
, ConvertToModelName( name
) ) != NULL
)
642 require_action( model
, exit
, err
= kNoMemoryErr
);
644 model
->infFileName
= filename
;
645 model
->displayName
= name
;
647 model
->driverInstalled
= false;
649 iter
->second
->models
.push_back(model
);
655 // pay no attention if we are in any other state
670 // -------------------------------------------------------
671 // LoadPrintDriverDefs
673 // This function is responsible for loading the print driver
674 // definitions of all print drivers that have been installed
676 // -------------------------------------------------------
678 CThirdPage::LoadPrintDriverDefs( Manufacturers
& manufacturers
)
680 BYTE
* buffer
= NULL
;
681 DWORD bytesReceived
= 0;
682 DWORD numPrinters
= 0;
687 // like a lot of win32 calls, we call this first to get the
688 // size of the buffer we need.
690 EnumPrinterDrivers(NULL
, L
"all", 6, NULL
, 0, &bytesReceived
, &numPrinters
);
692 if (bytesReceived
> 0)
696 buffer
= new BYTE
[bytesReceived
];
703 require_action( buffer
, exit
, err
= kNoMemoryErr
);
706 // this call gets the real info
708 ok
= EnumPrinterDrivers(NULL
, L
"all", 6, buffer
, bytesReceived
, &bytesReceived
, &numPrinters
);
709 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
710 require_noerr( err
, exit
);
712 DRIVER_INFO_6
* info
= (DRIVER_INFO_6
*) buffer
;
714 for (DWORD i
= 0; i
< numPrinters
; i
++)
716 Manufacturer
* manufacturer
;
721 // skip over anything that doesn't have a manufacturer field. This
722 // fixes a bug that I noticed that occurred after I installed
723 // ProComm. This program add a print driver with no manufacturer
724 // that screwed up this wizard.
726 if (info
[i
].pszMfgName
== NULL
)
732 // look for manufacturer
734 Manufacturers::iterator iter
;
739 name
= NormalizeManufacturerName( info
[i
].pszMfgName
);
741 iter
= manufacturers
.find(name
);
743 if (iter
!= manufacturers
.end())
745 manufacturer
= iter
->second
;
751 manufacturer
= new Manufacturer
;
758 require_action( manufacturer
, exit
, err
= kNoMemoryErr
);
760 manufacturer
->name
= name
;
762 manufacturers
[name
] = manufacturer
;
766 // now look to see if we have already seen this guy. this could
767 // happen if we have already installed printers that are described
768 // in ntprint.inf. the extant drivers will show up in EnumPrinterDrivers
769 // but we have already loaded their info
772 if ( MatchModel( manufacturer
, ConvertToModelName( info
[i
].pName
) ) == NULL
)
783 require_action( model
, exit
, err
= kNoMemoryErr
);
785 model
->displayName
= info
[i
].pName
;
786 model
->name
= info
[i
].pName
;
787 model
->driverInstalled
= true;
789 manufacturer
->models
.push_back(model
);
805 // -------------------------------------------------------
806 // LoadGenericPrintDriverDefs
808 // This function is responsible for loading polymorphic
809 // generic print drivers defs. The UI will read
810 // something like "Generic / Postscript" and we can map
811 // that to any print driver we want.
812 // -------------------------------------------------------
814 CThirdPage::LoadGenericPrintDriverDefs( Manufacturers
& manufacturers
)
816 Manufacturer
* manufacturer
;
818 Manufacturers::iterator iter
;
819 CString psDriverName
;
820 CString pclDriverName
;
823 // <rdar://problem/4030388> Generic drivers don't do color
825 // First try and find our generic driver names
827 iter
= manufacturers
.find(L
"HP");
828 require_action( iter
!= manufacturers
.end(), exit
, err
= kUnknownErr
);
829 manufacturer
= iter
->second
;
831 // Look for Postscript
833 model
= manufacturer
->find( kGenericPSColorDriver
);
837 model
= manufacturer
->find( kGenericPSDriver
);
842 psDriverName
= model
->name
;
848 model
= manufacturer
->find( kGenericPCLColorDriver
);
852 model
= manufacturer
->find( kGenericPCLDriver
);
857 pclDriverName
= model
->name
;
860 // If we found either a generic PS driver, or a generic PCL driver,
861 // then add them to the list
863 if ( psDriverName
.GetLength() || pclDriverName
.GetLength() )
865 // Try and find generic manufacturer if there is one
867 iter
= manufacturers
.find(L
"Generic");
869 if (iter
!= manufacturers
.end())
871 manufacturer
= iter
->second
;
877 manufacturer
= new Manufacturer
;
884 require_action( manufacturer
, exit
, err
= kNoMemoryErr
);
886 manufacturer
->name
= "Generic";
887 manufacturers
[manufacturer
->name
] = manufacturer
;
890 if ( psDriverName
.GetLength() > 0 )
894 m_genericPostscript
= new Model
;
898 m_genericPostscript
= NULL
;
901 require_action( m_genericPostscript
, exit
, err
= kNoMemoryErr
);
903 m_genericPostscript
->displayName
= kGenericPostscript
;
904 m_genericPostscript
->name
= psDriverName
;
905 m_genericPostscript
->driverInstalled
= false;
907 manufacturer
->models
.push_back( m_genericPostscript
);
910 if ( pclDriverName
.GetLength() > 0 )
914 m_genericPCL
= new Model
;
921 require_action( m_genericPCL
, exit
, err
= kNoMemoryErr
);
923 m_genericPCL
->displayName
= kGenericPCL
;
924 m_genericPCL
->name
= pclDriverName
;
925 m_genericPCL
->driverInstalled
= false;
927 manufacturer
->models
.push_back( m_genericPCL
);
936 // ------------------------------------------------------
937 // ConvertToManufacturerName
939 // This function is responsible for tweaking the
940 // name so that subsequent string operations won't fail because
941 // of capitalizations/different names for the same manufacturer
942 // (i.e. Hewlett-Packard/HP/Hewlett Packard)
945 CThirdPage::ConvertToManufacturerName( const CString
& name
)
948 // first we're going to convert all the characters to lower
951 CString lower
= name
;
955 // now we're going to check to see if the string says "hewlett-packard",
956 // because sometimes they refer to themselves as "hewlett-packard", and
957 // sometimes they refer to themselves as "hp".
959 if ( lower
== L
"hewlett-packard")
965 // tweak for Xerox Phaser, which doesn't announce itself
968 else if ( lower
.Find( L
"phaser", 0 ) != -1 )
977 // ------------------------------------------------------
978 // ConvertToModelName
980 // This function is responsible for ensuring that subsequent
981 // string operations don't fail because of differing capitalization
982 // schemes and the like
983 // ------------------------------------------------------
986 CThirdPage::ConvertToModelName( const CString
& name
)
989 // convert it to lowercase
991 CString lower
= name
;
998 // ------------------------------------------------------
999 // NormalizeManufacturerName
1001 // This function is responsible for tweaking the manufacturer
1002 // name so that there are no aliases for vendors
1005 CThirdPage::NormalizeManufacturerName( const CString
& name
)
1007 CString normalized
= name
;
1010 // now we're going to check to see if the string says "hewlett-packard",
1011 // because sometimes they refer to themselves as "hewlett-packard", and
1012 // sometimes they refer to themselves as "hp".
1014 if ( normalized
== L
"Hewlett-Packard")
1023 // -------------------------------------------------------
1026 // This function is responsible for matching a printer
1027 // to a list of manufacturers and models. It calls
1028 // MatchManufacturer and MatchModel in turn.
1031 OSStatus
CThirdPage::MatchPrinter(Manufacturers
& manufacturers
, Printer
* printer
, Service
* service
)
1033 CString normalizedProductName
;
1034 Manufacturer
* manufacturer
= NULL
;
1035 Manufacturer
* genericManufacturer
= NULL
;
1036 Model
* model
= NULL
;
1037 Model
* genericModel
= NULL
;
1040 OSStatus err
= kNoErr
;
1043 // first look to see if we have a usb_MFG descriptor
1045 if (service
->usb_MFG
.GetLength() > 0)
1047 manufacturer
= MatchManufacturer( manufacturers
, ConvertToManufacturerName ( service
->usb_MFG
) );
1050 if ( manufacturer
== NULL
)
1052 service
->product
.Remove('(');
1053 service
->product
.Remove(')');
1055 manufacturer
= MatchManufacturer( manufacturers
, ConvertToManufacturerName ( service
->product
) );
1059 // if we found the manufacturer, then start looking for the model
1061 if ( manufacturer
!= NULL
)
1063 if (service
->usb_MDL
.GetLength() > 0)
1065 model
= MatchModel ( manufacturer
, ConvertToModelName ( service
->usb_MDL
) );
1068 if ( ( model
== NULL
) && ( service
->product
.GetLength() > 0 ) )
1070 service
->product
.Remove('(');
1071 service
->product
.Remove(')');
1073 model
= MatchModel ( manufacturer
, ConvertToModelName ( service
->product
) );
1076 if ( model
!= NULL
)
1078 Manufacturers manufacturers
;
1080 manufacturers
[manufacturer
->name
] = manufacturer
;
1081 SelectMatch(printer
, service
, manufacturers
, manufacturer
, model
);
1087 // display a message to the user based on whether we could match
1092 text
.LoadString(IDS_PRINTER_MATCH_GOOD
);
1094 else if ( MatchGeneric( printer
, service
, &genericManufacturer
, &genericModel
) )
1096 Manufacturers
* pManufacturers
;
1097 Manufacturers manufacturers
;
1099 text
.LoadString(IDS_PRINTER_MATCH_MAYBE
);
1103 manufacturers
[genericManufacturer
->name
] = genericManufacturer
;
1104 manufacturers
[manufacturer
->name
] = manufacturer
;
1106 pManufacturers
= &manufacturers
;
1110 pManufacturers
= &m_manufacturers
;
1113 SelectMatch( printer
, service
, *pManufacturers
, genericManufacturer
, genericModel
);
1117 text
.LoadString(IDS_PRINTER_MATCH_BAD
);
1120 // if there was any crud in this list from before, get rid of it now
1122 m_modelListCtrl
.DeleteAllItems();
1125 // select the manufacturer if we found one
1127 if (manufacturer
!= NULL
)
1133 // select the manufacturer
1135 info
.flags
= LVFI_STRING
;
1136 info
.psz
= manufacturer
->name
;
1138 nIndex
= m_manufacturerListCtrl
.FindItem(&info
);
1142 m_manufacturerListCtrl
.SetItemState(nIndex
, LVIS_SELECTED
, LVIS_SELECTED
);
1143 m_manufacturerListCtrl
.EnsureVisible(nIndex
, FALSE
);
1148 m_printerSelectionText
.SetWindowText(text
);
1154 // ------------------------------------------------------
1155 // MatchManufacturer
1157 // This function is responsible for finding a manufacturer
1158 // object from a string name. It does a CString::Find, which
1159 // is like strstr, so it doesn't have to do an exact match
1161 // If it can't find a match, NULL is returned
1162 // ------------------------------------------------------
1165 CThirdPage::MatchManufacturer( Manufacturers
& manufacturers
, const CString
& name
)
1167 Manufacturers::iterator iter
;
1169 for (iter
= manufacturers
.begin(); iter
!= manufacturers
.end(); iter
++)
1172 // we're going to convert all the manufacturer names to lower case,
1173 // so we match the name passed in.
1175 CString lower
= iter
->second
->name
;
1179 // now try and find the lowered string in the name passed in.
1181 if (name
.Find(lower
) != -1)
1183 return iter
->second
;
1191 // -------------------------------------------------------
1194 // This function is responsible for matching a model from
1195 // a name. It does a CString::Find(), which works like strstr,
1196 // so it doesn't rely on doing an exact string match.
1200 CThirdPage::MatchModel(Manufacturer
* manufacturer
, const CString
& name
)
1202 Models::iterator iter
;
1204 iter
= manufacturer
->models
.begin();
1206 for (iter
= manufacturer
->models
.begin(); iter
!= manufacturer
->models
.end(); iter
++)
1208 Model
* model
= *iter
;
1211 // convert the model name to lower case
1213 CString lowered
= model
->name
;
1214 lowered
.MakeLower();
1216 if (lowered
.Find( name
) != -1)
1222 // <rdar://problem/3841218>
1223 // try removing the first substring and search again
1226 if ( name
.Find(' ') != -1 )
1228 CString altered
= name
;
1229 altered
.Delete( 0, altered
.Find(' ') + 1 );
1231 if ( lowered
.Find( altered
) != -1 )
1242 // -------------------------------------------------------
1245 // This function will attempt to find a generic printer
1246 // driver for a printer that we weren't able to match
1250 CThirdPage::MatchGeneric( Printer
* printer
, Service
* service
, Manufacturer
** manufacturer
, Model
** model
)
1255 DEBUG_UNUSED( printer
);
1257 Manufacturers::iterator iter
= m_manufacturers
.find( kGenericManufacturer
);
1258 require_action_quiet( iter
!= m_manufacturers
.end(), exit
, ok
= FALSE
);
1260 *manufacturer
= iter
->second
;
1265 if ( pdl
.Find( kPDLPCLKey
) != -1 )
1267 *model
= m_genericPCL
;
1270 else if ( pdl
.Find( kPDLPostscriptKey
) != -1 )
1272 *model
= m_genericPostscript
;
1282 // -----------------------------------------------------------
1285 // This function is responsible for doing initialization that
1286 // only occurs once during a run of the wizard
1289 OSStatus
CThirdPage::OnInitPage()
1293 OSStatus err
= kNoErr
;
1295 // Load printer icon
1299 check( m_printerImage
== NULL
);
1301 m_printerImage
= (CStatic
*) GetDlgItem( IDR_MANIFEST
);
1302 check( m_printerImage
);
1304 if ( m_printerImage
!= NULL
)
1306 m_printerImage
->SetIcon( LoadIcon( GetNonLocalizedResources(), MAKEINTRESOURCE( IDI_PRINTER
) ) );
1310 // The CTreeCtrl widget automatically sends a selection changed
1311 // message which initially we want to ignore, because the user
1312 // hasn't selected anything
1314 // this flag gets reset in the message handler. Every subsequent
1315 // message gets handled.
1319 // we have to make sure that we only do this once. Typically,
1320 // we would do this in something like OnInitDialog, but we don't
1321 // have this in Wizards, because the window is a PropertySheet.
1322 // We're considered fully initialized when we receive the first
1325 header
.LoadString(IDS_MANUFACTURER_HEADING
);
1326 m_manufacturerListCtrl
.InsertColumn(0, header
, LVCFMT_LEFT
, 138);
1327 m_manufacturerSelected
= NULL
;
1329 header
.LoadString(IDS_MODEL_HEADING
);
1330 m_modelListCtrl
.InsertColumn(0, header
, LVCFMT_LEFT
, 247);
1331 m_modelSelected
= NULL
;
1337 void CThirdPage::DoDataExchange(CDataExchange
* pDX
)
1339 CPropertyPage::DoDataExchange(pDX
);
1340 DDX_Control(pDX
, IDC_PRINTER_MANUFACTURER
, m_manufacturerListCtrl
);
1341 DDX_Control(pDX
, IDC_PRINTER_MODEL
, m_modelListCtrl
);
1342 DDX_Control(pDX
, IDC_PRINTER_NAME
, m_printerName
);
1343 DDX_Control(pDX
, IDC_DEFAULT_PRINTER
, m_defaultPrinterCtrl
);
1344 DDX_Control(pDX
, IDC_PRINTER_SELECTION_TEXT
, m_printerSelectionText
);
1349 // ----------------------------------------------------------
1352 // This function is called by MFC after the window has been
1357 CThirdPage::OnSetActive()
1359 CPrinterSetupWizardSheet
* psheet
;
1363 psheet
= reinterpret_cast<CPrinterSetupWizardSheet
*>(GetParent());
1364 require_quiet( psheet
, exit
);
1366 if ((m_manufacturerListCtrl
.GetFirstSelectedItemPosition() != NULL
) &&
1367 (m_modelListCtrl
.GetFirstSelectedItemPosition() != NULL
))
1369 psheet
->SetWizardButtons( PSWIZB_BACK
|PSWIZB_NEXT
);
1373 psheet
->SetWizardButtons( PSWIZB_BACK
);
1376 printer
= psheet
->GetSelectedPrinter();
1377 require_quiet( printer
, exit
);
1379 service
= printer
->services
.front();
1380 require_quiet( service
, exit
);
1383 // call OnInitPage once
1388 m_initialized
= true;
1392 // update the UI with the printer name
1394 m_printerName
.SetWindowText(printer
->displayName
);
1397 // populate the list controls with the manufacturers and models
1400 PopulateUI( m_manufacturers
);
1403 // and try and match the printer
1405 MatchPrinter( m_manufacturers
, printer
, service
);
1409 return CPropertyPage::OnSetActive();
1413 // -------------------------------------------------------
1416 // This function is called to populate the list of manufacturers
1419 CThirdPage::PopulateUI(Manufacturers
& manufacturers
)
1421 Manufacturers::iterator iter
;
1423 m_manufacturerListCtrl
.DeleteAllItems();
1425 for (iter
= manufacturers
.begin(); iter
!= manufacturers
.end(); iter
++)
1429 Manufacturer
* manufacturer
= iter
->second
;
1431 nIndex
= m_manufacturerListCtrl
.InsertItem(0, manufacturer
->name
);
1433 m_manufacturerListCtrl
.SetItemData(nIndex
, (DWORD_PTR
) manufacturer
);
1440 BEGIN_MESSAGE_MAP(CThirdPage
, CPropertyPage
)
1441 ON_NOTIFY(LVN_ITEMCHANGED
, IDC_PRINTER_MANUFACTURER
, OnLvnItemchangedManufacturer
)
1442 ON_NOTIFY(LVN_ITEMCHANGED
, IDC_PRINTER_MODEL
, OnLvnItemchangedPrinterModel
)
1443 ON_BN_CLICKED(IDC_DEFAULT_PRINTER
, OnBnClickedDefaultPrinter
)
1444 ON_BN_CLICKED(IDC_HAVE_DISK
, OnBnClickedHaveDisk
)
1448 // CThirdPage message handlers
1449 void CThirdPage::OnLvnItemchangedManufacturer(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1451 LPNMLISTVIEW pNMLV
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
1453 POSITION p
= m_manufacturerListCtrl
.GetFirstSelectedItemPosition();
1454 int nSelected
= m_manufacturerListCtrl
.GetNextSelectedItem(p
);
1456 if (nSelected
!= -1)
1458 m_manufacturerSelected
= (Manufacturer
*) m_manufacturerListCtrl
.GetItemData(nSelected
);
1460 m_modelListCtrl
.SetRedraw(FALSE
);
1462 m_modelListCtrl
.DeleteAllItems();
1463 m_modelSelected
= NULL
;
1465 Models::iterator iter
;
1467 for (iter
= m_manufacturerSelected
->models
.begin(); iter
!= m_manufacturerSelected
->models
.end(); iter
++)
1469 Model
* model
= *iter
;
1471 int nItem
= m_modelListCtrl
.InsertItem( 0, model
->displayName
);
1473 m_modelListCtrl
.SetItemData(nItem
, (DWORD_PTR
) model
);
1476 m_modelListCtrl
.SetRedraw(TRUE
);
1482 void CThirdPage::OnLvnItemchangedPrinterModel(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1484 LPNMLISTVIEW pNMLV
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
1486 CPrinterSetupWizardSheet
* psheet
;
1490 psheet
= reinterpret_cast<CPrinterSetupWizardSheet
*>(GetParent());
1491 require_quiet( psheet
, exit
);
1493 printer
= psheet
->GetSelectedPrinter();
1494 require_quiet( printer
, exit
);
1496 service
= printer
->services
.front();
1497 require_quiet( service
, exit
);
1499 check ( m_manufacturerSelected
);
1501 POSITION p
= m_modelListCtrl
.GetFirstSelectedItemPosition();
1502 int nSelected
= m_modelListCtrl
.GetNextSelectedItem(p
);
1504 if (nSelected
!= -1)
1506 m_modelSelected
= (Model
*) m_modelListCtrl
.GetItemData(nSelected
);
1508 CopyPrinterSettings( printer
, service
, m_manufacturerSelected
, m_modelSelected
);
1510 psheet
->SetWizardButtons(PSWIZB_BACK
|PSWIZB_NEXT
);
1514 psheet
->SetWizardButtons(PSWIZB_BACK
);
1523 void CThirdPage::OnBnClickedDefaultPrinter()
1525 CPrinterSetupWizardSheet
* psheet
;
1528 psheet
= reinterpret_cast<CPrinterSetupWizardSheet
*>(GetParent());
1529 require_quiet( psheet
, exit
);
1531 printer
= psheet
->GetSelectedPrinter();
1532 require_quiet( printer
, exit
);
1534 printer
->deflt
= m_defaultPrinterCtrl
.GetState() ? true : false;
1541 void CThirdPage::OnBnClickedHaveDisk()
1543 CPrinterSetupWizardSheet
* psheet
;
1547 CFileDialog
dlg(TRUE
, NULL
, NULL
, OFN_HIDEREADONLY
|OFN_FILEMUSTEXIST
, L
"Setup Information (*.inf)|*.inf||", this);
1549 psheet
= reinterpret_cast<CPrinterSetupWizardSheet
*>(GetParent());
1550 require_quiet( psheet
, exit
);
1552 printer
= psheet
->GetSelectedPrinter();
1553 require_quiet( printer
, exit
);
1555 service
= printer
->services
.front();
1556 require_quiet( service
, exit
);
1558 if ( dlg
.DoModal() == IDOK
)
1560 Manufacturers manufacturers
;
1561 CString filename
= dlg
.GetPathName();
1563 LoadPrintDriverDefsFromFile( manufacturers
, filename
, true );
1565 PopulateUI( manufacturers
);
1567 MatchPrinter( manufacturers
, printer
, service
);