1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 Change History (most recent first):
19 $Log: ThirdPage.cpp,v $
20 Revision 1.37 2007/06/08 06:30:26 herscher
21 <rdar://problem/5257700> Fix uninitialized pointers when detecting generic PCL and PS drivers
23 Revision 1.36 2007/06/06 20:39:10 cheshire
24 <rdar://problem/5254377> Printer Setup Wizard started crashing in Bonjour104A8, after update to Visual Studio 2005
26 Revision 1.35 2007/06/06 20:08:01 cheshire
27 <rdar://problem/4528853> mDNS: When auto-highlighting items in lists, scroll list so highlighted item is in the middle
28 AutoScroll model list as well as manufacturer list
30 Revision 1.34 2007/06/06 19:53:48 cheshire
31 <rdar://problem/5187308> Move build train to Visual Studio 2005
33 Revision 1.33 2007/04/20 22:58:10 herscher
34 <rdar://problem/4826126> mDNS: Printer Wizard doesn't offer generic HP printers or generic PS support on Vista RC2
36 Revision 1.32 2007/04/13 23:42:20 herscher
37 <rdar://problem/4580061> mDNS: Printers added using Bonjour should be set as the default printer.
39 Revision 1.31 2007/04/13 21:38:46 herscher
40 <rdar://problem/4528853> mDNS: When auto-highlighting items in lists, scroll list so highlighted item is in the middle
42 Revision 1.30 2007/04/13 20:23:40 herscher
43 Fixed mistake in previous checkin that reverted license text for this file
45 Revision 1.29 2007/04/13 18:10:24 herscher
46 <rdar://problem/4496652> mDNS: Don't allow user to choose non-working driver
48 Revision 1.28 2006/08/14 23:24:09 cheshire
49 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
51 Revision 1.27 2005/10/05 21:41:45 herscher
52 <rdar://problem/4190104> Use "application/octet-stream" to determine if CUPS shared queue supports raw
54 Revision 1.26 2005/07/11 20:17:15 shersche
55 <rdar://problem/4124524> UI fixes associated with CUPS printer workaround fix.
57 Revision 1.25 2005/07/07 17:53:20 shersche
58 Fix problems associated with the CUPS printer workaround fix.
60 Revision 1.24 2005/06/30 18:02:54 shersche
61 <rdar://problem/4124524> Workaround for Mac OS X Printer Sharing bug
63 Revision 1.23 2005/04/18 02:33:47 shersche
64 <rdar://problem/4091216> Default printer option cannot be deselected
66 Revision 1.22 2005/04/13 17:46:22 shersche
67 <rdar://problem/4082122> Generic PCL not selected when printers advertise multiple text records
69 Revision 1.21 2005/03/30 02:09:55 shersche
70 Auto-resize the column width to account for differing fonts and font sizes
72 Revision 1.20 2005/03/05 02:27:45 shersche
73 <rdar://problem/4030388> Generic drivers don't do color
75 Revision 1.19 2005/02/23 02:08:51 shersche
76 <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".
78 Revision 1.18 2005/02/15 07:02:51 shersche
79 <rdar://problem/4003724> Display different UI text when generic printer drivers are selected
81 Revision 1.17 2005/02/08 21:45:06 shersche
82 <rdar://problem/3947490> Default to Generic PostScript or PCL if unable to match driver
84 Revision 1.16 2005/02/08 18:56:03 shersche
85 Fix generated IPP url so that it doesn't add "/printers" string
87 Revision 1.15 2005/02/01 01:44:07 shersche
88 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.
90 Revision 1.14 2005/01/25 08:55:54 shersche
91 <rdar://problem/3911084> Load icons at run-time from resource DLL
94 Revision 1.13 2005/01/06 08:15:45 shersche
95 Append queue name to end of LPR port name, correctly build port name when queue name is absent
97 Revision 1.12 2005/01/05 01:06:12 shersche
98 <rdar://problem/3841218> Strip the first substring off the product key if an initial match can't be found with the whole product key.
101 Revision 1.11 2004/12/29 18:53:38 shersche
102 <rdar://problem/3725106>
103 <rdar://problem/3737413> Added support for LPR and IPP protocols as well as support for obtaining multiple text records. Reorganized and simplified codebase.
104 Bug #: 3725106, 3737413
106 Revision 1.10 2004/10/11 22:55:34 shersche
107 <rdar://problem/3827624> Use the IP port number when deriving the printer port name.
110 Revision 1.9 2004/06/27 23:08:00 shersche
111 code cleanup, make sure EnumPrintDrivers returns non-zero value, ignore comments in inf files
113 Revision 1.8 2004/06/27 08:06:45 shersche
114 Parse [Strings] section of inf file
116 Revision 1.7 2004/06/26 04:00:05 shersche
117 fix warnings compiling in debug mode
118 Submitted by: herscher
120 Revision 1.6 2004/06/26 03:19:57 shersche
121 clean up warning messages
123 Submitted by: herscher
125 Revision 1.5 2004/06/25 05:06:02 shersche
126 Trim whitespace from key/value pairs when parsing inf files
127 Submitted by: herscher
129 Revision 1.4 2004/06/25 02:44:13 shersche
130 Tweaked code to handle Xerox Phaser printer identification
131 Submitted by: herscher
133 Revision 1.3 2004/06/25 02:27:58 shersche
134 Do a CListCtrl::FindItem() before calling CListCtrl::SetItemState().
135 Submitted by: herscher
137 Revision 1.2 2004/06/23 18:09:23 shersche
138 Normalize tag names when parsing inf files.
139 Submitted by: herscher
141 Revision 1.1 2004/06/18 04:36:58 rpantos
146 #include "PrinterSetupWizardApp.h"
147 #include "PrinterSetupWizardSheet.h"
148 #include "ThirdPage.h"
149 #include "StdioFileEx.h"
152 #include <winspool.h>
154 // local variable is initialize but not referenced
155 #pragma warning(disable:4189)
158 // This is the printer description file that is shipped
159 // with Windows XP and below
161 #define kNTPrintFile L"inf\\ntprint.inf"
164 // Windows Vista ships with a set of prn*.inf files
166 #define kVistaPrintFiles L"inf\\prn*.inf"
169 // These are pre-defined names for Generic manufacturer and model
171 #define kGenericManufacturer L"Generic"
172 #define kGenericText L"Generic / Text Only"
173 #define kGenericPostscript L"Generic / Postscript"
174 #define kGenericPCL L"Generic / PCL"
175 #define kPDLPostscriptKey L"application/postscript"
176 #define kPDLPCLKey L"application/vnd.hp-pcl"
177 #define kGenericPSColorDriver L"HP Color LaserJet 4550 PS"
178 #define kGenericPSDriver L"HP LaserJet 4050 Series PS"
179 #define kGenericPCLColorDriver L"HP Color LaserJet 4550 PCL"
180 #define kGenericPCLDriver L"HP LaserJet 4050 Series PCL"
183 // states for parsing ntprint.inf
185 enum PrinterParsingState
188 ParsingManufacturers
,
195 IMPLEMENT_DYNAMIC(CThirdPage
, CPropertyPage
)
196 CThirdPage::CThirdPage()
197 : CPropertyPage(CThirdPage::IDD
),
198 m_manufacturerSelected( NULL
),
199 m_modelSelected( NULL
),
200 m_genericPostscript( NULL
),
201 m_genericPCL( NULL
),
202 m_initialized(false),
203 m_printerImage( NULL
)
205 static const int bufferSize
= 32768;
206 TCHAR windowsDirectory
[bufferSize
];
208 WIN32_FIND_DATA findFileData
;
215 m_psp
.dwFlags
&= ~(PSP_HASHELP
);
216 m_psp
.dwFlags
|= PSP_DEFAULT
|PSP_USEHEADERTITLE
|PSP_USEHEADERSUBTITLE
;
218 m_psp
.pszHeaderTitle
= MAKEINTRESOURCE(IDS_INSTALL_TITLE
);
219 m_psp
.pszHeaderSubTitle
= MAKEINTRESOURCE(IDS_INSTALL_SUBTITLE
);
222 // load printers from ntprint.inf
224 ok
= GetWindowsDirectory( windowsDirectory
, bufferSize
);
225 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
226 require_noerr( err
, exit
);
229 // <rdar://problem/4826126>
231 // If there are no *prn.inf files, we'll assume that the information
234 prnFiles
.Format( L
"%s\\%s", windowsDirectory
, kVistaPrintFiles
);
235 findHandle
= FindFirstFile( prnFiles
, &findFileData
);
237 if ( findHandle
!= INVALID_HANDLE_VALUE
)
241 absolute
.Format( L
"%s\\inf\\%s", windowsDirectory
, findFileData
.cFileName
);
242 err
= LoadPrintDriverDefsFromFile( m_manufacturers
, absolute
, false );
243 require_noerr( err
, exit
);
245 while ( FindNextFile( findHandle
, &findFileData
) )
247 absolute
.Format( L
"%s\\inf\\%s", windowsDirectory
, findFileData
.cFileName
);
248 err
= LoadPrintDriverDefsFromFile( m_manufacturers
, absolute
, false );
249 require_noerr( err
, exit
);
252 FindClose( findHandle
);
256 ntPrint
.Format(L
"%s\\%s", windowsDirectory
, kNTPrintFile
);
257 err
= LoadPrintDriverDefsFromFile( m_manufacturers
, ntPrint
, false );
258 require_noerr(err
, exit
);
262 // load printer drivers that have been installed on this machine
264 err
= LoadPrintDriverDefs( m_manufacturers
);
265 require_noerr(err
, exit
);
268 // load our own special generic printer defs
270 err
= LoadGenericPrintDriverDefs( m_manufacturers
);
271 require_noerr( err
, exit
);
278 CThirdPage::~CThirdPage()
281 // clean up all the printer manufacturers
283 while (m_manufacturers
.size())
285 Manufacturers::iterator iter
= m_manufacturers
.begin();
287 while (iter
->second
->models
.size())
289 Models::iterator it
= iter
->second
->models
.begin();
295 iter
->second
->models
.erase(it
);
300 m_manufacturers
.erase(iter
);
304 // ----------------------------------------------------
307 // SelectMatch will do all the UI work associated with
308 // selected a manufacturer and model of printer. It also
309 // makes sure the printer object is update with the
312 // ----------------------------------------------------
314 CThirdPage::SelectMatch(Printer
* printer
, Service
* service
, Manufacturer
* manufacturer
, Model
* model
)
319 check( printer
!= NULL
);
320 check( manufacturer
!= NULL
);
321 check( model
!= NULL
);
324 // select the manufacturer
326 info
.flags
= LVFI_STRING
;
327 info
.psz
= manufacturer
->name
;
329 nIndex
= m_manufacturerListCtrl
.FindItem(&info
);
333 m_manufacturerListCtrl
.SetItemState(nIndex
, LVIS_SELECTED
, LVIS_SELECTED
);
335 //<rdar://problem/4528853> mDNS: When auto-highlighting items in lists, scroll list so highlighted item is in the middle
337 AutoScroll(m_manufacturerListCtrl
, nIndex
);
343 info
.flags
= LVFI_STRING
;
344 info
.psz
= model
->displayName
;
346 nIndex
= m_modelListCtrl
.FindItem(&info
);
350 m_modelListCtrl
.SetItemState(nIndex
, LVIS_SELECTED
, LVIS_SELECTED
);
351 AutoScroll( m_modelListCtrl
, nIndex
);
353 m_modelListCtrl
.SetFocus();
356 CopyPrinterSettings( printer
, service
, manufacturer
, model
);
360 CThirdPage::SelectMatch(Manufacturers
& manufacturers
, Printer
* printer
, Service
* service
, Manufacturer
* manufacturer
, Model
* model
)
362 PopulateUI( manufacturers
);
364 SelectMatch( printer
, service
, manufacturer
, model
);
367 // --------------------------------------------------------
368 // CopyPrinterSettings
370 // This function makes sure that the printer object has the
371 // latest settings from the manufacturer and model objects
372 // --------------------------------------------------------
375 CThirdPage::CopyPrinterSettings( Printer
* printer
, Service
* service
, Manufacturer
* manufacturer
, Model
* model
)
377 printer
->manufacturer
= manufacturer
->name
;
378 printer
->displayModelName
= model
->displayName
;
379 printer
->modelName
= model
->name
;
380 printer
->driverInstalled
= model
->driverInstalled
;
381 printer
->infFileName
= model
->infFileName
;
383 if ( service
->type
== kPDLServiceType
)
385 printer
->portName
.Format(L
"IP_%s.%d", static_cast<LPCTSTR
>(service
->hostname
), service
->portNumber
);
386 service
->protocol
= L
"Raw";
388 else if ( service
->type
== kLPRServiceType
)
390 Queue
* q
= service
->queues
.front();
393 if ( q
->name
.GetLength() > 0 )
395 printer
->portName
.Format(L
"LPR_%s.%d.%s", static_cast<LPCTSTR
>(service
->hostname
), service
->portNumber
, static_cast<LPCTSTR
>(q
->name
) );
399 printer
->portName
.Format(L
"LPR_%s.%d", static_cast<LPCTSTR
>(service
->hostname
), service
->portNumber
);
402 service
->protocol
= L
"LPR";
404 else if ( service
->type
== kIPPServiceType
)
406 Queue
* q
= service
->queues
.front();
409 if ( q
->name
.GetLength() > 0 )
411 printer
->portName
.Format(L
"http://%s:%d/%s", static_cast<LPCTSTR
>(service
->hostname
), service
->portNumber
, static_cast<LPCTSTR
>(q
->name
) );
415 printer
->portName
.Format(L
"http://%s:%d/", static_cast<LPCTSTR
>(service
->hostname
), service
->portNumber
);
418 service
->protocol
= L
"IPP";
422 // --------------------------------------------------------
423 // DefaultPrinterExists
425 // Checks to see if a default printer has been configured
427 // --------------------------------------------------------
429 CThirdPage::DefaultPrinterExists()
431 CPrintDialog
dlg(FALSE
);
433 dlg
.m_pd
.Flags
|= PD_RETURNDEFAULT
;
435 return dlg
.GetDefaults();
438 // --------------------------------------------------------
441 // Ensure selected item is in middle of list
442 // --------------------------------------------------------
444 CThirdPage::AutoScroll( CListCtrl
& list
, int nIndex
)
447 //<rdar://problem/4528853> mDNS: When auto-highlighting items in lists, scroll list so highlighted item is in the middle
453 list
.EnsureVisible( nIndex
, FALSE
);
455 top
= list
.GetTopIndex();
456 count
= list
.GetCountPerPage();
458 if ( ( nIndex
== top
) || ( ( nIndex
+ 1 ) == ( top
+ count
) ) )
463 rows
= ( count
/ 2 );
467 list
.GetItemRect(0, rect
, LVIR_BOUNDS
);
468 list
.Scroll( CPoint( 0, rows
* rect
.Height() * -1 ) );
472 list
.GetItemRect(0, rect
, LVIR_BOUNDS
);
473 list
.Scroll( CPoint( 0, rows
* rect
.Height() ) );
478 // ------------------------------------------------------
479 // LoadPrintDriverDefsFromFile
481 // This function does all the heavy lifting in parsing inf
482 // files. It is called to parse both ntprint.inf, and driver
483 // files that might be shipped on a printer's installation
486 // The inf file is not totally parsed. I only want to determine
487 // the manufacturer and models that are involved. I leave it
488 // to printui.dll to actually copy the driver files to the
491 // I was aiming to parse as little as I could so as not to
492 // duplicate the parsing code that is contained in Windows. There
493 // are no public APIs for parsing inf files.
495 // That part of the inf file that we're interested in has a fairly
496 // easy format. Tags are strings that are enclosed in brackets.
497 // We are only interested in [MANUFACTURERS] and models.
499 // The only potentially opaque thing about this function is the
500 // checkForDuplicateModels flag. The problem here is that ntprint.inf
501 // doesn't contain duplicate models, and it has hundreds of models
502 // listed. You wouldn't check for duplicates there. But oftentimes,
503 // loading different windows print driver files contain multiple
504 // entries for the same printer. You don't want the UI to display
505 // the same printer multiple times, so in that case, you would ask
506 // this function to check for multiple models.
509 CThirdPage::LoadPrintDriverDefsFromFile(Manufacturers
& manufacturers
, const CString
& filename
, bool checkForDuplicateModels
)
511 PrinterParsingState state
= Looking
;
512 Manufacturers::iterator iter
= manufacturers
.end();
514 CFileException feError
;
519 typedef std::map
<CString
, CString
> StringMap
;
523 ok
= file
.Open( filename
, CFile::modeRead
|CFile::typeText
, &feError
);
524 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
525 require_noerr( err
, exit
);
527 check ( state
== Looking
);
528 check ( iter
== manufacturers
.end() );
531 // first, parse the file looking for string sections
533 while (file
.ReadString(s
))
538 if (s
.Find(';') == 0)
546 else if (s
.Find('[') == 0)
549 // handle any capitalization issues here
555 if (tag
== L
"[strings]")
557 state
= ParsingStrings
;
572 if (s
.GetLength() > 0)
574 CString key
= s
.Tokenize(L
"=",curPos
);
575 CString val
= s
.Tokenize(L
"=",curPos
);
578 // get rid of all delimiters
596 ok
= file
.Open( filename
, CFile::modeRead
|CFile::typeText
, &feError
);
597 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
598 require_noerr( err
, exit
);
602 check ( iter
== manufacturers
.end() );
604 while (file
.ReadString(s
))
609 if (s
.Find(';') == 0)
617 else if (s
.Find('[') == 0)
620 // handle any capitalization issues here
626 if (tag
== L
"[manufacturer]")
628 state
= ParsingManufacturers
;
636 // remove the leading and trailing delimiters
642 // <rdar://problem/4826126>
644 // Ignore decorations in model declarations
647 name
= s
.Tokenize( L
".", curPos
);
650 // check to see if this is a printer entry
652 iter
= manufacturers
.find( name
);
654 if (iter
!= manufacturers
.end())
656 state
= ParsingModels
;
665 // only look at this if the line isn't empty, or
666 // if it isn't a comment
668 else if ((s
.GetLength() > 0) && (s
.Find(';') != 0))
673 // if we're parsing manufacturers, then we will parse
674 // an entry of the form key=val, where key is a delimited
675 // string specifying a manufacturer name, and val is
676 // a tag that is used later in the file. the key is
677 // delimited by either '"' (quotes) or '%' (percent sign).
679 // the tag is used further down the file when models are
680 // declared. this allows multiple manufacturers to exist
681 // in a single inf file.
683 case ParsingManufacturers
:
685 Manufacturer
* manufacturer
;
688 CString key
= s
.Tokenize(L
"=",curPos
);
689 CString val
= s
.Tokenize(L
"=",curPos
);
693 manufacturer
= new Manufacturer
;
700 require_action( manufacturer
, exit
, err
= kNoMemoryErr
);
703 // if it's a variable, look it up
705 if (key
.Find('%') == 0)
707 StringMap::iterator it
;
711 it
= strings
.find(key
);
713 if (it
!= strings
.end())
727 // why is there no consistency in inf files?
729 if (val
.GetLength() == 0)
735 // fix the manufacturer name if necessary
738 val
= val
.Tokenize(L
",", curPos
);
744 decoration
= val
.Tokenize( L
",", curPos
);
746 if ( decoration
.GetLength() > 0 )
748 manufacturer
->decorations
.push_back( decoration
);
756 manufacturer
->name
= NormalizeManufacturerName( key
);
757 manufacturer
->tag
= val
;
759 manufacturers
[val
] = manufacturer
;
765 check( iter
!= manufacturers
.end() );
770 CString name
= s
.Tokenize(L
"=",curPos
);
771 CString description
= s
.Tokenize(L
"=",curPos
);
773 if (name
.Find('%') == 0)
775 StringMap::iterator it
;
779 it
= strings
.find(name
);
781 if (it
!= strings
.end())
795 // If true, see if we've seen this guy before
797 if (checkForDuplicateModels
== true)
799 if ( MatchModel( iter
->second
, ConvertToModelName( name
) ) != NULL
)
806 // Stock Vista printer inf files embed guids in the model
807 // declarations for Epson printers. Let's ignore those.
809 if ( name
.Find( L
"{", 0 ) != -1 )
823 require_action( model
, exit
, err
= kNoMemoryErr
);
825 model
->infFileName
= filename
;
826 model
->displayName
= name
;
828 model
->driverInstalled
= false;
830 iter
->second
->models
.push_back(model
);
836 // pay no attention if we are in any other state
850 // -------------------------------------------------------
851 // LoadPrintDriverDefs
853 // This function is responsible for loading the print driver
854 // definitions of all print drivers that have been installed
856 // -------------------------------------------------------
858 CThirdPage::LoadPrintDriverDefs( Manufacturers
& manufacturers
)
860 BYTE
* buffer
= NULL
;
861 DWORD bytesReceived
= 0;
862 DWORD numPrinters
= 0;
867 // like a lot of win32 calls, we call this first to get the
868 // size of the buffer we need.
870 EnumPrinterDrivers(NULL
, L
"all", 6, NULL
, 0, &bytesReceived
, &numPrinters
);
872 if (bytesReceived
> 0)
876 buffer
= new BYTE
[bytesReceived
];
883 require_action( buffer
, exit
, err
= kNoMemoryErr
);
886 // this call gets the real info
888 ok
= EnumPrinterDrivers(NULL
, L
"all", 6, buffer
, bytesReceived
, &bytesReceived
, &numPrinters
);
889 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
890 require_noerr( err
, exit
);
892 DRIVER_INFO_6
* info
= (DRIVER_INFO_6
*) buffer
;
894 for (DWORD i
= 0; i
< numPrinters
; i
++)
896 Manufacturer
* manufacturer
;
901 // skip over anything that doesn't have a manufacturer field. This
902 // fixes a bug that I noticed that occurred after I installed
903 // ProComm. This program add a print driver with no manufacturer
904 // that screwed up this wizard.
906 if (info
[i
].pszMfgName
== NULL
)
912 // look for manufacturer
914 Manufacturers::iterator iter
;
919 name
= NormalizeManufacturerName( info
[i
].pszMfgName
);
921 iter
= manufacturers
.find(name
);
923 if (iter
!= manufacturers
.end())
925 manufacturer
= iter
->second
;
931 manufacturer
= new Manufacturer
;
938 require_action( manufacturer
, exit
, err
= kNoMemoryErr
);
940 manufacturer
->name
= name
;
942 manufacturers
[name
] = manufacturer
;
946 // now look to see if we have already seen this guy. this could
947 // happen if we have already installed printers that are described
948 // in ntprint.inf. the extant drivers will show up in EnumPrinterDrivers
949 // but we have already loaded their info
952 if ( MatchModel( manufacturer
, ConvertToModelName( info
[i
].pName
) ) == NULL
)
963 require_action( model
, exit
, err
= kNoMemoryErr
);
965 model
->displayName
= info
[i
].pName
;
966 model
->name
= info
[i
].pName
;
967 model
->driverInstalled
= true;
969 manufacturer
->models
.push_back(model
);
984 // -------------------------------------------------------
985 // LoadGenericPrintDriverDefs
987 // This function is responsible for loading polymorphic
988 // generic print drivers defs. The UI will read
989 // something like "Generic / Postscript" and we can map
990 // that to any print driver we want.
991 // -------------------------------------------------------
993 CThirdPage::LoadGenericPrintDriverDefs( Manufacturers
& manufacturers
)
995 Manufacturer
* manufacturer
;
997 Manufacturers::iterator iter
;
998 CString psDriverName
;
999 CString pclDriverName
;
1002 // <rdar://problem/4030388> Generic drivers don't do color
1004 // First try and find our generic driver names
1006 iter
= m_manufacturers
.find(L
"HP");
1007 require_action( iter
!= m_manufacturers
.end(), exit
, err
= kUnknownErr
);
1008 manufacturer
= iter
->second
;
1010 // Look for Postscript
1012 model
= manufacturer
->find( kGenericPSColorDriver
);
1016 model
= manufacturer
->find( kGenericPSDriver
);
1021 psDriverName
= model
->name
;
1026 model
= manufacturer
->find( kGenericPCLColorDriver
);
1030 model
= manufacturer
->find( kGenericPCLDriver
);
1035 pclDriverName
= model
->name
;
1038 // If we found either a generic PS driver, or a generic PCL driver,
1039 // then add them to the list
1041 if ( psDriverName
.GetLength() || pclDriverName
.GetLength() )
1043 // Try and find generic manufacturer if there is one
1045 iter
= manufacturers
.find(L
"Generic");
1047 if (iter
!= manufacturers
.end())
1049 manufacturer
= iter
->second
;
1055 manufacturer
= new Manufacturer
;
1059 manufacturer
= NULL
;
1062 require_action( manufacturer
, exit
, err
= kNoMemoryErr
);
1064 manufacturer
->name
= "Generic";
1065 manufacturers
[manufacturer
->name
] = manufacturer
;
1068 if ( psDriverName
.GetLength() > 0 )
1072 m_genericPostscript
= new Model
;
1076 m_genericPostscript
= NULL
;
1079 require_action( m_genericPostscript
, exit
, err
= kNoMemoryErr
);
1081 m_genericPostscript
->displayName
= kGenericPostscript
;
1082 m_genericPostscript
->name
= psDriverName
;
1083 m_genericPostscript
->driverInstalled
= false;
1085 manufacturer
->models
.push_back( m_genericPostscript
);
1088 if ( pclDriverName
.GetLength() > 0 )
1092 m_genericPCL
= new Model
;
1096 m_genericPCL
= NULL
;
1099 require_action( m_genericPCL
, exit
, err
= kNoMemoryErr
);
1101 m_genericPCL
->displayName
= kGenericPCL
;
1102 m_genericPCL
->name
= pclDriverName
;
1103 m_genericPCL
->driverInstalled
= false;
1105 manufacturer
->models
.push_back( m_genericPCL
);
1114 // ------------------------------------------------------
1115 // ConvertToManufacturerName
1117 // This function is responsible for tweaking the
1118 // name so that subsequent string operations won't fail because
1119 // of capitalizations/different names for the same manufacturer
1120 // (i.e. Hewlett-Packard/HP/Hewlett Packard)
1123 CThirdPage::ConvertToManufacturerName( const CString
& name
)
1126 // first we're going to convert all the characters to lower
1129 CString lower
= name
;
1133 // now we're going to check to see if the string says "hewlett-packard",
1134 // because sometimes they refer to themselves as "hewlett-packard", and
1135 // sometimes they refer to themselves as "hp".
1137 if ( lower
== L
"hewlett-packard")
1143 // tweak for Xerox Phaser, which doesn't announce itself
1146 else if ( lower
.Find( L
"phaser", 0 ) != -1 )
1154 // ------------------------------------------------------
1155 // ConvertToModelName
1157 // This function is responsible for ensuring that subsequent
1158 // string operations don't fail because of differing capitalization
1159 // schemes and the like
1160 // ------------------------------------------------------
1163 CThirdPage::ConvertToModelName( const CString
& name
)
1166 // convert it to lowercase
1168 CString lower
= name
;
1174 // ------------------------------------------------------
1175 // NormalizeManufacturerName
1177 // This function is responsible for tweaking the manufacturer
1178 // name so that there are no aliases for vendors
1181 CThirdPage::NormalizeManufacturerName( const CString
& name
)
1183 CString normalized
= name
;
1186 // now we're going to check to see if the string says "hewlett-packard",
1187 // because sometimes they refer to themselves as "hewlett-packard", and
1188 // sometimes they refer to themselves as "hp".
1190 if ( normalized
== L
"Hewlett-Packard")
1198 // -------------------------------------------------------
1201 // This function is responsible for matching a printer
1202 // to a list of manufacturers and models. It calls
1203 // MatchManufacturer and MatchModel in turn.
1206 OSStatus
CThirdPage::MatchPrinter(Manufacturers
& manufacturers
, Printer
* printer
, Service
* service
, bool useCUPSWorkaround
)
1208 CString normalizedProductName
;
1209 Manufacturer
* manufacturer
= NULL
;
1210 Manufacturer
* genericManufacturer
= NULL
;
1211 Model
* model
= NULL
;
1212 Model
* genericModel
= NULL
;
1215 OSStatus err
= kNoErr
;
1220 Queue
* q
= service
->SelectedQueue();
1225 // first look to see if we have a usb_MFG descriptor
1227 if ( q
->usb_MFG
.GetLength() > 0)
1229 manufacturer
= MatchManufacturer( manufacturers
, ConvertToManufacturerName ( q
->usb_MFG
) );
1232 if ( manufacturer
== NULL
)
1234 q
->product
.Remove('(');
1235 q
->product
.Remove(')');
1237 manufacturer
= MatchManufacturer( manufacturers
, ConvertToManufacturerName ( q
->product
) );
1241 // if we found the manufacturer, then start looking for the model
1243 if ( manufacturer
!= NULL
)
1245 if ( q
->usb_MDL
.GetLength() > 0 )
1247 model
= MatchModel ( manufacturer
, ConvertToModelName ( q
->usb_MDL
) );
1250 if ( ( model
== NULL
) && ( q
->product
.GetLength() > 0 ) )
1252 q
->product
.Remove('(');
1253 q
->product
.Remove(')');
1255 model
= MatchModel ( manufacturer
, ConvertToModelName ( q
->product
) );
1258 if ( model
!= NULL
)
1260 // <rdar://problem/4124524> Offer Generic printers if printer advertises Postscript or PCL. Workaround
1261 // bug in OS X CUPS printer sharing by selecting Generic driver instead of matched printer.
1263 bool hasGenericDriver
= false;
1265 if ( MatchGeneric( manufacturers
, printer
, service
, &genericManufacturer
, &genericModel
) )
1267 hasGenericDriver
= true;
1270 // <rdar://problem/4190104> Use "application/octet-stream" to determine if CUPS
1271 // shared queue supports raw
1273 if ( q
->pdl
.Find( L
"application/octet-stream" ) != -1 )
1275 useCUPSWorkaround
= false;
1278 if ( useCUPSWorkaround
&& printer
->isSharedFromOSX
&& hasGenericDriver
)
1281 // <rdar://problem/4496652> mDNS: Don't allow user to choose non-working driver
1283 Manufacturers genericManufacturers
;
1285 LoadGenericPrintDriverDefs( genericManufacturers
);
1287 SelectMatch( genericManufacturers
, printer
, service
, genericManufacturer
, genericModel
);
1291 SelectMatch(manufacturers
, printer
, service
, manufacturer
, model
);
1299 // display a message to the user based on whether we could match
1304 text
.LoadString(IDS_PRINTER_MATCH_GOOD
);
1306 else if ( MatchGeneric( manufacturers
, printer
, service
, &genericManufacturer
, &genericModel
) )
1308 if ( printer
->isSharedFromOSX
)
1311 // <rdar://problem/4496652> mDNS: Don't allow user to choose non-working driver
1313 Manufacturers genericManufacturers
;
1315 LoadGenericPrintDriverDefs( genericManufacturers
);
1317 SelectMatch( genericManufacturers
, printer
, service
, genericManufacturer
, genericModel
);
1319 text
.LoadString(IDS_PRINTER_MATCH_GOOD
);
1323 SelectMatch( manufacturers
, printer
, service
, genericManufacturer
, genericModel
);
1324 text
.LoadString(IDS_PRINTER_MATCH_MAYBE
);
1329 text
.LoadString(IDS_PRINTER_MATCH_BAD
);
1332 // if there was any crud in this list from before, get rid of it now
1334 m_modelListCtrl
.DeleteAllItems();
1337 // select the manufacturer if we found one
1339 if (manufacturer
!= NULL
)
1345 // select the manufacturer
1347 info
.flags
= LVFI_STRING
;
1348 info
.psz
= manufacturer
->name
;
1350 nIndex
= m_manufacturerListCtrl
.FindItem(&info
);
1354 m_manufacturerListCtrl
.SetItemState(nIndex
, LVIS_SELECTED
, LVIS_SELECTED
);
1357 //<rdar://problem/4528853> mDNS: When auto-highlighting items in lists, scroll list so highlighted item is in the middle
1359 AutoScroll(m_manufacturerListCtrl
, nIndex
);
1364 m_printerSelectionText
.SetWindowText(text
);
1369 // ------------------------------------------------------
1370 // MatchManufacturer
1372 // This function is responsible for finding a manufacturer
1373 // object from a string name. It does a CString::Find, which
1374 // is like strstr, so it doesn't have to do an exact match
1376 // If it can't find a match, NULL is returned
1377 // ------------------------------------------------------
1380 CThirdPage::MatchManufacturer( Manufacturers
& manufacturers
, const CString
& name
)
1382 Manufacturers::iterator iter
;
1384 for (iter
= manufacturers
.begin(); iter
!= manufacturers
.end(); iter
++)
1387 // we're going to convert all the manufacturer names to lower case,
1388 // so we match the name passed in.
1390 CString lower
= iter
->second
->name
;
1394 // now try and find the lowered string in the name passed in.
1396 if (name
.Find(lower
) != -1)
1398 return iter
->second
;
1405 // -------------------------------------------------------
1408 // This function is responsible for matching a model from
1409 // a name. It does a CString::Find(), which works like strstr,
1410 // so it doesn't rely on doing an exact string match.
1414 CThirdPage::MatchModel(Manufacturer
* manufacturer
, const CString
& name
)
1416 Models::iterator iter
;
1418 iter
= manufacturer
->models
.begin();
1420 for (iter
= manufacturer
->models
.begin(); iter
!= manufacturer
->models
.end(); iter
++)
1422 Model
* model
= *iter
;
1425 // convert the model name to lower case
1427 CString lowered
= model
->name
;
1428 lowered
.MakeLower();
1430 if (lowered
.Find( name
) != -1)
1436 // <rdar://problem/3841218>
1437 // try removing the first substring and search again
1440 if ( name
.Find(' ') != -1 )
1442 CString altered
= name
;
1443 altered
.Delete( 0, altered
.Find(' ') + 1 );
1445 if ( lowered
.Find( altered
) != -1 )
1455 // -------------------------------------------------------
1458 // This function will attempt to find a generic printer
1459 // driver for a printer that we weren't able to match
1463 CThirdPage::MatchGeneric( Manufacturers
& manufacturers
, Printer
* printer
, Service
* service
, Manufacturer
** manufacturer
, Model
** model
)
1468 DEBUG_UNUSED( printer
);
1472 Queue
* q
= service
->SelectedQueue();
1476 Manufacturers::iterator iter
= manufacturers
.find( kGenericManufacturer
);
1477 require_action_quiet( iter
!= manufacturers
.end(), exit
, ok
= FALSE
);
1479 *manufacturer
= iter
->second
;
1484 if ( m_genericPCL
&& ( pdl
.Find( kPDLPCLKey
) != -1 ) )
1486 *model
= m_genericPCL
;
1489 else if ( m_genericPostscript
&& ( pdl
.Find( kPDLPostscriptKey
) != -1 ) )
1491 *model
= m_genericPostscript
;
1500 // -----------------------------------------------------------
1503 // This function is responsible for doing initialization that
1504 // only occurs once during a run of the wizard
1507 OSStatus
CThirdPage::OnInitPage()
1511 OSStatus err
= kNoErr
;
1513 // Load printer icon
1514 check( m_printerImage
== NULL
);
1516 m_printerImage
= (CStatic
*) GetDlgItem( 1 ); // 1 == IDR_MANIFEST
1517 check( m_printerImage
);
1519 if ( m_printerImage
!= NULL
)
1521 m_printerImage
->SetIcon( LoadIcon( GetNonLocalizedResources(), MAKEINTRESOURCE( IDI_PRINTER
) ) );
1525 // The CTreeCtrl widget automatically sends a selection changed
1526 // message which initially we want to ignore, because the user
1527 // hasn't selected anything
1529 // this flag gets reset in the message handler. Every subsequent
1530 // message gets handled.
1534 // we have to make sure that we only do this once. Typically,
1535 // we would do this in something like OnInitDialog, but we don't
1536 // have this in Wizards, because the window is a PropertySheet.
1537 // We're considered fully initialized when we receive the first
1540 header
.LoadString(IDS_MANUFACTURER_HEADING
);
1541 m_manufacturerListCtrl
.InsertColumn(0, header
, LVCFMT_LEFT
, -1 );
1542 m_manufacturerSelected
= NULL
;
1544 header
.LoadString(IDS_MODEL_HEADING
);
1545 m_modelListCtrl
.InsertColumn(0, header
, LVCFMT_LEFT
, -1 );
1546 m_modelSelected
= NULL
;
1551 void CThirdPage::DoDataExchange(CDataExchange
* pDX
)
1553 CPropertyPage::DoDataExchange(pDX
);
1554 DDX_Control(pDX
, IDC_PRINTER_MANUFACTURER
, m_manufacturerListCtrl
);
1555 DDX_Control(pDX
, IDC_PRINTER_MODEL
, m_modelListCtrl
);
1556 DDX_Control(pDX
, IDC_PRINTER_NAME
, m_printerName
);
1557 DDX_Control(pDX
, IDC_DEFAULT_PRINTER
, m_defaultPrinterCtrl
);
1558 DDX_Control(pDX
, IDC_PRINTER_SELECTION_TEXT
, m_printerSelectionText
);
1562 // ----------------------------------------------------------
1565 // This function is called by MFC after the window has been
1570 CThirdPage::OnSetActive()
1572 CPrinterSetupWizardSheet
* psheet
;
1576 psheet
= reinterpret_cast<CPrinterSetupWizardSheet
*>(GetParent());
1577 require_quiet( psheet
, exit
);
1579 psheet
->SetWizardButtons( PSWIZB_BACK
);
1581 printer
= psheet
->GetSelectedPrinter();
1582 require_quiet( printer
, exit
);
1584 service
= printer
->services
.front();
1585 require_quiet( service
, exit
);
1588 // call OnInitPage once
1593 m_initialized
= true;
1597 // <rdar://problem/4580061> mDNS: Printers added using Bonjour should be set as the default printer.
1599 if ( DefaultPrinterExists() )
1601 m_defaultPrinterCtrl
.SetCheck( BST_UNCHECKED
);
1602 printer
->deflt
= false;
1606 m_defaultPrinterCtrl
.SetCheck( BST_CHECKED
);
1607 printer
->deflt
= true;
1611 // update the UI with the printer name
1613 m_printerName
.SetWindowText(printer
->displayName
);
1616 // populate the list controls with the manufacturers and models
1619 PopulateUI( m_manufacturers
);
1622 // and try and match the printer
1625 if ( psheet
->GetLastPage() == psheet
->GetPage(1) )
1627 MatchPrinter( m_manufacturers
, printer
, service
, true );
1631 SelectMatch(printer
, service
, m_manufacturerSelected
, m_modelSelected
);
1636 return CPropertyPage::OnSetActive();
1640 CThirdPage::OnKillActive()
1642 CPrinterSetupWizardSheet
* psheet
;
1644 psheet
= reinterpret_cast<CPrinterSetupWizardSheet
*>(GetParent());
1645 require_quiet( psheet
, exit
);
1647 psheet
->SetLastPage(this);
1651 return CPropertyPage::OnKillActive();
1654 // -------------------------------------------------------
1657 // This function is called to populate the list of manufacturers
1660 CThirdPage::PopulateUI(Manufacturers
& manufacturers
)
1662 Manufacturers::iterator iter
;
1664 m_manufacturerListCtrl
.DeleteAllItems();
1666 for (iter
= manufacturers
.begin(); iter
!= manufacturers
.end(); iter
++)
1670 Manufacturer
* manufacturer
= iter
->second
;
1672 nIndex
= m_manufacturerListCtrl
.InsertItem(0, manufacturer
->name
);
1674 m_manufacturerListCtrl
.SetItemData(nIndex
, (DWORD_PTR
) manufacturer
);
1676 m_manufacturerListCtrl
.SetColumnWidth( 0, LVSCW_AUTOSIZE_USEHEADER
);
1682 BEGIN_MESSAGE_MAP(CThirdPage
, CPropertyPage
)
1683 ON_NOTIFY(LVN_ITEMCHANGED
, IDC_PRINTER_MANUFACTURER
, OnLvnItemchangedManufacturer
)
1684 ON_NOTIFY(LVN_ITEMCHANGED
, IDC_PRINTER_MODEL
, OnLvnItemchangedPrinterModel
)
1685 ON_BN_CLICKED(IDC_DEFAULT_PRINTER
, OnBnClickedDefaultPrinter
)
1686 ON_BN_CLICKED(IDC_HAVE_DISK
, OnBnClickedHaveDisk
)
1689 // CThirdPage message handlers
1690 void CThirdPage::OnLvnItemchangedManufacturer(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1692 LPNMLISTVIEW pNMLV
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
1694 POSITION p
= m_manufacturerListCtrl
.GetFirstSelectedItemPosition();
1695 int nSelected
= m_manufacturerListCtrl
.GetNextSelectedItem(p
);
1697 if (nSelected
!= -1)
1699 m_manufacturerSelected
= (Manufacturer
*) m_manufacturerListCtrl
.GetItemData(nSelected
);
1701 m_modelListCtrl
.SetRedraw(FALSE
);
1703 m_modelListCtrl
.DeleteAllItems();
1704 m_modelSelected
= NULL
;
1706 Models::iterator iter
;
1708 for (iter
= m_manufacturerSelected
->models
.begin(); iter
!= m_manufacturerSelected
->models
.end(); iter
++)
1710 Model
* model
= *iter
;
1712 int nItem
= m_modelListCtrl
.InsertItem( 0, model
->displayName
);
1714 m_modelListCtrl
.SetItemData(nItem
, (DWORD_PTR
) model
);
1716 m_modelListCtrl
.SetColumnWidth( 0, LVSCW_AUTOSIZE_USEHEADER
);
1719 m_modelListCtrl
.SetRedraw(TRUE
);
1725 void CThirdPage::OnLvnItemchangedPrinterModel(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1727 LPNMLISTVIEW pNMLV
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
1729 CPrinterSetupWizardSheet
* psheet
;
1733 psheet
= reinterpret_cast<CPrinterSetupWizardSheet
*>(GetParent());
1734 require_quiet( psheet
, exit
);
1736 printer
= psheet
->GetSelectedPrinter();
1737 require_quiet( printer
, exit
);
1739 service
= printer
->services
.front();
1740 require_quiet( service
, exit
);
1742 check ( m_manufacturerSelected
);
1744 POSITION p
= m_modelListCtrl
.GetFirstSelectedItemPosition();
1745 int nSelected
= m_modelListCtrl
.GetNextSelectedItem(p
);
1747 if (nSelected
!= -1)
1749 m_modelSelected
= (Model
*) m_modelListCtrl
.GetItemData(nSelected
);
1751 CopyPrinterSettings( printer
, service
, m_manufacturerSelected
, m_modelSelected
);
1753 psheet
->SetWizardButtons(PSWIZB_BACK
|PSWIZB_NEXT
);
1757 psheet
->SetWizardButtons(PSWIZB_BACK
);
1765 void CThirdPage::OnBnClickedDefaultPrinter()
1767 CPrinterSetupWizardSheet
* psheet
;
1770 psheet
= reinterpret_cast<CPrinterSetupWizardSheet
*>(GetParent());
1771 require_quiet( psheet
, exit
);
1773 printer
= psheet
->GetSelectedPrinter();
1774 require_quiet( printer
, exit
);
1776 printer
->deflt
= ( m_defaultPrinterCtrl
.GetCheck() == BST_CHECKED
) ? true : false;
1783 void CThirdPage::OnBnClickedHaveDisk()
1785 CPrinterSetupWizardSheet
* psheet
;
1788 Manufacturers manufacturers
;
1790 CFileDialog
dlg(TRUE
, NULL
, NULL
, OFN_HIDEREADONLY
|OFN_FILEMUSTEXIST
, L
"Setup Information (*.inf)|*.inf||", this);
1792 psheet
= reinterpret_cast<CPrinterSetupWizardSheet
*>(GetParent());
1793 require_quiet( psheet
, exit
);
1795 printer
= psheet
->GetSelectedPrinter();
1796 require_quiet( printer
, exit
);
1798 service
= printer
->services
.front();
1799 require_quiet( service
, exit
);
1803 if ( dlg
.DoModal() == IDOK
)
1805 CString filename
= dlg
.GetPathName();
1807 LoadPrintDriverDefsFromFile( manufacturers
, filename
, true );
1811 if ( manufacturers
.size() > 0 )
1813 PopulateUI( manufacturers
);
1815 MatchPrinter( manufacturers
, printer
, service
, false );