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.23 2005/04/18 02:33:47 shersche
27 <rdar://problem/4091216> Default printer option cannot be deselected
29 Revision 1.22 2005/04/13 17:46:22 shersche
30 <rdar://problem/4082122> Generic PCL not selected when printers advertise multiple text records
32 Revision 1.21 2005/03/30 02:09:55 shersche
33 Auto-resize the column width to account for differing fonts and font sizes
35 Revision 1.20 2005/03/05 02:27:45 shersche
36 <rdar://problem/4030388> Generic drivers don't do color
38 Revision 1.19 2005/02/23 02:08:51 shersche
39 <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".
41 Revision 1.18 2005/02/15 07:02:51 shersche
42 <rdar://problem/4003724> Display different UI text when generic printer drivers are selected
44 Revision 1.17 2005/02/08 21:45:06 shersche
45 <rdar://problem/3947490> Default to Generic PostScript or PCL if unable to match driver
47 Revision 1.16 2005/02/08 18:56:03 shersche
48 Fix generated IPP url so that it doesn't add "/printers" string
50 Revision 1.15 2005/02/01 01:44:07 shersche
51 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.
53 Revision 1.14 2005/01/25 08:55:54 shersche
54 <rdar://problem/3911084> Load icons at run-time from resource DLL
57 Revision 1.13 2005/01/06 08:15:45 shersche
58 Append queue name to end of LPR port name, correctly build port name when queue name is absent
60 Revision 1.12 2005/01/05 01:06:12 shersche
61 <rdar://problem/3841218> Strip the first substring off the product key if an initial match can't be found with the whole product key.
64 Revision 1.11 2004/12/29 18:53:38 shersche
65 <rdar://problem/3725106>
66 <rdar://problem/3737413> Added support for LPR and IPP protocols as well as support for obtaining multiple text records. Reorganized and simplified codebase.
67 Bug #: 3725106, 3737413
69 Revision 1.10 2004/10/11 22:55:34 shersche
70 <rdar://problem/3827624> Use the IP port number when deriving the printer port name.
73 Revision 1.9 2004/06/27 23:08:00 shersche
74 code cleanup, make sure EnumPrintDrivers returns non-zero value, ignore comments in inf files
76 Revision 1.8 2004/06/27 08:06:45 shersche
77 Parse [Strings] section of inf file
79 Revision 1.7 2004/06/26 04:00:05 shersche
80 fix warnings compiling in debug mode
81 Submitted by: herscher
83 Revision 1.6 2004/06/26 03:19:57 shersche
84 clean up warning messages
86 Submitted by: herscher
88 Revision 1.5 2004/06/25 05:06:02 shersche
89 Trim whitespace from key/value pairs when parsing inf files
90 Submitted by: herscher
92 Revision 1.4 2004/06/25 02:44:13 shersche
93 Tweaked code to handle Xerox Phaser printer identification
94 Submitted by: herscher
96 Revision 1.3 2004/06/25 02:27:58 shersche
97 Do a CListCtrl::FindItem() before calling CListCtrl::SetItemState().
98 Submitted by: herscher
100 Revision 1.2 2004/06/23 18:09:23 shersche
101 Normalize tag names when parsing inf files.
102 Submitted by: herscher
104 Revision 1.1 2004/06/18 04:36:58 rpantos
111 #include "PrinterSetupWizardApp.h"
112 #include "PrinterSetupWizardSheet.h"
113 #include "ThirdPage.h"
114 #include "StdioFileEx.h"
117 #include <winspool.h>
119 // local variable is initialize but not referenced
120 #pragma warning(disable:4189)
124 // This is the printer description file that is shipped
127 #define kNTPrintFile L"inf\\ntprint.inf"
130 // These are pre-defined names for Generic manufacturer and model
132 #define kGenericManufacturer L"Generic"
133 #define kGenericText L"Generic / Text Only"
134 #define kGenericPostscript L"Generic / Postscript"
135 #define kGenericPCL L"Generic / PCL"
136 #define kPDLPostscriptKey L"application/postscript"
137 #define kPDLPCLKey L"application/vnd.hp-pcl"
138 #define kGenericPSColorDriver L"HP Color LaserJet 4550 PS"
139 #define kGenericPSDriver L"HP LaserJet 4050 Series PS"
140 #define kGenericPCLColorDriver L"HP Color LaserJet 4550 PCL"
141 #define kGenericPCLDriver L"HP LaserJet 4050 Series PCL"
145 // states for parsing ntprint.inf
147 enum PrinterParsingState
150 ParsingManufacturers
,
158 IMPLEMENT_DYNAMIC(CThirdPage
, CPropertyPage
)
159 CThirdPage::CThirdPage()
160 : CPropertyPage(CThirdPage::IDD
),
161 m_initialized(false),
162 m_printerImage( NULL
)
164 static const int bufferSize
= 32768;
165 TCHAR windowsDirectory
[bufferSize
];
171 m_psp
.dwFlags
&= ~(PSP_HASHELP
);
172 m_psp
.dwFlags
|= PSP_DEFAULT
|PSP_USEHEADERTITLE
|PSP_USEHEADERSUBTITLE
;
174 m_psp
.pszHeaderTitle
= MAKEINTRESOURCE(IDS_INSTALL_TITLE
);
175 m_psp
.pszHeaderSubTitle
= MAKEINTRESOURCE(IDS_INSTALL_SUBTITLE
);
178 // load printers from ntprint.inf
180 ok
= GetWindowsDirectory( windowsDirectory
, bufferSize
);
181 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
182 require_noerr( err
, exit
);
184 ntPrint
.Format(L
"%s\\%s", windowsDirectory
, kNTPrintFile
);
185 err
= LoadPrintDriverDefsFromFile( m_manufacturers
, ntPrint
, false );
186 require_noerr(err
, exit
);
189 // load printer drivers that have been installed on this machine
191 err
= LoadPrintDriverDefs( m_manufacturers
);
192 require_noerr(err
, exit
);
195 // and lastly load our own special generic printer defs
197 err
= LoadGenericPrintDriverDefs( m_manufacturers
);
198 require_noerr( err
, exit
);
206 CThirdPage::~CThirdPage()
209 // clean up all the printer manufacturers
211 while (m_manufacturers
.size())
213 Manufacturers::iterator iter
= m_manufacturers
.begin();
215 while (iter
->second
->models
.size())
217 Models::iterator it
= iter
->second
->models
.begin();
223 iter
->second
->models
.erase(it
);
228 m_manufacturers
.erase(iter
);
233 // ----------------------------------------------------
236 // SelectMatch will do all the UI work associated with
237 // selected a manufacturer and model of printer. It also
238 // makes sure the printer object is update with the
241 // ----------------------------------------------------
243 CThirdPage::SelectMatch(Printer
* printer
, Service
* service
, Manufacturers
& manufacturers
, Manufacturer
* manufacturer
, Model
* model
)
248 check( printer
!= NULL
);
249 check( manufacturer
!= NULL
);
250 check( model
!= NULL
);
252 PopulateUI( manufacturers
);
255 // select the manufacturer
257 info
.flags
= LVFI_STRING
;
258 info
.psz
= manufacturer
->name
;
260 nIndex
= m_manufacturerListCtrl
.FindItem(&info
);
264 m_manufacturerListCtrl
.SetItemState(nIndex
, LVIS_SELECTED
, LVIS_SELECTED
);
265 m_manufacturerListCtrl
.EnsureVisible(nIndex
, FALSE
);
271 info
.flags
= LVFI_STRING
;
272 info
.psz
= model
->displayName
;
274 nIndex
= m_modelListCtrl
.FindItem(&info
);
278 m_modelListCtrl
.SetItemState(nIndex
, LVIS_SELECTED
, LVIS_SELECTED
);
279 m_modelListCtrl
.EnsureVisible(nIndex
, FALSE
);
281 m_modelListCtrl
.SetFocus();
284 CopyPrinterSettings( printer
, service
, manufacturer
, model
);
288 // --------------------------------------------------------
289 // CopyPrinterSettings
291 // This function makes sure that the printer object has the
292 // latest settings from the manufacturer and model objects
293 // --------------------------------------------------------
296 CThirdPage::CopyPrinterSettings( Printer
* printer
, Service
* service
, Manufacturer
* manufacturer
, Model
* model
)
298 printer
->manufacturer
= manufacturer
->name
;
299 printer
->displayModelName
= model
->displayName
;
300 printer
->modelName
= model
->name
;
301 printer
->driverInstalled
= model
->driverInstalled
;
302 printer
->infFileName
= model
->infFileName
;
304 if ( service
->type
== kPDLServiceType
)
306 printer
->portName
.Format(L
"IP_%s.%d", static_cast<LPCTSTR
>(service
->hostname
), service
->portNumber
);
307 service
->protocol
= L
"Raw";
309 else if ( service
->type
== kLPRServiceType
)
311 Queue
* q
= service
->queues
.front();
314 if ( q
->name
.GetLength() > 0 )
316 printer
->portName
.Format(L
"LPR_%s.%d.%s", static_cast<LPCTSTR
>(service
->hostname
), service
->portNumber
, static_cast<LPCTSTR
>(q
->name
) );
320 printer
->portName
.Format(L
"LPR_%s.%d", static_cast<LPCTSTR
>(service
->hostname
), service
->portNumber
);
323 service
->protocol
= L
"LPR";
325 else if ( service
->type
== kIPPServiceType
)
327 Queue
* q
= service
->queues
.front();
330 if ( q
->name
.GetLength() > 0 )
332 printer
->portName
.Format(L
"http://%s:%d/%s", static_cast<LPCTSTR
>(service
->hostname
), service
->portNumber
, static_cast<LPCTSTR
>(q
->name
) );
336 printer
->portName
.Format(L
"http://%s:%d/", static_cast<LPCTSTR
>(service
->hostname
), service
->portNumber
);
339 service
->protocol
= L
"IPP";
344 // ------------------------------------------------------
345 // LoadPrintDriverDefsFromFile
347 // This function does all the heavy lifting in parsing inf
348 // files. It is called to parse both ntprint.inf, and driver
349 // files that might be shipped on a printer's installation
352 // The inf file is not totally parsed. I only want to determine
353 // the manufacturer and models that are involved. I leave it
354 // to printui.dll to actually copy the driver files to the
357 // I was aiming to parse as little as I could so as not to
358 // duplicate the parsing code that is contained in Windows. There
359 // are no public APIs for parsing inf files.
361 // That part of the inf file that we're interested in has a fairly
362 // easy format. Tags are strings that are enclosed in brackets.
363 // We are only interested in [MANUFACTURERS] and models.
365 // The only potentially opaque thing about this function is the
366 // checkForDuplicateModels flag. The problem here is that ntprint.inf
367 // doesn't contain duplicate models, and it has hundreds of models
368 // listed. You wouldn't check for duplicates there. But oftentimes,
369 // loading different windows print driver files contain multiple
370 // entries for the same printer. You don't want the UI to display
371 // the same printer multiple times, so in that case, you would ask
372 // this function to check for multiple models.
375 CThirdPage::LoadPrintDriverDefsFromFile(Manufacturers
& manufacturers
, const CString
& filename
, bool checkForDuplicateModels
)
377 PrinterParsingState state
= Looking
;
378 Manufacturers::iterator iter
= manufacturers
.end();
380 CFileException feError
;
385 typedef std::map
<CString
, CString
> StringMap
;
389 ok
= file
.Open( filename
, CFile::modeRead
|CFile::typeText
, &feError
);
390 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
391 require_noerr( err
, exit
);
393 check ( state
== Looking
);
394 check ( iter
== manufacturers
.end() );
397 // first, parse the file looking for string sections
399 while (file
.ReadString(s
))
404 if (s
.Find(';') == 0)
412 else if (s
.Find('[') == 0)
415 // handle any capitalization issues here
421 if (tag
== L
"[strings]")
423 state
= ParsingStrings
;
438 if (s
.GetLength() > 0)
440 CString key
= s
.Tokenize(L
"=",curPos
);
441 CString val
= s
.Tokenize(L
"=",curPos
);
444 // get rid of all delimiters
462 ok
= file
.Open( filename
, CFile::modeRead
|CFile::typeText
, &feError
);
463 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
464 require_noerr( err
, exit
);
468 check ( iter
== manufacturers
.end() );
470 while (file
.ReadString(s
))
475 if (s
.Find(';') == 0)
483 else if (s
.Find('[') == 0)
486 // handle any capitalization issues here
492 if (tag
== L
"[manufacturer]")
494 state
= ParsingManufacturers
;
498 // remove the leading and trailing delimiters
503 // check to see if this is a printer entry
505 iter
= manufacturers
.find(s
);
507 if (iter
!= manufacturers
.end())
509 state
= ParsingModels
;
518 // only look at this if the line isn't empty, or
519 // if it isn't a comment
521 else if ((s
.GetLength() > 0) && (s
.Find(';') != 0))
526 // if we're parsing manufacturers, then we will parse
527 // an entry of the form key=val, where key is a delimited
528 // string specifying a manufacturer name, and val is
529 // a tag that is used later in the file. the key is
530 // delimited by either '"' (quotes) or '%' (percent sign).
532 // the tag is used further down the file when models are
533 // declared. this allows multiple manufacturers to exist
534 // in a single inf file.
536 case ParsingManufacturers
:
538 Manufacturer
* manufacturer
;
541 CString key
= s
.Tokenize(L
"=",curPos
);
542 CString val
= s
.Tokenize(L
"=",curPos
);
546 manufacturer
= new Manufacturer
;
553 require_action( manufacturer
, exit
, err
= kNoMemoryErr
);
556 // if it's a variable, look it up
558 if (key
.Find('%') == 0)
560 StringMap::iterator it
;
564 it
= strings
.find(key
);
566 if (it
!= strings
.end())
580 // why is there no consistency in inf files?
582 if (val
.GetLength() == 0)
588 // fix the manufacturer name if necessary
591 val
= val
.Tokenize(L
",", curPos
);
593 manufacturer
->name
= NormalizeManufacturerName( key
);
594 manufacturer
->tag
= val
;
596 manufacturers
[val
] = manufacturer
;
602 check( iter
!= manufacturers
.end() );
607 CString name
= s
.Tokenize(L
"=",curPos
);
608 CString description
= s
.Tokenize(L
"=",curPos
);
610 if (name
.Find('%') == 0)
612 StringMap::iterator it
;
616 it
= strings
.find(name
);
618 if (it
!= strings
.end())
632 // If true, see if we've seen this guy before
634 if (checkForDuplicateModels
== true)
636 if ( MatchModel( iter
->second
, ConvertToModelName( name
) ) != NULL
)
651 require_action( model
, exit
, err
= kNoMemoryErr
);
653 model
->infFileName
= filename
;
654 model
->displayName
= name
;
656 model
->driverInstalled
= false;
658 iter
->second
->models
.push_back(model
);
664 // pay no attention if we are in any other state
679 // -------------------------------------------------------
680 // LoadPrintDriverDefs
682 // This function is responsible for loading the print driver
683 // definitions of all print drivers that have been installed
685 // -------------------------------------------------------
687 CThirdPage::LoadPrintDriverDefs( Manufacturers
& manufacturers
)
689 BYTE
* buffer
= NULL
;
690 DWORD bytesReceived
= 0;
691 DWORD numPrinters
= 0;
696 // like a lot of win32 calls, we call this first to get the
697 // size of the buffer we need.
699 EnumPrinterDrivers(NULL
, L
"all", 6, NULL
, 0, &bytesReceived
, &numPrinters
);
701 if (bytesReceived
> 0)
705 buffer
= new BYTE
[bytesReceived
];
712 require_action( buffer
, exit
, err
= kNoMemoryErr
);
715 // this call gets the real info
717 ok
= EnumPrinterDrivers(NULL
, L
"all", 6, buffer
, bytesReceived
, &bytesReceived
, &numPrinters
);
718 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
719 require_noerr( err
, exit
);
721 DRIVER_INFO_6
* info
= (DRIVER_INFO_6
*) buffer
;
723 for (DWORD i
= 0; i
< numPrinters
; i
++)
725 Manufacturer
* manufacturer
;
730 // skip over anything that doesn't have a manufacturer field. This
731 // fixes a bug that I noticed that occurred after I installed
732 // ProComm. This program add a print driver with no manufacturer
733 // that screwed up this wizard.
735 if (info
[i
].pszMfgName
== NULL
)
741 // look for manufacturer
743 Manufacturers::iterator iter
;
748 name
= NormalizeManufacturerName( info
[i
].pszMfgName
);
750 iter
= manufacturers
.find(name
);
752 if (iter
!= manufacturers
.end())
754 manufacturer
= iter
->second
;
760 manufacturer
= new Manufacturer
;
767 require_action( manufacturer
, exit
, err
= kNoMemoryErr
);
769 manufacturer
->name
= name
;
771 manufacturers
[name
] = manufacturer
;
775 // now look to see if we have already seen this guy. this could
776 // happen if we have already installed printers that are described
777 // in ntprint.inf. the extant drivers will show up in EnumPrinterDrivers
778 // but we have already loaded their info
781 if ( MatchModel( manufacturer
, ConvertToModelName( info
[i
].pName
) ) == NULL
)
792 require_action( model
, exit
, err
= kNoMemoryErr
);
794 model
->displayName
= info
[i
].pName
;
795 model
->name
= info
[i
].pName
;
796 model
->driverInstalled
= true;
798 manufacturer
->models
.push_back(model
);
814 // -------------------------------------------------------
815 // LoadGenericPrintDriverDefs
817 // This function is responsible for loading polymorphic
818 // generic print drivers defs. The UI will read
819 // something like "Generic / Postscript" and we can map
820 // that to any print driver we want.
821 // -------------------------------------------------------
823 CThirdPage::LoadGenericPrintDriverDefs( Manufacturers
& manufacturers
)
825 Manufacturer
* manufacturer
;
827 Manufacturers::iterator iter
;
828 CString psDriverName
;
829 CString pclDriverName
;
832 // <rdar://problem/4030388> Generic drivers don't do color
834 // First try and find our generic driver names
836 iter
= manufacturers
.find(L
"HP");
837 require_action( iter
!= manufacturers
.end(), exit
, err
= kUnknownErr
);
838 manufacturer
= iter
->second
;
840 // Look for Postscript
842 model
= manufacturer
->find( kGenericPSColorDriver
);
846 model
= manufacturer
->find( kGenericPSDriver
);
851 psDriverName
= model
->name
;
857 model
= manufacturer
->find( kGenericPCLColorDriver
);
861 model
= manufacturer
->find( kGenericPCLDriver
);
866 pclDriverName
= model
->name
;
869 // If we found either a generic PS driver, or a generic PCL driver,
870 // then add them to the list
872 if ( psDriverName
.GetLength() || pclDriverName
.GetLength() )
874 // Try and find generic manufacturer if there is one
876 iter
= manufacturers
.find(L
"Generic");
878 if (iter
!= manufacturers
.end())
880 manufacturer
= iter
->second
;
886 manufacturer
= new Manufacturer
;
893 require_action( manufacturer
, exit
, err
= kNoMemoryErr
);
895 manufacturer
->name
= "Generic";
896 manufacturers
[manufacturer
->name
] = manufacturer
;
899 if ( psDriverName
.GetLength() > 0 )
903 m_genericPostscript
= new Model
;
907 m_genericPostscript
= NULL
;
910 require_action( m_genericPostscript
, exit
, err
= kNoMemoryErr
);
912 m_genericPostscript
->displayName
= kGenericPostscript
;
913 m_genericPostscript
->name
= psDriverName
;
914 m_genericPostscript
->driverInstalled
= false;
916 manufacturer
->models
.push_back( m_genericPostscript
);
919 if ( pclDriverName
.GetLength() > 0 )
923 m_genericPCL
= new Model
;
930 require_action( m_genericPCL
, exit
, err
= kNoMemoryErr
);
932 m_genericPCL
->displayName
= kGenericPCL
;
933 m_genericPCL
->name
= pclDriverName
;
934 m_genericPCL
->driverInstalled
= false;
936 manufacturer
->models
.push_back( m_genericPCL
);
945 // ------------------------------------------------------
946 // ConvertToManufacturerName
948 // This function is responsible for tweaking the
949 // name so that subsequent string operations won't fail because
950 // of capitalizations/different names for the same manufacturer
951 // (i.e. Hewlett-Packard/HP/Hewlett Packard)
954 CThirdPage::ConvertToManufacturerName( const CString
& name
)
957 // first we're going to convert all the characters to lower
960 CString lower
= name
;
964 // now we're going to check to see if the string says "hewlett-packard",
965 // because sometimes they refer to themselves as "hewlett-packard", and
966 // sometimes they refer to themselves as "hp".
968 if ( lower
== L
"hewlett-packard")
974 // tweak for Xerox Phaser, which doesn't announce itself
977 else if ( lower
.Find( L
"phaser", 0 ) != -1 )
986 // ------------------------------------------------------
987 // ConvertToModelName
989 // This function is responsible for ensuring that subsequent
990 // string operations don't fail because of differing capitalization
991 // schemes and the like
992 // ------------------------------------------------------
995 CThirdPage::ConvertToModelName( const CString
& name
)
998 // convert it to lowercase
1000 CString lower
= name
;
1007 // ------------------------------------------------------
1008 // NormalizeManufacturerName
1010 // This function is responsible for tweaking the manufacturer
1011 // name so that there are no aliases for vendors
1014 CThirdPage::NormalizeManufacturerName( const CString
& name
)
1016 CString normalized
= name
;
1019 // now we're going to check to see if the string says "hewlett-packard",
1020 // because sometimes they refer to themselves as "hewlett-packard", and
1021 // sometimes they refer to themselves as "hp".
1023 if ( normalized
== L
"Hewlett-Packard")
1032 // -------------------------------------------------------
1035 // This function is responsible for matching a printer
1036 // to a list of manufacturers and models. It calls
1037 // MatchManufacturer and MatchModel in turn.
1040 OSStatus
CThirdPage::MatchPrinter(Manufacturers
& manufacturers
, Printer
* printer
, Service
* service
)
1042 CString normalizedProductName
;
1043 Manufacturer
* manufacturer
= NULL
;
1044 Manufacturer
* genericManufacturer
= NULL
;
1045 Model
* model
= NULL
;
1046 Model
* genericModel
= NULL
;
1049 OSStatus err
= kNoErr
;
1054 Queue
* q
= service
->SelectedQueue();
1059 // first look to see if we have a usb_MFG descriptor
1061 if ( q
->usb_MFG
.GetLength() > 0)
1063 manufacturer
= MatchManufacturer( manufacturers
, ConvertToManufacturerName ( q
->usb_MFG
) );
1066 if ( manufacturer
== NULL
)
1068 q
->product
.Remove('(');
1069 q
->product
.Remove(')');
1071 manufacturer
= MatchManufacturer( manufacturers
, ConvertToManufacturerName ( q
->product
) );
1075 // if we found the manufacturer, then start looking for the model
1077 if ( manufacturer
!= NULL
)
1079 if ( q
->usb_MDL
.GetLength() > 0 )
1081 model
= MatchModel ( manufacturer
, ConvertToModelName ( q
->usb_MDL
) );
1084 if ( ( model
== NULL
) && ( q
->product
.GetLength() > 0 ) )
1086 q
->product
.Remove('(');
1087 q
->product
.Remove(')');
1089 model
= MatchModel ( manufacturer
, ConvertToModelName ( q
->product
) );
1092 if ( model
!= NULL
)
1094 Manufacturers manufacturers
;
1096 manufacturers
[manufacturer
->name
] = manufacturer
;
1097 SelectMatch(printer
, service
, manufacturers
, manufacturer
, model
);
1103 // display a message to the user based on whether we could match
1108 text
.LoadString(IDS_PRINTER_MATCH_GOOD
);
1110 else if ( MatchGeneric( printer
, service
, &genericManufacturer
, &genericModel
) )
1112 Manufacturers
* pManufacturers
;
1113 Manufacturers manufacturers
;
1115 text
.LoadString(IDS_PRINTER_MATCH_MAYBE
);
1119 manufacturers
[genericManufacturer
->name
] = genericManufacturer
;
1120 manufacturers
[manufacturer
->name
] = manufacturer
;
1122 pManufacturers
= &manufacturers
;
1126 pManufacturers
= &m_manufacturers
;
1129 SelectMatch( printer
, service
, *pManufacturers
, genericManufacturer
, genericModel
);
1133 text
.LoadString(IDS_PRINTER_MATCH_BAD
);
1136 // if there was any crud in this list from before, get rid of it now
1138 m_modelListCtrl
.DeleteAllItems();
1141 // select the manufacturer if we found one
1143 if (manufacturer
!= NULL
)
1149 // select the manufacturer
1151 info
.flags
= LVFI_STRING
;
1152 info
.psz
= manufacturer
->name
;
1154 nIndex
= m_manufacturerListCtrl
.FindItem(&info
);
1158 m_manufacturerListCtrl
.SetItemState(nIndex
, LVIS_SELECTED
, LVIS_SELECTED
);
1159 m_manufacturerListCtrl
.EnsureVisible(nIndex
, FALSE
);
1164 m_printerSelectionText
.SetWindowText(text
);
1170 // ------------------------------------------------------
1171 // MatchManufacturer
1173 // This function is responsible for finding a manufacturer
1174 // object from a string name. It does a CString::Find, which
1175 // is like strstr, so it doesn't have to do an exact match
1177 // If it can't find a match, NULL is returned
1178 // ------------------------------------------------------
1181 CThirdPage::MatchManufacturer( Manufacturers
& manufacturers
, const CString
& name
)
1183 Manufacturers::iterator iter
;
1185 for (iter
= manufacturers
.begin(); iter
!= manufacturers
.end(); iter
++)
1188 // we're going to convert all the manufacturer names to lower case,
1189 // so we match the name passed in.
1191 CString lower
= iter
->second
->name
;
1195 // now try and find the lowered string in the name passed in.
1197 if (name
.Find(lower
) != -1)
1199 return iter
->second
;
1207 // -------------------------------------------------------
1210 // This function is responsible for matching a model from
1211 // a name. It does a CString::Find(), which works like strstr,
1212 // so it doesn't rely on doing an exact string match.
1216 CThirdPage::MatchModel(Manufacturer
* manufacturer
, const CString
& name
)
1218 Models::iterator iter
;
1220 iter
= manufacturer
->models
.begin();
1222 for (iter
= manufacturer
->models
.begin(); iter
!= manufacturer
->models
.end(); iter
++)
1224 Model
* model
= *iter
;
1227 // convert the model name to lower case
1229 CString lowered
= model
->name
;
1230 lowered
.MakeLower();
1232 if (lowered
.Find( name
) != -1)
1238 // <rdar://problem/3841218>
1239 // try removing the first substring and search again
1242 if ( name
.Find(' ') != -1 )
1244 CString altered
= name
;
1245 altered
.Delete( 0, altered
.Find(' ') + 1 );
1247 if ( lowered
.Find( altered
) != -1 )
1258 // -------------------------------------------------------
1261 // This function will attempt to find a generic printer
1262 // driver for a printer that we weren't able to match
1266 CThirdPage::MatchGeneric( Printer
* printer
, Service
* service
, Manufacturer
** manufacturer
, Model
** model
)
1271 DEBUG_UNUSED( printer
);
1275 Queue
* q
= service
->SelectedQueue();
1279 Manufacturers::iterator iter
= m_manufacturers
.find( kGenericManufacturer
);
1280 require_action_quiet( iter
!= m_manufacturers
.end(), exit
, ok
= FALSE
);
1282 *manufacturer
= iter
->second
;
1287 if ( pdl
.Find( kPDLPCLKey
) != -1 )
1289 *model
= m_genericPCL
;
1292 else if ( pdl
.Find( kPDLPostscriptKey
) != -1 )
1294 *model
= m_genericPostscript
;
1304 // -----------------------------------------------------------
1307 // This function is responsible for doing initialization that
1308 // only occurs once during a run of the wizard
1311 OSStatus
CThirdPage::OnInitPage()
1315 OSStatus err
= kNoErr
;
1317 // Load printer icon
1321 check( m_printerImage
== NULL
);
1323 m_printerImage
= (CStatic
*) GetDlgItem( IDR_MANIFEST
);
1324 check( m_printerImage
);
1326 if ( m_printerImage
!= NULL
)
1328 m_printerImage
->SetIcon( LoadIcon( GetNonLocalizedResources(), MAKEINTRESOURCE( IDI_PRINTER
) ) );
1332 // The CTreeCtrl widget automatically sends a selection changed
1333 // message which initially we want to ignore, because the user
1334 // hasn't selected anything
1336 // this flag gets reset in the message handler. Every subsequent
1337 // message gets handled.
1341 // we have to make sure that we only do this once. Typically,
1342 // we would do this in something like OnInitDialog, but we don't
1343 // have this in Wizards, because the window is a PropertySheet.
1344 // We're considered fully initialized when we receive the first
1347 header
.LoadString(IDS_MANUFACTURER_HEADING
);
1348 m_manufacturerListCtrl
.InsertColumn(0, header
, LVCFMT_LEFT
, -1 );
1349 m_manufacturerSelected
= NULL
;
1351 header
.LoadString(IDS_MODEL_HEADING
);
1352 m_modelListCtrl
.InsertColumn(0, header
, LVCFMT_LEFT
, -1 );
1353 m_modelSelected
= NULL
;
1359 void CThirdPage::DoDataExchange(CDataExchange
* pDX
)
1361 CPropertyPage::DoDataExchange(pDX
);
1362 DDX_Control(pDX
, IDC_PRINTER_MANUFACTURER
, m_manufacturerListCtrl
);
1363 DDX_Control(pDX
, IDC_PRINTER_MODEL
, m_modelListCtrl
);
1364 DDX_Control(pDX
, IDC_PRINTER_NAME
, m_printerName
);
1365 DDX_Control(pDX
, IDC_DEFAULT_PRINTER
, m_defaultPrinterCtrl
);
1366 DDX_Control(pDX
, IDC_PRINTER_SELECTION_TEXT
, m_printerSelectionText
);
1371 // ----------------------------------------------------------
1374 // This function is called by MFC after the window has been
1379 CThirdPage::OnSetActive()
1381 CPrinterSetupWizardSheet
* psheet
;
1385 psheet
= reinterpret_cast<CPrinterSetupWizardSheet
*>(GetParent());
1386 require_quiet( psheet
, exit
);
1388 if ((m_manufacturerListCtrl
.GetFirstSelectedItemPosition() != NULL
) &&
1389 (m_modelListCtrl
.GetFirstSelectedItemPosition() != NULL
))
1391 psheet
->SetWizardButtons( PSWIZB_BACK
|PSWIZB_NEXT
);
1395 psheet
->SetWizardButtons( PSWIZB_BACK
);
1398 printer
= psheet
->GetSelectedPrinter();
1399 require_quiet( printer
, exit
);
1401 service
= printer
->services
.front();
1402 require_quiet( service
, exit
);
1405 // call OnInitPage once
1410 m_initialized
= true;
1414 // update the UI with the printer name
1416 m_printerName
.SetWindowText(printer
->displayName
);
1419 // populate the list controls with the manufacturers and models
1422 PopulateUI( m_manufacturers
);
1425 // and try and match the printer
1427 MatchPrinter( m_manufacturers
, printer
, service
);
1431 return CPropertyPage::OnSetActive();
1435 // -------------------------------------------------------
1438 // This function is called to populate the list of manufacturers
1441 CThirdPage::PopulateUI(Manufacturers
& manufacturers
)
1443 Manufacturers::iterator iter
;
1445 m_manufacturerListCtrl
.DeleteAllItems();
1447 for (iter
= manufacturers
.begin(); iter
!= manufacturers
.end(); iter
++)
1451 Manufacturer
* manufacturer
= iter
->second
;
1453 nIndex
= m_manufacturerListCtrl
.InsertItem(0, manufacturer
->name
);
1455 m_manufacturerListCtrl
.SetItemData(nIndex
, (DWORD_PTR
) manufacturer
);
1457 m_manufacturerListCtrl
.SetColumnWidth( 0, LVSCW_AUTOSIZE_USEHEADER
);
1464 BEGIN_MESSAGE_MAP(CThirdPage
, CPropertyPage
)
1465 ON_NOTIFY(LVN_ITEMCHANGED
, IDC_PRINTER_MANUFACTURER
, OnLvnItemchangedManufacturer
)
1466 ON_NOTIFY(LVN_ITEMCHANGED
, IDC_PRINTER_MODEL
, OnLvnItemchangedPrinterModel
)
1467 ON_BN_CLICKED(IDC_DEFAULT_PRINTER
, OnBnClickedDefaultPrinter
)
1468 ON_BN_CLICKED(IDC_HAVE_DISK
, OnBnClickedHaveDisk
)
1472 // CThirdPage message handlers
1473 void CThirdPage::OnLvnItemchangedManufacturer(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1475 LPNMLISTVIEW pNMLV
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
1477 POSITION p
= m_manufacturerListCtrl
.GetFirstSelectedItemPosition();
1478 int nSelected
= m_manufacturerListCtrl
.GetNextSelectedItem(p
);
1480 if (nSelected
!= -1)
1482 m_manufacturerSelected
= (Manufacturer
*) m_manufacturerListCtrl
.GetItemData(nSelected
);
1484 m_modelListCtrl
.SetRedraw(FALSE
);
1486 m_modelListCtrl
.DeleteAllItems();
1487 m_modelSelected
= NULL
;
1489 Models::iterator iter
;
1491 for (iter
= m_manufacturerSelected
->models
.begin(); iter
!= m_manufacturerSelected
->models
.end(); iter
++)
1493 Model
* model
= *iter
;
1495 int nItem
= m_modelListCtrl
.InsertItem( 0, model
->displayName
);
1497 m_modelListCtrl
.SetItemData(nItem
, (DWORD_PTR
) model
);
1499 m_modelListCtrl
.SetColumnWidth( 0, LVSCW_AUTOSIZE_USEHEADER
);
1502 m_modelListCtrl
.SetRedraw(TRUE
);
1508 void CThirdPage::OnLvnItemchangedPrinterModel(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1510 LPNMLISTVIEW pNMLV
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
1512 CPrinterSetupWizardSheet
* psheet
;
1516 psheet
= reinterpret_cast<CPrinterSetupWizardSheet
*>(GetParent());
1517 require_quiet( psheet
, exit
);
1519 printer
= psheet
->GetSelectedPrinter();
1520 require_quiet( printer
, exit
);
1522 service
= printer
->services
.front();
1523 require_quiet( service
, exit
);
1525 check ( m_manufacturerSelected
);
1527 POSITION p
= m_modelListCtrl
.GetFirstSelectedItemPosition();
1528 int nSelected
= m_modelListCtrl
.GetNextSelectedItem(p
);
1530 if (nSelected
!= -1)
1532 m_modelSelected
= (Model
*) m_modelListCtrl
.GetItemData(nSelected
);
1534 CopyPrinterSettings( printer
, service
, m_manufacturerSelected
, m_modelSelected
);
1536 psheet
->SetWizardButtons(PSWIZB_BACK
|PSWIZB_NEXT
);
1540 psheet
->SetWizardButtons(PSWIZB_BACK
);
1549 void CThirdPage::OnBnClickedDefaultPrinter()
1551 CPrinterSetupWizardSheet
* psheet
;
1554 psheet
= reinterpret_cast<CPrinterSetupWizardSheet
*>(GetParent());
1555 require_quiet( psheet
, exit
);
1557 printer
= psheet
->GetSelectedPrinter();
1558 require_quiet( printer
, exit
);
1560 printer
->deflt
= ( m_defaultPrinterCtrl
.GetCheck() == BST_CHECKED
) ? true : false;
1567 void CThirdPage::OnBnClickedHaveDisk()
1569 CPrinterSetupWizardSheet
* psheet
;
1573 CFileDialog
dlg(TRUE
, NULL
, NULL
, OFN_HIDEREADONLY
|OFN_FILEMUSTEXIST
, L
"Setup Information (*.inf)|*.inf||", this);
1575 psheet
= reinterpret_cast<CPrinterSetupWizardSheet
*>(GetParent());
1576 require_quiet( psheet
, exit
);
1578 printer
= psheet
->GetSelectedPrinter();
1579 require_quiet( printer
, exit
);
1581 service
= printer
->services
.front();
1582 require_quiet( service
, exit
);
1584 if ( dlg
.DoModal() == IDOK
)
1586 Manufacturers manufacturers
;
1587 CString filename
= dlg
.GetPathName();
1589 LoadPrintDriverDefsFromFile( manufacturers
, filename
, true );
1591 PopulateUI( manufacturers
);
1593 MatchPrinter( manufacturers
, printer
, service
);