]> git.saurik.com Git - apple/mdnsresponder.git/blob - Clients/PrinterSetupWizard/ThirdPage.cpp
mDNSResponder-214.tar.gz
[apple/mdnsresponder.git] / Clients / PrinterSetupWizard / ThirdPage.cpp
1 /* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved.
4 *
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
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
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.
16
17 Change History (most recent first):
18
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
22
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")
25
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
28
29 Revision 1.39 2009/05/27 06:25:49 herscher
30 <rdar://problem/4176334> Need error dialog when selecting bad INF file
31
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
35
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
38
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
41
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
45
46 Revision 1.34 2007/06/06 19:53:48 cheshire
47 <rdar://problem/5187308> Move build train to Visual Studio 2005
48
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
51
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.
54
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
57
58 Revision 1.30 2007/04/13 20:23:40 herscher
59 Fixed mistake in previous checkin that reverted license text for this file
60
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
63
64 Revision 1.28 2006/08/14 23:24:09 cheshire
65 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
66
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
69
70 Revision 1.26 2005/07/11 20:17:15 shersche
71 <rdar://problem/4124524> UI fixes associated with CUPS printer workaround fix.
72
73 Revision 1.25 2005/07/07 17:53:20 shersche
74 Fix problems associated with the CUPS printer workaround fix.
75
76 Revision 1.24 2005/06/30 18:02:54 shersche
77 <rdar://problem/4124524> Workaround for Mac OS X Printer Sharing bug
78
79 Revision 1.23 2005/04/18 02:33:47 shersche
80 <rdar://problem/4091216> Default printer option cannot be deselected
81
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
84
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
87
88 Revision 1.20 2005/03/05 02:27:45 shersche
89 <rdar://problem/4030388> Generic drivers don't do color
90
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".
93
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
96
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
99
100 Revision 1.16 2005/02/08 18:56:03 shersche
101 Fix generated IPP url so that it doesn't add "/printers" string
102
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.
105
106 Revision 1.14 2005/01/25 08:55:54 shersche
107 <rdar://problem/3911084> Load icons at run-time from resource DLL
108 Bug #: 3911084
109
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
112
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.
115 Bug #: 3841218
116
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
121
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.
124 Bug #: 3827624
125
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
128
129 Revision 1.8 2004/06/27 08:06:45 shersche
130 Parse [Strings] section of inf file
131
132 Revision 1.7 2004/06/26 04:00:05 shersche
133 fix warnings compiling in debug mode
134 Submitted by: herscher
135
136 Revision 1.6 2004/06/26 03:19:57 shersche
137 clean up warning messages
138
139 Submitted by: herscher
140
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
144
145 Revision 1.4 2004/06/25 02:44:13 shersche
146 Tweaked code to handle Xerox Phaser printer identification
147 Submitted by: herscher
148
149 Revision 1.3 2004/06/25 02:27:58 shersche
150 Do a CListCtrl::FindItem() before calling CListCtrl::SetItemState().
151 Submitted by: herscher
152
153 Revision 1.2 2004/06/23 18:09:23 shersche
154 Normalize tag names when parsing inf files.
155 Submitted by: herscher
156
157 Revision 1.1 2004/06/18 04:36:58 rpantos
158 First checked in
159 */
160
161 #include "stdafx.h"
162 #include "PrinterSetupWizardApp.h"
163 #include "PrinterSetupWizardSheet.h"
164 #include "ThirdPage.h"
165 #include "StdioFileEx.h"
166 #include <dns_sd.h>
167 #include <tcpxcv.h>
168 #include <winspool.h>
169 #include <setupapi.h>
170
171 // local variable is initialize but not referenced
172 #pragma warning(disable:4189)
173
174 //
175 // This is the printer description file that is shipped
176 // with Windows XP and below
177 //
178 #define kNTPrintFile L"inf\\ntprint.inf"
179
180 //
181 // Windows Vista ships with a set of prn*.inf files
182 //
183 #define kVistaPrintFiles L"inf\\prn*.inf"
184
185 //
186 // These are pre-defined names for Generic manufacturer and model
187 //
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"
198
199
200 // CThirdPage dialog
201
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 )
211 {
212 static const int bufferSize = 32768;
213 TCHAR windowsDirectory[bufferSize];
214 CString header;
215 WIN32_FIND_DATA findFileData;
216 HANDLE findHandle;
217 CString prnFiles;
218 CString ntPrint;
219 OSStatus err;
220 BOOL ok;
221
222 m_psp.dwFlags &= ~(PSP_HASHELP);
223 m_psp.dwFlags |= PSP_DEFAULT|PSP_USEHEADERTITLE|PSP_USEHEADERSUBTITLE;
224
225 m_psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_INSTALL_TITLE);
226 m_psp.pszHeaderSubTitle = MAKEINTRESOURCE(IDS_INSTALL_SUBTITLE);
227
228 //
229 // load printers from ntprint.inf
230 //
231 ok = GetWindowsDirectory( windowsDirectory, bufferSize );
232 err = translate_errno( ok, errno_compat(), kUnknownErr );
233 require_noerr( err, exit );
234
235 //
236 // <rdar://problem/4826126>
237 //
238 // If there are no *prn.inf files, we'll assume that the information
239 // is in ntprint.inf
240 //
241 prnFiles.Format( L"%s\\%s", windowsDirectory, kVistaPrintFiles );
242 findHandle = FindFirstFile( prnFiles, &findFileData );
243
244 if ( findHandle != INVALID_HANDLE_VALUE )
245 {
246 CString absolute;
247
248 absolute.Format( L"%s\\inf\\%s", windowsDirectory, findFileData.cFileName );
249 err = LoadPrintDriverDefsFromFile( m_manufacturers, absolute, false );
250 require_noerr( err, exit );
251
252 while ( FindNextFile( findHandle, &findFileData ) )
253 {
254 absolute.Format( L"%s\\inf\\%s", windowsDirectory, findFileData.cFileName );
255 err = LoadPrintDriverDefsFromFile( m_manufacturers, absolute, false );
256 require_noerr( err, exit );
257 }
258
259 FindClose( findHandle );
260 }
261 else
262 {
263 ntPrint.Format(L"%s\\%s", windowsDirectory, kNTPrintFile);
264 err = LoadPrintDriverDefsFromFile( m_manufacturers, ntPrint, false );
265 require_noerr(err, exit);
266 }
267
268 //
269 // load printer drivers that have been installed on this machine
270 //
271 err = LoadPrintDriverDefs( m_manufacturers );
272 require_noerr(err, exit);
273
274 //
275 // load our own special generic printer defs
276 //
277 err = LoadGenericPrintDriverDefs( m_manufacturers );
278 require_noerr( err, exit );
279
280 exit:
281
282 return;
283 }
284
285 CThirdPage::~CThirdPage()
286 {
287 //
288 // clean up all the printer manufacturers
289 //
290 while (m_manufacturers.size())
291 {
292 Manufacturers::iterator iter = m_manufacturers.begin();
293
294 while (iter->second->models.size())
295 {
296 Models::iterator it = iter->second->models.begin();
297
298 Model * model = *it;
299
300 delete model;
301
302 iter->second->models.erase(it);
303 }
304
305 delete iter->second;
306
307 m_manufacturers.erase(iter);
308 }
309 }
310
311 // ----------------------------------------------------
312 // SelectMatch
313 //
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
317 // latest settings
318 //
319 // ----------------------------------------------------
320 void
321 CThirdPage::SelectMatch(Printer * printer, Service * service, Manufacturer * manufacturer, Model * model)
322 {
323 LVFINDINFO info;
324 int nIndex;
325
326 check( printer != NULL );
327 check( manufacturer != NULL );
328 check( model != NULL );
329
330 //
331 // select the manufacturer
332 //
333 info.flags = LVFI_STRING;
334 info.psz = manufacturer->name;
335
336 nIndex = m_manufacturerListCtrl.FindItem(&info);
337
338 if (nIndex != -1)
339 {
340 m_manufacturerListCtrl.SetItemState(nIndex, LVIS_SELECTED, LVIS_SELECTED);
341 //
342 //<rdar://problem/4528853> mDNS: When auto-highlighting items in lists, scroll list so highlighted item is in the middle
343 //
344 AutoScroll(m_manufacturerListCtrl, nIndex);
345 }
346
347 //
348 // select the model
349 //
350 info.flags = LVFI_STRING;
351 info.psz = model->displayName;
352
353 nIndex = m_modelListCtrl.FindItem(&info);
354
355 if (nIndex != -1)
356 {
357 m_modelListCtrl.SetItemState(nIndex, LVIS_SELECTED, LVIS_SELECTED);
358 AutoScroll( m_modelListCtrl, nIndex );
359
360 m_modelListCtrl.SetFocus();
361 }
362
363 CopyPrinterSettings( printer, service, manufacturer, model );
364 }
365
366 void
367 CThirdPage::SelectMatch(Manufacturers & manufacturers, Printer * printer, Service * service, Manufacturer * manufacturer, Model * model)
368 {
369 PopulateUI( manufacturers );
370
371 SelectMatch( printer, service, manufacturer, model );
372 }
373
374 // --------------------------------------------------------
375 // CopyPrinterSettings
376 //
377 // This function makes sure that the printer object has the
378 // latest settings from the manufacturer and model objects
379 // --------------------------------------------------------
380
381 void
382 CThirdPage::CopyPrinterSettings( Printer * printer, Service * service, Manufacturer * manufacturer, Model * model )
383 {
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;
389
390 if ( service->type == kPDLServiceType )
391 {
392 printer->portName.Format(L"IP_%s.%d", static_cast<LPCTSTR>(service->hostname), service->portNumber);
393 service->protocol = L"Raw";
394 }
395 else if ( service->type == kLPRServiceType )
396 {
397 Queue * q = service->queues.front();
398 check( q );
399
400 if ( q->name.GetLength() > 0 )
401 {
402 printer->portName.Format(L"LPR_%s.%d.%s", static_cast<LPCTSTR>(service->hostname), service->portNumber, static_cast<LPCTSTR>(q->name) );
403 }
404 else
405 {
406 printer->portName.Format(L"LPR_%s.%d", static_cast<LPCTSTR>(service->hostname), service->portNumber);
407 }
408
409 service->protocol = L"LPR";
410 }
411 else if ( service->type == kIPPServiceType )
412 {
413 Queue * q = service->queues.front();
414 check( q );
415
416 if ( q->name.GetLength() > 0 )
417 {
418 printer->portName.Format(L"http://%s:%d/%s", static_cast<LPCTSTR>(service->hostname), service->portNumber, static_cast<LPCTSTR>(q->name) );
419 }
420 else
421 {
422 printer->portName.Format(L"http://%s:%d/", static_cast<LPCTSTR>(service->hostname), service->portNumber );
423 }
424
425 service->protocol = L"IPP";
426 }
427 }
428
429 // --------------------------------------------------------
430 // DefaultPrinterExists
431 //
432 // Checks to see if a default printer has been configured
433 // on this machine
434 // --------------------------------------------------------
435 BOOL
436 CThirdPage::DefaultPrinterExists()
437 {
438 CPrintDialog dlg(FALSE);
439
440 dlg.m_pd.Flags |= PD_RETURNDEFAULT;
441
442 return dlg.GetDefaults();
443 }
444
445 // --------------------------------------------------------
446 // AutoScroll
447 //
448 // Ensure selected item is in middle of list
449 // --------------------------------------------------------
450 void
451 CThirdPage::AutoScroll( CListCtrl & list, int nIndex )
452 {
453 //
454 //<rdar://problem/4528853> mDNS: When auto-highlighting items in lists, scroll list so highlighted item is in the middle
455 //
456
457 int top;
458 int count;
459
460 list.EnsureVisible( nIndex, FALSE );
461
462 top = list.GetTopIndex();
463 count = list.GetCountPerPage();
464
465 if ( ( nIndex == top ) || ( ( nIndex + 1 ) == ( top + count ) ) )
466 {
467 CRect rect;
468 int rows;
469
470 rows = ( count / 2 );
471
472 if ( nIndex == top )
473 {
474 list.GetItemRect(0, rect, LVIR_BOUNDS);
475 list.Scroll( CPoint( 0, rows * rect.Height() * -1 ) );
476 }
477 else
478 {
479 list.GetItemRect(0, rect, LVIR_BOUNDS);
480 list.Scroll( CPoint( 0, rows * rect.Height() ) );
481 }
482 }
483 }
484
485 // ------------------------------------------------------
486 // LoadPrintDriverDefsFromFile
487 //
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.
496
497 OSStatus
498 CThirdPage::LoadPrintDriverDefsFromFile(Manufacturers & manufacturers, const CString & filename, bool checkForDuplicateModels )
499 {
500 HINF handle = INVALID_HANDLE_VALUE;
501 const TCHAR * section = TEXT( "Manufacturer" );
502 LONG sectionCount;
503 TCHAR line[ 1000 ];
504 CString klass;
505 INFCONTEXT manufacturerContext;
506 BOOL ok;
507 OSStatus err = 0;
508
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 );
513
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 );
518 klass = line;
519 require_action( klass == TEXT( "Printer" ), exit, err = kUnknownErr );
520
521 sectionCount = SetupGetLineCount( handle, section );
522 translate_errno( sectionCount != -1, GetLastError(), kUnknownErr );
523 require_noerr( err, exit );
524
525 memset( &manufacturerContext, 0, sizeof( manufacturerContext ) );
526
527 for ( LONG i = 0; i < sectionCount; i++ )
528 {
529 Manufacturers::iterator iter;
530 Manufacturer * manufacturer;
531 CString manufacturerName;
532 CString temp;
533 CStringList modelSectionNameDecl;
534 CString modelSectionName;
535 CString baseModelName;
536 CString model;
537 INFCONTEXT modelContext;
538 LONG modelCount;
539 POSITION p;
540
541 if ( i == 0 )
542 {
543 ok = SetupFindFirstLine( handle, section, NULL, &manufacturerContext );
544 err = translate_errno( ok, GetLastError(), kUnknownErr );
545 require_noerr( err, exit );
546 }
547 else
548 {
549 ok = SetupFindNextLine( &manufacturerContext, &manufacturerContext );
550 err = translate_errno( ok, GetLastError(), kUnknownErr );
551 require_noerr( err, exit );
552 }
553
554 ok = SetupGetStringField( &manufacturerContext, 0, line, sizeof( line ), NULL );
555 err = translate_errno( ok, GetLastError(), kUnknownErr );
556 require_noerr( err, exit );
557 manufacturerName = line;
558
559 ok = SetupGetLineText( &manufacturerContext, handle, NULL, NULL, line, sizeof( line ), NULL );
560 err = translate_errno( ok, GetLastError(), kUnknownErr );
561 require_noerr( err, exit );
562
563 // Try to find some model section name that has entries. Explanation of int file structure
564 // can be found at:
565 //
566 // <http://msdn.microsoft.com/en-us/library/ms794359.aspx>
567 Split( line, ',', modelSectionNameDecl );
568
569 p = modelSectionNameDecl.GetHeadPosition();
570 modelSectionName = modelSectionNameDecl.GetNext( p );
571 modelCount = SetupGetLineCount( handle, modelSectionName );
572 baseModelName = modelSectionName;
573
574 while ( modelCount <= 0 && p )
575 {
576 CString targetOSVersion;
577
578 targetOSVersion = modelSectionNameDecl.GetNext( p );
579 modelSectionName = baseModelName + TEXT( "." ) + targetOSVersion;
580 modelCount = SetupGetLineCount( handle, modelSectionName );
581 }
582
583 if ( modelCount > 0 )
584 {
585 manufacturerName = NormalizeManufacturerName( manufacturerName );
586
587 iter = manufacturers.find( manufacturerName );
588
589 if ( iter != manufacturers.end() )
590 {
591 manufacturer = iter->second;
592 require_action( manufacturer, exit, err = kUnknownErr );
593 }
594 else
595 {
596 try
597 {
598 manufacturer = new Manufacturer;
599 }
600 catch (...)
601 {
602 manufacturer = NULL;
603 }
604
605 require_action( manufacturer, exit, err = kNoMemoryErr );
606
607 manufacturer->name = manufacturerName;
608 manufacturers[ manufacturerName ] = manufacturer;
609 }
610
611 memset( &modelContext, 0, sizeof( modelContext ) );
612
613 for ( LONG j = 0; j < modelCount; j++ )
614 {
615 CString modelName;
616 Model * model;
617
618 if ( j == 0 )
619 {
620 ok = SetupFindFirstLine( handle, modelSectionName, NULL, &modelContext );
621 err = translate_errno( ok, GetLastError(), kUnknownErr );
622 require_noerr( err, exit );
623 }
624 else
625 {
626 SetupFindNextLine( &modelContext, &modelContext );
627 err = translate_errno( ok, GetLastError(), kUnknownErr );
628 require_noerr( err, exit );
629 }
630
631 ok = SetupGetStringField( &modelContext, 0, line, sizeof( line ), NULL );
632 err = translate_errno( ok, GetLastError(), kUnknownErr );
633 require_noerr( err, exit );
634
635 modelName = line;
636
637 if (checkForDuplicateModels == true)
638 {
639 if ( MatchModel( manufacturer, ConvertToModelName( modelName ) ) != NULL )
640 {
641 continue;
642 }
643 }
644
645 //
646 // Stock Vista printer inf files embed guids in the model
647 // declarations for Epson printers. Let's ignore those.
648 //
649 if ( modelName.Find( TEXT( "{" ), 0 ) != -1 )
650 {
651 continue;
652 }
653
654 try
655 {
656 model = new Model;
657 }
658 catch (...)
659 {
660 model = NULL;
661 }
662
663 require_action( model, exit, err = kNoMemoryErr );
664
665 model->infFileName = filename;
666 model->displayName = modelName;
667 model->name = modelName;
668 model->driverInstalled = false;
669
670 manufacturer->models.push_back(model);
671 }
672 }
673 }
674
675 exit:
676
677 if ( handle != INVALID_HANDLE_VALUE )
678 {
679 SetupCloseInfFile( handle );
680 handle = NULL;
681 }
682
683 return err;
684 }
685
686
687 // -------------------------------------------------------
688 // LoadPrintDriverDefs
689 //
690 // This function is responsible for loading the print driver
691 // definitions of all print drivers that have been installed
692 // on this machine.
693 // -------------------------------------------------------
694 OSStatus
695 CThirdPage::LoadPrintDriverDefs( Manufacturers & manufacturers )
696 {
697 BYTE * buffer = NULL;
698 DWORD bytesReceived = 0;
699 DWORD numPrinters = 0;
700 OSStatus err = 0;
701 BOOL ok;
702
703 //
704 // like a lot of win32 calls, we call this first to get the
705 // size of the buffer we need.
706 //
707 EnumPrinterDrivers(NULL, L"all", 6, NULL, 0, &bytesReceived, &numPrinters);
708
709 if (bytesReceived > 0)
710 {
711 try
712 {
713 buffer = new BYTE[bytesReceived];
714 }
715 catch (...)
716 {
717 buffer = NULL;
718 }
719
720 require_action( buffer, exit, err = kNoMemoryErr );
721
722 //
723 // this call gets the real info
724 //
725 ok = EnumPrinterDrivers(NULL, L"all", 6, buffer, bytesReceived, &bytesReceived, &numPrinters);
726 err = translate_errno( ok, errno_compat(), kUnknownErr );
727 require_noerr( err, exit );
728
729 DRIVER_INFO_6 * info = (DRIVER_INFO_6*) buffer;
730
731 for (DWORD i = 0; i < numPrinters; i++)
732 {
733 Manufacturer * manufacturer;
734 Model * model;
735 CString name;
736
737 //
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.
742 //
743 if (info[i].pszMfgName == NULL)
744 {
745 continue;
746 }
747
748 //
749 // look for manufacturer
750 //
751 Manufacturers::iterator iter;
752
753 //
754 // save the name
755 //
756 name = NormalizeManufacturerName( info[i].pszMfgName );
757
758 iter = manufacturers.find(name);
759
760 if (iter != manufacturers.end())
761 {
762 manufacturer = iter->second;
763 }
764 else
765 {
766 try
767 {
768 manufacturer = new Manufacturer;
769 }
770 catch (...)
771 {
772 manufacturer = NULL;
773 }
774
775 require_action( manufacturer, exit, err = kNoMemoryErr );
776
777 manufacturer->name = name;
778
779 manufacturers[name] = manufacturer;
780 }
781
782 //
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
787 //
788 //
789 if ( MatchModel( manufacturer, ConvertToModelName( info[i].pName ) ) == NULL )
790 {
791 try
792 {
793 model = new Model;
794 }
795 catch (...)
796 {
797 model = NULL;
798 }
799
800 require_action( model, exit, err = kNoMemoryErr );
801
802 model->displayName = info[i].pName;
803 model->name = info[i].pName;
804 model->driverInstalled = true;
805
806 manufacturer->models.push_back(model);
807 }
808 }
809 }
810
811 exit:
812
813 if (buffer != NULL)
814 {
815 delete [] buffer;
816 }
817
818 return err;
819 }
820
821 // -------------------------------------------------------
822 // LoadGenericPrintDriverDefs
823 //
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 // -------------------------------------------------------
829 OSStatus
830 CThirdPage::LoadGenericPrintDriverDefs( Manufacturers & manufacturers )
831 {
832 Manufacturer * manufacturer;
833 Model * model;
834 Manufacturers::iterator iter;
835 CString psDriverName;
836 CString pclDriverName;
837 OSStatus err = 0;
838
839 // <rdar://problem/4030388> Generic drivers don't do color
840
841 // First try and find our generic driver names
842
843 iter = m_manufacturers.find(L"HP");
844 require_action( iter != m_manufacturers.end(), exit, err = kUnknownErr );
845 manufacturer = iter->second;
846
847 // Look for Postscript
848
849 model = manufacturer->find( kGenericPSColorDriver );
850
851 if ( !model )
852 {
853 model = manufacturer->find( kGenericPSDriver );
854 }
855
856 if ( model )
857 {
858 psDriverName = model->name;
859 }
860
861 // Look for PCL
862
863 model = manufacturer->find( kGenericPCLColorDriver );
864
865 if ( !model )
866 {
867 model = manufacturer->find( kGenericPCLDriver );
868 }
869
870 if ( model )
871 {
872 pclDriverName = model->name;
873 }
874
875 // If we found either a generic PS driver, or a generic PCL driver,
876 // then add them to the list
877
878 if ( psDriverName.GetLength() || pclDriverName.GetLength() )
879 {
880 // Try and find generic manufacturer if there is one
881
882 iter = manufacturers.find(L"Generic");
883
884 if (iter != manufacturers.end())
885 {
886 manufacturer = iter->second;
887 }
888 else
889 {
890 try
891 {
892 manufacturer = new Manufacturer;
893 }
894 catch (...)
895 {
896 manufacturer = NULL;
897 }
898
899 require_action( manufacturer, exit, err = kNoMemoryErr );
900
901 manufacturer->name = "Generic";
902 manufacturers[manufacturer->name] = manufacturer;
903 }
904
905 if ( psDriverName.GetLength() > 0 )
906 {
907 try
908 {
909 m_genericPostscript = new Model;
910 }
911 catch (...)
912 {
913 m_genericPostscript = NULL;
914 }
915
916 require_action( m_genericPostscript, exit, err = kNoMemoryErr );
917
918 m_genericPostscript->displayName = kGenericPostscript;
919 m_genericPostscript->name = psDriverName;
920 m_genericPostscript->driverInstalled = false;
921
922 manufacturer->models.push_back( m_genericPostscript );
923 }
924
925 if ( pclDriverName.GetLength() > 0 )
926 {
927 try
928 {
929 m_genericPCL = new Model;
930 }
931 catch (...)
932 {
933 m_genericPCL = NULL;
934 }
935
936 require_action( m_genericPCL, exit, err = kNoMemoryErr );
937
938 m_genericPCL->displayName = kGenericPCL;
939 m_genericPCL->name = pclDriverName;
940 m_genericPCL->driverInstalled = false;
941
942 manufacturer->models.push_back( m_genericPCL );
943 }
944 }
945
946 exit:
947
948 return err;
949 }
950
951 // ------------------------------------------------------
952 // ConvertToManufacturerName
953 //
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)
958 //
959 CString
960 CThirdPage::ConvertToManufacturerName( const CString & name )
961 {
962 //
963 // first we're going to convert all the characters to lower
964 // case
965 //
966 CString lower = name;
967 lower.MakeLower();
968
969 //
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".
973 //
974 if ( lower == L"hewlett-packard")
975 {
976 lower = "hp";
977 }
978
979 //
980 // tweak for Xerox Phaser, which doesn't announce itself
981 // as a xerox
982 //
983 else if ( lower.Find( L"phaser", 0 ) != -1 )
984 {
985 lower = "xerox";
986 }
987
988 return lower;
989 }
990
991 // ------------------------------------------------------
992 // ConvertToModelName
993 //
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 // ------------------------------------------------------
998
999 CString
1000 CThirdPage::ConvertToModelName( const CString & name )
1001 {
1002 //
1003 // convert it to lowercase
1004 //
1005 CString lower = name;
1006 lower.MakeLower();
1007
1008 return lower;
1009 }
1010
1011 // ------------------------------------------------------
1012 // NormalizeManufacturerName
1013 //
1014 // This function is responsible for tweaking the manufacturer
1015 // name so that there are no aliases for vendors
1016 //
1017 CString
1018 CThirdPage::NormalizeManufacturerName( const CString & name )
1019 {
1020 CString normalized = name;
1021
1022 //
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".
1026 //
1027 if ( normalized == L"Hewlett-Packard")
1028 {
1029 normalized = "HP";
1030 }
1031
1032 return normalized;
1033 }
1034
1035 // -------------------------------------------------------
1036 // MatchPrinter
1037 //
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.
1041 //
1042
1043 OSStatus CThirdPage::MatchPrinter(Manufacturers & manufacturers, Printer * printer, Service * service, bool useCUPSWorkaround)
1044 {
1045 CString normalizedProductName;
1046 Manufacturer * manufacturer = NULL;
1047 Manufacturer * genericManufacturer = NULL;
1048 Model * model = NULL;
1049 Model * genericModel = NULL;
1050 bool found = false;
1051 CString text;
1052 OSStatus err = kNoErr;
1053
1054 check( printer );
1055 check( service );
1056
1057 Queue * q = service->SelectedQueue();
1058
1059 check( q );
1060
1061 //
1062 // first look to see if we have a usb_MFG descriptor
1063 //
1064 if ( q->usb_MFG.GetLength() > 0)
1065 {
1066 manufacturer = MatchManufacturer( manufacturers, ConvertToManufacturerName ( q->usb_MFG ) );
1067 }
1068
1069 if ( manufacturer == NULL )
1070 {
1071 q->product.Remove('(');
1072 q->product.Remove(')');
1073
1074 manufacturer = MatchManufacturer( manufacturers, ConvertToManufacturerName ( q->product ) );
1075 }
1076
1077 //
1078 // if we found the manufacturer, then start looking for the model
1079 //
1080 if ( manufacturer != NULL )
1081 {
1082 if ( q->usb_MDL.GetLength() > 0 )
1083 {
1084 model = MatchModel ( manufacturer, ConvertToModelName ( q->usb_MDL ) );
1085 }
1086
1087 if ( ( model == NULL ) && ( q->product.GetLength() > 0 ) )
1088 {
1089 q->product.Remove('(');
1090 q->product.Remove(')');
1091
1092 model = MatchModel ( manufacturer, ConvertToModelName ( q->product ) );
1093 }
1094
1095 if ( model != NULL )
1096 {
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.
1099
1100 bool hasGenericDriver = false;
1101
1102 if ( MatchGeneric( manufacturers, printer, service, &genericManufacturer, &genericModel ) )
1103 {
1104 hasGenericDriver = true;
1105 }
1106
1107 // <rdar://problem/4190104> Use "application/octet-stream" to determine if CUPS
1108 // shared queue supports raw
1109
1110 if ( q->pdl.Find( L"application/octet-stream" ) != -1 )
1111 {
1112 useCUPSWorkaround = false;
1113 }
1114
1115 if ( useCUPSWorkaround && printer->isSharedFromOSX && hasGenericDriver )
1116 {
1117 //
1118 // <rdar://problem/4496652> mDNS: Don't allow user to choose non-working driver
1119 //
1120 Manufacturers genericManufacturers;
1121
1122 LoadGenericPrintDriverDefs( genericManufacturers );
1123
1124 SelectMatch( genericManufacturers, printer, service, genericManufacturer, genericModel );
1125 }
1126 else
1127 {
1128 SelectMatch(manufacturers, printer, service, manufacturer, model);
1129 }
1130
1131 found = true;
1132 }
1133 }
1134
1135 //
1136 // display a message to the user based on whether we could match
1137 // this printer
1138 //
1139 if (found)
1140 {
1141 text.LoadString(IDS_PRINTER_MATCH_GOOD);
1142 err = kNoErr;
1143 }
1144 else if ( MatchGeneric( manufacturers, printer, service, &genericManufacturer, &genericModel ) )
1145 {
1146 if ( printer->isSharedFromOSX )
1147 {
1148 //
1149 // <rdar://problem/4496652> mDNS: Don't allow user to choose non-working driver
1150 //
1151 Manufacturers genericManufacturers;
1152
1153 LoadGenericPrintDriverDefs( genericManufacturers );
1154
1155 SelectMatch( genericManufacturers, printer, service, genericManufacturer, genericModel );
1156
1157 text.LoadString(IDS_PRINTER_MATCH_GOOD);
1158 }
1159 else
1160 {
1161 SelectMatch( manufacturers, printer, service, genericManufacturer, genericModel );
1162 text.LoadString(IDS_PRINTER_MATCH_MAYBE);
1163 }
1164
1165 err = kNoErr;
1166 }
1167 else
1168 {
1169 text.LoadString(IDS_PRINTER_MATCH_BAD);
1170
1171 //
1172 // if there was any crud in this list from before, get rid of it now
1173 //
1174 m_modelListCtrl.DeleteAllItems();
1175
1176 //
1177 // select the manufacturer if we found one
1178 //
1179 if (manufacturer != NULL)
1180 {
1181 LVFINDINFO info;
1182 int nIndex;
1183
1184 //
1185 // select the manufacturer
1186 //
1187 info.flags = LVFI_STRING;
1188 info.psz = manufacturer->name;
1189
1190 nIndex = m_manufacturerListCtrl.FindItem(&info);
1191
1192 if (nIndex != -1)
1193 {
1194 m_manufacturerListCtrl.SetItemState(nIndex, LVIS_SELECTED, LVIS_SELECTED);
1195
1196 //
1197 //<rdar://problem/4528853> mDNS: When auto-highlighting items in lists, scroll list so highlighted item is in the middle
1198 //
1199 AutoScroll(m_manufacturerListCtrl, nIndex);
1200 }
1201 }
1202
1203 err = kUnknownErr;
1204 }
1205
1206 m_printerSelectionText.SetWindowText(text);
1207
1208 return err;
1209 }
1210
1211 // ------------------------------------------------------
1212 // MatchManufacturer
1213 //
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
1217 //
1218 // If it can't find a match, NULL is returned
1219 // ------------------------------------------------------
1220
1221 Manufacturer*
1222 CThirdPage::MatchManufacturer( Manufacturers & manufacturers, const CString & name)
1223 {
1224 Manufacturers::iterator iter;
1225
1226 for (iter = manufacturers.begin(); iter != manufacturers.end(); iter++)
1227 {
1228 //
1229 // we're going to convert all the manufacturer names to lower case,
1230 // so we match the name passed in.
1231 //
1232 CString lower = iter->second->name;
1233 lower.MakeLower();
1234
1235 //
1236 // now try and find the lowered string in the name passed in.
1237 //
1238 if (name.Find(lower) != -1)
1239 {
1240 return iter->second;
1241 }
1242 }
1243
1244 return NULL;
1245 }
1246
1247 // -------------------------------------------------------
1248 // MatchModel
1249 //
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.
1253 //
1254
1255 Model*
1256 CThirdPage::MatchModel(Manufacturer * manufacturer, const CString & name)
1257 {
1258 Models::iterator iter;
1259
1260 iter = manufacturer->models.begin();
1261
1262 for (iter = manufacturer->models.begin(); iter != manufacturer->models.end(); iter++)
1263 {
1264 Model * model = *iter;
1265
1266 //
1267 // convert the model name to lower case
1268 //
1269 CString lowered = model->name;
1270 lowered.MakeLower();
1271
1272 if (lowered.Find( name ) != -1)
1273 {
1274 return model;
1275 }
1276
1277 //
1278 // <rdar://problem/3841218>
1279 // try removing the first substring and search again
1280 //
1281
1282 if ( name.Find(' ') != -1 )
1283 {
1284 CString altered = name;
1285 altered.Delete( 0, altered.Find(' ') + 1 );
1286
1287 if ( lowered.Find( altered ) != -1 )
1288 {
1289 return model;
1290 }
1291 }
1292 }
1293
1294 return NULL;
1295 }
1296
1297 // -------------------------------------------------------
1298 // MatchGeneric
1299 //
1300 // This function will attempt to find a generic printer
1301 // driver for a printer that we weren't able to match
1302 // specifically
1303 //
1304 BOOL
1305 CThirdPage::MatchGeneric( Manufacturers & manufacturers, Printer * printer, Service * service, Manufacturer ** manufacturer, Model ** model )
1306 {
1307 CString pdl;
1308 BOOL ok = FALSE;
1309
1310 DEBUG_UNUSED( printer );
1311
1312 check( service );
1313
1314 Queue * q = service->SelectedQueue();
1315
1316 check( q );
1317
1318 Manufacturers::iterator iter = manufacturers.find( kGenericManufacturer );
1319 require_action_quiet( iter != manufacturers.end(), exit, ok = FALSE );
1320
1321 *manufacturer = iter->second;
1322
1323 pdl = q->pdl;
1324 pdl.MakeLower();
1325
1326 if ( m_genericPCL && ( pdl.Find( kPDLPCLKey ) != -1 ) )
1327 {
1328 *model = m_genericPCL;
1329 ok = TRUE;
1330 }
1331 else if ( m_genericPostscript && ( pdl.Find( kPDLPostscriptKey ) != -1 ) )
1332 {
1333 *model = m_genericPostscript;
1334 ok = TRUE;
1335 }
1336
1337 exit:
1338
1339 return ok;
1340 }
1341
1342 // -----------------------------------------------------------
1343 // OnInitPage
1344 //
1345 // This function is responsible for doing initialization that
1346 // only occurs once during a run of the wizard
1347 //
1348
1349 OSStatus CThirdPage::OnInitPage()
1350 {
1351 CString header;
1352 CString ntPrint;
1353 OSStatus err = kNoErr;
1354
1355 // Load printer icon
1356 check( m_printerImage == NULL );
1357
1358 m_printerImage = (CStatic*) GetDlgItem( 1 ); // 1 == IDR_MANIFEST
1359 check( m_printerImage );
1360
1361 if ( m_printerImage != NULL )
1362 {
1363 m_printerImage->SetIcon( LoadIcon( GetNonLocalizedResources(), MAKEINTRESOURCE( IDI_PRINTER ) ) );
1364 }
1365
1366 //
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
1370 //
1371 // this flag gets reset in the message handler. Every subsequent
1372 // message gets handled.
1373 //
1374
1375 //
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
1380 // selection notice
1381 //
1382 header.LoadString(IDS_MANUFACTURER_HEADING);
1383 m_manufacturerListCtrl.InsertColumn(0, header, LVCFMT_LEFT, -1 );
1384 m_manufacturerSelected = NULL;
1385
1386 header.LoadString(IDS_MODEL_HEADING);
1387 m_modelListCtrl.InsertColumn(0, header, LVCFMT_LEFT, -1 );
1388 m_modelSelected = NULL;
1389
1390 return (err);
1391 }
1392
1393 void CThirdPage::DoDataExchange(CDataExchange* pDX)
1394 {
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);
1401
1402 }
1403
1404 // ----------------------------------------------------------
1405 // OnSetActive
1406 //
1407 // This function is called by MFC after the window has been
1408 // activated.
1409 //
1410
1411 BOOL
1412 CThirdPage::OnSetActive()
1413 {
1414 CPrinterSetupWizardSheet * psheet;
1415 Printer * printer;
1416 Service * service;
1417
1418 psheet = reinterpret_cast<CPrinterSetupWizardSheet*>(GetParent());
1419 require_quiet( psheet, exit );
1420
1421 psheet->SetWizardButtons( PSWIZB_BACK );
1422
1423 printer = psheet->GetSelectedPrinter();
1424 require_quiet( printer, exit );
1425
1426 service = printer->services.front();
1427 require_quiet( service, exit );
1428
1429 //
1430 // call OnInitPage once
1431 //
1432 if (!m_initialized)
1433 {
1434 OnInitPage();
1435 m_initialized = true;
1436 }
1437
1438 //
1439 // <rdar://problem/4580061> mDNS: Printers added using Bonjour should be set as the default printer.
1440 //
1441 if ( DefaultPrinterExists() )
1442 {
1443 m_defaultPrinterCtrl.SetCheck( BST_UNCHECKED );
1444 printer->deflt = false;
1445 }
1446 else
1447 {
1448 m_defaultPrinterCtrl.SetCheck( BST_CHECKED );
1449 printer->deflt = true;
1450 }
1451
1452 //
1453 // update the UI with the printer name
1454 //
1455 m_printerName.SetWindowText(printer->displayName);
1456
1457 //
1458 // populate the list controls with the manufacturers and models
1459 // from ntprint.inf
1460 //
1461 PopulateUI( m_manufacturers );
1462
1463 //
1464 // and try and match the printer
1465 //
1466
1467 if ( psheet->GetLastPage() == psheet->GetPage(0) )
1468 {
1469 MatchPrinter( m_manufacturers, printer, service, true );
1470
1471 if ( ( m_manufacturerSelected != NULL ) && ( m_modelSelected != NULL ) )
1472 {
1473 GetParent()->PostMessage(PSM_SETCURSEL, 2 );
1474 }
1475 }
1476 else
1477 {
1478 SelectMatch(printer, service, m_manufacturerSelected, m_modelSelected);
1479 }
1480
1481 exit:
1482
1483 return CPropertyPage::OnSetActive();
1484 }
1485
1486 BOOL
1487 CThirdPage::OnKillActive()
1488 {
1489 CPrinterSetupWizardSheet * psheet;
1490
1491 psheet = reinterpret_cast<CPrinterSetupWizardSheet*>(GetParent());
1492 require_quiet( psheet, exit );
1493
1494 psheet->SetLastPage(this);
1495
1496 exit:
1497
1498 return CPropertyPage::OnKillActive();
1499 }
1500
1501 // -------------------------------------------------------
1502 // PopulateUI
1503 //
1504 // This function is called to populate the list of manufacturers
1505 //
1506 OSStatus
1507 CThirdPage::PopulateUI(Manufacturers & manufacturers)
1508 {
1509 Manufacturers::iterator iter;
1510
1511 m_manufacturerListCtrl.DeleteAllItems();
1512
1513 for (iter = manufacturers.begin(); iter != manufacturers.end(); iter++)
1514 {
1515 int nIndex;
1516
1517 Manufacturer * manufacturer = iter->second;
1518
1519 nIndex = m_manufacturerListCtrl.InsertItem(0, manufacturer->name);
1520
1521 m_manufacturerListCtrl.SetItemData(nIndex, (DWORD_PTR) manufacturer);
1522
1523 m_manufacturerListCtrl.SetColumnWidth( 0, LVSCW_AUTOSIZE_USEHEADER );
1524 }
1525
1526 return 0;
1527 }
1528
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)
1534 END_MESSAGE_MAP()
1535
1536 // CThirdPage message handlers
1537 void CThirdPage::OnLvnItemchangedManufacturer(NMHDR *pNMHDR, LRESULT *pResult)
1538 {
1539 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
1540
1541 POSITION p = m_manufacturerListCtrl.GetFirstSelectedItemPosition();
1542 int nSelected = m_manufacturerListCtrl.GetNextSelectedItem(p);
1543
1544 if (nSelected != -1)
1545 {
1546 m_manufacturerSelected = (Manufacturer*) m_manufacturerListCtrl.GetItemData(nSelected);
1547
1548 m_modelListCtrl.SetRedraw(FALSE);
1549
1550 m_modelListCtrl.DeleteAllItems();
1551 m_modelSelected = NULL;
1552
1553 Models::iterator iter;
1554
1555 for (iter = m_manufacturerSelected->models.begin(); iter != m_manufacturerSelected->models.end(); iter++)
1556 {
1557 Model * model = *iter;
1558
1559 int nItem = m_modelListCtrl.InsertItem( 0, model->displayName );
1560
1561 m_modelListCtrl.SetItemData(nItem, (DWORD_PTR) model);
1562
1563 m_modelListCtrl.SetColumnWidth( 0, LVSCW_AUTOSIZE_USEHEADER );
1564 }
1565
1566 m_modelListCtrl.SetRedraw(TRUE);
1567 }
1568
1569 *pResult = 0;
1570 }
1571
1572 void CThirdPage::OnLvnItemchangedPrinterModel(NMHDR *pNMHDR, LRESULT *pResult)
1573 {
1574 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
1575
1576 CPrinterSetupWizardSheet * psheet;
1577 Printer * printer;
1578 Service * service;
1579
1580 psheet = reinterpret_cast<CPrinterSetupWizardSheet*>(GetParent());
1581 require_quiet( psheet, exit );
1582
1583 printer = psheet->GetSelectedPrinter();
1584 require_quiet( printer, exit );
1585
1586 service = printer->services.front();
1587 require_quiet( service, exit );
1588
1589 check ( m_manufacturerSelected );
1590
1591 POSITION p = m_modelListCtrl.GetFirstSelectedItemPosition();
1592 int nSelected = m_modelListCtrl.GetNextSelectedItem(p);
1593
1594 if (nSelected != -1)
1595 {
1596 m_modelSelected = (Model*) m_modelListCtrl.GetItemData(nSelected);
1597
1598 CopyPrinterSettings( printer, service, m_manufacturerSelected, m_modelSelected );
1599
1600 psheet->SetWizardButtons(PSWIZB_BACK|PSWIZB_NEXT);
1601 }
1602 else
1603 {
1604 psheet->SetWizardButtons(PSWIZB_BACK);
1605 }
1606
1607 exit:
1608
1609 *pResult = 0;
1610 }
1611
1612 void CThirdPage::OnBnClickedDefaultPrinter()
1613 {
1614 CPrinterSetupWizardSheet * psheet;
1615 Printer * printer;
1616
1617 psheet = reinterpret_cast<CPrinterSetupWizardSheet*>(GetParent());
1618 require_quiet( psheet, exit );
1619
1620 printer = psheet->GetSelectedPrinter();
1621 require_quiet( printer, exit );
1622
1623 printer->deflt = ( m_defaultPrinterCtrl.GetCheck() == BST_CHECKED ) ? true : false;
1624
1625 exit:
1626
1627 return;
1628 }
1629
1630 void CThirdPage::OnBnClickedHaveDisk()
1631 {
1632 CPrinterSetupWizardSheet * psheet;
1633 Printer * printer;
1634 Service * service;
1635 Manufacturers manufacturers;
1636
1637 CFileDialog dlg(TRUE, NULL, NULL, OFN_HIDEREADONLY|OFN_FILEMUSTEXIST, L"Setup Information (*.inf)|*.inf||", this);
1638
1639 psheet = reinterpret_cast<CPrinterSetupWizardSheet*>(GetParent());
1640 require_quiet( psheet, exit );
1641
1642 printer = psheet->GetSelectedPrinter();
1643 require_quiet( printer, exit );
1644
1645 service = printer->services.front();
1646 require_quiet( service, exit );
1647
1648 for ( ;; )
1649 {
1650 if ( dlg.DoModal() == IDOK )
1651 {
1652 CString filename = dlg.GetPathName();
1653
1654 LoadPrintDriverDefsFromFile( manufacturers, filename, true );
1655
1656 // Sanity check
1657
1658 if ( manufacturers.size() > 0 )
1659 {
1660 PopulateUI( manufacturers );
1661
1662 if ( MatchPrinter( manufacturers, printer, service, false ) != kNoErr )
1663 {
1664 CString errorMessage;
1665 CString errorCaption;
1666
1667 errorMessage.LoadString( IDS_NO_MATCH_INF_FILE );
1668 errorCaption.LoadString( IDS_NO_MATCH_INF_FILE_CAPTION );
1669
1670 MessageBox( errorMessage, errorCaption, MB_OK );
1671 }
1672
1673 break;
1674 }
1675 else
1676 {
1677 CString errorMessage;
1678 CString errorCaption;
1679
1680 errorMessage.LoadString( IDS_BAD_INF_FILE );
1681 errorCaption.LoadString( IDS_BAD_INF_FILE_CAPTION );
1682
1683 MessageBox( errorMessage, errorCaption, MB_OK );
1684 }
1685 }
1686 else
1687 {
1688 break;
1689 }
1690 }
1691
1692 exit:
1693
1694 return;
1695 }
1696
1697
1698 void
1699 CThirdPage::Split( const CString & string, TCHAR ch, CStringList & components )
1700 {
1701 CString temp;
1702 int n;
1703
1704 temp = string;
1705
1706 while ( ( n = temp.Find( ch ) ) != -1 )
1707 {
1708 components.AddTail( temp.Left( n ) );
1709 temp = temp.Right( temp.GetLength() - ( n + 1 ) );
1710 }
1711
1712 components.AddTail( temp );
1713 }