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