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.42 2009/07/07 22:04:55 herscher
21 <rdar://problem/4176343> LOC Impact: Need custom text when selecting the wrong printer driver
23 Revision 1.41 2009/06/18 18:05:50 herscher
24 <rdar://problem/4694554> Eliminate the first screen of Printer Wizard and maybe combine others ("I'm Feeling Lucky")
26 Revision 1.40 2009/05/29 20:43:36 herscher
27 <rdar://problem/6928136> Printer Wizard doesn't work correctly in Windows 7 64 bit
29 Revision 1.39 2009/05/27 06:25:49 herscher
30 <rdar://problem/4176334> Need error dialog when selecting bad INF file
32 Revision 1.38 2009/05/27 04:59:57 herscher
33 <rdar://problem/4517393> COMPATIBILITY WITH HP CLJ4700
34 <rdar://problem/6142138> Compatibility with Samsung print driver files
36 Revision 1.37 2007/06/08 06:30:26 herscher
37 <rdar://problem/5257700> Fix uninitialized pointers when detecting generic PCL and PS drivers
39 Revision 1.36 2007/06/06 20:39:10 cheshire
40 <rdar://problem/5254377> Printer Setup Wizard started crashing in Bonjour104A8, after update to Visual Studio 2005
42 Revision 1.35 2007/06/06 20:08:01 cheshire
43 <rdar://problem/4528853> mDNS: When auto-highlighting items in lists, scroll list so highlighted item is in the middle
44 AutoScroll model list as well as manufacturer list
46 Revision 1.34 2007/06/06 19:53:48 cheshire
47 <rdar://problem/5187308> Move build train to Visual Studio 2005
49 Revision 1.33 2007/04/20 22:58:10 herscher
50 <rdar://problem/4826126> mDNS: Printer Wizard doesn't offer generic HP printers or generic PS support on Vista RC2
52 Revision 1.32 2007/04/13 23:42:20 herscher
53 <rdar://problem/4580061> mDNS: Printers added using Bonjour should be set as the default printer.
55 Revision 1.31 2007/04/13 21:38:46 herscher
56 <rdar://problem/4528853> mDNS: When auto-highlighting items in lists, scroll list so highlighted item is in the middle
58 Revision 1.30 2007/04/13 20:23:40 herscher
59 Fixed mistake in previous checkin that reverted license text for this file
61 Revision 1.29 2007/04/13 18:10:24 herscher
62 <rdar://problem/4496652> mDNS: Don't allow user to choose non-working driver
64 Revision 1.28 2006/08/14 23:24:09 cheshire
65 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
67 Revision 1.27 2005/10/05 21:41:45 herscher
68 <rdar://problem/4190104> Use "application/octet-stream" to determine if CUPS shared queue supports raw
70 Revision 1.26 2005/07/11 20:17:15 shersche
71 <rdar://problem/4124524> UI fixes associated with CUPS printer workaround fix.
73 Revision 1.25 2005/07/07 17:53:20 shersche
74 Fix problems associated with the CUPS printer workaround fix.
76 Revision 1.24 2005/06/30 18:02:54 shersche
77 <rdar://problem/4124524> Workaround for Mac OS X Printer Sharing bug
79 Revision 1.23 2005/04/18 02:33:47 shersche
80 <rdar://problem/4091216> Default printer option cannot be deselected
82 Revision 1.22 2005/04/13 17:46:22 shersche
83 <rdar://problem/4082122> Generic PCL not selected when printers advertise multiple text records
85 Revision 1.21 2005/03/30 02:09:55 shersche
86 Auto-resize the column width to account for differing fonts and font sizes
88 Revision 1.20 2005/03/05 02:27:45 shersche
89 <rdar://problem/4030388> Generic drivers don't do color
91 Revision 1.19 2005/02/23 02:08:51 shersche
92 <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".
94 Revision 1.18 2005/02/15 07:02:51 shersche
95 <rdar://problem/4003724> Display different UI text when generic printer drivers are selected
97 Revision 1.17 2005/02/08 21:45:06 shersche
98 <rdar://problem/3947490> Default to Generic PostScript or PCL if unable to match driver
100 Revision 1.16 2005/02/08 18:56:03 shersche
101 Fix generated IPP url so that it doesn't add "/printers" string
103 Revision 1.15 2005/02/01 01:44:07 shersche
104 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.
106 Revision 1.14 2005/01/25 08:55:54 shersche
107 <rdar://problem/3911084> Load icons at run-time from resource DLL
110 Revision 1.13 2005/01/06 08:15:45 shersche
111 Append queue name to end of LPR port name, correctly build port name when queue name is absent
113 Revision 1.12 2005/01/05 01:06:12 shersche
114 <rdar://problem/3841218> Strip the first substring off the product key if an initial match can't be found with the whole product key.
117 Revision 1.11 2004/12/29 18:53:38 shersche
118 <rdar://problem/3725106>
119 <rdar://problem/3737413> Added support for LPR and IPP protocols as well as support for obtaining multiple text records. Reorganized and simplified codebase.
120 Bug #: 3725106, 3737413
122 Revision 1.10 2004/10/11 22:55:34 shersche
123 <rdar://problem/3827624> Use the IP port number when deriving the printer port name.
126 Revision 1.9 2004/06/27 23:08:00 shersche
127 code cleanup, make sure EnumPrintDrivers returns non-zero value, ignore comments in inf files
129 Revision 1.8 2004/06/27 08:06:45 shersche
130 Parse [Strings] section of inf file
132 Revision 1.7 2004/06/26 04:00:05 shersche
133 fix warnings compiling in debug mode
134 Submitted by: herscher
136 Revision 1.6 2004/06/26 03:19:57 shersche
137 clean up warning messages
139 Submitted by: herscher
141 Revision 1.5 2004/06/25 05:06:02 shersche
142 Trim whitespace from key/value pairs when parsing inf files
143 Submitted by: herscher
145 Revision 1.4 2004/06/25 02:44:13 shersche
146 Tweaked code to handle Xerox Phaser printer identification
147 Submitted by: herscher
149 Revision 1.3 2004/06/25 02:27:58 shersche
150 Do a CListCtrl::FindItem() before calling CListCtrl::SetItemState().
151 Submitted by: herscher
153 Revision 1.2 2004/06/23 18:09:23 shersche
154 Normalize tag names when parsing inf files.
155 Submitted by: herscher
157 Revision 1.1 2004/06/18 04:36:58 rpantos
162 #include "PrinterSetupWizardApp.h"
163 #include "PrinterSetupWizardSheet.h"
164 #include "ThirdPage.h"
165 #include "StdioFileEx.h"
168 #include <winspool.h>
169 #include <setupapi.h>
171 // local variable is initialize but not referenced
172 #pragma warning(disable:4189)
175 // This is the printer description file that is shipped
176 // with Windows XP and below
178 #define kNTPrintFile L"inf\\ntprint.inf"
181 // Windows Vista ships with a set of prn*.inf files
183 #define kVistaPrintFiles L"inf\\prn*.inf"
186 // These are pre-defined names for Generic manufacturer and model
188 #define kGenericManufacturer L"Generic"
189 #define kGenericText L"Generic / Text Only"
190 #define kGenericPostscript L"Generic / Postscript"
191 #define kGenericPCL L"Generic / PCL"
192 #define kPDLPostscriptKey L"application/postscript"
193 #define kPDLPCLKey L"application/vnd.hp-pcl"
194 #define kGenericPSColorDriver L"HP Color LaserJet 4550 PS"
195 #define kGenericPSDriver L"HP LaserJet 4050 Series PS"
196 #define kGenericPCLColorDriver L"HP Color LaserJet 4550 PCL"
197 #define kGenericPCLDriver L"HP LaserJet 4050 Series PCL"
202 IMPLEMENT_DYNAMIC(CThirdPage
, CPropertyPage
)
203 CThirdPage::CThirdPage()
204 : CPropertyPage(CThirdPage::IDD
),
205 m_manufacturerSelected( NULL
),
206 m_modelSelected( NULL
),
207 m_genericPostscript( NULL
),
208 m_genericPCL( NULL
),
209 m_initialized(false),
210 m_printerImage( NULL
)
212 static const int bufferSize
= 32768;
213 TCHAR windowsDirectory
[bufferSize
];
215 WIN32_FIND_DATA findFileData
;
222 m_psp
.dwFlags
&= ~(PSP_HASHELP
);
223 m_psp
.dwFlags
|= PSP_DEFAULT
|PSP_USEHEADERTITLE
|PSP_USEHEADERSUBTITLE
;
225 m_psp
.pszHeaderTitle
= MAKEINTRESOURCE(IDS_INSTALL_TITLE
);
226 m_psp
.pszHeaderSubTitle
= MAKEINTRESOURCE(IDS_INSTALL_SUBTITLE
);
229 // load printers from ntprint.inf
231 ok
= GetWindowsDirectory( windowsDirectory
, bufferSize
);
232 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
233 require_noerr( err
, exit
);
236 // <rdar://problem/4826126>
238 // If there are no *prn.inf files, we'll assume that the information
241 prnFiles
.Format( L
"%s\\%s", windowsDirectory
, kVistaPrintFiles
);
242 findHandle
= FindFirstFile( prnFiles
, &findFileData
);
244 if ( findHandle
!= INVALID_HANDLE_VALUE
)
248 absolute
.Format( L
"%s\\inf\\%s", windowsDirectory
, findFileData
.cFileName
);
249 err
= LoadPrintDriverDefsFromFile( m_manufacturers
, absolute
, false );
250 require_noerr( err
, exit
);
252 while ( FindNextFile( findHandle
, &findFileData
) )
254 absolute
.Format( L
"%s\\inf\\%s", windowsDirectory
, findFileData
.cFileName
);
255 err
= LoadPrintDriverDefsFromFile( m_manufacturers
, absolute
, false );
256 require_noerr( err
, exit
);
259 FindClose( findHandle
);
263 ntPrint
.Format(L
"%s\\%s", windowsDirectory
, kNTPrintFile
);
264 err
= LoadPrintDriverDefsFromFile( m_manufacturers
, ntPrint
, false );
265 require_noerr(err
, exit
);
269 // load printer drivers that have been installed on this machine
271 err
= LoadPrintDriverDefs( m_manufacturers
);
272 require_noerr(err
, exit
);
275 // load our own special generic printer defs
277 err
= LoadGenericPrintDriverDefs( m_manufacturers
);
278 require_noerr( err
, exit
);
285 CThirdPage::~CThirdPage()
288 // clean up all the printer manufacturers
290 while (m_manufacturers
.size())
292 Manufacturers::iterator iter
= m_manufacturers
.begin();
294 while (iter
->second
->models
.size())
296 Models::iterator it
= iter
->second
->models
.begin();
302 iter
->second
->models
.erase(it
);
307 m_manufacturers
.erase(iter
);
311 // ----------------------------------------------------
314 // SelectMatch will do all the UI work associated with
315 // selected a manufacturer and model of printer. It also
316 // makes sure the printer object is update with the
319 // ----------------------------------------------------
321 CThirdPage::SelectMatch(Printer
* printer
, Service
* service
, Manufacturer
* manufacturer
, Model
* model
)
326 check( printer
!= NULL
);
327 check( manufacturer
!= NULL
);
328 check( model
!= NULL
);
331 // select the manufacturer
333 info
.flags
= LVFI_STRING
;
334 info
.psz
= manufacturer
->name
;
336 nIndex
= m_manufacturerListCtrl
.FindItem(&info
);
340 m_manufacturerListCtrl
.SetItemState(nIndex
, LVIS_SELECTED
, LVIS_SELECTED
);
342 //<rdar://problem/4528853> mDNS: When auto-highlighting items in lists, scroll list so highlighted item is in the middle
344 AutoScroll(m_manufacturerListCtrl
, nIndex
);
350 info
.flags
= LVFI_STRING
;
351 info
.psz
= model
->displayName
;
353 nIndex
= m_modelListCtrl
.FindItem(&info
);
357 m_modelListCtrl
.SetItemState(nIndex
, LVIS_SELECTED
, LVIS_SELECTED
);
358 AutoScroll( m_modelListCtrl
, nIndex
);
360 m_modelListCtrl
.SetFocus();
363 CopyPrinterSettings( printer
, service
, manufacturer
, model
);
367 CThirdPage::SelectMatch(Manufacturers
& manufacturers
, Printer
* printer
, Service
* service
, Manufacturer
* manufacturer
, Model
* model
)
369 PopulateUI( manufacturers
);
371 SelectMatch( printer
, service
, manufacturer
, model
);
374 // --------------------------------------------------------
375 // CopyPrinterSettings
377 // This function makes sure that the printer object has the
378 // latest settings from the manufacturer and model objects
379 // --------------------------------------------------------
382 CThirdPage::CopyPrinterSettings( Printer
* printer
, Service
* service
, Manufacturer
* manufacturer
, Model
* model
)
384 printer
->manufacturer
= manufacturer
->name
;
385 printer
->displayModelName
= model
->displayName
;
386 printer
->modelName
= model
->name
;
387 printer
->driverInstalled
= model
->driverInstalled
;
388 printer
->infFileName
= model
->infFileName
;
390 if ( service
->type
== kPDLServiceType
)
392 printer
->portName
.Format(L
"IP_%s.%d", static_cast<LPCTSTR
>(service
->hostname
), service
->portNumber
);
393 service
->protocol
= L
"Raw";
395 else if ( service
->type
== kLPRServiceType
)
397 Queue
* q
= service
->queues
.front();
400 if ( q
->name
.GetLength() > 0 )
402 printer
->portName
.Format(L
"LPR_%s.%d.%s", static_cast<LPCTSTR
>(service
->hostname
), service
->portNumber
, static_cast<LPCTSTR
>(q
->name
) );
406 printer
->portName
.Format(L
"LPR_%s.%d", static_cast<LPCTSTR
>(service
->hostname
), service
->portNumber
);
409 service
->protocol
= L
"LPR";
411 else if ( service
->type
== kIPPServiceType
)
413 Queue
* q
= service
->queues
.front();
416 if ( q
->name
.GetLength() > 0 )
418 printer
->portName
.Format(L
"http://%s:%d/%s", static_cast<LPCTSTR
>(service
->hostname
), service
->portNumber
, static_cast<LPCTSTR
>(q
->name
) );
422 printer
->portName
.Format(L
"http://%s:%d/", static_cast<LPCTSTR
>(service
->hostname
), service
->portNumber
);
425 service
->protocol
= L
"IPP";
429 // --------------------------------------------------------
430 // DefaultPrinterExists
432 // Checks to see if a default printer has been configured
434 // --------------------------------------------------------
436 CThirdPage::DefaultPrinterExists()
438 CPrintDialog
dlg(FALSE
);
440 dlg
.m_pd
.Flags
|= PD_RETURNDEFAULT
;
442 return dlg
.GetDefaults();
445 // --------------------------------------------------------
448 // Ensure selected item is in middle of list
449 // --------------------------------------------------------
451 CThirdPage::AutoScroll( CListCtrl
& list
, int nIndex
)
454 //<rdar://problem/4528853> mDNS: When auto-highlighting items in lists, scroll list so highlighted item is in the middle
460 list
.EnsureVisible( nIndex
, FALSE
);
462 top
= list
.GetTopIndex();
463 count
= list
.GetCountPerPage();
465 if ( ( nIndex
== top
) || ( ( nIndex
+ 1 ) == ( top
+ count
) ) )
470 rows
= ( count
/ 2 );
474 list
.GetItemRect(0, rect
, LVIR_BOUNDS
);
475 list
.Scroll( CPoint( 0, rows
* rect
.Height() * -1 ) );
479 list
.GetItemRect(0, rect
, LVIR_BOUNDS
);
480 list
.Scroll( CPoint( 0, rows
* rect
.Height() ) );
485 // ------------------------------------------------------
486 // LoadPrintDriverDefsFromFile
488 // The only potentially opaque thing about this function is the
489 // checkForDuplicateModels flag. The problem here is that ntprint.inf
490 // doesn't contain duplicate models, and it has hundreds of models
491 // listed. You wouldn't check for duplicates there. But oftentimes,
492 // loading different windows print driver files contain multiple
493 // entries for the same printer. You don't want the UI to display
494 // the same printer multiple times, so in that case, you would ask
495 // this function to check for multiple models.
498 CThirdPage::LoadPrintDriverDefsFromFile(Manufacturers
& manufacturers
, const CString
& filename
, bool checkForDuplicateModels
)
500 HINF handle
= INVALID_HANDLE_VALUE
;
501 const TCHAR
* section
= TEXT( "Manufacturer" );
505 INFCONTEXT manufacturerContext
;
509 // Make sure we can open the file
510 handle
= SetupOpenInfFile( filename
, NULL
, INF_STYLE_WIN4
, NULL
);
511 translate_errno( handle
!= INVALID_HANDLE_VALUE
, GetLastError(), kUnknownErr
);
512 require_noerr( err
, exit
);
514 // Make sure it's a printer file
515 ok
= SetupGetLineText( NULL
, handle
, TEXT( "Version" ), TEXT( "Class" ), line
, sizeof( line
), NULL
);
516 translate_errno( ok
, GetLastError(), kUnknownErr
);
517 require_noerr( err
, exit
);
519 require_action( klass
== TEXT( "Printer" ), exit
, err
= kUnknownErr
);
521 sectionCount
= SetupGetLineCount( handle
, section
);
522 translate_errno( sectionCount
!= -1, GetLastError(), kUnknownErr
);
523 require_noerr( err
, exit
);
525 memset( &manufacturerContext
, 0, sizeof( manufacturerContext
) );
527 for ( LONG i
= 0; i
< sectionCount
; i
++ )
529 Manufacturers::iterator iter
;
530 Manufacturer
* manufacturer
;
531 CString manufacturerName
;
533 CStringList modelSectionNameDecl
;
534 CString modelSectionName
;
535 CString baseModelName
;
537 INFCONTEXT modelContext
;
543 ok
= SetupFindFirstLine( handle
, section
, NULL
, &manufacturerContext
);
544 err
= translate_errno( ok
, GetLastError(), kUnknownErr
);
545 require_noerr( err
, exit
);
549 ok
= SetupFindNextLine( &manufacturerContext
, &manufacturerContext
);
550 err
= translate_errno( ok
, GetLastError(), kUnknownErr
);
551 require_noerr( err
, exit
);
554 ok
= SetupGetStringField( &manufacturerContext
, 0, line
, sizeof( line
), NULL
);
555 err
= translate_errno( ok
, GetLastError(), kUnknownErr
);
556 require_noerr( err
, exit
);
557 manufacturerName
= line
;
559 ok
= SetupGetLineText( &manufacturerContext
, handle
, NULL
, NULL
, line
, sizeof( line
), NULL
);
560 err
= translate_errno( ok
, GetLastError(), kUnknownErr
);
561 require_noerr( err
, exit
);
563 // Try to find some model section name that has entries. Explanation of int file structure
566 // <http://msdn.microsoft.com/en-us/library/ms794359.aspx>
567 Split( line
, ',', modelSectionNameDecl
);
569 p
= modelSectionNameDecl
.GetHeadPosition();
570 modelSectionName
= modelSectionNameDecl
.GetNext( p
);
571 modelCount
= SetupGetLineCount( handle
, modelSectionName
);
572 baseModelName
= modelSectionName
;
574 while ( modelCount
<= 0 && p
)
576 CString targetOSVersion
;
578 targetOSVersion
= modelSectionNameDecl
.GetNext( p
);
579 modelSectionName
= baseModelName
+ TEXT( "." ) + targetOSVersion
;
580 modelCount
= SetupGetLineCount( handle
, modelSectionName
);
583 if ( modelCount
> 0 )
585 manufacturerName
= NormalizeManufacturerName( manufacturerName
);
587 iter
= manufacturers
.find( manufacturerName
);
589 if ( iter
!= manufacturers
.end() )
591 manufacturer
= iter
->second
;
592 require_action( manufacturer
, exit
, err
= kUnknownErr
);
598 manufacturer
= new Manufacturer
;
605 require_action( manufacturer
, exit
, err
= kNoMemoryErr
);
607 manufacturer
->name
= manufacturerName
;
608 manufacturers
[ manufacturerName
] = manufacturer
;
611 memset( &modelContext
, 0, sizeof( modelContext
) );
613 for ( LONG j
= 0; j
< modelCount
; j
++ )
620 ok
= SetupFindFirstLine( handle
, modelSectionName
, NULL
, &modelContext
);
621 err
= translate_errno( ok
, GetLastError(), kUnknownErr
);
622 require_noerr( err
, exit
);
626 SetupFindNextLine( &modelContext
, &modelContext
);
627 err
= translate_errno( ok
, GetLastError(), kUnknownErr
);
628 require_noerr( err
, exit
);
631 ok
= SetupGetStringField( &modelContext
, 0, line
, sizeof( line
), NULL
);
632 err
= translate_errno( ok
, GetLastError(), kUnknownErr
);
633 require_noerr( err
, exit
);
637 if (checkForDuplicateModels
== true)
639 if ( MatchModel( manufacturer
, ConvertToModelName( modelName
) ) != NULL
)
646 // Stock Vista printer inf files embed guids in the model
647 // declarations for Epson printers. Let's ignore those.
649 if ( modelName
.Find( TEXT( "{" ), 0 ) != -1 )
663 require_action( model
, exit
, err
= kNoMemoryErr
);
665 model
->infFileName
= filename
;
666 model
->displayName
= modelName
;
667 model
->name
= modelName
;
668 model
->driverInstalled
= false;
670 manufacturer
->models
.push_back(model
);
677 if ( handle
!= INVALID_HANDLE_VALUE
)
679 SetupCloseInfFile( handle
);
687 // -------------------------------------------------------
688 // LoadPrintDriverDefs
690 // This function is responsible for loading the print driver
691 // definitions of all print drivers that have been installed
693 // -------------------------------------------------------
695 CThirdPage::LoadPrintDriverDefs( Manufacturers
& manufacturers
)
697 BYTE
* buffer
= NULL
;
698 DWORD bytesReceived
= 0;
699 DWORD numPrinters
= 0;
704 // like a lot of win32 calls, we call this first to get the
705 // size of the buffer we need.
707 EnumPrinterDrivers(NULL
, L
"all", 6, NULL
, 0, &bytesReceived
, &numPrinters
);
709 if (bytesReceived
> 0)
713 buffer
= new BYTE
[bytesReceived
];
720 require_action( buffer
, exit
, err
= kNoMemoryErr
);
723 // this call gets the real info
725 ok
= EnumPrinterDrivers(NULL
, L
"all", 6, buffer
, bytesReceived
, &bytesReceived
, &numPrinters
);
726 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
727 require_noerr( err
, exit
);
729 DRIVER_INFO_6
* info
= (DRIVER_INFO_6
*) buffer
;
731 for (DWORD i
= 0; i
< numPrinters
; i
++)
733 Manufacturer
* manufacturer
;
738 // skip over anything that doesn't have a manufacturer field. This
739 // fixes a bug that I noticed that occurred after I installed
740 // ProComm. This program add a print driver with no manufacturer
741 // that screwed up this wizard.
743 if (info
[i
].pszMfgName
== NULL
)
749 // look for manufacturer
751 Manufacturers::iterator iter
;
756 name
= NormalizeManufacturerName( info
[i
].pszMfgName
);
758 iter
= manufacturers
.find(name
);
760 if (iter
!= manufacturers
.end())
762 manufacturer
= iter
->second
;
768 manufacturer
= new Manufacturer
;
775 require_action( manufacturer
, exit
, err
= kNoMemoryErr
);
777 manufacturer
->name
= name
;
779 manufacturers
[name
] = manufacturer
;
783 // now look to see if we have already seen this guy. this could
784 // happen if we have already installed printers that are described
785 // in ntprint.inf. the extant drivers will show up in EnumPrinterDrivers
786 // but we have already loaded their info
789 if ( MatchModel( manufacturer
, ConvertToModelName( info
[i
].pName
) ) == NULL
)
800 require_action( model
, exit
, err
= kNoMemoryErr
);
802 model
->displayName
= info
[i
].pName
;
803 model
->name
= info
[i
].pName
;
804 model
->driverInstalled
= true;
806 manufacturer
->models
.push_back(model
);
821 // -------------------------------------------------------
822 // LoadGenericPrintDriverDefs
824 // This function is responsible for loading polymorphic
825 // generic print drivers defs. The UI will read
826 // something like "Generic / Postscript" and we can map
827 // that to any print driver we want.
828 // -------------------------------------------------------
830 CThirdPage::LoadGenericPrintDriverDefs( Manufacturers
& manufacturers
)
832 Manufacturer
* manufacturer
;
834 Manufacturers::iterator iter
;
835 CString psDriverName
;
836 CString pclDriverName
;
839 // <rdar://problem/4030388> Generic drivers don't do color
841 // First try and find our generic driver names
843 iter
= m_manufacturers
.find(L
"HP");
844 require_action( iter
!= m_manufacturers
.end(), exit
, err
= kUnknownErr
);
845 manufacturer
= iter
->second
;
847 // Look for Postscript
849 model
= manufacturer
->find( kGenericPSColorDriver
);
853 model
= manufacturer
->find( kGenericPSDriver
);
858 psDriverName
= model
->name
;
863 model
= manufacturer
->find( kGenericPCLColorDriver
);
867 model
= manufacturer
->find( kGenericPCLDriver
);
872 pclDriverName
= model
->name
;
875 // If we found either a generic PS driver, or a generic PCL driver,
876 // then add them to the list
878 if ( psDriverName
.GetLength() || pclDriverName
.GetLength() )
880 // Try and find generic manufacturer if there is one
882 iter
= manufacturers
.find(L
"Generic");
884 if (iter
!= manufacturers
.end())
886 manufacturer
= iter
->second
;
892 manufacturer
= new Manufacturer
;
899 require_action( manufacturer
, exit
, err
= kNoMemoryErr
);
901 manufacturer
->name
= "Generic";
902 manufacturers
[manufacturer
->name
] = manufacturer
;
905 if ( psDriverName
.GetLength() > 0 )
909 m_genericPostscript
= new Model
;
913 m_genericPostscript
= NULL
;
916 require_action( m_genericPostscript
, exit
, err
= kNoMemoryErr
);
918 m_genericPostscript
->displayName
= kGenericPostscript
;
919 m_genericPostscript
->name
= psDriverName
;
920 m_genericPostscript
->driverInstalled
= false;
922 manufacturer
->models
.push_back( m_genericPostscript
);
925 if ( pclDriverName
.GetLength() > 0 )
929 m_genericPCL
= new Model
;
936 require_action( m_genericPCL
, exit
, err
= kNoMemoryErr
);
938 m_genericPCL
->displayName
= kGenericPCL
;
939 m_genericPCL
->name
= pclDriverName
;
940 m_genericPCL
->driverInstalled
= false;
942 manufacturer
->models
.push_back( m_genericPCL
);
951 // ------------------------------------------------------
952 // ConvertToManufacturerName
954 // This function is responsible for tweaking the
955 // name so that subsequent string operations won't fail because
956 // of capitalizations/different names for the same manufacturer
957 // (i.e. Hewlett-Packard/HP/Hewlett Packard)
960 CThirdPage::ConvertToManufacturerName( const CString
& name
)
963 // first we're going to convert all the characters to lower
966 CString lower
= name
;
970 // now we're going to check to see if the string says "hewlett-packard",
971 // because sometimes they refer to themselves as "hewlett-packard", and
972 // sometimes they refer to themselves as "hp".
974 if ( lower
== L
"hewlett-packard")
980 // tweak for Xerox Phaser, which doesn't announce itself
983 else if ( lower
.Find( L
"phaser", 0 ) != -1 )
991 // ------------------------------------------------------
992 // ConvertToModelName
994 // This function is responsible for ensuring that subsequent
995 // string operations don't fail because of differing capitalization
996 // schemes and the like
997 // ------------------------------------------------------
1000 CThirdPage::ConvertToModelName( const CString
& name
)
1003 // convert it to lowercase
1005 CString lower
= name
;
1011 // ------------------------------------------------------
1012 // NormalizeManufacturerName
1014 // This function is responsible for tweaking the manufacturer
1015 // name so that there are no aliases for vendors
1018 CThirdPage::NormalizeManufacturerName( const CString
& name
)
1020 CString normalized
= name
;
1023 // now we're going to check to see if the string says "hewlett-packard",
1024 // because sometimes they refer to themselves as "hewlett-packard", and
1025 // sometimes they refer to themselves as "hp".
1027 if ( normalized
== L
"Hewlett-Packard")
1035 // -------------------------------------------------------
1038 // This function is responsible for matching a printer
1039 // to a list of manufacturers and models. It calls
1040 // MatchManufacturer and MatchModel in turn.
1043 OSStatus
CThirdPage::MatchPrinter(Manufacturers
& manufacturers
, Printer
* printer
, Service
* service
, bool useCUPSWorkaround
)
1045 CString normalizedProductName
;
1046 Manufacturer
* manufacturer
= NULL
;
1047 Manufacturer
* genericManufacturer
= NULL
;
1048 Model
* model
= NULL
;
1049 Model
* genericModel
= NULL
;
1052 OSStatus err
= kNoErr
;
1057 Queue
* q
= service
->SelectedQueue();
1062 // first look to see if we have a usb_MFG descriptor
1064 if ( q
->usb_MFG
.GetLength() > 0)
1066 manufacturer
= MatchManufacturer( manufacturers
, ConvertToManufacturerName ( q
->usb_MFG
) );
1069 if ( manufacturer
== NULL
)
1071 q
->product
.Remove('(');
1072 q
->product
.Remove(')');
1074 manufacturer
= MatchManufacturer( manufacturers
, ConvertToManufacturerName ( q
->product
) );
1078 // if we found the manufacturer, then start looking for the model
1080 if ( manufacturer
!= NULL
)
1082 if ( q
->usb_MDL
.GetLength() > 0 )
1084 model
= MatchModel ( manufacturer
, ConvertToModelName ( q
->usb_MDL
) );
1087 if ( ( model
== NULL
) && ( q
->product
.GetLength() > 0 ) )
1089 q
->product
.Remove('(');
1090 q
->product
.Remove(')');
1092 model
= MatchModel ( manufacturer
, ConvertToModelName ( q
->product
) );
1095 if ( model
!= NULL
)
1097 // <rdar://problem/4124524> Offer Generic printers if printer advertises Postscript or PCL. Workaround
1098 // bug in OS X CUPS printer sharing by selecting Generic driver instead of matched printer.
1100 bool hasGenericDriver
= false;
1102 if ( MatchGeneric( manufacturers
, printer
, service
, &genericManufacturer
, &genericModel
) )
1104 hasGenericDriver
= true;
1107 // <rdar://problem/4190104> Use "application/octet-stream" to determine if CUPS
1108 // shared queue supports raw
1110 if ( q
->pdl
.Find( L
"application/octet-stream" ) != -1 )
1112 useCUPSWorkaround
= false;
1115 if ( useCUPSWorkaround
&& printer
->isSharedFromOSX
&& hasGenericDriver
)
1118 // <rdar://problem/4496652> mDNS: Don't allow user to choose non-working driver
1120 Manufacturers genericManufacturers
;
1122 LoadGenericPrintDriverDefs( genericManufacturers
);
1124 SelectMatch( genericManufacturers
, printer
, service
, genericManufacturer
, genericModel
);
1128 SelectMatch(manufacturers
, printer
, service
, manufacturer
, model
);
1136 // display a message to the user based on whether we could match
1141 text
.LoadString(IDS_PRINTER_MATCH_GOOD
);
1144 else if ( MatchGeneric( manufacturers
, printer
, service
, &genericManufacturer
, &genericModel
) )
1146 if ( printer
->isSharedFromOSX
)
1149 // <rdar://problem/4496652> mDNS: Don't allow user to choose non-working driver
1151 Manufacturers genericManufacturers
;
1153 LoadGenericPrintDriverDefs( genericManufacturers
);
1155 SelectMatch( genericManufacturers
, printer
, service
, genericManufacturer
, genericModel
);
1157 text
.LoadString(IDS_PRINTER_MATCH_GOOD
);
1161 SelectMatch( manufacturers
, printer
, service
, genericManufacturer
, genericModel
);
1162 text
.LoadString(IDS_PRINTER_MATCH_MAYBE
);
1169 text
.LoadString(IDS_PRINTER_MATCH_BAD
);
1172 // if there was any crud in this list from before, get rid of it now
1174 m_modelListCtrl
.DeleteAllItems();
1177 // select the manufacturer if we found one
1179 if (manufacturer
!= NULL
)
1185 // select the manufacturer
1187 info
.flags
= LVFI_STRING
;
1188 info
.psz
= manufacturer
->name
;
1190 nIndex
= m_manufacturerListCtrl
.FindItem(&info
);
1194 m_manufacturerListCtrl
.SetItemState(nIndex
, LVIS_SELECTED
, LVIS_SELECTED
);
1197 //<rdar://problem/4528853> mDNS: When auto-highlighting items in lists, scroll list so highlighted item is in the middle
1199 AutoScroll(m_manufacturerListCtrl
, nIndex
);
1206 m_printerSelectionText
.SetWindowText(text
);
1211 // ------------------------------------------------------
1212 // MatchManufacturer
1214 // This function is responsible for finding a manufacturer
1215 // object from a string name. It does a CString::Find, which
1216 // is like strstr, so it doesn't have to do an exact match
1218 // If it can't find a match, NULL is returned
1219 // ------------------------------------------------------
1222 CThirdPage::MatchManufacturer( Manufacturers
& manufacturers
, const CString
& name
)
1224 Manufacturers::iterator iter
;
1226 for (iter
= manufacturers
.begin(); iter
!= manufacturers
.end(); iter
++)
1229 // we're going to convert all the manufacturer names to lower case,
1230 // so we match the name passed in.
1232 CString lower
= iter
->second
->name
;
1236 // now try and find the lowered string in the name passed in.
1238 if (name
.Find(lower
) != -1)
1240 return iter
->second
;
1247 // -------------------------------------------------------
1250 // This function is responsible for matching a model from
1251 // a name. It does a CString::Find(), which works like strstr,
1252 // so it doesn't rely on doing an exact string match.
1256 CThirdPage::MatchModel(Manufacturer
* manufacturer
, const CString
& name
)
1258 Models::iterator iter
;
1260 iter
= manufacturer
->models
.begin();
1262 for (iter
= manufacturer
->models
.begin(); iter
!= manufacturer
->models
.end(); iter
++)
1264 Model
* model
= *iter
;
1267 // convert the model name to lower case
1269 CString lowered
= model
->name
;
1270 lowered
.MakeLower();
1272 if (lowered
.Find( name
) != -1)
1278 // <rdar://problem/3841218>
1279 // try removing the first substring and search again
1282 if ( name
.Find(' ') != -1 )
1284 CString altered
= name
;
1285 altered
.Delete( 0, altered
.Find(' ') + 1 );
1287 if ( lowered
.Find( altered
) != -1 )
1297 // -------------------------------------------------------
1300 // This function will attempt to find a generic printer
1301 // driver for a printer that we weren't able to match
1305 CThirdPage::MatchGeneric( Manufacturers
& manufacturers
, Printer
* printer
, Service
* service
, Manufacturer
** manufacturer
, Model
** model
)
1310 DEBUG_UNUSED( printer
);
1314 Queue
* q
= service
->SelectedQueue();
1318 Manufacturers::iterator iter
= manufacturers
.find( kGenericManufacturer
);
1319 require_action_quiet( iter
!= manufacturers
.end(), exit
, ok
= FALSE
);
1321 *manufacturer
= iter
->second
;
1326 if ( m_genericPCL
&& ( pdl
.Find( kPDLPCLKey
) != -1 ) )
1328 *model
= m_genericPCL
;
1331 else if ( m_genericPostscript
&& ( pdl
.Find( kPDLPostscriptKey
) != -1 ) )
1333 *model
= m_genericPostscript
;
1342 // -----------------------------------------------------------
1345 // This function is responsible for doing initialization that
1346 // only occurs once during a run of the wizard
1349 OSStatus
CThirdPage::OnInitPage()
1353 OSStatus err
= kNoErr
;
1355 // Load printer icon
1356 check( m_printerImage
== NULL
);
1358 m_printerImage
= (CStatic
*) GetDlgItem( 1 ); // 1 == IDR_MANIFEST
1359 check( m_printerImage
);
1361 if ( m_printerImage
!= NULL
)
1363 m_printerImage
->SetIcon( LoadIcon( GetNonLocalizedResources(), MAKEINTRESOURCE( IDI_PRINTER
) ) );
1367 // The CTreeCtrl widget automatically sends a selection changed
1368 // message which initially we want to ignore, because the user
1369 // hasn't selected anything
1371 // this flag gets reset in the message handler. Every subsequent
1372 // message gets handled.
1376 // we have to make sure that we only do this once. Typically,
1377 // we would do this in something like OnInitDialog, but we don't
1378 // have this in Wizards, because the window is a PropertySheet.
1379 // We're considered fully initialized when we receive the first
1382 header
.LoadString(IDS_MANUFACTURER_HEADING
);
1383 m_manufacturerListCtrl
.InsertColumn(0, header
, LVCFMT_LEFT
, -1 );
1384 m_manufacturerSelected
= NULL
;
1386 header
.LoadString(IDS_MODEL_HEADING
);
1387 m_modelListCtrl
.InsertColumn(0, header
, LVCFMT_LEFT
, -1 );
1388 m_modelSelected
= NULL
;
1393 void CThirdPage::DoDataExchange(CDataExchange
* pDX
)
1395 CPropertyPage::DoDataExchange(pDX
);
1396 DDX_Control(pDX
, IDC_PRINTER_MANUFACTURER
, m_manufacturerListCtrl
);
1397 DDX_Control(pDX
, IDC_PRINTER_MODEL
, m_modelListCtrl
);
1398 DDX_Control(pDX
, IDC_PRINTER_NAME
, m_printerName
);
1399 DDX_Control(pDX
, IDC_DEFAULT_PRINTER
, m_defaultPrinterCtrl
);
1400 DDX_Control(pDX
, IDC_PRINTER_SELECTION_TEXT
, m_printerSelectionText
);
1404 // ----------------------------------------------------------
1407 // This function is called by MFC after the window has been
1412 CThirdPage::OnSetActive()
1414 CPrinterSetupWizardSheet
* psheet
;
1418 psheet
= reinterpret_cast<CPrinterSetupWizardSheet
*>(GetParent());
1419 require_quiet( psheet
, exit
);
1421 psheet
->SetWizardButtons( PSWIZB_BACK
);
1423 printer
= psheet
->GetSelectedPrinter();
1424 require_quiet( printer
, exit
);
1426 service
= printer
->services
.front();
1427 require_quiet( service
, exit
);
1430 // call OnInitPage once
1435 m_initialized
= true;
1439 // <rdar://problem/4580061> mDNS: Printers added using Bonjour should be set as the default printer.
1441 if ( DefaultPrinterExists() )
1443 m_defaultPrinterCtrl
.SetCheck( BST_UNCHECKED
);
1444 printer
->deflt
= false;
1448 m_defaultPrinterCtrl
.SetCheck( BST_CHECKED
);
1449 printer
->deflt
= true;
1453 // update the UI with the printer name
1455 m_printerName
.SetWindowText(printer
->displayName
);
1458 // populate the list controls with the manufacturers and models
1461 PopulateUI( m_manufacturers
);
1464 // and try and match the printer
1467 if ( psheet
->GetLastPage() == psheet
->GetPage(0) )
1469 MatchPrinter( m_manufacturers
, printer
, service
, true );
1471 if ( ( m_manufacturerSelected
!= NULL
) && ( m_modelSelected
!= NULL
) )
1473 GetParent()->PostMessage(PSM_SETCURSEL
, 2 );
1478 SelectMatch(printer
, service
, m_manufacturerSelected
, m_modelSelected
);
1483 return CPropertyPage::OnSetActive();
1487 CThirdPage::OnKillActive()
1489 CPrinterSetupWizardSheet
* psheet
;
1491 psheet
= reinterpret_cast<CPrinterSetupWizardSheet
*>(GetParent());
1492 require_quiet( psheet
, exit
);
1494 psheet
->SetLastPage(this);
1498 return CPropertyPage::OnKillActive();
1501 // -------------------------------------------------------
1504 // This function is called to populate the list of manufacturers
1507 CThirdPage::PopulateUI(Manufacturers
& manufacturers
)
1509 Manufacturers::iterator iter
;
1511 m_manufacturerListCtrl
.DeleteAllItems();
1513 for (iter
= manufacturers
.begin(); iter
!= manufacturers
.end(); iter
++)
1517 Manufacturer
* manufacturer
= iter
->second
;
1519 nIndex
= m_manufacturerListCtrl
.InsertItem(0, manufacturer
->name
);
1521 m_manufacturerListCtrl
.SetItemData(nIndex
, (DWORD_PTR
) manufacturer
);
1523 m_manufacturerListCtrl
.SetColumnWidth( 0, LVSCW_AUTOSIZE_USEHEADER
);
1529 BEGIN_MESSAGE_MAP(CThirdPage
, CPropertyPage
)
1530 ON_NOTIFY(LVN_ITEMCHANGED
, IDC_PRINTER_MANUFACTURER
, OnLvnItemchangedManufacturer
)
1531 ON_NOTIFY(LVN_ITEMCHANGED
, IDC_PRINTER_MODEL
, OnLvnItemchangedPrinterModel
)
1532 ON_BN_CLICKED(IDC_DEFAULT_PRINTER
, OnBnClickedDefaultPrinter
)
1533 ON_BN_CLICKED(IDC_HAVE_DISK
, OnBnClickedHaveDisk
)
1536 // CThirdPage message handlers
1537 void CThirdPage::OnLvnItemchangedManufacturer(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1539 LPNMLISTVIEW pNMLV
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
1541 POSITION p
= m_manufacturerListCtrl
.GetFirstSelectedItemPosition();
1542 int nSelected
= m_manufacturerListCtrl
.GetNextSelectedItem(p
);
1544 if (nSelected
!= -1)
1546 m_manufacturerSelected
= (Manufacturer
*) m_manufacturerListCtrl
.GetItemData(nSelected
);
1548 m_modelListCtrl
.SetRedraw(FALSE
);
1550 m_modelListCtrl
.DeleteAllItems();
1551 m_modelSelected
= NULL
;
1553 Models::iterator iter
;
1555 for (iter
= m_manufacturerSelected
->models
.begin(); iter
!= m_manufacturerSelected
->models
.end(); iter
++)
1557 Model
* model
= *iter
;
1559 int nItem
= m_modelListCtrl
.InsertItem( 0, model
->displayName
);
1561 m_modelListCtrl
.SetItemData(nItem
, (DWORD_PTR
) model
);
1563 m_modelListCtrl
.SetColumnWidth( 0, LVSCW_AUTOSIZE_USEHEADER
);
1566 m_modelListCtrl
.SetRedraw(TRUE
);
1572 void CThirdPage::OnLvnItemchangedPrinterModel(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1574 LPNMLISTVIEW pNMLV
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
1576 CPrinterSetupWizardSheet
* psheet
;
1580 psheet
= reinterpret_cast<CPrinterSetupWizardSheet
*>(GetParent());
1581 require_quiet( psheet
, exit
);
1583 printer
= psheet
->GetSelectedPrinter();
1584 require_quiet( printer
, exit
);
1586 service
= printer
->services
.front();
1587 require_quiet( service
, exit
);
1589 check ( m_manufacturerSelected
);
1591 POSITION p
= m_modelListCtrl
.GetFirstSelectedItemPosition();
1592 int nSelected
= m_modelListCtrl
.GetNextSelectedItem(p
);
1594 if (nSelected
!= -1)
1596 m_modelSelected
= (Model
*) m_modelListCtrl
.GetItemData(nSelected
);
1598 CopyPrinterSettings( printer
, service
, m_manufacturerSelected
, m_modelSelected
);
1600 psheet
->SetWizardButtons(PSWIZB_BACK
|PSWIZB_NEXT
);
1604 psheet
->SetWizardButtons(PSWIZB_BACK
);
1612 void CThirdPage::OnBnClickedDefaultPrinter()
1614 CPrinterSetupWizardSheet
* psheet
;
1617 psheet
= reinterpret_cast<CPrinterSetupWizardSheet
*>(GetParent());
1618 require_quiet( psheet
, exit
);
1620 printer
= psheet
->GetSelectedPrinter();
1621 require_quiet( printer
, exit
);
1623 printer
->deflt
= ( m_defaultPrinterCtrl
.GetCheck() == BST_CHECKED
) ? true : false;
1630 void CThirdPage::OnBnClickedHaveDisk()
1632 CPrinterSetupWizardSheet
* psheet
;
1635 Manufacturers manufacturers
;
1637 CFileDialog
dlg(TRUE
, NULL
, NULL
, OFN_HIDEREADONLY
|OFN_FILEMUSTEXIST
, L
"Setup Information (*.inf)|*.inf||", this);
1639 psheet
= reinterpret_cast<CPrinterSetupWizardSheet
*>(GetParent());
1640 require_quiet( psheet
, exit
);
1642 printer
= psheet
->GetSelectedPrinter();
1643 require_quiet( printer
, exit
);
1645 service
= printer
->services
.front();
1646 require_quiet( service
, exit
);
1650 if ( dlg
.DoModal() == IDOK
)
1652 CString filename
= dlg
.GetPathName();
1654 LoadPrintDriverDefsFromFile( manufacturers
, filename
, true );
1658 if ( manufacturers
.size() > 0 )
1660 PopulateUI( manufacturers
);
1662 if ( MatchPrinter( manufacturers
, printer
, service
, false ) != kNoErr
)
1664 CString errorMessage
;
1665 CString errorCaption
;
1667 errorMessage
.LoadString( IDS_NO_MATCH_INF_FILE
);
1668 errorCaption
.LoadString( IDS_NO_MATCH_INF_FILE_CAPTION
);
1670 MessageBox( errorMessage
, errorCaption
, MB_OK
);
1677 CString errorMessage
;
1678 CString errorCaption
;
1680 errorMessage
.LoadString( IDS_BAD_INF_FILE
);
1681 errorCaption
.LoadString( IDS_BAD_INF_FILE_CAPTION
);
1683 MessageBox( errorMessage
, errorCaption
, MB_OK
);
1699 CThirdPage::Split( const CString
& string
, TCHAR ch
, CStringList
& components
)
1706 while ( ( n
= temp
.Find( ch
) ) != -1 )
1708 components
.AddTail( temp
.Left( n
) );
1709 temp
= temp
.Right( temp
.GetLength() - ( n
+ 1 ) );
1712 components
.AddTail( temp
);