]> git.saurik.com Git - apple/mdnsresponder.git/blob - Clients/PrinterSetupWizard/ThirdPage.cpp
mDNSResponder-161.1.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.37 2007/06/08 06:30:26 herscher
21 <rdar://problem/5257700> Fix uninitialized pointers when detecting generic PCL and PS drivers
22
23 Revision 1.36 2007/06/06 20:39:10 cheshire
24 <rdar://problem/5254377> Printer Setup Wizard started crashing in Bonjour104A8, after update to Visual Studio 2005
25
26 Revision 1.35 2007/06/06 20:08:01 cheshire
27 <rdar://problem/4528853> mDNS: When auto-highlighting items in lists, scroll list so highlighted item is in the middle
28 AutoScroll model list as well as manufacturer list
29
30 Revision 1.34 2007/06/06 19:53:48 cheshire
31 <rdar://problem/5187308> Move build train to Visual Studio 2005
32
33 Revision 1.33 2007/04/20 22:58:10 herscher
34 <rdar://problem/4826126> mDNS: Printer Wizard doesn't offer generic HP printers or generic PS support on Vista RC2
35
36 Revision 1.32 2007/04/13 23:42:20 herscher
37 <rdar://problem/4580061> mDNS: Printers added using Bonjour should be set as the default printer.
38
39 Revision 1.31 2007/04/13 21:38:46 herscher
40 <rdar://problem/4528853> mDNS: When auto-highlighting items in lists, scroll list so highlighted item is in the middle
41
42 Revision 1.30 2007/04/13 20:23:40 herscher
43 Fixed mistake in previous checkin that reverted license text for this file
44
45 Revision 1.29 2007/04/13 18:10:24 herscher
46 <rdar://problem/4496652> mDNS: Don't allow user to choose non-working driver
47
48 Revision 1.28 2006/08/14 23:24:09 cheshire
49 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
50
51 Revision 1.27 2005/10/05 21:41:45 herscher
52 <rdar://problem/4190104> Use "application/octet-stream" to determine if CUPS shared queue supports raw
53
54 Revision 1.26 2005/07/11 20:17:15 shersche
55 <rdar://problem/4124524> UI fixes associated with CUPS printer workaround fix.
56
57 Revision 1.25 2005/07/07 17:53:20 shersche
58 Fix problems associated with the CUPS printer workaround fix.
59
60 Revision 1.24 2005/06/30 18:02:54 shersche
61 <rdar://problem/4124524> Workaround for Mac OS X Printer Sharing bug
62
63 Revision 1.23 2005/04/18 02:33:47 shersche
64 <rdar://problem/4091216> Default printer option cannot be deselected
65
66 Revision 1.22 2005/04/13 17:46:22 shersche
67 <rdar://problem/4082122> Generic PCL not selected when printers advertise multiple text records
68
69 Revision 1.21 2005/03/30 02:09:55 shersche
70 Auto-resize the column width to account for differing fonts and font sizes
71
72 Revision 1.20 2005/03/05 02:27:45 shersche
73 <rdar://problem/4030388> Generic drivers don't do color
74
75 Revision 1.19 2005/02/23 02:08:51 shersche
76 <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".
77
78 Revision 1.18 2005/02/15 07:02:51 shersche
79 <rdar://problem/4003724> Display different UI text when generic printer drivers are selected
80
81 Revision 1.17 2005/02/08 21:45:06 shersche
82 <rdar://problem/3947490> Default to Generic PostScript or PCL if unable to match driver
83
84 Revision 1.16 2005/02/08 18:56:03 shersche
85 Fix generated IPP url so that it doesn't add "/printers" string
86
87 Revision 1.15 2005/02/01 01:44:07 shersche
88 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.
89
90 Revision 1.14 2005/01/25 08:55:54 shersche
91 <rdar://problem/3911084> Load icons at run-time from resource DLL
92 Bug #: 3911084
93
94 Revision 1.13 2005/01/06 08:15:45 shersche
95 Append queue name to end of LPR port name, correctly build port name when queue name is absent
96
97 Revision 1.12 2005/01/05 01:06:12 shersche
98 <rdar://problem/3841218> Strip the first substring off the product key if an initial match can't be found with the whole product key.
99 Bug #: 3841218
100
101 Revision 1.11 2004/12/29 18:53:38 shersche
102 <rdar://problem/3725106>
103 <rdar://problem/3737413> Added support for LPR and IPP protocols as well as support for obtaining multiple text records. Reorganized and simplified codebase.
104 Bug #: 3725106, 3737413
105
106 Revision 1.10 2004/10/11 22:55:34 shersche
107 <rdar://problem/3827624> Use the IP port number when deriving the printer port name.
108 Bug #: 3827624
109
110 Revision 1.9 2004/06/27 23:08:00 shersche
111 code cleanup, make sure EnumPrintDrivers returns non-zero value, ignore comments in inf files
112
113 Revision 1.8 2004/06/27 08:06:45 shersche
114 Parse [Strings] section of inf file
115
116 Revision 1.7 2004/06/26 04:00:05 shersche
117 fix warnings compiling in debug mode
118 Submitted by: herscher
119
120 Revision 1.6 2004/06/26 03:19:57 shersche
121 clean up warning messages
122
123 Submitted by: herscher
124
125 Revision 1.5 2004/06/25 05:06:02 shersche
126 Trim whitespace from key/value pairs when parsing inf files
127 Submitted by: herscher
128
129 Revision 1.4 2004/06/25 02:44:13 shersche
130 Tweaked code to handle Xerox Phaser printer identification
131 Submitted by: herscher
132
133 Revision 1.3 2004/06/25 02:27:58 shersche
134 Do a CListCtrl::FindItem() before calling CListCtrl::SetItemState().
135 Submitted by: herscher
136
137 Revision 1.2 2004/06/23 18:09:23 shersche
138 Normalize tag names when parsing inf files.
139 Submitted by: herscher
140
141 Revision 1.1 2004/06/18 04:36:58 rpantos
142 First checked in
143 */
144
145 #include "stdafx.h"
146 #include "PrinterSetupWizardApp.h"
147 #include "PrinterSetupWizardSheet.h"
148 #include "ThirdPage.h"
149 #include "StdioFileEx.h"
150 #include <dns_sd.h>
151 #include <tcpxcv.h>
152 #include <winspool.h>
153
154 // local variable is initialize but not referenced
155 #pragma warning(disable:4189)
156
157 //
158 // This is the printer description file that is shipped
159 // with Windows XP and below
160 //
161 #define kNTPrintFile L"inf\\ntprint.inf"
162
163 //
164 // Windows Vista ships with a set of prn*.inf files
165 //
166 #define kVistaPrintFiles L"inf\\prn*.inf"
167
168 //
169 // These are pre-defined names for Generic manufacturer and model
170 //
171 #define kGenericManufacturer L"Generic"
172 #define kGenericText L"Generic / Text Only"
173 #define kGenericPostscript L"Generic / Postscript"
174 #define kGenericPCL L"Generic / PCL"
175 #define kPDLPostscriptKey L"application/postscript"
176 #define kPDLPCLKey L"application/vnd.hp-pcl"
177 #define kGenericPSColorDriver L"HP Color LaserJet 4550 PS"
178 #define kGenericPSDriver L"HP LaserJet 4050 Series PS"
179 #define kGenericPCLColorDriver L"HP Color LaserJet 4550 PCL"
180 #define kGenericPCLDriver L"HP LaserJet 4050 Series PCL"
181
182 //
183 // states for parsing ntprint.inf
184 //
185 enum PrinterParsingState
186 {
187 Looking,
188 ParsingManufacturers,
189 ParsingModels,
190 ParsingStrings
191 };
192
193 // CThirdPage dialog
194
195 IMPLEMENT_DYNAMIC(CThirdPage, CPropertyPage)
196 CThirdPage::CThirdPage()
197 : CPropertyPage(CThirdPage::IDD),
198 m_manufacturerSelected( NULL ),
199 m_modelSelected( NULL ),
200 m_genericPostscript( NULL ),
201 m_genericPCL( NULL ),
202 m_initialized(false),
203 m_printerImage( NULL )
204 {
205 static const int bufferSize = 32768;
206 TCHAR windowsDirectory[bufferSize];
207 CString header;
208 WIN32_FIND_DATA findFileData;
209 HANDLE findHandle;
210 CString prnFiles;
211 CString ntPrint;
212 OSStatus err;
213 BOOL ok;
214
215 m_psp.dwFlags &= ~(PSP_HASHELP);
216 m_psp.dwFlags |= PSP_DEFAULT|PSP_USEHEADERTITLE|PSP_USEHEADERSUBTITLE;
217
218 m_psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_INSTALL_TITLE);
219 m_psp.pszHeaderSubTitle = MAKEINTRESOURCE(IDS_INSTALL_SUBTITLE);
220
221 //
222 // load printers from ntprint.inf
223 //
224 ok = GetWindowsDirectory( windowsDirectory, bufferSize );
225 err = translate_errno( ok, errno_compat(), kUnknownErr );
226 require_noerr( err, exit );
227
228 //
229 // <rdar://problem/4826126>
230 //
231 // If there are no *prn.inf files, we'll assume that the information
232 // is in ntprint.inf
233 //
234 prnFiles.Format( L"%s\\%s", windowsDirectory, kVistaPrintFiles );
235 findHandle = FindFirstFile( prnFiles, &findFileData );
236
237 if ( findHandle != INVALID_HANDLE_VALUE )
238 {
239 CString absolute;
240
241 absolute.Format( L"%s\\inf\\%s", windowsDirectory, findFileData.cFileName );
242 err = LoadPrintDriverDefsFromFile( m_manufacturers, absolute, false );
243 require_noerr( err, exit );
244
245 while ( FindNextFile( findHandle, &findFileData ) )
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
252 FindClose( findHandle );
253 }
254 else
255 {
256 ntPrint.Format(L"%s\\%s", windowsDirectory, kNTPrintFile);
257 err = LoadPrintDriverDefsFromFile( m_manufacturers, ntPrint, false );
258 require_noerr(err, exit);
259 }
260
261 //
262 // load printer drivers that have been installed on this machine
263 //
264 err = LoadPrintDriverDefs( m_manufacturers );
265 require_noerr(err, exit);
266
267 //
268 // load our own special generic printer defs
269 //
270 err = LoadGenericPrintDriverDefs( m_manufacturers );
271 require_noerr( err, exit );
272
273 exit:
274
275 return;
276 }
277
278 CThirdPage::~CThirdPage()
279 {
280 //
281 // clean up all the printer manufacturers
282 //
283 while (m_manufacturers.size())
284 {
285 Manufacturers::iterator iter = m_manufacturers.begin();
286
287 while (iter->second->models.size())
288 {
289 Models::iterator it = iter->second->models.begin();
290
291 Model * model = *it;
292
293 delete model;
294
295 iter->second->models.erase(it);
296 }
297
298 delete iter->second;
299
300 m_manufacturers.erase(iter);
301 }
302 }
303
304 // ----------------------------------------------------
305 // SelectMatch
306 //
307 // SelectMatch will do all the UI work associated with
308 // selected a manufacturer and model of printer. It also
309 // makes sure the printer object is update with the
310 // latest settings
311 //
312 // ----------------------------------------------------
313 void
314 CThirdPage::SelectMatch(Printer * printer, Service * service, Manufacturer * manufacturer, Model * model)
315 {
316 LVFINDINFO info;
317 int nIndex;
318
319 check( printer != NULL );
320 check( manufacturer != NULL );
321 check( model != NULL );
322
323 //
324 // select the manufacturer
325 //
326 info.flags = LVFI_STRING;
327 info.psz = manufacturer->name;
328
329 nIndex = m_manufacturerListCtrl.FindItem(&info);
330
331 if (nIndex != -1)
332 {
333 m_manufacturerListCtrl.SetItemState(nIndex, LVIS_SELECTED, LVIS_SELECTED);
334 //
335 //<rdar://problem/4528853> mDNS: When auto-highlighting items in lists, scroll list so highlighted item is in the middle
336 //
337 AutoScroll(m_manufacturerListCtrl, nIndex);
338 }
339
340 //
341 // select the model
342 //
343 info.flags = LVFI_STRING;
344 info.psz = model->displayName;
345
346 nIndex = m_modelListCtrl.FindItem(&info);
347
348 if (nIndex != -1)
349 {
350 m_modelListCtrl.SetItemState(nIndex, LVIS_SELECTED, LVIS_SELECTED);
351 AutoScroll( m_modelListCtrl, nIndex );
352
353 m_modelListCtrl.SetFocus();
354 }
355
356 CopyPrinterSettings( printer, service, manufacturer, model );
357 }
358
359 void
360 CThirdPage::SelectMatch(Manufacturers & manufacturers, Printer * printer, Service * service, Manufacturer * manufacturer, Model * model)
361 {
362 PopulateUI( manufacturers );
363
364 SelectMatch( printer, service, manufacturer, model );
365 }
366
367 // --------------------------------------------------------
368 // CopyPrinterSettings
369 //
370 // This function makes sure that the printer object has the
371 // latest settings from the manufacturer and model objects
372 // --------------------------------------------------------
373
374 void
375 CThirdPage::CopyPrinterSettings( Printer * printer, Service * service, Manufacturer * manufacturer, Model * model )
376 {
377 printer->manufacturer = manufacturer->name;
378 printer->displayModelName = model->displayName;
379 printer->modelName = model->name;
380 printer->driverInstalled = model->driverInstalled;
381 printer->infFileName = model->infFileName;
382
383 if ( service->type == kPDLServiceType )
384 {
385 printer->portName.Format(L"IP_%s.%d", static_cast<LPCTSTR>(service->hostname), service->portNumber);
386 service->protocol = L"Raw";
387 }
388 else if ( service->type == kLPRServiceType )
389 {
390 Queue * q = service->queues.front();
391 check( q );
392
393 if ( q->name.GetLength() > 0 )
394 {
395 printer->portName.Format(L"LPR_%s.%d.%s", static_cast<LPCTSTR>(service->hostname), service->portNumber, static_cast<LPCTSTR>(q->name) );
396 }
397 else
398 {
399 printer->portName.Format(L"LPR_%s.%d", static_cast<LPCTSTR>(service->hostname), service->portNumber);
400 }
401
402 service->protocol = L"LPR";
403 }
404 else if ( service->type == kIPPServiceType )
405 {
406 Queue * q = service->queues.front();
407 check( q );
408
409 if ( q->name.GetLength() > 0 )
410 {
411 printer->portName.Format(L"http://%s:%d/%s", static_cast<LPCTSTR>(service->hostname), service->portNumber, static_cast<LPCTSTR>(q->name) );
412 }
413 else
414 {
415 printer->portName.Format(L"http://%s:%d/", static_cast<LPCTSTR>(service->hostname), service->portNumber );
416 }
417
418 service->protocol = L"IPP";
419 }
420 }
421
422 // --------------------------------------------------------
423 // DefaultPrinterExists
424 //
425 // Checks to see if a default printer has been configured
426 // on this machine
427 // --------------------------------------------------------
428 BOOL
429 CThirdPage::DefaultPrinterExists()
430 {
431 CPrintDialog dlg(FALSE);
432
433 dlg.m_pd.Flags |= PD_RETURNDEFAULT;
434
435 return dlg.GetDefaults();
436 }
437
438 // --------------------------------------------------------
439 // AutoScroll
440 //
441 // Ensure selected item is in middle of list
442 // --------------------------------------------------------
443 void
444 CThirdPage::AutoScroll( CListCtrl & list, int nIndex )
445 {
446 //
447 //<rdar://problem/4528853> mDNS: When auto-highlighting items in lists, scroll list so highlighted item is in the middle
448 //
449
450 int top;
451 int count;
452
453 list.EnsureVisible( nIndex, FALSE );
454
455 top = list.GetTopIndex();
456 count = list.GetCountPerPage();
457
458 if ( ( nIndex == top ) || ( ( nIndex + 1 ) == ( top + count ) ) )
459 {
460 CRect rect;
461 int rows;
462
463 rows = ( count / 2 );
464
465 if ( nIndex == top )
466 {
467 list.GetItemRect(0, rect, LVIR_BOUNDS);
468 list.Scroll( CPoint( 0, rows * rect.Height() * -1 ) );
469 }
470 else
471 {
472 list.GetItemRect(0, rect, LVIR_BOUNDS);
473 list.Scroll( CPoint( 0, rows * rect.Height() ) );
474 }
475 }
476 }
477
478 // ------------------------------------------------------
479 // LoadPrintDriverDefsFromFile
480 //
481 // This function does all the heavy lifting in parsing inf
482 // files. It is called to parse both ntprint.inf, and driver
483 // files that might be shipped on a printer's installation
484 // disk
485 //
486 // The inf file is not totally parsed. I only want to determine
487 // the manufacturer and models that are involved. I leave it
488 // to printui.dll to actually copy the driver files to the
489 // right places.
490 //
491 // I was aiming to parse as little as I could so as not to
492 // duplicate the parsing code that is contained in Windows. There
493 // are no public APIs for parsing inf files.
494 //
495 // That part of the inf file that we're interested in has a fairly
496 // easy format. Tags are strings that are enclosed in brackets.
497 // We are only interested in [MANUFACTURERS] and models.
498 //
499 // The only potentially opaque thing about this function is the
500 // checkForDuplicateModels flag. The problem here is that ntprint.inf
501 // doesn't contain duplicate models, and it has hundreds of models
502 // listed. You wouldn't check for duplicates there. But oftentimes,
503 // loading different windows print driver files contain multiple
504 // entries for the same printer. You don't want the UI to display
505 // the same printer multiple times, so in that case, you would ask
506 // this function to check for multiple models.
507
508 OSStatus
509 CThirdPage::LoadPrintDriverDefsFromFile(Manufacturers & manufacturers, const CString & filename, bool checkForDuplicateModels )
510 {
511 PrinterParsingState state = Looking;
512 Manufacturers::iterator iter = manufacturers.end();
513 CStdioFileEx file;
514 CFileException feError;
515 CString s;
516 OSStatus err;
517 BOOL ok;
518
519 typedef std::map<CString, CString> StringMap;
520
521 StringMap strings;
522
523 ok = file.Open( filename, CFile::modeRead|CFile::typeText, &feError);
524 err = translate_errno( ok, errno_compat(), kUnknownErr );
525 require_noerr( err, exit );
526
527 check ( state == Looking );
528 check ( iter == manufacturers.end() );
529
530 //
531 // first, parse the file looking for string sections
532 //
533 while (file.ReadString(s))
534 {
535 //
536 // check for comment
537 //
538 if (s.Find(';') == 0)
539 {
540 continue;
541 }
542
543 //
544 // check for tag
545 //
546 else if (s.Find('[') == 0)
547 {
548 //
549 // handle any capitalization issues here
550 //
551 CString tag = s;
552
553 tag.MakeLower();
554
555 if (tag == L"[strings]")
556 {
557 state = ParsingStrings;
558 }
559 else
560 {
561 state = Looking;
562 }
563 }
564 else
565 {
566 switch (state)
567 {
568 case ParsingStrings:
569 {
570 int curPos = 0;
571
572 if (s.GetLength() > 0)
573 {
574 CString key = s.Tokenize(L"=",curPos);
575 CString val = s.Tokenize(L"=",curPos);
576
577 //
578 // get rid of all delimiters
579 //
580 key.Trim();
581 val.Remove('"');
582
583 //
584 // and store it
585 //
586 strings[key] = val;
587 }
588 }
589 break;
590 }
591 }
592 }
593
594 file.Close();
595
596 ok = file.Open( filename, CFile::modeRead|CFile::typeText, &feError);
597 err = translate_errno( ok, errno_compat(), kUnknownErr );
598 require_noerr( err, exit );
599
600 state = Looking;
601
602 check ( iter == manufacturers.end() );
603
604 while (file.ReadString(s))
605 {
606 //
607 // check for comment
608 //
609 if (s.Find(';') == 0)
610 {
611 continue;
612 }
613
614 //
615 // check for tag
616 //
617 else if (s.Find('[') == 0)
618 {
619 //
620 // handle any capitalization issues here
621 //
622 CString tag = s;
623
624 tag.MakeLower();
625
626 if (tag == L"[manufacturer]")
627 {
628 state = ParsingManufacturers;
629 }
630 else
631 {
632 CString name;
633 int curPos;
634
635 //
636 // remove the leading and trailing delimiters
637 //
638 s.Remove('[');
639 s.Remove(']');
640
641 //
642 // <rdar://problem/4826126>
643 //
644 // Ignore decorations in model declarations
645 //
646 curPos = 0;
647 name = s.Tokenize( L".", curPos );
648
649 //
650 // check to see if this is a printer entry
651 //
652 iter = manufacturers.find( name );
653
654 if (iter != manufacturers.end())
655 {
656 state = ParsingModels;
657 }
658 else
659 {
660 state = Looking;
661 }
662 }
663 }
664 //
665 // only look at this if the line isn't empty, or
666 // if it isn't a comment
667 //
668 else if ((s.GetLength() > 0) && (s.Find(';') != 0))
669 {
670 switch (state)
671 {
672 //
673 // if we're parsing manufacturers, then we will parse
674 // an entry of the form key=val, where key is a delimited
675 // string specifying a manufacturer name, and val is
676 // a tag that is used later in the file. the key is
677 // delimited by either '"' (quotes) or '%' (percent sign).
678 //
679 // the tag is used further down the file when models are
680 // declared. this allows multiple manufacturers to exist
681 // in a single inf file.
682 //
683 case ParsingManufacturers:
684 {
685 Manufacturer * manufacturer;
686 int curPos = 0;
687
688 CString key = s.Tokenize(L"=",curPos);
689 CString val = s.Tokenize(L"=",curPos);
690
691 try
692 {
693 manufacturer = new Manufacturer;
694 }
695 catch (...)
696 {
697 manufacturer = NULL;
698 }
699
700 require_action( manufacturer, exit, err = kNoMemoryErr );
701
702 //
703 // if it's a variable, look it up
704 //
705 if (key.Find('%') == 0)
706 {
707 StringMap::iterator it;
708
709 key.Remove('%');
710
711 it = strings.find(key);
712
713 if (it != strings.end())
714 {
715 key = it->second;
716 }
717 }
718 else
719 {
720 key.Remove('"');
721 }
722
723 val.TrimLeft();
724 val.TrimRight();
725
726 //
727 // why is there no consistency in inf files?
728 //
729 if (val.GetLength() == 0)
730 {
731 val = key;
732 }
733
734 //
735 // fix the manufacturer name if necessary
736 //
737 curPos = 0;
738 val = val.Tokenize(L",", curPos);
739
740 for ( ;; )
741 {
742 CString decoration;
743
744 decoration = val.Tokenize( L",", curPos );
745
746 if ( decoration.GetLength() > 0 )
747 {
748 manufacturer->decorations.push_back( decoration );
749 }
750 else
751 {
752 break;
753 }
754 }
755
756 manufacturer->name = NormalizeManufacturerName( key );
757 manufacturer->tag = val;
758
759 manufacturers[val] = manufacturer;
760 }
761 break;
762
763 case ParsingModels:
764 {
765 check( iter != manufacturers.end() );
766
767 Model * model;
768 int curPos = 0;
769
770 CString name = s.Tokenize(L"=",curPos);
771 CString description = s.Tokenize(L"=",curPos);
772
773 if (name.Find('%') == 0)
774 {
775 StringMap::iterator it;
776
777 name.Remove('%');
778
779 it = strings.find(name);
780
781 if (it != strings.end())
782 {
783 name = it->second;
784 }
785 }
786 else
787 {
788 name.Remove('"');
789 }
790
791 name.Trim();
792 description.Trim();
793
794 //
795 // If true, see if we've seen this guy before
796 //
797 if (checkForDuplicateModels == true)
798 {
799 if ( MatchModel( iter->second, ConvertToModelName( name ) ) != NULL )
800 {
801 continue;
802 }
803 }
804
805 //
806 // Stock Vista printer inf files embed guids in the model
807 // declarations for Epson printers. Let's ignore those.
808 //
809 if ( name.Find( L"{", 0 ) != -1 )
810 {
811 continue;
812 }
813
814 try
815 {
816 model = new Model;
817 }
818 catch (...)
819 {
820 model = NULL;
821 }
822
823 require_action( model, exit, err = kNoMemoryErr );
824
825 model->infFileName = filename;
826 model->displayName = name;
827 model->name = name;
828 model->driverInstalled = false;
829
830 iter->second->models.push_back(model);
831 }
832 break;
833
834 default:
835 {
836 // pay no attention if we are in any other state
837 }
838 break;
839 }
840 }
841 }
842
843 exit:
844
845 file.Close();
846
847 return (err);
848 }
849
850 // -------------------------------------------------------
851 // LoadPrintDriverDefs
852 //
853 // This function is responsible for loading the print driver
854 // definitions of all print drivers that have been installed
855 // on this machine.
856 // -------------------------------------------------------
857 OSStatus
858 CThirdPage::LoadPrintDriverDefs( Manufacturers & manufacturers )
859 {
860 BYTE * buffer = NULL;
861 DWORD bytesReceived = 0;
862 DWORD numPrinters = 0;
863 OSStatus err = 0;
864 BOOL ok;
865
866 //
867 // like a lot of win32 calls, we call this first to get the
868 // size of the buffer we need.
869 //
870 EnumPrinterDrivers(NULL, L"all", 6, NULL, 0, &bytesReceived, &numPrinters);
871
872 if (bytesReceived > 0)
873 {
874 try
875 {
876 buffer = new BYTE[bytesReceived];
877 }
878 catch (...)
879 {
880 buffer = NULL;
881 }
882
883 require_action( buffer, exit, err = kNoMemoryErr );
884
885 //
886 // this call gets the real info
887 //
888 ok = EnumPrinterDrivers(NULL, L"all", 6, buffer, bytesReceived, &bytesReceived, &numPrinters);
889 err = translate_errno( ok, errno_compat(), kUnknownErr );
890 require_noerr( err, exit );
891
892 DRIVER_INFO_6 * info = (DRIVER_INFO_6*) buffer;
893
894 for (DWORD i = 0; i < numPrinters; i++)
895 {
896 Manufacturer * manufacturer;
897 Model * model;
898 CString name;
899
900 //
901 // skip over anything that doesn't have a manufacturer field. This
902 // fixes a bug that I noticed that occurred after I installed
903 // ProComm. This program add a print driver with no manufacturer
904 // that screwed up this wizard.
905 //
906 if (info[i].pszMfgName == NULL)
907 {
908 continue;
909 }
910
911 //
912 // look for manufacturer
913 //
914 Manufacturers::iterator iter;
915
916 //
917 // save the name
918 //
919 name = NormalizeManufacturerName( info[i].pszMfgName );
920
921 iter = manufacturers.find(name);
922
923 if (iter != manufacturers.end())
924 {
925 manufacturer = iter->second;
926 }
927 else
928 {
929 try
930 {
931 manufacturer = new Manufacturer;
932 }
933 catch (...)
934 {
935 manufacturer = NULL;
936 }
937
938 require_action( manufacturer, exit, err = kNoMemoryErr );
939
940 manufacturer->name = name;
941
942 manufacturers[name] = manufacturer;
943 }
944
945 //
946 // now look to see if we have already seen this guy. this could
947 // happen if we have already installed printers that are described
948 // in ntprint.inf. the extant drivers will show up in EnumPrinterDrivers
949 // but we have already loaded their info
950 //
951 //
952 if ( MatchModel( manufacturer, ConvertToModelName( info[i].pName ) ) == NULL )
953 {
954 try
955 {
956 model = new Model;
957 }
958 catch (...)
959 {
960 model = NULL;
961 }
962
963 require_action( model, exit, err = kNoMemoryErr );
964
965 model->displayName = info[i].pName;
966 model->name = info[i].pName;
967 model->driverInstalled = true;
968
969 manufacturer->models.push_back(model);
970 }
971 }
972 }
973
974 exit:
975
976 if (buffer != NULL)
977 {
978 delete [] buffer;
979 }
980
981 return err;
982 }
983
984 // -------------------------------------------------------
985 // LoadGenericPrintDriverDefs
986 //
987 // This function is responsible for loading polymorphic
988 // generic print drivers defs. The UI will read
989 // something like "Generic / Postscript" and we can map
990 // that to any print driver we want.
991 // -------------------------------------------------------
992 OSStatus
993 CThirdPage::LoadGenericPrintDriverDefs( Manufacturers & manufacturers )
994 {
995 Manufacturer * manufacturer;
996 Model * model;
997 Manufacturers::iterator iter;
998 CString psDriverName;
999 CString pclDriverName;
1000 OSStatus err = 0;
1001
1002 // <rdar://problem/4030388> Generic drivers don't do color
1003
1004 // First try and find our generic driver names
1005
1006 iter = m_manufacturers.find(L"HP");
1007 require_action( iter != m_manufacturers.end(), exit, err = kUnknownErr );
1008 manufacturer = iter->second;
1009
1010 // Look for Postscript
1011
1012 model = manufacturer->find( kGenericPSColorDriver );
1013
1014 if ( !model )
1015 {
1016 model = manufacturer->find( kGenericPSDriver );
1017 }
1018
1019 if ( model )
1020 {
1021 psDriverName = model->name;
1022 }
1023
1024 // Look for PCL
1025
1026 model = manufacturer->find( kGenericPCLColorDriver );
1027
1028 if ( !model )
1029 {
1030 model = manufacturer->find( kGenericPCLDriver );
1031 }
1032
1033 if ( model )
1034 {
1035 pclDriverName = model->name;
1036 }
1037
1038 // If we found either a generic PS driver, or a generic PCL driver,
1039 // then add them to the list
1040
1041 if ( psDriverName.GetLength() || pclDriverName.GetLength() )
1042 {
1043 // Try and find generic manufacturer if there is one
1044
1045 iter = manufacturers.find(L"Generic");
1046
1047 if (iter != manufacturers.end())
1048 {
1049 manufacturer = iter->second;
1050 }
1051 else
1052 {
1053 try
1054 {
1055 manufacturer = new Manufacturer;
1056 }
1057 catch (...)
1058 {
1059 manufacturer = NULL;
1060 }
1061
1062 require_action( manufacturer, exit, err = kNoMemoryErr );
1063
1064 manufacturer->name = "Generic";
1065 manufacturers[manufacturer->name] = manufacturer;
1066 }
1067
1068 if ( psDriverName.GetLength() > 0 )
1069 {
1070 try
1071 {
1072 m_genericPostscript = new Model;
1073 }
1074 catch (...)
1075 {
1076 m_genericPostscript = NULL;
1077 }
1078
1079 require_action( m_genericPostscript, exit, err = kNoMemoryErr );
1080
1081 m_genericPostscript->displayName = kGenericPostscript;
1082 m_genericPostscript->name = psDriverName;
1083 m_genericPostscript->driverInstalled = false;
1084
1085 manufacturer->models.push_back( m_genericPostscript );
1086 }
1087
1088 if ( pclDriverName.GetLength() > 0 )
1089 {
1090 try
1091 {
1092 m_genericPCL = new Model;
1093 }
1094 catch (...)
1095 {
1096 m_genericPCL = NULL;
1097 }
1098
1099 require_action( m_genericPCL, exit, err = kNoMemoryErr );
1100
1101 m_genericPCL->displayName = kGenericPCL;
1102 m_genericPCL->name = pclDriverName;
1103 m_genericPCL->driverInstalled = false;
1104
1105 manufacturer->models.push_back( m_genericPCL );
1106 }
1107 }
1108
1109 exit:
1110
1111 return err;
1112 }
1113
1114 // ------------------------------------------------------
1115 // ConvertToManufacturerName
1116 //
1117 // This function is responsible for tweaking the
1118 // name so that subsequent string operations won't fail because
1119 // of capitalizations/different names for the same manufacturer
1120 // (i.e. Hewlett-Packard/HP/Hewlett Packard)
1121 //
1122 CString
1123 CThirdPage::ConvertToManufacturerName( const CString & name )
1124 {
1125 //
1126 // first we're going to convert all the characters to lower
1127 // case
1128 //
1129 CString lower = name;
1130 lower.MakeLower();
1131
1132 //
1133 // now we're going to check to see if the string says "hewlett-packard",
1134 // because sometimes they refer to themselves as "hewlett-packard", and
1135 // sometimes they refer to themselves as "hp".
1136 //
1137 if ( lower == L"hewlett-packard")
1138 {
1139 lower = "hp";
1140 }
1141
1142 //
1143 // tweak for Xerox Phaser, which doesn't announce itself
1144 // as a xerox
1145 //
1146 else if ( lower.Find( L"phaser", 0 ) != -1 )
1147 {
1148 lower = "xerox";
1149 }
1150
1151 return lower;
1152 }
1153
1154 // ------------------------------------------------------
1155 // ConvertToModelName
1156 //
1157 // This function is responsible for ensuring that subsequent
1158 // string operations don't fail because of differing capitalization
1159 // schemes and the like
1160 // ------------------------------------------------------
1161
1162 CString
1163 CThirdPage::ConvertToModelName( const CString & name )
1164 {
1165 //
1166 // convert it to lowercase
1167 //
1168 CString lower = name;
1169 lower.MakeLower();
1170
1171 return lower;
1172 }
1173
1174 // ------------------------------------------------------
1175 // NormalizeManufacturerName
1176 //
1177 // This function is responsible for tweaking the manufacturer
1178 // name so that there are no aliases for vendors
1179 //
1180 CString
1181 CThirdPage::NormalizeManufacturerName( const CString & name )
1182 {
1183 CString normalized = name;
1184
1185 //
1186 // now we're going to check to see if the string says "hewlett-packard",
1187 // because sometimes they refer to themselves as "hewlett-packard", and
1188 // sometimes they refer to themselves as "hp".
1189 //
1190 if ( normalized == L"Hewlett-Packard")
1191 {
1192 normalized = "HP";
1193 }
1194
1195 return normalized;
1196 }
1197
1198 // -------------------------------------------------------
1199 // MatchPrinter
1200 //
1201 // This function is responsible for matching a printer
1202 // to a list of manufacturers and models. It calls
1203 // MatchManufacturer and MatchModel in turn.
1204 //
1205
1206 OSStatus CThirdPage::MatchPrinter(Manufacturers & manufacturers, Printer * printer, Service * service, bool useCUPSWorkaround)
1207 {
1208 CString normalizedProductName;
1209 Manufacturer * manufacturer = NULL;
1210 Manufacturer * genericManufacturer = NULL;
1211 Model * model = NULL;
1212 Model * genericModel = NULL;
1213 bool found = false;
1214 CString text;
1215 OSStatus err = kNoErr;
1216
1217 check( printer );
1218 check( service );
1219
1220 Queue * q = service->SelectedQueue();
1221
1222 check( q );
1223
1224 //
1225 // first look to see if we have a usb_MFG descriptor
1226 //
1227 if ( q->usb_MFG.GetLength() > 0)
1228 {
1229 manufacturer = MatchManufacturer( manufacturers, ConvertToManufacturerName ( q->usb_MFG ) );
1230 }
1231
1232 if ( manufacturer == NULL )
1233 {
1234 q->product.Remove('(');
1235 q->product.Remove(')');
1236
1237 manufacturer = MatchManufacturer( manufacturers, ConvertToManufacturerName ( q->product ) );
1238 }
1239
1240 //
1241 // if we found the manufacturer, then start looking for the model
1242 //
1243 if ( manufacturer != NULL )
1244 {
1245 if ( q->usb_MDL.GetLength() > 0 )
1246 {
1247 model = MatchModel ( manufacturer, ConvertToModelName ( q->usb_MDL ) );
1248 }
1249
1250 if ( ( model == NULL ) && ( q->product.GetLength() > 0 ) )
1251 {
1252 q->product.Remove('(');
1253 q->product.Remove(')');
1254
1255 model = MatchModel ( manufacturer, ConvertToModelName ( q->product ) );
1256 }
1257
1258 if ( model != NULL )
1259 {
1260 // <rdar://problem/4124524> Offer Generic printers if printer advertises Postscript or PCL. Workaround
1261 // bug in OS X CUPS printer sharing by selecting Generic driver instead of matched printer.
1262
1263 bool hasGenericDriver = false;
1264
1265 if ( MatchGeneric( manufacturers, printer, service, &genericManufacturer, &genericModel ) )
1266 {
1267 hasGenericDriver = true;
1268 }
1269
1270 // <rdar://problem/4190104> Use "application/octet-stream" to determine if CUPS
1271 // shared queue supports raw
1272
1273 if ( q->pdl.Find( L"application/octet-stream" ) != -1 )
1274 {
1275 useCUPSWorkaround = false;
1276 }
1277
1278 if ( useCUPSWorkaround && printer->isSharedFromOSX && hasGenericDriver )
1279 {
1280 //
1281 // <rdar://problem/4496652> mDNS: Don't allow user to choose non-working driver
1282 //
1283 Manufacturers genericManufacturers;
1284
1285 LoadGenericPrintDriverDefs( genericManufacturers );
1286
1287 SelectMatch( genericManufacturers, printer, service, genericManufacturer, genericModel );
1288 }
1289 else
1290 {
1291 SelectMatch(manufacturers, printer, service, manufacturer, model);
1292 }
1293
1294 found = true;
1295 }
1296 }
1297
1298 //
1299 // display a message to the user based on whether we could match
1300 // this printer
1301 //
1302 if (found)
1303 {
1304 text.LoadString(IDS_PRINTER_MATCH_GOOD);
1305 }
1306 else if ( MatchGeneric( manufacturers, printer, service, &genericManufacturer, &genericModel ) )
1307 {
1308 if ( printer->isSharedFromOSX )
1309 {
1310 //
1311 // <rdar://problem/4496652> mDNS: Don't allow user to choose non-working driver
1312 //
1313 Manufacturers genericManufacturers;
1314
1315 LoadGenericPrintDriverDefs( genericManufacturers );
1316
1317 SelectMatch( genericManufacturers, printer, service, genericManufacturer, genericModel );
1318
1319 text.LoadString(IDS_PRINTER_MATCH_GOOD);
1320 }
1321 else
1322 {
1323 SelectMatch( manufacturers, printer, service, genericManufacturer, genericModel );
1324 text.LoadString(IDS_PRINTER_MATCH_MAYBE);
1325 }
1326 }
1327 else
1328 {
1329 text.LoadString(IDS_PRINTER_MATCH_BAD);
1330
1331 //
1332 // if there was any crud in this list from before, get rid of it now
1333 //
1334 m_modelListCtrl.DeleteAllItems();
1335
1336 //
1337 // select the manufacturer if we found one
1338 //
1339 if (manufacturer != NULL)
1340 {
1341 LVFINDINFO info;
1342 int nIndex;
1343
1344 //
1345 // select the manufacturer
1346 //
1347 info.flags = LVFI_STRING;
1348 info.psz = manufacturer->name;
1349
1350 nIndex = m_manufacturerListCtrl.FindItem(&info);
1351
1352 if (nIndex != -1)
1353 {
1354 m_manufacturerListCtrl.SetItemState(nIndex, LVIS_SELECTED, LVIS_SELECTED);
1355
1356 //
1357 //<rdar://problem/4528853> mDNS: When auto-highlighting items in lists, scroll list so highlighted item is in the middle
1358 //
1359 AutoScroll(m_manufacturerListCtrl, nIndex);
1360 }
1361 }
1362 }
1363
1364 m_printerSelectionText.SetWindowText(text);
1365
1366 return err;
1367 }
1368
1369 // ------------------------------------------------------
1370 // MatchManufacturer
1371 //
1372 // This function is responsible for finding a manufacturer
1373 // object from a string name. It does a CString::Find, which
1374 // is like strstr, so it doesn't have to do an exact match
1375 //
1376 // If it can't find a match, NULL is returned
1377 // ------------------------------------------------------
1378
1379 Manufacturer*
1380 CThirdPage::MatchManufacturer( Manufacturers & manufacturers, const CString & name)
1381 {
1382 Manufacturers::iterator iter;
1383
1384 for (iter = manufacturers.begin(); iter != manufacturers.end(); iter++)
1385 {
1386 //
1387 // we're going to convert all the manufacturer names to lower case,
1388 // so we match the name passed in.
1389 //
1390 CString lower = iter->second->name;
1391 lower.MakeLower();
1392
1393 //
1394 // now try and find the lowered string in the name passed in.
1395 //
1396 if (name.Find(lower) != -1)
1397 {
1398 return iter->second;
1399 }
1400 }
1401
1402 return NULL;
1403 }
1404
1405 // -------------------------------------------------------
1406 // MatchModel
1407 //
1408 // This function is responsible for matching a model from
1409 // a name. It does a CString::Find(), which works like strstr,
1410 // so it doesn't rely on doing an exact string match.
1411 //
1412
1413 Model*
1414 CThirdPage::MatchModel(Manufacturer * manufacturer, const CString & name)
1415 {
1416 Models::iterator iter;
1417
1418 iter = manufacturer->models.begin();
1419
1420 for (iter = manufacturer->models.begin(); iter != manufacturer->models.end(); iter++)
1421 {
1422 Model * model = *iter;
1423
1424 //
1425 // convert the model name to lower case
1426 //
1427 CString lowered = model->name;
1428 lowered.MakeLower();
1429
1430 if (lowered.Find( name ) != -1)
1431 {
1432 return model;
1433 }
1434
1435 //
1436 // <rdar://problem/3841218>
1437 // try removing the first substring and search again
1438 //
1439
1440 if ( name.Find(' ') != -1 )
1441 {
1442 CString altered = name;
1443 altered.Delete( 0, altered.Find(' ') + 1 );
1444
1445 if ( lowered.Find( altered ) != -1 )
1446 {
1447 return model;
1448 }
1449 }
1450 }
1451
1452 return NULL;
1453 }
1454
1455 // -------------------------------------------------------
1456 // MatchGeneric
1457 //
1458 // This function will attempt to find a generic printer
1459 // driver for a printer that we weren't able to match
1460 // specifically
1461 //
1462 BOOL
1463 CThirdPage::MatchGeneric( Manufacturers & manufacturers, Printer * printer, Service * service, Manufacturer ** manufacturer, Model ** model )
1464 {
1465 CString pdl;
1466 BOOL ok = FALSE;
1467
1468 DEBUG_UNUSED( printer );
1469
1470 check( service );
1471
1472 Queue * q = service->SelectedQueue();
1473
1474 check( q );
1475
1476 Manufacturers::iterator iter = manufacturers.find( kGenericManufacturer );
1477 require_action_quiet( iter != manufacturers.end(), exit, ok = FALSE );
1478
1479 *manufacturer = iter->second;
1480
1481 pdl = q->pdl;
1482 pdl.MakeLower();
1483
1484 if ( m_genericPCL && ( pdl.Find( kPDLPCLKey ) != -1 ) )
1485 {
1486 *model = m_genericPCL;
1487 ok = TRUE;
1488 }
1489 else if ( m_genericPostscript && ( pdl.Find( kPDLPostscriptKey ) != -1 ) )
1490 {
1491 *model = m_genericPostscript;
1492 ok = TRUE;
1493 }
1494
1495 exit:
1496
1497 return ok;
1498 }
1499
1500 // -----------------------------------------------------------
1501 // OnInitPage
1502 //
1503 // This function is responsible for doing initialization that
1504 // only occurs once during a run of the wizard
1505 //
1506
1507 OSStatus CThirdPage::OnInitPage()
1508 {
1509 CString header;
1510 CString ntPrint;
1511 OSStatus err = kNoErr;
1512
1513 // Load printer icon
1514 check( m_printerImage == NULL );
1515
1516 m_printerImage = (CStatic*) GetDlgItem( 1 ); // 1 == IDR_MANIFEST
1517 check( m_printerImage );
1518
1519 if ( m_printerImage != NULL )
1520 {
1521 m_printerImage->SetIcon( LoadIcon( GetNonLocalizedResources(), MAKEINTRESOURCE( IDI_PRINTER ) ) );
1522 }
1523
1524 //
1525 // The CTreeCtrl widget automatically sends a selection changed
1526 // message which initially we want to ignore, because the user
1527 // hasn't selected anything
1528 //
1529 // this flag gets reset in the message handler. Every subsequent
1530 // message gets handled.
1531 //
1532
1533 //
1534 // we have to make sure that we only do this once. Typically,
1535 // we would do this in something like OnInitDialog, but we don't
1536 // have this in Wizards, because the window is a PropertySheet.
1537 // We're considered fully initialized when we receive the first
1538 // selection notice
1539 //
1540 header.LoadString(IDS_MANUFACTURER_HEADING);
1541 m_manufacturerListCtrl.InsertColumn(0, header, LVCFMT_LEFT, -1 );
1542 m_manufacturerSelected = NULL;
1543
1544 header.LoadString(IDS_MODEL_HEADING);
1545 m_modelListCtrl.InsertColumn(0, header, LVCFMT_LEFT, -1 );
1546 m_modelSelected = NULL;
1547
1548 return (err);
1549 }
1550
1551 void CThirdPage::DoDataExchange(CDataExchange* pDX)
1552 {
1553 CPropertyPage::DoDataExchange(pDX);
1554 DDX_Control(pDX, IDC_PRINTER_MANUFACTURER, m_manufacturerListCtrl);
1555 DDX_Control(pDX, IDC_PRINTER_MODEL, m_modelListCtrl);
1556 DDX_Control(pDX, IDC_PRINTER_NAME, m_printerName);
1557 DDX_Control(pDX, IDC_DEFAULT_PRINTER, m_defaultPrinterCtrl);
1558 DDX_Control(pDX, IDC_PRINTER_SELECTION_TEXT, m_printerSelectionText);
1559
1560 }
1561
1562 // ----------------------------------------------------------
1563 // OnSetActive
1564 //
1565 // This function is called by MFC after the window has been
1566 // activated.
1567 //
1568
1569 BOOL
1570 CThirdPage::OnSetActive()
1571 {
1572 CPrinterSetupWizardSheet * psheet;
1573 Printer * printer;
1574 Service * service;
1575
1576 psheet = reinterpret_cast<CPrinterSetupWizardSheet*>(GetParent());
1577 require_quiet( psheet, exit );
1578
1579 psheet->SetWizardButtons( PSWIZB_BACK );
1580
1581 printer = psheet->GetSelectedPrinter();
1582 require_quiet( printer, exit );
1583
1584 service = printer->services.front();
1585 require_quiet( service, exit );
1586
1587 //
1588 // call OnInitPage once
1589 //
1590 if (!m_initialized)
1591 {
1592 OnInitPage();
1593 m_initialized = true;
1594 }
1595
1596 //
1597 // <rdar://problem/4580061> mDNS: Printers added using Bonjour should be set as the default printer.
1598 //
1599 if ( DefaultPrinterExists() )
1600 {
1601 m_defaultPrinterCtrl.SetCheck( BST_UNCHECKED );
1602 printer->deflt = false;
1603 }
1604 else
1605 {
1606 m_defaultPrinterCtrl.SetCheck( BST_CHECKED );
1607 printer->deflt = true;
1608 }
1609
1610 //
1611 // update the UI with the printer name
1612 //
1613 m_printerName.SetWindowText(printer->displayName);
1614
1615 //
1616 // populate the list controls with the manufacturers and models
1617 // from ntprint.inf
1618 //
1619 PopulateUI( m_manufacturers );
1620
1621 //
1622 // and try and match the printer
1623 //
1624
1625 if ( psheet->GetLastPage() == psheet->GetPage(1) )
1626 {
1627 MatchPrinter( m_manufacturers, printer, service, true );
1628 }
1629 else
1630 {
1631 SelectMatch(printer, service, m_manufacturerSelected, m_modelSelected);
1632 }
1633
1634 exit:
1635
1636 return CPropertyPage::OnSetActive();
1637 }
1638
1639 BOOL
1640 CThirdPage::OnKillActive()
1641 {
1642 CPrinterSetupWizardSheet * psheet;
1643
1644 psheet = reinterpret_cast<CPrinterSetupWizardSheet*>(GetParent());
1645 require_quiet( psheet, exit );
1646
1647 psheet->SetLastPage(this);
1648
1649 exit:
1650
1651 return CPropertyPage::OnKillActive();
1652 }
1653
1654 // -------------------------------------------------------
1655 // PopulateUI
1656 //
1657 // This function is called to populate the list of manufacturers
1658 //
1659 OSStatus
1660 CThirdPage::PopulateUI(Manufacturers & manufacturers)
1661 {
1662 Manufacturers::iterator iter;
1663
1664 m_manufacturerListCtrl.DeleteAllItems();
1665
1666 for (iter = manufacturers.begin(); iter != manufacturers.end(); iter++)
1667 {
1668 int nIndex;
1669
1670 Manufacturer * manufacturer = iter->second;
1671
1672 nIndex = m_manufacturerListCtrl.InsertItem(0, manufacturer->name);
1673
1674 m_manufacturerListCtrl.SetItemData(nIndex, (DWORD_PTR) manufacturer);
1675
1676 m_manufacturerListCtrl.SetColumnWidth( 0, LVSCW_AUTOSIZE_USEHEADER );
1677 }
1678
1679 return 0;
1680 }
1681
1682 BEGIN_MESSAGE_MAP(CThirdPage, CPropertyPage)
1683 ON_NOTIFY(LVN_ITEMCHANGED, IDC_PRINTER_MANUFACTURER, OnLvnItemchangedManufacturer)
1684 ON_NOTIFY(LVN_ITEMCHANGED, IDC_PRINTER_MODEL, OnLvnItemchangedPrinterModel)
1685 ON_BN_CLICKED(IDC_DEFAULT_PRINTER, OnBnClickedDefaultPrinter)
1686 ON_BN_CLICKED(IDC_HAVE_DISK, OnBnClickedHaveDisk)
1687 END_MESSAGE_MAP()
1688
1689 // CThirdPage message handlers
1690 void CThirdPage::OnLvnItemchangedManufacturer(NMHDR *pNMHDR, LRESULT *pResult)
1691 {
1692 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
1693
1694 POSITION p = m_manufacturerListCtrl.GetFirstSelectedItemPosition();
1695 int nSelected = m_manufacturerListCtrl.GetNextSelectedItem(p);
1696
1697 if (nSelected != -1)
1698 {
1699 m_manufacturerSelected = (Manufacturer*) m_manufacturerListCtrl.GetItemData(nSelected);
1700
1701 m_modelListCtrl.SetRedraw(FALSE);
1702
1703 m_modelListCtrl.DeleteAllItems();
1704 m_modelSelected = NULL;
1705
1706 Models::iterator iter;
1707
1708 for (iter = m_manufacturerSelected->models.begin(); iter != m_manufacturerSelected->models.end(); iter++)
1709 {
1710 Model * model = *iter;
1711
1712 int nItem = m_modelListCtrl.InsertItem( 0, model->displayName );
1713
1714 m_modelListCtrl.SetItemData(nItem, (DWORD_PTR) model);
1715
1716 m_modelListCtrl.SetColumnWidth( 0, LVSCW_AUTOSIZE_USEHEADER );
1717 }
1718
1719 m_modelListCtrl.SetRedraw(TRUE);
1720 }
1721
1722 *pResult = 0;
1723 }
1724
1725 void CThirdPage::OnLvnItemchangedPrinterModel(NMHDR *pNMHDR, LRESULT *pResult)
1726 {
1727 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
1728
1729 CPrinterSetupWizardSheet * psheet;
1730 Printer * printer;
1731 Service * service;
1732
1733 psheet = reinterpret_cast<CPrinterSetupWizardSheet*>(GetParent());
1734 require_quiet( psheet, exit );
1735
1736 printer = psheet->GetSelectedPrinter();
1737 require_quiet( printer, exit );
1738
1739 service = printer->services.front();
1740 require_quiet( service, exit );
1741
1742 check ( m_manufacturerSelected );
1743
1744 POSITION p = m_modelListCtrl.GetFirstSelectedItemPosition();
1745 int nSelected = m_modelListCtrl.GetNextSelectedItem(p);
1746
1747 if (nSelected != -1)
1748 {
1749 m_modelSelected = (Model*) m_modelListCtrl.GetItemData(nSelected);
1750
1751 CopyPrinterSettings( printer, service, m_manufacturerSelected, m_modelSelected );
1752
1753 psheet->SetWizardButtons(PSWIZB_BACK|PSWIZB_NEXT);
1754 }
1755 else
1756 {
1757 psheet->SetWizardButtons(PSWIZB_BACK);
1758 }
1759
1760 exit:
1761
1762 *pResult = 0;
1763 }
1764
1765 void CThirdPage::OnBnClickedDefaultPrinter()
1766 {
1767 CPrinterSetupWizardSheet * psheet;
1768 Printer * printer;
1769
1770 psheet = reinterpret_cast<CPrinterSetupWizardSheet*>(GetParent());
1771 require_quiet( psheet, exit );
1772
1773 printer = psheet->GetSelectedPrinter();
1774 require_quiet( printer, exit );
1775
1776 printer->deflt = ( m_defaultPrinterCtrl.GetCheck() == BST_CHECKED ) ? true : false;
1777
1778 exit:
1779
1780 return;
1781 }
1782
1783 void CThirdPage::OnBnClickedHaveDisk()
1784 {
1785 CPrinterSetupWizardSheet * psheet;
1786 Printer * printer;
1787 Service * service;
1788 Manufacturers manufacturers;
1789
1790 CFileDialog dlg(TRUE, NULL, NULL, OFN_HIDEREADONLY|OFN_FILEMUSTEXIST, L"Setup Information (*.inf)|*.inf||", this);
1791
1792 psheet = reinterpret_cast<CPrinterSetupWizardSheet*>(GetParent());
1793 require_quiet( psheet, exit );
1794
1795 printer = psheet->GetSelectedPrinter();
1796 require_quiet( printer, exit );
1797
1798 service = printer->services.front();
1799 require_quiet( service, exit );
1800
1801 for ( ;; )
1802 {
1803 if ( dlg.DoModal() == IDOK )
1804 {
1805 CString filename = dlg.GetPathName();
1806
1807 LoadPrintDriverDefsFromFile( manufacturers, filename, true );
1808
1809 // Sanity check
1810
1811 if ( manufacturers.size() > 0 )
1812 {
1813 PopulateUI( manufacturers );
1814
1815 MatchPrinter( manufacturers, printer, service, false );
1816
1817 break;
1818 }
1819 }
1820 else
1821 {
1822 break;
1823 }
1824 }
1825
1826 exit:
1827
1828 return;
1829 }