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.
19 #include "PrinterSetupWizardApp.h"
20 #include "PrinterSetupWizardSheet.h"
21 #include "ThirdPage.h"
27 // local variable is initialize but not referenced
28 #pragma warning(disable:4189)
31 // This is the printer description file that is shipped
32 // with Windows XP and below
34 #define kNTPrintFile L"inf\\ntprint.inf"
37 // Windows Vista ships with a set of prn*.inf files
39 #define kVistaPrintFiles L"inf\\prn*.inf"
42 // These are pre-defined names for Generic manufacturer and model
44 #define kGenericManufacturer L"Generic"
45 #define kGenericText L"Generic / Text Only"
46 #define kGenericPostscript L"Generic / Postscript"
47 #define kGenericPCL L"Generic / PCL"
48 #define kPDLPostscriptKey L"application/postscript"
49 #define kPDLPCLKey L"application/vnd.hp-pcl"
50 #define kGenericPSColorDriver L"HP Color LaserJet 4550 PS"
51 #define kGenericPSDriver L"HP LaserJet 4050 Series PS"
52 #define kGenericPCLColorDriver L"HP Color LaserJet 4550 PCL"
53 #define kGenericPCLDriver L"HP LaserJet 4050 Series PCL"
58 IMPLEMENT_DYNAMIC(CThirdPage
, CPropertyPage
)
59 CThirdPage::CThirdPage()
60 : CPropertyPage(CThirdPage::IDD
),
61 m_manufacturerSelected( NULL
),
62 m_modelSelected( NULL
),
63 m_genericPostscript( NULL
),
66 m_printerImage( NULL
)
68 static const int bufferSize
= 32768;
69 TCHAR windowsDirectory
[bufferSize
];
71 WIN32_FIND_DATA findFileData
;
78 m_psp
.dwFlags
&= ~(PSP_HASHELP
);
79 m_psp
.dwFlags
|= PSP_DEFAULT
|PSP_USEHEADERTITLE
|PSP_USEHEADERSUBTITLE
;
81 m_psp
.pszHeaderTitle
= MAKEINTRESOURCE(IDS_INSTALL_TITLE
);
82 m_psp
.pszHeaderSubTitle
= MAKEINTRESOURCE(IDS_INSTALL_SUBTITLE
);
85 // load printers from ntprint.inf
87 ok
= GetWindowsDirectory( windowsDirectory
, bufferSize
);
88 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
89 require_noerr( err
, exit
);
92 // <rdar://problem/4826126>
94 // If there are no *prn.inf files, we'll assume that the information
97 prnFiles
.Format( L
"%s\\%s", windowsDirectory
, kVistaPrintFiles
);
98 findHandle
= FindFirstFile( prnFiles
, &findFileData
);
100 if ( findHandle
!= INVALID_HANDLE_VALUE
)
104 absolute
.Format( L
"%s\\inf\\%s", windowsDirectory
, findFileData
.cFileName
);
105 err
= LoadPrintDriverDefsFromFile( m_manufacturers
, absolute
, false );
106 require_noerr( err
, exit
);
108 while ( FindNextFile( findHandle
, &findFileData
) )
110 absolute
.Format( L
"%s\\inf\\%s", windowsDirectory
, findFileData
.cFileName
);
111 err
= LoadPrintDriverDefsFromFile( m_manufacturers
, absolute
, false );
112 require_noerr( err
, exit
);
115 FindClose( findHandle
);
119 ntPrint
.Format(L
"%s\\%s", windowsDirectory
, kNTPrintFile
);
120 err
= LoadPrintDriverDefsFromFile( m_manufacturers
, ntPrint
, false );
121 require_noerr(err
, exit
);
125 // load printer drivers that have been installed on this machine
127 err
= LoadPrintDriverDefs( m_manufacturers
);
128 require_noerr(err
, exit
);
131 // load our own special generic printer defs
133 err
= LoadGenericPrintDriverDefs( m_manufacturers
);
134 require_noerr( err
, exit
);
143 CThirdPage::FreeManufacturers( Manufacturers
& manufacturers
)
145 for ( Manufacturers::iterator it
= manufacturers
.begin(); it
!= manufacturers
.end(); it
++ )
147 for ( Models::iterator it2
= it
->second
->models
.begin(); it2
!= it
->second
->models
.end(); it2
++ )
157 CThirdPage::~CThirdPage()
159 FreeManufacturers( m_manufacturers
);
162 // ----------------------------------------------------
165 // SelectMatch will do all the UI work associated with
166 // selected a manufacturer and model of printer. It also
167 // makes sure the printer object is update with the
170 // ----------------------------------------------------
172 CThirdPage::SelectMatch(Printer
* printer
, Service
* service
, Manufacturer
* manufacturer
, Model
* model
)
177 check( printer
!= NULL
);
178 check( manufacturer
!= NULL
);
179 check( model
!= NULL
);
182 // select the manufacturer
184 info
.flags
= LVFI_STRING
;
185 info
.psz
= manufacturer
->name
;
187 nIndex
= m_manufacturerListCtrl
.FindItem(&info
);
191 m_manufacturerListCtrl
.SetItemState(nIndex
, LVIS_SELECTED
, LVIS_SELECTED
);
193 //<rdar://problem/4528853> mDNS: When auto-highlighting items in lists, scroll list so highlighted item is in the middle
195 AutoScroll(m_manufacturerListCtrl
, nIndex
);
201 info
.flags
= LVFI_STRING
;
202 info
.psz
= model
->displayName
;
204 nIndex
= m_modelListCtrl
.FindItem(&info
);
208 m_modelListCtrl
.SetItemState(nIndex
, LVIS_SELECTED
, LVIS_SELECTED
);
209 AutoScroll( m_modelListCtrl
, nIndex
);
211 m_modelListCtrl
.SetFocus();
214 CopyPrinterSettings( printer
, service
, manufacturer
, model
);
218 CThirdPage::SelectMatch(Manufacturers
& manufacturers
, Printer
* printer
, Service
* service
, Manufacturer
* manufacturer
, Model
* model
)
220 PopulateUI( manufacturers
);
222 SelectMatch( printer
, service
, manufacturer
, model
);
225 // --------------------------------------------------------
226 // CopyPrinterSettings
228 // This function makes sure that the printer object has the
229 // latest settings from the manufacturer and model objects
230 // --------------------------------------------------------
233 CThirdPage::CopyPrinterSettings( Printer
* printer
, Service
* service
, Manufacturer
* manufacturer
, Model
* model
)
237 printer
->manufacturer
= manufacturer
->name
;
238 printer
->displayModelName
= model
->displayName
;
239 printer
->modelName
= model
->name
;
240 printer
->driverInstalled
= model
->driverInstalled
;
241 printer
->infFileName
= model
->infFileName
;
243 if ( service
->type
== kPDLServiceType
)
245 printer
->portName
.Format(L
"IP_%s.%d", static_cast<LPCTSTR
>(service
->hostname
), service
->portNumber
);
246 service
->protocol
= L
"Raw";
248 else if ( service
->type
== kLPRServiceType
)
250 Queue
* q
= service
->queues
.front();
253 if ( q
->name
.GetLength() > 0 )
255 printer
->portName
.Format(L
"LPR_%s.%d.%s", static_cast<LPCTSTR
>(service
->hostname
), service
->portNumber
, static_cast<LPCTSTR
>(q
->name
) );
259 printer
->portName
.Format(L
"LPR_%s.%d", static_cast<LPCTSTR
>(service
->hostname
), service
->portNumber
);
262 service
->protocol
= L
"LPR";
264 else if ( service
->type
== kIPPServiceType
)
266 Queue
* q
= service
->queues
.front();
269 if ( q
->name
.GetLength() > 0 )
271 printer
->portName
.Format(L
"http://%s:%d/%s", static_cast<LPCTSTR
>(service
->hostname
), service
->portNumber
, static_cast<LPCTSTR
>(q
->name
) );
275 printer
->portName
.Format(L
"http://%s:%d/", static_cast<LPCTSTR
>(service
->hostname
), service
->portNumber
);
278 service
->protocol
= L
"IPP";
281 // If it's not an IPP printr, truncate the portName so that it's valid
283 if ( service
->type
!= kIPPServiceType
)
285 portNameLen
= printer
->portName
.GetLength() + 1;
287 if ( portNameLen
> MAX_PORTNAME_LEN
)
289 printer
->portName
.Delete( MAX_PORTNAME_LEN
- 1, ( portNameLen
- MAX_PORTNAME_LEN
) );
294 // --------------------------------------------------------
295 // DefaultPrinterExists
297 // Checks to see if a default printer has been configured
299 // --------------------------------------------------------
301 CThirdPage::DefaultPrinterExists()
303 CPrintDialog
dlg(FALSE
);
305 dlg
.m_pd
.Flags
|= PD_RETURNDEFAULT
;
307 return dlg
.GetDefaults();
310 // --------------------------------------------------------
313 // Ensure selected item is in middle of list
314 // --------------------------------------------------------
316 CThirdPage::AutoScroll( CListCtrl
& list
, int nIndex
)
319 //<rdar://problem/4528853> mDNS: When auto-highlighting items in lists, scroll list so highlighted item is in the middle
325 list
.EnsureVisible( nIndex
, FALSE
);
327 top
= list
.GetTopIndex();
328 count
= list
.GetCountPerPage();
330 if ( ( nIndex
== top
) || ( ( nIndex
+ 1 ) == ( top
+ count
) ) )
335 rows
= ( count
/ 2 );
339 list
.GetItemRect(0, rect
, LVIR_BOUNDS
);
340 list
.Scroll( CPoint( 0, rows
* rect
.Height() * -1 ) );
344 list
.GetItemRect(0, rect
, LVIR_BOUNDS
);
345 list
.Scroll( CPoint( 0, rows
* rect
.Height() ) );
350 // ------------------------------------------------------
351 // LoadPrintDriverDefsFromFile
353 // The only potentially opaque thing about this function is the
354 // checkForDuplicateModels flag. The problem here is that ntprint.inf
355 // doesn't contain duplicate models, and it has hundreds of models
356 // listed. You wouldn't check for duplicates there. But oftentimes,
357 // loading different windows print driver files contain multiple
358 // entries for the same printer. You don't want the UI to display
359 // the same printer multiple times, so in that case, you would ask
360 // this function to check for multiple models.
363 CThirdPage::LoadPrintDriverDefsFromFile(Manufacturers
& manufacturers
, const CString
& filename
, bool checkForDuplicateModels
)
365 HINF handle
= INVALID_HANDLE_VALUE
;
366 const TCHAR
* section
= TEXT( "Manufacturer" );
370 INFCONTEXT manufacturerContext
;
374 // Make sure we can open the file
375 handle
= SetupOpenInfFile( filename
, NULL
, INF_STYLE_WIN4
, NULL
);
376 translate_errno( handle
!= INVALID_HANDLE_VALUE
, GetLastError(), kUnknownErr
);
377 require_noerr( err
, exit
);
379 // Make sure it's a printer file
380 ok
= SetupGetLineText( NULL
, handle
, TEXT( "Version" ), TEXT( "Class" ), line
, sizeof( line
), NULL
);
381 translate_errno( ok
, GetLastError(), kUnknownErr
);
382 require_noerr( err
, exit
);
384 require_action( klass
== TEXT( "Printer" ), exit
, err
= kUnknownErr
);
386 sectionCount
= SetupGetLineCount( handle
, section
);
387 translate_errno( sectionCount
!= -1, GetLastError(), kUnknownErr
);
388 require_noerr( err
, exit
);
390 memset( &manufacturerContext
, 0, sizeof( manufacturerContext
) );
392 for ( LONG i
= 0; i
< sectionCount
; i
++ )
394 Manufacturers::iterator iter
;
395 Manufacturer
* manufacturer
;
396 CString manufacturerName
;
398 CStringList modelSectionNameDecl
;
399 CString modelSectionName
;
400 CString baseModelName
;
402 INFCONTEXT modelContext
;
408 ok
= SetupFindFirstLine( handle
, section
, NULL
, &manufacturerContext
);
409 err
= translate_errno( ok
, GetLastError(), kUnknownErr
);
410 require_noerr( err
, exit
);
414 ok
= SetupFindNextLine( &manufacturerContext
, &manufacturerContext
);
415 err
= translate_errno( ok
, GetLastError(), kUnknownErr
);
416 require_noerr( err
, exit
);
419 ok
= SetupGetStringField( &manufacturerContext
, 0, line
, sizeof( line
), NULL
);
420 err
= translate_errno( ok
, GetLastError(), kUnknownErr
);
421 require_noerr( err
, exit
);
422 manufacturerName
= line
;
424 ok
= SetupGetLineText( &manufacturerContext
, handle
, NULL
, NULL
, line
, sizeof( line
), NULL
);
425 err
= translate_errno( ok
, GetLastError(), kUnknownErr
);
426 require_noerr( err
, exit
);
428 // Try to find some model section name that has entries. Explanation of int file structure
431 // <http://msdn.microsoft.com/en-us/library/ms794359.aspx>
432 Split( line
, ',', modelSectionNameDecl
);
434 p
= modelSectionNameDecl
.GetHeadPosition();
435 modelSectionName
= modelSectionNameDecl
.GetNext( p
);
436 modelCount
= SetupGetLineCount( handle
, modelSectionName
);
437 baseModelName
= modelSectionName
;
439 while ( modelCount
<= 0 && p
)
441 CString targetOSVersion
;
443 targetOSVersion
= modelSectionNameDecl
.GetNext( p
);
444 modelSectionName
= baseModelName
+ TEXT( "." ) + targetOSVersion
;
445 modelCount
= SetupGetLineCount( handle
, modelSectionName
);
448 if ( modelCount
> 0 )
450 manufacturerName
= NormalizeManufacturerName( manufacturerName
);
452 iter
= manufacturers
.find( manufacturerName
);
454 if ( iter
!= manufacturers
.end() )
456 manufacturer
= iter
->second
;
457 require_action( manufacturer
, exit
, err
= kUnknownErr
);
463 manufacturer
= new Manufacturer
;
470 require_action( manufacturer
, exit
, err
= kNoMemoryErr
);
472 manufacturer
->name
= manufacturerName
;
473 manufacturers
[ manufacturerName
] = manufacturer
;
476 memset( &modelContext
, 0, sizeof( modelContext
) );
478 for ( LONG j
= 0; j
< modelCount
; j
++ )
485 ok
= SetupFindFirstLine( handle
, modelSectionName
, NULL
, &modelContext
);
486 err
= translate_errno( ok
, GetLastError(), kUnknownErr
);
487 require_noerr( err
, exit
);
491 SetupFindNextLine( &modelContext
, &modelContext
);
492 err
= translate_errno( ok
, GetLastError(), kUnknownErr
);
493 require_noerr( err
, exit
);
496 ok
= SetupGetStringField( &modelContext
, 0, line
, sizeof( line
), NULL
);
497 err
= translate_errno( ok
, GetLastError(), kUnknownErr
);
498 require_noerr( err
, exit
);
502 if (checkForDuplicateModels
== true)
504 if ( MatchModel( manufacturer
, ConvertToModelName( modelName
) ) != NULL
)
511 // Stock Vista printer inf files embed guids in the model
512 // declarations for Epson printers. Let's ignore those.
514 if ( modelName
.Find( TEXT( "{" ), 0 ) != -1 )
528 require_action( model
, exit
, err
= kNoMemoryErr
);
530 model
->infFileName
= filename
;
531 model
->displayName
= modelName
;
532 model
->name
= modelName
;
533 model
->driverInstalled
= false;
535 manufacturer
->models
.push_back(model
);
542 if ( handle
!= INVALID_HANDLE_VALUE
)
544 SetupCloseInfFile( handle
);
552 // -------------------------------------------------------
553 // LoadPrintDriverDefs
555 // This function is responsible for loading the print driver
556 // definitions of all print drivers that have been installed
558 // -------------------------------------------------------
560 CThirdPage::LoadPrintDriverDefs( Manufacturers
& manufacturers
)
562 BYTE
* buffer
= NULL
;
563 DWORD bytesReceived
= 0;
564 DWORD numPrinters
= 0;
569 // like a lot of win32 calls, we call this first to get the
570 // size of the buffer we need.
572 EnumPrinterDrivers(NULL
, L
"all", 6, NULL
, 0, &bytesReceived
, &numPrinters
);
574 if (bytesReceived
> 0)
578 buffer
= new BYTE
[bytesReceived
];
585 require_action( buffer
, exit
, err
= kNoMemoryErr
);
588 // this call gets the real info
590 ok
= EnumPrinterDrivers(NULL
, L
"all", 6, buffer
, bytesReceived
, &bytesReceived
, &numPrinters
);
591 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
592 require_noerr( err
, exit
);
594 DRIVER_INFO_6
* info
= (DRIVER_INFO_6
*) buffer
;
596 for (DWORD i
= 0; i
< numPrinters
; i
++)
598 Manufacturer
* manufacturer
;
603 // skip over anything that doesn't have a manufacturer field. This
604 // fixes a bug that I noticed that occurred after I installed
605 // ProComm. This program add a print driver with no manufacturer
606 // that screwed up this wizard.
608 if (info
[i
].pszMfgName
== NULL
)
614 // look for manufacturer
616 Manufacturers::iterator iter
;
621 name
= NormalizeManufacturerName( info
[i
].pszMfgName
);
623 iter
= manufacturers
.find(name
);
625 if (iter
!= manufacturers
.end())
627 manufacturer
= iter
->second
;
633 manufacturer
= new Manufacturer
;
640 require_action( manufacturer
, exit
, err
= kNoMemoryErr
);
642 manufacturer
->name
= name
;
644 manufacturers
[name
] = manufacturer
;
648 // now look to see if we have already seen this guy. this could
649 // happen if we have already installed printers that are described
650 // in ntprint.inf. the extant drivers will show up in EnumPrinterDrivers
651 // but we have already loaded their info
654 if ( MatchModel( manufacturer
, ConvertToModelName( info
[i
].pName
) ) == NULL
)
665 require_action( model
, exit
, err
= kNoMemoryErr
);
667 model
->displayName
= info
[i
].pName
;
668 model
->name
= info
[i
].pName
;
669 model
->driverInstalled
= true;
671 manufacturer
->models
.push_back(model
);
686 // -------------------------------------------------------
687 // LoadGenericPrintDriverDefs
689 // This function is responsible for loading polymorphic
690 // generic print drivers defs. The UI will read
691 // something like "Generic / Postscript" and we can map
692 // that to any print driver we want.
693 // -------------------------------------------------------
695 CThirdPage::LoadGenericPrintDriverDefs( Manufacturers
& manufacturers
)
697 Manufacturer
* manufacturer
;
699 Manufacturers::iterator iter
;
700 CString psDriverName
;
701 CString pclDriverName
;
704 // <rdar://problem/4030388> Generic drivers don't do color
706 // First try and find our generic driver names
708 iter
= m_manufacturers
.find(L
"HP");
709 require_action( iter
!= m_manufacturers
.end(), exit
, err
= kUnknownErr
);
710 manufacturer
= iter
->second
;
712 // Look for Postscript
714 model
= manufacturer
->find( kGenericPSColorDriver
);
718 model
= manufacturer
->find( kGenericPSDriver
);
723 psDriverName
= model
->name
;
728 model
= manufacturer
->find( kGenericPCLColorDriver
);
732 model
= manufacturer
->find( kGenericPCLDriver
);
737 pclDriverName
= model
->name
;
740 // If we found either a generic PS driver, or a generic PCL driver,
741 // then add them to the list
743 if ( psDriverName
.GetLength() || pclDriverName
.GetLength() )
745 // Try and find generic manufacturer if there is one
747 iter
= manufacturers
.find(L
"Generic");
749 if (iter
!= manufacturers
.end())
751 manufacturer
= iter
->second
;
757 manufacturer
= new Manufacturer
;
764 require_action( manufacturer
, exit
, err
= kNoMemoryErr
);
766 manufacturer
->name
= "Generic";
767 manufacturers
[manufacturer
->name
] = manufacturer
;
770 if ( psDriverName
.GetLength() > 0 )
774 m_genericPostscript
= new Model
;
778 m_genericPostscript
= NULL
;
781 require_action( m_genericPostscript
, exit
, err
= kNoMemoryErr
);
783 m_genericPostscript
->displayName
= kGenericPostscript
;
784 m_genericPostscript
->name
= psDriverName
;
785 m_genericPostscript
->driverInstalled
= false;
787 manufacturer
->models
.push_back( m_genericPostscript
);
790 if ( pclDriverName
.GetLength() > 0 )
794 m_genericPCL
= new Model
;
801 require_action( m_genericPCL
, exit
, err
= kNoMemoryErr
);
803 m_genericPCL
->displayName
= kGenericPCL
;
804 m_genericPCL
->name
= pclDriverName
;
805 m_genericPCL
->driverInstalled
= false;
807 manufacturer
->models
.push_back( m_genericPCL
);
816 // ------------------------------------------------------
817 // ConvertToManufacturerName
819 // This function is responsible for tweaking the
820 // name so that subsequent string operations won't fail because
821 // of capitalizations/different names for the same manufacturer
822 // (i.e. Hewlett-Packard/HP/Hewlett Packard)
825 CThirdPage::ConvertToManufacturerName( const CString
& name
)
828 // first we're going to convert all the characters to lower
831 CString lower
= name
;
835 // now we're going to check to see if the string says "hewlett-packard",
836 // because sometimes they refer to themselves as "hewlett-packard", and
837 // sometimes they refer to themselves as "hp".
839 if ( lower
== L
"hewlett-packard")
845 // tweak for Xerox Phaser, which doesn't announce itself
848 else if ( lower
.Find( L
"phaser", 0 ) != -1 )
856 // ------------------------------------------------------
857 // ConvertToModelName
859 // This function is responsible for ensuring that subsequent
860 // string operations don't fail because of differing capitalization
861 // schemes and the like
862 // ------------------------------------------------------
865 CThirdPage::ConvertToModelName( const CString
& name
)
868 // convert it to lowercase
870 CString lower
= name
;
876 // ------------------------------------------------------
877 // NormalizeManufacturerName
879 // This function is responsible for tweaking the manufacturer
880 // name so that there are no aliases for vendors
883 CThirdPage::NormalizeManufacturerName( const CString
& name
)
885 CString normalized
= name
;
888 // now we're going to check to see if the string says "hewlett-packard",
889 // because sometimes they refer to themselves as "hewlett-packard", and
890 // sometimes they refer to themselves as "hp".
892 if ( normalized
== L
"Hewlett-Packard")
900 // -------------------------------------------------------
903 // This function is responsible for matching a printer
904 // to a list of manufacturers and models. It calls
905 // MatchManufacturer and MatchModel in turn.
908 OSStatus
CThirdPage::MatchPrinter(Manufacturers
& manufacturers
, Printer
* printer
, Service
* service
, bool useCUPSWorkaround
)
910 CString normalizedProductName
;
911 Manufacturer
* manufacturer
= NULL
;
912 Manufacturer
* genericManufacturer
= NULL
;
913 Model
* model
= NULL
;
914 Model
* genericModel
= NULL
;
917 OSStatus err
= kNoErr
;
922 Queue
* q
= service
->SelectedQueue();
927 // first look to see if we have a usb_MFG descriptor
929 if ( q
->usb_MFG
.GetLength() > 0)
931 manufacturer
= MatchManufacturer( manufacturers
, ConvertToManufacturerName ( q
->usb_MFG
) );
934 if ( manufacturer
== NULL
)
936 q
->product
.Remove('(');
937 q
->product
.Remove(')');
939 manufacturer
= MatchManufacturer( manufacturers
, ConvertToManufacturerName ( q
->product
) );
943 // if we found the manufacturer, then start looking for the model
945 if ( manufacturer
!= NULL
)
947 if ( q
->usb_MDL
.GetLength() > 0 )
949 model
= MatchModel ( manufacturer
, ConvertToModelName ( q
->usb_MDL
) );
952 if ( ( model
== NULL
) && ( q
->product
.GetLength() > 0 ) )
954 q
->product
.Remove('(');
955 q
->product
.Remove(')');
957 model
= MatchModel ( manufacturer
, ConvertToModelName ( q
->product
) );
962 // <rdar://problem/4124524> Offer Generic printers if printer advertises Postscript or PCL. Workaround
963 // bug in OS X CUPS printer sharing by selecting Generic driver instead of matched printer.
965 bool hasGenericDriver
= false;
967 if ( MatchGeneric( manufacturers
, printer
, service
, &genericManufacturer
, &genericModel
) )
969 hasGenericDriver
= true;
972 // <rdar://problem/4190104> Use "application/octet-stream" to determine if CUPS
973 // shared queue supports raw
975 if ( q
->pdl
.Find( L
"application/octet-stream" ) != -1 )
977 useCUPSWorkaround
= false;
980 if ( useCUPSWorkaround
&& printer
->isCUPSPrinter
&& hasGenericDriver
)
983 // <rdar://problem/4496652> mDNS: Don't allow user to choose non-working driver
985 Manufacturers genericManufacturers
;
987 LoadGenericPrintDriverDefs( genericManufacturers
);
989 SelectMatch( genericManufacturers
, printer
, service
, genericManufacturer
, genericModel
);
991 FreeManufacturers( genericManufacturers
);
995 SelectMatch(manufacturers
, printer
, service
, manufacturer
, model
);
1003 // display a message to the user based on whether we could match
1008 text
.LoadString(IDS_PRINTER_MATCH_GOOD
);
1011 else if ( MatchGeneric( manufacturers
, printer
, service
, &genericManufacturer
, &genericModel
) )
1013 if ( printer
->isCUPSPrinter
)
1016 // <rdar://problem/4496652> mDNS: Don't allow user to choose non-working driver
1018 Manufacturers genericManufacturers
;
1020 LoadGenericPrintDriverDefs( genericManufacturers
);
1022 SelectMatch( genericManufacturers
, printer
, service
, genericManufacturer
, genericModel
);
1024 text
.LoadString(IDS_PRINTER_MATCH_GOOD
);
1026 FreeManufacturers( genericManufacturers
);
1030 SelectMatch( manufacturers
, printer
, service
, genericManufacturer
, genericModel
);
1031 text
.LoadString(IDS_PRINTER_MATCH_MAYBE
);
1038 text
.LoadString(IDS_PRINTER_MATCH_BAD
);
1041 // if there was any crud in this list from before, get rid of it now
1043 m_modelListCtrl
.DeleteAllItems();
1046 // select the manufacturer if we found one
1048 if (manufacturer
!= NULL
)
1054 // select the manufacturer
1056 info
.flags
= LVFI_STRING
;
1057 info
.psz
= manufacturer
->name
;
1059 nIndex
= m_manufacturerListCtrl
.FindItem(&info
);
1063 m_manufacturerListCtrl
.SetItemState(nIndex
, LVIS_SELECTED
, LVIS_SELECTED
);
1066 //<rdar://problem/4528853> mDNS: When auto-highlighting items in lists, scroll list so highlighted item is in the middle
1068 AutoScroll(m_manufacturerListCtrl
, nIndex
);
1075 m_printerSelectionText
.SetWindowText(text
);
1080 // ------------------------------------------------------
1081 // MatchManufacturer
1083 // This function is responsible for finding a manufacturer
1084 // object from a string name. It does a CString::Find, which
1085 // is like strstr, so it doesn't have to do an exact match
1087 // If it can't find a match, NULL is returned
1088 // ------------------------------------------------------
1091 CThirdPage::MatchManufacturer( Manufacturers
& manufacturers
, const CString
& name
)
1093 Manufacturers::iterator iter
;
1095 for (iter
= manufacturers
.begin(); iter
!= manufacturers
.end(); iter
++)
1098 // we're going to convert all the manufacturer names to lower case,
1099 // so we match the name passed in.
1101 CString lower
= iter
->second
->name
;
1105 // now try and find the lowered string in the name passed in.
1107 if (name
.Find(lower
) != -1)
1109 return iter
->second
;
1116 // -------------------------------------------------------
1119 // This function is responsible for matching a model from
1120 // a name. It does a CString::Find(), which works like strstr,
1121 // so it doesn't rely on doing an exact string match.
1125 CThirdPage::MatchModel(Manufacturer
* manufacturer
, const CString
& name
)
1127 Models::iterator iter
;
1129 iter
= manufacturer
->models
.begin();
1131 for (iter
= manufacturer
->models
.begin(); iter
!= manufacturer
->models
.end(); iter
++)
1133 Model
* model
= *iter
;
1136 // convert the model name to lower case
1138 CString lowered
= model
->name
;
1139 lowered
.MakeLower();
1141 if (lowered
.Find( name
) != -1)
1147 // <rdar://problem/3841218>
1148 // try removing the first substring and search again
1151 if ( name
.Find(' ') != -1 )
1153 CString altered
= name
;
1154 altered
.Delete( 0, altered
.Find(' ') + 1 );
1156 if ( lowered
.Find( altered
) != -1 )
1166 // -------------------------------------------------------
1169 // This function will attempt to find a generic printer
1170 // driver for a printer that we weren't able to match
1174 CThirdPage::MatchGeneric( Manufacturers
& manufacturers
, Printer
* printer
, Service
* service
, Manufacturer
** manufacturer
, Model
** model
)
1179 DEBUG_UNUSED( printer
);
1183 Queue
* q
= service
->SelectedQueue();
1187 Manufacturers::iterator iter
= manufacturers
.find( kGenericManufacturer
);
1188 require_action_quiet( iter
!= manufacturers
.end(), exit
, ok
= FALSE
);
1190 *manufacturer
= iter
->second
;
1195 if ( m_genericPCL
&& ( pdl
.Find( kPDLPCLKey
) != -1 ) )
1197 *model
= m_genericPCL
;
1200 else if ( m_genericPostscript
&& ( pdl
.Find( kPDLPostscriptKey
) != -1 ) )
1202 *model
= m_genericPostscript
;
1211 // -----------------------------------------------------------
1214 // This function is responsible for doing initialization that
1215 // only occurs once during a run of the wizard
1218 OSStatus
CThirdPage::OnInitPage()
1222 OSStatus err
= kNoErr
;
1224 // Load printer icon
1225 check( m_printerImage
== NULL
);
1227 m_printerImage
= (CStatic
*) GetDlgItem( 1 ); // 1 == IDR_MANIFEST
1228 check( m_printerImage
);
1230 if ( m_printerImage
!= NULL
)
1232 m_printerImage
->SetIcon( LoadIcon( GetNonLocalizedResources(), MAKEINTRESOURCE( IDI_PRINTER
) ) );
1236 // The CTreeCtrl widget automatically sends a selection changed
1237 // message which initially we want to ignore, because the user
1238 // hasn't selected anything
1240 // this flag gets reset in the message handler. Every subsequent
1241 // message gets handled.
1245 // we have to make sure that we only do this once. Typically,
1246 // we would do this in something like OnInitDialog, but we don't
1247 // have this in Wizards, because the window is a PropertySheet.
1248 // We're considered fully initialized when we receive the first
1251 header
.LoadString(IDS_MANUFACTURER_HEADING
);
1252 m_manufacturerListCtrl
.InsertColumn(0, header
, LVCFMT_LEFT
, -1 );
1253 m_manufacturerSelected
= NULL
;
1255 header
.LoadString(IDS_MODEL_HEADING
);
1256 m_modelListCtrl
.InsertColumn(0, header
, LVCFMT_LEFT
, -1 );
1257 m_modelSelected
= NULL
;
1262 void CThirdPage::DoDataExchange(CDataExchange
* pDX
)
1264 CPropertyPage::DoDataExchange(pDX
);
1265 DDX_Control(pDX
, IDC_PRINTER_MANUFACTURER
, m_manufacturerListCtrl
);
1266 DDX_Control(pDX
, IDC_PRINTER_MODEL
, m_modelListCtrl
);
1267 DDX_Control(pDX
, IDC_PRINTER_NAME
, m_printerName
);
1268 DDX_Control(pDX
, IDC_DEFAULT_PRINTER
, m_defaultPrinterCtrl
);
1269 DDX_Control(pDX
, IDC_PRINTER_SELECTION_TEXT
, m_printerSelectionText
);
1273 // ----------------------------------------------------------
1276 // This function is called by MFC after the window has been
1281 CThirdPage::OnSetActive()
1283 CPrinterSetupWizardSheet
* psheet
;
1287 psheet
= reinterpret_cast<CPrinterSetupWizardSheet
*>(GetParent());
1288 require_quiet( psheet
, exit
);
1290 psheet
->SetWizardButtons( PSWIZB_BACK
);
1292 printer
= psheet
->GetSelectedPrinter();
1293 require_quiet( printer
, exit
);
1295 service
= printer
->services
.front();
1296 require_quiet( service
, exit
);
1299 // call OnInitPage once
1304 m_initialized
= true;
1308 // <rdar://problem/4580061> mDNS: Printers added using Bonjour should be set as the default printer.
1310 if ( DefaultPrinterExists() )
1312 m_defaultPrinterCtrl
.SetCheck( BST_UNCHECKED
);
1313 printer
->deflt
= false;
1317 m_defaultPrinterCtrl
.SetCheck( BST_CHECKED
);
1318 printer
->deflt
= true;
1322 // update the UI with the printer name
1324 m_printerName
.SetWindowText(printer
->displayName
);
1327 // populate the list controls with the manufacturers and models
1330 PopulateUI( m_manufacturers
);
1333 // and try and match the printer
1336 if ( psheet
->GetLastPage() == psheet
->GetPage(0) )
1338 MatchPrinter( m_manufacturers
, printer
, service
, true );
1340 if ( ( m_manufacturerSelected
!= NULL
) && ( m_modelSelected
!= NULL
) )
1342 GetParent()->PostMessage(PSM_SETCURSEL
, 2 );
1347 SelectMatch(printer
, service
, m_manufacturerSelected
, m_modelSelected
);
1352 return CPropertyPage::OnSetActive();
1356 CThirdPage::OnKillActive()
1358 CPrinterSetupWizardSheet
* psheet
;
1360 psheet
= reinterpret_cast<CPrinterSetupWizardSheet
*>(GetParent());
1361 require_quiet( psheet
, exit
);
1363 psheet
->SetLastPage(this);
1367 return CPropertyPage::OnKillActive();
1370 // -------------------------------------------------------
1373 // This function is called to populate the list of manufacturers
1376 CThirdPage::PopulateUI(Manufacturers
& manufacturers
)
1378 Manufacturers::iterator iter
;
1380 m_manufacturerListCtrl
.DeleteAllItems();
1382 for (iter
= manufacturers
.begin(); iter
!= manufacturers
.end(); iter
++)
1386 Manufacturer
* manufacturer
= iter
->second
;
1388 nIndex
= m_manufacturerListCtrl
.InsertItem(0, manufacturer
->name
);
1390 m_manufacturerListCtrl
.SetItemData(nIndex
, (DWORD_PTR
) manufacturer
);
1392 m_manufacturerListCtrl
.SetColumnWidth( 0, LVSCW_AUTOSIZE_USEHEADER
);
1398 BEGIN_MESSAGE_MAP(CThirdPage
, CPropertyPage
)
1399 ON_NOTIFY(LVN_ITEMCHANGED
, IDC_PRINTER_MANUFACTURER
, OnLvnItemchangedManufacturer
)
1400 ON_NOTIFY(LVN_ITEMCHANGED
, IDC_PRINTER_MODEL
, OnLvnItemchangedPrinterModel
)
1401 ON_BN_CLICKED(IDC_DEFAULT_PRINTER
, OnBnClickedDefaultPrinter
)
1402 ON_BN_CLICKED(IDC_HAVE_DISK
, OnBnClickedHaveDisk
)
1405 // CThirdPage message handlers
1406 void CThirdPage::OnLvnItemchangedManufacturer(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1408 LPNMLISTVIEW pNMLV
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
1410 POSITION p
= m_manufacturerListCtrl
.GetFirstSelectedItemPosition();
1411 int nSelected
= m_manufacturerListCtrl
.GetNextSelectedItem(p
);
1413 if (nSelected
!= -1)
1415 m_manufacturerSelected
= (Manufacturer
*) m_manufacturerListCtrl
.GetItemData(nSelected
);
1417 m_modelListCtrl
.SetRedraw(FALSE
);
1419 m_modelListCtrl
.DeleteAllItems();
1420 m_modelSelected
= NULL
;
1422 Models::iterator iter
;
1424 for (iter
= m_manufacturerSelected
->models
.begin(); iter
!= m_manufacturerSelected
->models
.end(); iter
++)
1426 Model
* model
= *iter
;
1428 int nItem
= m_modelListCtrl
.InsertItem( 0, model
->displayName
);
1430 m_modelListCtrl
.SetItemData(nItem
, (DWORD_PTR
) model
);
1432 m_modelListCtrl
.SetColumnWidth( 0, LVSCW_AUTOSIZE_USEHEADER
);
1435 m_modelListCtrl
.SetRedraw(TRUE
);
1441 void CThirdPage::OnLvnItemchangedPrinterModel(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1443 LPNMLISTVIEW pNMLV
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
1445 CPrinterSetupWizardSheet
* psheet
;
1449 psheet
= reinterpret_cast<CPrinterSetupWizardSheet
*>(GetParent());
1450 require_quiet( psheet
, exit
);
1452 printer
= psheet
->GetSelectedPrinter();
1453 require_quiet( printer
, exit
);
1455 service
= printer
->services
.front();
1456 require_quiet( service
, exit
);
1458 check ( m_manufacturerSelected
);
1460 POSITION p
= m_modelListCtrl
.GetFirstSelectedItemPosition();
1461 int nSelected
= m_modelListCtrl
.GetNextSelectedItem(p
);
1463 if (nSelected
!= -1)
1465 m_modelSelected
= (Model
*) m_modelListCtrl
.GetItemData(nSelected
);
1467 CopyPrinterSettings( printer
, service
, m_manufacturerSelected
, m_modelSelected
);
1469 psheet
->SetWizardButtons(PSWIZB_BACK
|PSWIZB_NEXT
);
1473 psheet
->SetWizardButtons(PSWIZB_BACK
);
1481 void CThirdPage::OnBnClickedDefaultPrinter()
1483 CPrinterSetupWizardSheet
* psheet
;
1486 psheet
= reinterpret_cast<CPrinterSetupWizardSheet
*>(GetParent());
1487 require_quiet( psheet
, exit
);
1489 printer
= psheet
->GetSelectedPrinter();
1490 require_quiet( printer
, exit
);
1492 printer
->deflt
= ( m_defaultPrinterCtrl
.GetCheck() == BST_CHECKED
) ? true : false;
1499 void CThirdPage::OnBnClickedHaveDisk()
1501 CPrinterSetupWizardSheet
* psheet
;
1504 Manufacturers manufacturers
;
1506 CFileDialog
dlg(TRUE
, NULL
, NULL
, OFN_HIDEREADONLY
|OFN_FILEMUSTEXIST
, L
"Setup Information (*.inf)|*.inf||", this);
1508 psheet
= reinterpret_cast<CPrinterSetupWizardSheet
*>(GetParent());
1509 require_quiet( psheet
, exit
);
1511 printer
= psheet
->GetSelectedPrinter();
1512 require_quiet( printer
, exit
);
1514 service
= printer
->services
.front();
1515 require_quiet( service
, exit
);
1519 if ( dlg
.DoModal() == IDOK
)
1521 CString filename
= dlg
.GetPathName();
1523 LoadPrintDriverDefsFromFile( manufacturers
, filename
, true );
1527 if ( manufacturers
.size() > 0 )
1529 PopulateUI( manufacturers
);
1531 if ( MatchPrinter( manufacturers
, printer
, service
, false ) != kNoErr
)
1533 CString errorMessage
;
1534 CString errorCaption
;
1536 errorMessage
.LoadString( IDS_NO_MATCH_INF_FILE
);
1537 errorCaption
.LoadString( IDS_NO_MATCH_INF_FILE_CAPTION
);
1539 MessageBox( errorMessage
, errorCaption
, MB_OK
);
1546 CString errorMessage
;
1547 CString errorCaption
;
1549 errorMessage
.LoadString( IDS_BAD_INF_FILE
);
1550 errorCaption
.LoadString( IDS_BAD_INF_FILE_CAPTION
);
1552 MessageBox( errorMessage
, errorCaption
, MB_OK
);
1563 FreeManufacturers( manufacturers
);
1569 CThirdPage::Split( const CString
& string
, TCHAR ch
, CStringList
& components
)
1576 while ( ( n
= temp
.Find( ch
) ) != -1 )
1578 components
.AddTail( temp
.Left( n
) );
1579 temp
= temp
.Right( temp
.GetLength() - ( n
+ 1 ) );
1582 components
.AddTail( temp
);