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.28 2006/08/14 23:24:09 cheshire
21 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
23 Revision 1.27 2005/10/05 21:41:45 herscher
24 <rdar://problem/4190104> Use "application/octet-stream" to determine if CUPS shared queue supports raw
26 Revision 1.26 2005/07/11 20:17:15 shersche
27 <rdar://problem/4124524> UI fixes associated with CUPS printer workaround fix.
29 Revision 1.25 2005/07/07 17:53:20 shersche
30 Fix problems associated with the CUPS printer workaround fix.
32 Revision 1.24 2005/06/30 18:02:54 shersche
33 <rdar://problem/4124524> Workaround for Mac OS X Printer Sharing bug
35 Revision 1.23 2005/04/18 02:33:47 shersche
36 <rdar://problem/4091216> Default printer option cannot be deselected
38 Revision 1.22 2005/04/13 17:46:22 shersche
39 <rdar://problem/4082122> Generic PCL not selected when printers advertise multiple text records
41 Revision 1.21 2005/03/30 02:09:55 shersche
42 Auto-resize the column width to account for differing fonts and font sizes
44 Revision 1.20 2005/03/05 02:27:45 shersche
45 <rdar://problem/4030388> Generic drivers don't do color
47 Revision 1.19 2005/02/23 02:08:51 shersche
48 <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".
50 Revision 1.18 2005/02/15 07:02:51 shersche
51 <rdar://problem/4003724> Display different UI text when generic printer drivers are selected
53 Revision 1.17 2005/02/08 21:45:06 shersche
54 <rdar://problem/3947490> Default to Generic PostScript or PCL if unable to match driver
56 Revision 1.16 2005/02/08 18:56:03 shersche
57 Fix generated IPP url so that it doesn't add "/printers" string
59 Revision 1.15 2005/02/01 01:44:07 shersche
60 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.
62 Revision 1.14 2005/01/25 08:55:54 shersche
63 <rdar://problem/3911084> Load icons at run-time from resource DLL
66 Revision 1.13 2005/01/06 08:15:45 shersche
67 Append queue name to end of LPR port name, correctly build port name when queue name is absent
69 Revision 1.12 2005/01/05 01:06:12 shersche
70 <rdar://problem/3841218> Strip the first substring off the product key if an initial match can't be found with the whole product key.
73 Revision 1.11 2004/12/29 18:53:38 shersche
74 <rdar://problem/3725106>
75 <rdar://problem/3737413> Added support for LPR and IPP protocols as well as support for obtaining multiple text records. Reorganized and simplified codebase.
76 Bug #: 3725106, 3737413
78 Revision 1.10 2004/10/11 22:55:34 shersche
79 <rdar://problem/3827624> Use the IP port number when deriving the printer port name.
82 Revision 1.9 2004/06/27 23:08:00 shersche
83 code cleanup, make sure EnumPrintDrivers returns non-zero value, ignore comments in inf files
85 Revision 1.8 2004/06/27 08:06:45 shersche
86 Parse [Strings] section of inf file
88 Revision 1.7 2004/06/26 04:00:05 shersche
89 fix warnings compiling in debug mode
90 Submitted by: herscher
92 Revision 1.6 2004/06/26 03:19:57 shersche
93 clean up warning messages
95 Submitted by: herscher
97 Revision 1.5 2004/06/25 05:06:02 shersche
98 Trim whitespace from key/value pairs when parsing inf files
99 Submitted by: herscher
101 Revision 1.4 2004/06/25 02:44:13 shersche
102 Tweaked code to handle Xerox Phaser printer identification
103 Submitted by: herscher
105 Revision 1.3 2004/06/25 02:27:58 shersche
106 Do a CListCtrl::FindItem() before calling CListCtrl::SetItemState().
107 Submitted by: herscher
109 Revision 1.2 2004/06/23 18:09:23 shersche
110 Normalize tag names when parsing inf files.
111 Submitted by: herscher
113 Revision 1.1 2004/06/18 04:36:58 rpantos
120 #include "PrinterSetupWizardApp.h"
121 #include "PrinterSetupWizardSheet.h"
122 #include "ThirdPage.h"
123 #include "StdioFileEx.h"
126 #include <winspool.h>
128 // local variable is initialize but not referenced
129 #pragma warning(disable:4189)
133 // This is the printer description file that is shipped
136 #define kNTPrintFile L"inf\\ntprint.inf"
139 // These are pre-defined names for Generic manufacturer and model
141 #define kGenericManufacturer L"Generic"
142 #define kGenericText L"Generic / Text Only"
143 #define kGenericPostscript L"Generic / Postscript"
144 #define kGenericPCL L"Generic / PCL"
145 #define kPDLPostscriptKey L"application/postscript"
146 #define kPDLPCLKey L"application/vnd.hp-pcl"
147 #define kGenericPSColorDriver L"HP Color LaserJet 4550 PS"
148 #define kGenericPSDriver L"HP LaserJet 4050 Series PS"
149 #define kGenericPCLColorDriver L"HP Color LaserJet 4550 PCL"
150 #define kGenericPCLDriver L"HP LaserJet 4050 Series PCL"
154 // states for parsing ntprint.inf
156 enum PrinterParsingState
159 ParsingManufacturers
,
167 IMPLEMENT_DYNAMIC(CThirdPage
, CPropertyPage
)
168 CThirdPage::CThirdPage()
169 : CPropertyPage(CThirdPage::IDD
),
170 m_initialized(false),
171 m_printerImage( NULL
)
173 static const int bufferSize
= 32768;
174 TCHAR windowsDirectory
[bufferSize
];
180 m_psp
.dwFlags
&= ~(PSP_HASHELP
);
181 m_psp
.dwFlags
|= PSP_DEFAULT
|PSP_USEHEADERTITLE
|PSP_USEHEADERSUBTITLE
;
183 m_psp
.pszHeaderTitle
= MAKEINTRESOURCE(IDS_INSTALL_TITLE
);
184 m_psp
.pszHeaderSubTitle
= MAKEINTRESOURCE(IDS_INSTALL_SUBTITLE
);
187 // load printers from ntprint.inf
189 ok
= GetWindowsDirectory( windowsDirectory
, bufferSize
);
190 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
191 require_noerr( err
, exit
);
193 ntPrint
.Format(L
"%s\\%s", windowsDirectory
, kNTPrintFile
);
194 err
= LoadPrintDriverDefsFromFile( m_manufacturers
, ntPrint
, false );
195 require_noerr(err
, exit
);
198 // load printer drivers that have been installed on this machine
200 err
= LoadPrintDriverDefs( m_manufacturers
);
201 require_noerr(err
, exit
);
204 // load our own special generic printer defs
206 err
= LoadGenericPrintDriverDefs( m_manufacturers
);
207 require_noerr( err
, exit
);
215 CThirdPage::~CThirdPage()
218 // clean up all the printer manufacturers
220 while (m_manufacturers
.size())
222 Manufacturers::iterator iter
= m_manufacturers
.begin();
224 while (iter
->second
->models
.size())
226 Models::iterator it
= iter
->second
->models
.begin();
232 iter
->second
->models
.erase(it
);
237 m_manufacturers
.erase(iter
);
242 // ----------------------------------------------------
245 // SelectMatch will do all the UI work associated with
246 // selected a manufacturer and model of printer. It also
247 // makes sure the printer object is update with the
250 // ----------------------------------------------------
252 CThirdPage::SelectMatch(Printer
* printer
, Service
* service
, Manufacturer
* manufacturer
, Model
* model
)
257 check( printer
!= NULL
);
258 check( manufacturer
!= NULL
);
259 check( model
!= NULL
);
262 // select the manufacturer
264 info
.flags
= LVFI_STRING
;
265 info
.psz
= manufacturer
->name
;
267 nIndex
= m_manufacturerListCtrl
.FindItem(&info
);
271 m_manufacturerListCtrl
.SetItemState(nIndex
, LVIS_SELECTED
, LVIS_SELECTED
);
272 m_manufacturerListCtrl
.EnsureVisible(nIndex
, FALSE
);
278 info
.flags
= LVFI_STRING
;
279 info
.psz
= model
->displayName
;
281 nIndex
= m_modelListCtrl
.FindItem(&info
);
285 m_modelListCtrl
.SetItemState(nIndex
, LVIS_SELECTED
, LVIS_SELECTED
);
286 m_modelListCtrl
.EnsureVisible(nIndex
, FALSE
);
288 m_modelListCtrl
.SetFocus();
291 CopyPrinterSettings( printer
, service
, manufacturer
, model
);
296 CThirdPage::SelectMatch(Manufacturers
& manufacturers
, Printer
* printer
, Service
* service
, Manufacturer
* manufacturer
, Model
* model
)
298 PopulateUI( manufacturers
);
300 SelectMatch( printer
, service
, manufacturer
, model
);
304 // --------------------------------------------------------
305 // CopyPrinterSettings
307 // This function makes sure that the printer object has the
308 // latest settings from the manufacturer and model objects
309 // --------------------------------------------------------
312 CThirdPage::CopyPrinterSettings( Printer
* printer
, Service
* service
, Manufacturer
* manufacturer
, Model
* model
)
314 printer
->manufacturer
= manufacturer
->name
;
315 printer
->displayModelName
= model
->displayName
;
316 printer
->modelName
= model
->name
;
317 printer
->driverInstalled
= model
->driverInstalled
;
318 printer
->infFileName
= model
->infFileName
;
320 if ( service
->type
== kPDLServiceType
)
322 printer
->portName
.Format(L
"IP_%s.%d", static_cast<LPCTSTR
>(service
->hostname
), service
->portNumber
);
323 service
->protocol
= L
"Raw";
325 else if ( service
->type
== kLPRServiceType
)
327 Queue
* q
= service
->queues
.front();
330 if ( q
->name
.GetLength() > 0 )
332 printer
->portName
.Format(L
"LPR_%s.%d.%s", static_cast<LPCTSTR
>(service
->hostname
), service
->portNumber
, static_cast<LPCTSTR
>(q
->name
) );
336 printer
->portName
.Format(L
"LPR_%s.%d", static_cast<LPCTSTR
>(service
->hostname
), service
->portNumber
);
339 service
->protocol
= L
"LPR";
341 else if ( service
->type
== kIPPServiceType
)
343 Queue
* q
= service
->queues
.front();
346 if ( q
->name
.GetLength() > 0 )
348 printer
->portName
.Format(L
"http://%s:%d/%s", static_cast<LPCTSTR
>(service
->hostname
), service
->portNumber
, static_cast<LPCTSTR
>(q
->name
) );
352 printer
->portName
.Format(L
"http://%s:%d/", static_cast<LPCTSTR
>(service
->hostname
), service
->portNumber
);
355 service
->protocol
= L
"IPP";
360 // ------------------------------------------------------
361 // LoadPrintDriverDefsFromFile
363 // This function does all the heavy lifting in parsing inf
364 // files. It is called to parse both ntprint.inf, and driver
365 // files that might be shipped on a printer's installation
368 // The inf file is not totally parsed. I only want to determine
369 // the manufacturer and models that are involved. I leave it
370 // to printui.dll to actually copy the driver files to the
373 // I was aiming to parse as little as I could so as not to
374 // duplicate the parsing code that is contained in Windows. There
375 // are no public APIs for parsing inf files.
377 // That part of the inf file that we're interested in has a fairly
378 // easy format. Tags are strings that are enclosed in brackets.
379 // We are only interested in [MANUFACTURERS] and models.
381 // The only potentially opaque thing about this function is the
382 // checkForDuplicateModels flag. The problem here is that ntprint.inf
383 // doesn't contain duplicate models, and it has hundreds of models
384 // listed. You wouldn't check for duplicates there. But oftentimes,
385 // loading different windows print driver files contain multiple
386 // entries for the same printer. You don't want the UI to display
387 // the same printer multiple times, so in that case, you would ask
388 // this function to check for multiple models.
391 CThirdPage::LoadPrintDriverDefsFromFile(Manufacturers
& manufacturers
, const CString
& filename
, bool checkForDuplicateModels
)
393 PrinterParsingState state
= Looking
;
394 Manufacturers::iterator iter
= manufacturers
.end();
396 CFileException feError
;
401 typedef std::map
<CString
, CString
> StringMap
;
405 ok
= file
.Open( filename
, CFile::modeRead
|CFile::typeText
, &feError
);
406 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
407 require_noerr( err
, exit
);
409 check ( state
== Looking
);
410 check ( iter
== manufacturers
.end() );
413 // first, parse the file looking for string sections
415 while (file
.ReadString(s
))
420 if (s
.Find(';') == 0)
428 else if (s
.Find('[') == 0)
431 // handle any capitalization issues here
437 if (tag
== L
"[strings]")
439 state
= ParsingStrings
;
454 if (s
.GetLength() > 0)
456 CString key
= s
.Tokenize(L
"=",curPos
);
457 CString val
= s
.Tokenize(L
"=",curPos
);
460 // get rid of all delimiters
478 ok
= file
.Open( filename
, CFile::modeRead
|CFile::typeText
, &feError
);
479 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
480 require_noerr( err
, exit
);
484 check ( iter
== manufacturers
.end() );
486 while (file
.ReadString(s
))
491 if (s
.Find(';') == 0)
499 else if (s
.Find('[') == 0)
502 // handle any capitalization issues here
508 if (tag
== L
"[manufacturer]")
510 state
= ParsingManufacturers
;
514 // remove the leading and trailing delimiters
519 // check to see if this is a printer entry
521 iter
= manufacturers
.find(s
);
523 if (iter
!= manufacturers
.end())
525 state
= ParsingModels
;
534 // only look at this if the line isn't empty, or
535 // if it isn't a comment
537 else if ((s
.GetLength() > 0) && (s
.Find(';') != 0))
542 // if we're parsing manufacturers, then we will parse
543 // an entry of the form key=val, where key is a delimited
544 // string specifying a manufacturer name, and val is
545 // a tag that is used later in the file. the key is
546 // delimited by either '"' (quotes) or '%' (percent sign).
548 // the tag is used further down the file when models are
549 // declared. this allows multiple manufacturers to exist
550 // in a single inf file.
552 case ParsingManufacturers
:
554 Manufacturer
* manufacturer
;
557 CString key
= s
.Tokenize(L
"=",curPos
);
558 CString val
= s
.Tokenize(L
"=",curPos
);
562 manufacturer
= new Manufacturer
;
569 require_action( manufacturer
, exit
, err
= kNoMemoryErr
);
572 // if it's a variable, look it up
574 if (key
.Find('%') == 0)
576 StringMap::iterator it
;
580 it
= strings
.find(key
);
582 if (it
!= strings
.end())
596 // why is there no consistency in inf files?
598 if (val
.GetLength() == 0)
604 // fix the manufacturer name if necessary
607 val
= val
.Tokenize(L
",", curPos
);
609 manufacturer
->name
= NormalizeManufacturerName( key
);
610 manufacturer
->tag
= val
;
612 manufacturers
[val
] = manufacturer
;
618 check( iter
!= manufacturers
.end() );
623 CString name
= s
.Tokenize(L
"=",curPos
);
624 CString description
= s
.Tokenize(L
"=",curPos
);
626 if (name
.Find('%') == 0)
628 StringMap::iterator it
;
632 it
= strings
.find(name
);
634 if (it
!= strings
.end())
648 // If true, see if we've seen this guy before
650 if (checkForDuplicateModels
== true)
652 if ( MatchModel( iter
->second
, ConvertToModelName( name
) ) != NULL
)
667 require_action( model
, exit
, err
= kNoMemoryErr
);
669 model
->infFileName
= filename
;
670 model
->displayName
= name
;
672 model
->driverInstalled
= false;
674 iter
->second
->models
.push_back(model
);
680 // pay no attention if we are in any other state
695 // -------------------------------------------------------
696 // LoadPrintDriverDefs
698 // This function is responsible for loading the print driver
699 // definitions of all print drivers that have been installed
701 // -------------------------------------------------------
703 CThirdPage::LoadPrintDriverDefs( Manufacturers
& manufacturers
)
705 BYTE
* buffer
= NULL
;
706 DWORD bytesReceived
= 0;
707 DWORD numPrinters
= 0;
712 // like a lot of win32 calls, we call this first to get the
713 // size of the buffer we need.
715 EnumPrinterDrivers(NULL
, L
"all", 6, NULL
, 0, &bytesReceived
, &numPrinters
);
717 if (bytesReceived
> 0)
721 buffer
= new BYTE
[bytesReceived
];
728 require_action( buffer
, exit
, err
= kNoMemoryErr
);
731 // this call gets the real info
733 ok
= EnumPrinterDrivers(NULL
, L
"all", 6, buffer
, bytesReceived
, &bytesReceived
, &numPrinters
);
734 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
735 require_noerr( err
, exit
);
737 DRIVER_INFO_6
* info
= (DRIVER_INFO_6
*) buffer
;
739 for (DWORD i
= 0; i
< numPrinters
; i
++)
741 Manufacturer
* manufacturer
;
746 // skip over anything that doesn't have a manufacturer field. This
747 // fixes a bug that I noticed that occurred after I installed
748 // ProComm. This program add a print driver with no manufacturer
749 // that screwed up this wizard.
751 if (info
[i
].pszMfgName
== NULL
)
757 // look for manufacturer
759 Manufacturers::iterator iter
;
764 name
= NormalizeManufacturerName( info
[i
].pszMfgName
);
766 iter
= manufacturers
.find(name
);
768 if (iter
!= manufacturers
.end())
770 manufacturer
= iter
->second
;
776 manufacturer
= new Manufacturer
;
783 require_action( manufacturer
, exit
, err
= kNoMemoryErr
);
785 manufacturer
->name
= name
;
787 manufacturers
[name
] = manufacturer
;
791 // now look to see if we have already seen this guy. this could
792 // happen if we have already installed printers that are described
793 // in ntprint.inf. the extant drivers will show up in EnumPrinterDrivers
794 // but we have already loaded their info
797 if ( MatchModel( manufacturer
, ConvertToModelName( info
[i
].pName
) ) == NULL
)
808 require_action( model
, exit
, err
= kNoMemoryErr
);
810 model
->displayName
= info
[i
].pName
;
811 model
->name
= info
[i
].pName
;
812 model
->driverInstalled
= true;
814 manufacturer
->models
.push_back(model
);
830 // -------------------------------------------------------
831 // LoadGenericPrintDriverDefs
833 // This function is responsible for loading polymorphic
834 // generic print drivers defs. The UI will read
835 // something like "Generic / Postscript" and we can map
836 // that to any print driver we want.
837 // -------------------------------------------------------
839 CThirdPage::LoadGenericPrintDriverDefs( Manufacturers
& manufacturers
)
841 Manufacturer
* manufacturer
;
843 Manufacturers::iterator iter
;
844 CString psDriverName
;
845 CString pclDriverName
;
848 // <rdar://problem/4030388> Generic drivers don't do color
850 // First try and find our generic driver names
852 iter
= m_manufacturers
.find(L
"HP");
853 require_action( iter
!= manufacturers
.end(), exit
, err
= kUnknownErr
);
854 manufacturer
= iter
->second
;
856 // Look for Postscript
858 model
= manufacturer
->find( kGenericPSColorDriver
);
862 model
= manufacturer
->find( kGenericPSDriver
);
867 psDriverName
= model
->name
;
873 model
= manufacturer
->find( kGenericPCLColorDriver
);
877 model
= manufacturer
->find( kGenericPCLDriver
);
882 pclDriverName
= model
->name
;
885 // If we found either a generic PS driver, or a generic PCL driver,
886 // then add them to the list
888 if ( psDriverName
.GetLength() || pclDriverName
.GetLength() )
890 // Try and find generic manufacturer if there is one
892 iter
= manufacturers
.find(L
"Generic");
894 if (iter
!= manufacturers
.end())
896 manufacturer
= iter
->second
;
902 manufacturer
= new Manufacturer
;
909 require_action( manufacturer
, exit
, err
= kNoMemoryErr
);
911 manufacturer
->name
= "Generic";
912 manufacturers
[manufacturer
->name
] = manufacturer
;
915 if ( psDriverName
.GetLength() > 0 )
919 m_genericPostscript
= new Model
;
923 m_genericPostscript
= NULL
;
926 require_action( m_genericPostscript
, exit
, err
= kNoMemoryErr
);
928 m_genericPostscript
->displayName
= kGenericPostscript
;
929 m_genericPostscript
->name
= psDriverName
;
930 m_genericPostscript
->driverInstalled
= false;
932 manufacturer
->models
.push_back( m_genericPostscript
);
935 if ( pclDriverName
.GetLength() > 0 )
939 m_genericPCL
= new Model
;
946 require_action( m_genericPCL
, exit
, err
= kNoMemoryErr
);
948 m_genericPCL
->displayName
= kGenericPCL
;
949 m_genericPCL
->name
= pclDriverName
;
950 m_genericPCL
->driverInstalled
= false;
952 manufacturer
->models
.push_back( m_genericPCL
);
961 // ------------------------------------------------------
962 // ConvertToManufacturerName
964 // This function is responsible for tweaking the
965 // name so that subsequent string operations won't fail because
966 // of capitalizations/different names for the same manufacturer
967 // (i.e. Hewlett-Packard/HP/Hewlett Packard)
970 CThirdPage::ConvertToManufacturerName( const CString
& name
)
973 // first we're going to convert all the characters to lower
976 CString lower
= name
;
980 // now we're going to check to see if the string says "hewlett-packard",
981 // because sometimes they refer to themselves as "hewlett-packard", and
982 // sometimes they refer to themselves as "hp".
984 if ( lower
== L
"hewlett-packard")
990 // tweak for Xerox Phaser, which doesn't announce itself
993 else if ( lower
.Find( L
"phaser", 0 ) != -1 )
1002 // ------------------------------------------------------
1003 // ConvertToModelName
1005 // This function is responsible for ensuring that subsequent
1006 // string operations don't fail because of differing capitalization
1007 // schemes and the like
1008 // ------------------------------------------------------
1011 CThirdPage::ConvertToModelName( const CString
& name
)
1014 // convert it to lowercase
1016 CString lower
= name
;
1023 // ------------------------------------------------------
1024 // NormalizeManufacturerName
1026 // This function is responsible for tweaking the manufacturer
1027 // name so that there are no aliases for vendors
1030 CThirdPage::NormalizeManufacturerName( const CString
& name
)
1032 CString normalized
= name
;
1035 // now we're going to check to see if the string says "hewlett-packard",
1036 // because sometimes they refer to themselves as "hewlett-packard", and
1037 // sometimes they refer to themselves as "hp".
1039 if ( normalized
== L
"Hewlett-Packard")
1048 // -------------------------------------------------------
1051 // This function is responsible for matching a printer
1052 // to a list of manufacturers and models. It calls
1053 // MatchManufacturer and MatchModel in turn.
1056 OSStatus
CThirdPage::MatchPrinter(Manufacturers
& manufacturers
, Printer
* printer
, Service
* service
, bool useCUPSWorkaround
)
1058 CString normalizedProductName
;
1059 Manufacturer
* manufacturer
= NULL
;
1060 Manufacturer
* genericManufacturer
= NULL
;
1061 Model
* model
= NULL
;
1062 Model
* genericModel
= NULL
;
1065 OSStatus err
= kNoErr
;
1070 Queue
* q
= service
->SelectedQueue();
1075 // first look to see if we have a usb_MFG descriptor
1077 if ( q
->usb_MFG
.GetLength() > 0)
1079 manufacturer
= MatchManufacturer( manufacturers
, ConvertToManufacturerName ( q
->usb_MFG
) );
1082 if ( manufacturer
== NULL
)
1084 q
->product
.Remove('(');
1085 q
->product
.Remove(')');
1087 manufacturer
= MatchManufacturer( manufacturers
, ConvertToManufacturerName ( q
->product
) );
1091 // if we found the manufacturer, then start looking for the model
1093 if ( manufacturer
!= NULL
)
1095 if ( q
->usb_MDL
.GetLength() > 0 )
1097 model
= MatchModel ( manufacturer
, ConvertToModelName ( q
->usb_MDL
) );
1100 if ( ( model
== NULL
) && ( q
->product
.GetLength() > 0 ) )
1102 q
->product
.Remove('(');
1103 q
->product
.Remove(')');
1105 model
= MatchModel ( manufacturer
, ConvertToModelName ( q
->product
) );
1108 if ( model
!= NULL
)
1110 // <rdar://problem/4124524> Offer Generic printers if printer advertises Postscript or PCL. Workaround
1111 // bug in OS X CUPS printer sharing by selecting Generic driver instead of matched printer.
1113 bool hasGenericDriver
= false;
1115 if ( MatchGeneric( manufacturers
, printer
, service
, &genericManufacturer
, &genericModel
) )
1117 hasGenericDriver
= true;
1120 // <rdar://problem/4190104> Use "application/octet-stream" to determine if CUPS
1121 // shared queue supports raw
1123 if ( q
->pdl
.Find( L
"application/octet-stream" ) != -1 )
1125 useCUPSWorkaround
= false;
1128 if ( useCUPSWorkaround
&& printer
->isSharedFromOSX
&& hasGenericDriver
)
1130 SelectMatch(manufacturers
, printer
, service
, genericManufacturer
, genericModel
);
1134 SelectMatch(manufacturers
, printer
, service
, manufacturer
, model
);
1142 // display a message to the user based on whether we could match
1147 text
.LoadString(IDS_PRINTER_MATCH_GOOD
);
1149 else if ( MatchGeneric( manufacturers
, printer
, service
, &genericManufacturer
, &genericModel
) )
1151 if ( printer
->isSharedFromOSX
)
1153 text
.LoadString(IDS_PRINTER_MATCH_GOOD
);
1157 text
.LoadString(IDS_PRINTER_MATCH_MAYBE
);
1160 SelectMatch( manufacturers
, printer
, service
, genericManufacturer
, genericModel
);
1164 text
.LoadString(IDS_PRINTER_MATCH_BAD
);
1167 // if there was any crud in this list from before, get rid of it now
1169 m_modelListCtrl
.DeleteAllItems();
1172 // select the manufacturer if we found one
1174 if (manufacturer
!= NULL
)
1180 // select the manufacturer
1182 info
.flags
= LVFI_STRING
;
1183 info
.psz
= manufacturer
->name
;
1185 nIndex
= m_manufacturerListCtrl
.FindItem(&info
);
1189 m_manufacturerListCtrl
.SetItemState(nIndex
, LVIS_SELECTED
, LVIS_SELECTED
);
1190 m_manufacturerListCtrl
.EnsureVisible(nIndex
, FALSE
);
1195 m_printerSelectionText
.SetWindowText(text
);
1201 // ------------------------------------------------------
1202 // MatchManufacturer
1204 // This function is responsible for finding a manufacturer
1205 // object from a string name. It does a CString::Find, which
1206 // is like strstr, so it doesn't have to do an exact match
1208 // If it can't find a match, NULL is returned
1209 // ------------------------------------------------------
1212 CThirdPage::MatchManufacturer( Manufacturers
& manufacturers
, const CString
& name
)
1214 Manufacturers::iterator iter
;
1216 for (iter
= manufacturers
.begin(); iter
!= manufacturers
.end(); iter
++)
1219 // we're going to convert all the manufacturer names to lower case,
1220 // so we match the name passed in.
1222 CString lower
= iter
->second
->name
;
1226 // now try and find the lowered string in the name passed in.
1228 if (name
.Find(lower
) != -1)
1230 return iter
->second
;
1238 // -------------------------------------------------------
1241 // This function is responsible for matching a model from
1242 // a name. It does a CString::Find(), which works like strstr,
1243 // so it doesn't rely on doing an exact string match.
1247 CThirdPage::MatchModel(Manufacturer
* manufacturer
, const CString
& name
)
1249 Models::iterator iter
;
1251 iter
= manufacturer
->models
.begin();
1253 for (iter
= manufacturer
->models
.begin(); iter
!= manufacturer
->models
.end(); iter
++)
1255 Model
* model
= *iter
;
1258 // convert the model name to lower case
1260 CString lowered
= model
->name
;
1261 lowered
.MakeLower();
1263 if (lowered
.Find( name
) != -1)
1269 // <rdar://problem/3841218>
1270 // try removing the first substring and search again
1273 if ( name
.Find(' ') != -1 )
1275 CString altered
= name
;
1276 altered
.Delete( 0, altered
.Find(' ') + 1 );
1278 if ( lowered
.Find( altered
) != -1 )
1289 // -------------------------------------------------------
1292 // This function will attempt to find a generic printer
1293 // driver for a printer that we weren't able to match
1297 CThirdPage::MatchGeneric( Manufacturers
& manufacturers
, Printer
* printer
, Service
* service
, Manufacturer
** manufacturer
, Model
** model
)
1302 DEBUG_UNUSED( printer
);
1306 Queue
* q
= service
->SelectedQueue();
1310 Manufacturers::iterator iter
= manufacturers
.find( kGenericManufacturer
);
1311 require_action_quiet( iter
!= manufacturers
.end(), exit
, ok
= FALSE
);
1313 *manufacturer
= iter
->second
;
1318 if ( pdl
.Find( kPDLPCLKey
) != -1 )
1320 *model
= m_genericPCL
;
1323 else if ( pdl
.Find( kPDLPostscriptKey
) != -1 )
1325 *model
= m_genericPostscript
;
1335 // -----------------------------------------------------------
1338 // This function is responsible for doing initialization that
1339 // only occurs once during a run of the wizard
1342 OSStatus
CThirdPage::OnInitPage()
1346 OSStatus err
= kNoErr
;
1348 // Load printer icon
1352 check( m_printerImage
== NULL
);
1354 m_printerImage
= (CStatic
*) GetDlgItem( IDR_MANIFEST
);
1355 check( m_printerImage
);
1357 if ( m_printerImage
!= NULL
)
1359 m_printerImage
->SetIcon( LoadIcon( GetNonLocalizedResources(), MAKEINTRESOURCE( IDI_PRINTER
) ) );
1363 // The CTreeCtrl widget automatically sends a selection changed
1364 // message which initially we want to ignore, because the user
1365 // hasn't selected anything
1367 // this flag gets reset in the message handler. Every subsequent
1368 // message gets handled.
1372 // we have to make sure that we only do this once. Typically,
1373 // we would do this in something like OnInitDialog, but we don't
1374 // have this in Wizards, because the window is a PropertySheet.
1375 // We're considered fully initialized when we receive the first
1378 header
.LoadString(IDS_MANUFACTURER_HEADING
);
1379 m_manufacturerListCtrl
.InsertColumn(0, header
, LVCFMT_LEFT
, -1 );
1380 m_manufacturerSelected
= NULL
;
1382 header
.LoadString(IDS_MODEL_HEADING
);
1383 m_modelListCtrl
.InsertColumn(0, header
, LVCFMT_LEFT
, -1 );
1384 m_modelSelected
= NULL
;
1390 void CThirdPage::DoDataExchange(CDataExchange
* pDX
)
1392 CPropertyPage::DoDataExchange(pDX
);
1393 DDX_Control(pDX
, IDC_PRINTER_MANUFACTURER
, m_manufacturerListCtrl
);
1394 DDX_Control(pDX
, IDC_PRINTER_MODEL
, m_modelListCtrl
);
1395 DDX_Control(pDX
, IDC_PRINTER_NAME
, m_printerName
);
1396 DDX_Control(pDX
, IDC_DEFAULT_PRINTER
, m_defaultPrinterCtrl
);
1397 DDX_Control(pDX
, IDC_PRINTER_SELECTION_TEXT
, m_printerSelectionText
);
1402 // ----------------------------------------------------------
1405 // This function is called by MFC after the window has been
1410 CThirdPage::OnSetActive()
1412 CPrinterSetupWizardSheet
* psheet
;
1416 psheet
= reinterpret_cast<CPrinterSetupWizardSheet
*>(GetParent());
1417 require_quiet( psheet
, exit
);
1419 psheet
->SetWizardButtons( PSWIZB_BACK
);
1421 printer
= psheet
->GetSelectedPrinter();
1422 require_quiet( printer
, exit
);
1424 service
= printer
->services
.front();
1425 require_quiet( service
, exit
);
1428 // call OnInitPage once
1433 m_initialized
= true;
1437 // update the UI with the printer name
1439 m_printerName
.SetWindowText(printer
->displayName
);
1442 // populate the list controls with the manufacturers and models
1445 PopulateUI( m_manufacturers
);
1448 // and try and match the printer
1451 if ( psheet
->GetLastPage() == psheet
->GetPage(1) )
1453 MatchPrinter( m_manufacturers
, printer
, service
, true );
1457 SelectMatch(printer
, service
, m_manufacturerSelected
, m_modelSelected
);
1462 return CPropertyPage::OnSetActive();
1467 CThirdPage::OnKillActive()
1469 CPrinterSetupWizardSheet
* psheet
;
1471 psheet
= reinterpret_cast<CPrinterSetupWizardSheet
*>(GetParent());
1472 require_quiet( psheet
, exit
);
1474 psheet
->SetLastPage(this);
1478 return CPropertyPage::OnKillActive();
1482 // -------------------------------------------------------
1485 // This function is called to populate the list of manufacturers
1488 CThirdPage::PopulateUI(Manufacturers
& manufacturers
)
1490 Manufacturers::iterator iter
;
1492 m_manufacturerListCtrl
.DeleteAllItems();
1494 for (iter
= manufacturers
.begin(); iter
!= manufacturers
.end(); iter
++)
1498 Manufacturer
* manufacturer
= iter
->second
;
1500 nIndex
= m_manufacturerListCtrl
.InsertItem(0, manufacturer
->name
);
1502 m_manufacturerListCtrl
.SetItemData(nIndex
, (DWORD_PTR
) manufacturer
);
1504 m_manufacturerListCtrl
.SetColumnWidth( 0, LVSCW_AUTOSIZE_USEHEADER
);
1511 BEGIN_MESSAGE_MAP(CThirdPage
, CPropertyPage
)
1512 ON_NOTIFY(LVN_ITEMCHANGED
, IDC_PRINTER_MANUFACTURER
, OnLvnItemchangedManufacturer
)
1513 ON_NOTIFY(LVN_ITEMCHANGED
, IDC_PRINTER_MODEL
, OnLvnItemchangedPrinterModel
)
1514 ON_BN_CLICKED(IDC_DEFAULT_PRINTER
, OnBnClickedDefaultPrinter
)
1515 ON_BN_CLICKED(IDC_HAVE_DISK
, OnBnClickedHaveDisk
)
1519 // CThirdPage message handlers
1520 void CThirdPage::OnLvnItemchangedManufacturer(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1522 LPNMLISTVIEW pNMLV
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
1524 POSITION p
= m_manufacturerListCtrl
.GetFirstSelectedItemPosition();
1525 int nSelected
= m_manufacturerListCtrl
.GetNextSelectedItem(p
);
1527 if (nSelected
!= -1)
1529 m_manufacturerSelected
= (Manufacturer
*) m_manufacturerListCtrl
.GetItemData(nSelected
);
1531 m_modelListCtrl
.SetRedraw(FALSE
);
1533 m_modelListCtrl
.DeleteAllItems();
1534 m_modelSelected
= NULL
;
1536 Models::iterator iter
;
1538 for (iter
= m_manufacturerSelected
->models
.begin(); iter
!= m_manufacturerSelected
->models
.end(); iter
++)
1540 Model
* model
= *iter
;
1542 int nItem
= m_modelListCtrl
.InsertItem( 0, model
->displayName
);
1544 m_modelListCtrl
.SetItemData(nItem
, (DWORD_PTR
) model
);
1546 m_modelListCtrl
.SetColumnWidth( 0, LVSCW_AUTOSIZE_USEHEADER
);
1549 m_modelListCtrl
.SetRedraw(TRUE
);
1555 void CThirdPage::OnLvnItemchangedPrinterModel(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1557 LPNMLISTVIEW pNMLV
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
1559 CPrinterSetupWizardSheet
* psheet
;
1563 psheet
= reinterpret_cast<CPrinterSetupWizardSheet
*>(GetParent());
1564 require_quiet( psheet
, exit
);
1566 printer
= psheet
->GetSelectedPrinter();
1567 require_quiet( printer
, exit
);
1569 service
= printer
->services
.front();
1570 require_quiet( service
, exit
);
1572 check ( m_manufacturerSelected
);
1574 POSITION p
= m_modelListCtrl
.GetFirstSelectedItemPosition();
1575 int nSelected
= m_modelListCtrl
.GetNextSelectedItem(p
);
1577 if (nSelected
!= -1)
1579 m_modelSelected
= (Model
*) m_modelListCtrl
.GetItemData(nSelected
);
1581 CopyPrinterSettings( printer
, service
, m_manufacturerSelected
, m_modelSelected
);
1583 psheet
->SetWizardButtons(PSWIZB_BACK
|PSWIZB_NEXT
);
1587 psheet
->SetWizardButtons(PSWIZB_BACK
);
1596 void CThirdPage::OnBnClickedDefaultPrinter()
1598 CPrinterSetupWizardSheet
* psheet
;
1601 psheet
= reinterpret_cast<CPrinterSetupWizardSheet
*>(GetParent());
1602 require_quiet( psheet
, exit
);
1604 printer
= psheet
->GetSelectedPrinter();
1605 require_quiet( printer
, exit
);
1607 printer
->deflt
= ( m_defaultPrinterCtrl
.GetCheck() == BST_CHECKED
) ? true : false;
1614 void CThirdPage::OnBnClickedHaveDisk()
1616 CPrinterSetupWizardSheet
* psheet
;
1619 Manufacturers manufacturers
;
1621 CFileDialog
dlg(TRUE
, NULL
, NULL
, OFN_HIDEREADONLY
|OFN_FILEMUSTEXIST
, L
"Setup Information (*.inf)|*.inf||", this);
1623 psheet
= reinterpret_cast<CPrinterSetupWizardSheet
*>(GetParent());
1624 require_quiet( psheet
, exit
);
1626 printer
= psheet
->GetSelectedPrinter();
1627 require_quiet( printer
, exit
);
1629 service
= printer
->services
.front();
1630 require_quiet( service
, exit
);
1634 if ( dlg
.DoModal() == IDOK
)
1636 CString filename
= dlg
.GetPathName();
1638 LoadPrintDriverDefsFromFile( manufacturers
, filename
, true );
1642 if ( manufacturers
.size() > 0 )
1644 PopulateUI( manufacturers
);
1646 MatchPrinter( manufacturers
, printer
, service
, false );