]> git.saurik.com Git - apple/mdnsresponder.git/blob - Clients/PrinterSetupWizard/ThirdPage.cpp
mDNSResponder-98.tar.gz
[apple/mdnsresponder.git] / Clients / PrinterSetupWizard / ThirdPage.cpp
1 /*
2 * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22
23 Change History (most recent first):
24
25 $Log: ThirdPage.cpp,v $
26 Revision 1.14 2005/01/25 08:55:54 shersche
27 <rdar://problem/3911084> Load icons at run-time from resource DLL
28 Bug #: 3911084
29
30 Revision 1.13 2005/01/06 08:15:45 shersche
31 Append queue name to end of LPR port name, correctly build port name when queue name is absent
32
33 Revision 1.12 2005/01/05 01:06:12 shersche
34 <rdar://problem/3841218> Strip the first substring off the product key if an initial match can't be found with the whole product key.
35 Bug #: 3841218
36
37 Revision 1.11 2004/12/29 18:53:38 shersche
38 <rdar://problem/3725106>
39 <rdar://problem/3737413> Added support for LPR and IPP protocols as well as support for obtaining multiple text records. Reorganized and simplified codebase.
40 Bug #: 3725106, 3737413
41
42 Revision 1.10 2004/10/11 22:55:34 shersche
43 <rdar://problem/3827624> Use the IP port number when deriving the printer port name.
44 Bug #: 3827624
45
46 Revision 1.9 2004/06/27 23:08:00 shersche
47 code cleanup, make sure EnumPrintDrivers returns non-zero value, ignore comments in inf files
48
49 Revision 1.8 2004/06/27 08:06:45 shersche
50 Parse [Strings] section of inf file
51
52 Revision 1.7 2004/06/26 04:00:05 shersche
53 fix warnings compiling in debug mode
54 Submitted by: herscher
55
56 Revision 1.6 2004/06/26 03:19:57 shersche
57 clean up warning messages
58
59 Submitted by: herscher
60
61 Revision 1.5 2004/06/25 05:06:02 shersche
62 Trim whitespace from key/value pairs when parsing inf files
63 Submitted by: herscher
64
65 Revision 1.4 2004/06/25 02:44:13 shersche
66 Tweaked code to handle Xerox Phaser printer identification
67 Submitted by: herscher
68
69 Revision 1.3 2004/06/25 02:27:58 shersche
70 Do a CListCtrl::FindItem() before calling CListCtrl::SetItemState().
71 Submitted by: herscher
72
73 Revision 1.2 2004/06/23 18:09:23 shersche
74 Normalize tag names when parsing inf files.
75 Submitted by: herscher
76
77 Revision 1.1 2004/06/18 04:36:58 rpantos
78 First checked in
79
80
81 */
82
83 #include "stdafx.h"
84 #include "PrinterSetupWizardApp.h"
85 #include "PrinterSetupWizardSheet.h"
86 #include "ThirdPage.h"
87 #include "StdioFileEx.h"
88 #include <dns_sd.h>
89 #include <tcpxcv.h>
90 #include <winspool.h>
91
92 // local variable is initialize but not referenced
93 #pragma warning(disable:4189)
94
95
96 //
97 // This is the printer description file that is shipped
98 // with Windows
99 //
100 #define kNTPrintFile L"inf\\ntprint.inf"
101
102 //
103 // These are pre-defined names for Generic manufacturer and model
104 //
105 #define kGenericManufacturer L"Generic"
106 #define kGenericModel L"Generic / Text Only"
107
108 //
109 // states for parsing ntprint.inf
110 //
111 enum PrinterParsingState
112 {
113 Looking,
114 ParsingManufacturers,
115 ParsingModels,
116 ParsingStrings
117 };
118
119
120 // CThirdPage dialog
121
122 IMPLEMENT_DYNAMIC(CThirdPage, CPropertyPage)
123 CThirdPage::CThirdPage()
124 : CPropertyPage(CThirdPage::IDD),
125 m_initialized(false),
126 m_printerImage( NULL )
127 {
128 m_psp.dwFlags &= ~(PSP_HASHELP);
129 m_psp.dwFlags |= PSP_DEFAULT|PSP_USEHEADERTITLE|PSP_USEHEADERSUBTITLE;
130
131 m_psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_INSTALL_TITLE);
132 m_psp.pszHeaderSubTitle = MAKEINTRESOURCE(IDS_INSTALL_SUBTITLE);
133 }
134
135
136 CThirdPage::~CThirdPage()
137 {
138 //
139 // clean up all the printer manufacturers
140 //
141 while (m_manufacturers.size())
142 {
143 Manufacturers::iterator iter = m_manufacturers.begin();
144
145 while (iter->second->models.size())
146 {
147 Models::iterator it = iter->second->models.begin();
148
149 Model * model = *it;
150
151 delete model;
152
153 iter->second->models.erase(it);
154 }
155
156 delete iter->second;
157
158 m_manufacturers.erase(iter);
159 }
160 }
161
162
163 // ----------------------------------------------------
164 // SelectMatch
165 //
166 // SelectMatch will do all the UI work associated with
167 // selected a manufacturer and model of printer. It also
168 // makes sure the printer object is update with the
169 // latest settings
170 //
171 // ----------------------------------------------------
172 void
173 CThirdPage::SelectMatch(Printer * printer, Service * service, Manufacturer * manufacturer, Model * model)
174 {
175 LVFINDINFO info;
176 int nIndex;
177
178 check( printer != NULL );
179 check( manufacturer != NULL );
180 check( model != NULL );
181
182 Manufacturers manufacturers;
183 manufacturers[manufacturer->name] = manufacturer;
184
185 PopulateUI( manufacturers );
186
187 //
188 // select the manufacturer
189 //
190 info.flags = LVFI_STRING;
191 info.psz = manufacturer->name;
192
193 nIndex = m_manufacturerListCtrl.FindItem(&info);
194
195 if (nIndex != -1)
196 {
197 m_manufacturerListCtrl.SetItemState(nIndex, LVIS_SELECTED, LVIS_SELECTED);
198 m_manufacturerListCtrl.EnsureVisible(nIndex, FALSE);
199 }
200
201 //
202 // select the model
203 //
204 info.flags = LVFI_STRING;
205 info.psz = model->name;
206
207 nIndex = m_modelListCtrl.FindItem(&info);
208
209 if (nIndex != -1)
210 {
211 m_modelListCtrl.SetItemState(nIndex, LVIS_SELECTED, LVIS_SELECTED);
212 m_modelListCtrl.EnsureVisible(nIndex, FALSE);
213
214 m_modelListCtrl.SetFocus();
215 }
216
217 CopyPrinterSettings( printer, service, manufacturer, model );
218 }
219
220
221 // --------------------------------------------------------
222 // CopyPrinterSettings
223 //
224 // This function makes sure that the printer object has the
225 // latest settings from the manufacturer and model objects
226 // --------------------------------------------------------
227
228 void
229 CThirdPage::CopyPrinterSettings( Printer * printer, Service * service, Manufacturer * manufacturer, Model * model )
230 {
231 printer->manufacturer = manufacturer->name;
232 printer->model = model->name;
233 printer->driverInstalled = model->driverInstalled;
234 printer->infFileName = model->infFileName;
235
236 if ( service->type == kPDLServiceType )
237 {
238 printer->portName.Format(L"IP_%s.%d", static_cast<LPCTSTR>(service->hostname), service->portNumber);
239 service->protocol = L"Raw";
240 }
241 else if ( service->type == kLPRServiceType )
242 {
243 Queue * q = service->queues.front();
244 check( q );
245
246 if ( q->name.GetLength() > 0 )
247 {
248 printer->portName.Format(L"LPR_%s.%d.%s", static_cast<LPCTSTR>(service->hostname), service->portNumber, static_cast<LPCTSTR>(q->name) );
249 }
250 else
251 {
252 printer->portName.Format(L"LPR_%s.%d", static_cast<LPCTSTR>(service->hostname), service->portNumber);
253 }
254
255 service->protocol = L"LPR";
256 }
257 else if ( service->type == kIPPServiceType )
258 {
259 Queue * q = service->queues.front();
260 check( q );
261
262 if ( q->name.GetLength() > 0 )
263 {
264 printer->portName.Format(L"http://%s:%d/printers/%s", static_cast<LPCTSTR>(service->hostname), service->portNumber, static_cast<LPCTSTR>(q->name) );
265 }
266 else
267 {
268 printer->portName.Format(L"http://%s:%d/", static_cast<LPCTSTR>(service->hostname), service->portNumber );
269 }
270
271 service->protocol = L"IPP";
272 }
273 }
274
275
276 // ------------------------------------------------------
277 // LoadPrintDriverDefsFromFile
278 //
279 // This function does all the heavy lifting in parsing inf
280 // files. It is called to parse both ntprint.inf, and driver
281 // files that might be shipped on a printer's installation
282 // disk
283 //
284 // The inf file is not totally parsed. I only want to determine
285 // the manufacturer and models that are involved. I leave it
286 // to printui.dll to actually copy the driver files to the
287 // right places.
288 //
289 // I was aiming to parse as little as I could so as not to
290 // duplicate the parsing code that is contained in Windows. There
291 // are no public APIs for parsing inf files.
292 //
293 // That part of the inf file that we're interested in has a fairly
294 // easy format. Tags are strings that are enclosed in brackets.
295 // We are only interested in [MANUFACTURERS] and models.
296 //
297 // The only potentially opaque thing about this function is the
298 // checkForDuplicateModels flag. The problem here is that ntprint.inf
299 // doesn't contain duplicate models, and it has hundreds of models
300 // listed. You wouldn't check for duplicates there. But oftentimes,
301 // loading different windows print driver files contain multiple
302 // entries for the same printer. You don't want the UI to display
303 // the same printer multiple times, so in that case, you would ask
304 // this function to check for multiple models.
305
306 OSStatus
307 CThirdPage::LoadPrintDriverDefsFromFile(Manufacturers & manufacturers, const CString & filename, bool checkForDuplicateModels )
308 {
309 PrinterParsingState state = Looking;
310 Manufacturers::iterator iter = manufacturers.end();
311 CStdioFileEx file;
312 CFileException feError;
313 CString s;
314 OSStatus err;
315 BOOL ok;
316
317 typedef std::map<CString, CString> StringMap;
318
319 StringMap strings;
320
321 ok = file.Open( filename, CFile::modeRead|CFile::typeText, &feError);
322 err = translate_errno( ok, errno_compat(), kUnknownErr );
323 require_noerr( err, exit );
324
325 check ( state == Looking );
326 check ( iter == manufacturers.end() );
327
328 //
329 // first, parse the file looking for string sections
330 //
331 while (file.ReadString(s))
332 {
333 //
334 // check for comment
335 //
336 if (s.Find(';') == 0)
337 {
338 continue;
339 }
340
341 //
342 // check for tag
343 //
344 else if (s.Find('[') == 0)
345 {
346 //
347 // handle any capitalization issues here
348 //
349 CString tag = s;
350
351 tag.MakeLower();
352
353 if (tag == L"[strings]")
354 {
355 state = ParsingStrings;
356 }
357 else
358 {
359 state = Looking;
360 }
361 }
362 else
363 {
364 switch (state)
365 {
366 case ParsingStrings:
367 {
368 int curPos = 0;
369
370 if (s.GetLength() > 0)
371 {
372 CString key = s.Tokenize(L"=",curPos);
373 CString val = s.Tokenize(L"=",curPos);
374
375 //
376 // get rid of all delimiters
377 //
378 key.Trim();
379 val.Remove('"');
380
381 //
382 // and store it
383 //
384 strings[key] = val;
385 }
386 }
387 break;
388 }
389 }
390 }
391
392 file.Close();
393
394 ok = file.Open( filename, CFile::modeRead|CFile::typeText, &feError);
395 err = translate_errno( ok, errno_compat(), kUnknownErr );
396 require_noerr( err, exit );
397
398 state = Looking;
399
400 check ( iter == manufacturers.end() );
401
402 while (file.ReadString(s))
403 {
404 //
405 // check for comment
406 //
407 if (s.Find(';') == 0)
408 {
409 continue;
410 }
411
412 //
413 // check for tag
414 //
415 else if (s.Find('[') == 0)
416 {
417 //
418 // handle any capitalization issues here
419 //
420 CString tag = s;
421
422 tag.MakeLower();
423
424 if (tag == L"[manufacturer]")
425 {
426 state = ParsingManufacturers;
427 }
428 else
429 {
430 // remove the leading and trailing delimiters
431 //
432 s.Remove('[');
433 s.Remove(']');
434
435 // check to see if this is a printer entry
436 //
437 iter = manufacturers.find(s);
438
439 if (iter != manufacturers.end())
440 {
441 state = ParsingModels;
442 }
443 else
444 {
445 state = Looking;
446 }
447 }
448 }
449 //
450 // only look at this if the line isn't empty, or
451 // if it isn't a comment
452 //
453 else if ((s.GetLength() > 0) && (s.Find(';') != 0))
454 {
455 switch (state)
456 {
457 //
458 // if we're parsing manufacturers, then we will parse
459 // an entry of the form key=val, where key is a delimited
460 // string specifying a manufacturer name, and val is
461 // a tag that is used later in the file. the key is
462 // delimited by either '"' (quotes) or '%' (percent sign).
463 //
464 // the tag is used further down the file when models are
465 // declared. this allows multiple manufacturers to exist
466 // in a single inf file.
467 //
468 case ParsingManufacturers:
469 {
470 Manufacturer * manufacturer;
471 int curPos = 0;
472
473 CString key = s.Tokenize(L"=",curPos);
474 CString val = s.Tokenize(L"=",curPos);
475
476 try
477 {
478 manufacturer = new Manufacturer;
479 }
480 catch (...)
481 {
482 manufacturer = NULL;
483 }
484
485 require_action( manufacturer, exit, err = kNoMemoryErr );
486
487 //
488 // if it's a variable, look it up
489 //
490 if (key.Find('%') == 0)
491 {
492 StringMap::iterator it;
493
494 key.Remove('%');
495
496 it = strings.find(key);
497
498 if (it != strings.end())
499 {
500 key = it->second;
501 }
502 }
503 else
504 {
505 key.Remove('"');
506 }
507
508 val.TrimLeft();
509 val.TrimRight();
510
511 //
512 // why is there no consistency in inf files?
513 //
514 if (val.GetLength() == 0)
515 {
516 val = key;
517 }
518
519 //
520 // fix the manufacturer name if necessary
521 //
522 curPos = 0;
523 val = val.Tokenize(L",", curPos);
524
525 manufacturer->name = NormalizeManufacturerName( key );
526 manufacturer->tag = val;
527
528 manufacturers[val] = manufacturer;
529 }
530 break;
531
532 case ParsingModels:
533 {
534 check( iter != manufacturers.end() );
535
536 Model * model;
537 int curPos = 0;
538
539 CString name = s.Tokenize(L"=",curPos);
540 CString description = s.Tokenize(L"=",curPos);
541
542 if (name.Find('%') == 0)
543 {
544 StringMap::iterator it;
545
546 name.Remove('%');
547
548 it = strings.find(name);
549
550 if (it != strings.end())
551 {
552 name = it->second;
553 }
554 }
555 else
556 {
557 name.Remove('"');
558 }
559
560 name.Trim();
561 description.Trim();
562
563 //
564 // If true, see if we've seen this guy before
565 //
566 if (checkForDuplicateModels == true)
567 {
568 if ( MatchModel( iter->second, ConvertToModelName( name ) ) != NULL )
569 {
570 continue;
571 }
572 }
573
574 try
575 {
576 model = new Model;
577 }
578 catch (...)
579 {
580 model = NULL;
581 }
582
583 require_action( model, exit, err = kNoMemoryErr );
584
585 model->infFileName = filename;
586 model->name = name;
587 model->driverInstalled = false;
588
589 iter->second->models.push_back(model);
590 }
591 break;
592
593 default:
594 {
595 // pay no attention if we are in any other state
596 }
597 break;
598 }
599 }
600 }
601
602 exit:
603
604 file.Close();
605
606 return (err);
607 }
608
609
610 // -------------------------------------------------------
611 // LoadPrintDriverDefs
612 //
613 // This function is responsible for loading the print driver
614 // definitions of all print drivers that have been installed
615 // on this machine.
616 // -------------------------------------------------------
617 OSStatus
618 CThirdPage::LoadPrintDriverDefs( Manufacturers & manufacturers )
619 {
620 BYTE * buffer = NULL;
621 DWORD bytesReceived = 0;
622 DWORD numPrinters = 0;
623 OSStatus err = 0;
624 BOOL ok;
625
626 //
627 // like a lot of win32 calls, we call this first to get the
628 // size of the buffer we need.
629 //
630 EnumPrinterDrivers(NULL, L"all", 6, NULL, 0, &bytesReceived, &numPrinters);
631
632 if (bytesReceived > 0)
633 {
634 try
635 {
636 buffer = new BYTE[bytesReceived];
637 }
638 catch (...)
639 {
640 buffer = NULL;
641 }
642
643 require_action( buffer, exit, err = kNoMemoryErr );
644
645 //
646 // this call gets the real info
647 //
648 ok = EnumPrinterDrivers(NULL, L"all", 6, buffer, bytesReceived, &bytesReceived, &numPrinters);
649 err = translate_errno( ok, errno_compat(), kUnknownErr );
650 require_noerr( err, exit );
651
652 DRIVER_INFO_6 * info = (DRIVER_INFO_6*) buffer;
653
654 for (DWORD i = 0; i < numPrinters; i++)
655 {
656 Manufacturer * manufacturer;
657 Model * model;
658 CString name;
659
660 //
661 // skip over anything that doesn't have a manufacturer field. This
662 // fixes a bug that I noticed that occurred after I installed
663 // ProComm. This program add a print driver with no manufacturer
664 // that screwed up this wizard.
665 //
666 if (info[i].pszMfgName == NULL)
667 {
668 continue;
669 }
670
671 //
672 // look for manufacturer
673 //
674 Manufacturers::iterator iter;
675
676 //
677 // save the name
678 //
679 name = NormalizeManufacturerName( info[i].pszMfgName );
680
681 iter = manufacturers.find(name);
682
683 if (iter != manufacturers.end())
684 {
685 manufacturer = iter->second;
686 }
687 else
688 {
689 try
690 {
691 manufacturer = new Manufacturer;
692 }
693 catch (...)
694 {
695 manufacturer = NULL;
696 }
697
698 require_action( manufacturer, exit, err = kNoMemoryErr );
699
700 manufacturer->name = name;
701
702 manufacturers[name] = manufacturer;
703 }
704
705 //
706 // now look to see if we have already seen this guy. this could
707 // happen if we have already installed printers that are described
708 // in ntprint.inf. the extant drivers will show up in EnumPrinterDrivers
709 // but we have already loaded their info
710 //
711 //
712 if ( MatchModel( manufacturer, ConvertToModelName( info[i].pName ) ) == NULL )
713 {
714 try
715 {
716 model = new Model;
717 }
718 catch (...)
719 {
720 model = NULL;
721 }
722
723 require_action( model, exit, err = kNoMemoryErr );
724
725 model->name = info[i].pName;
726 model->driverInstalled = true;
727
728 manufacturer->models.push_back(model);
729 }
730 }
731 }
732
733 exit:
734
735 if (buffer != NULL)
736 {
737 delete [] buffer;
738 }
739
740 return err;
741 }
742
743
744 // ------------------------------------------------------
745 // ConvertToManufacturerName
746 //
747 // This function is responsible for tweaking the
748 // name so that subsequent string operations won't fail because
749 // of capitalizations/different names for the same manufacturer
750 // (i.e. Hewlett-Packard/HP/Hewlett Packard)
751 //
752 CString
753 CThirdPage::ConvertToManufacturerName( const CString & name )
754 {
755 //
756 // first we're going to convert all the characters to lower
757 // case
758 //
759 CString lower = name;
760 lower.MakeLower();
761
762 //
763 // now we're going to check to see if the string says "hewlett-packard",
764 // because sometimes they refer to themselves as "hewlett-packard", and
765 // sometimes they refer to themselves as "hp".
766 //
767 if ( lower == L"hewlett-packard")
768 {
769 lower = "hp";
770 }
771
772 //
773 // tweak for Xerox Phaser, which doesn't announce itself
774 // as a xerox
775 //
776 else if ( lower.Find( L"phaser", 0 ) != -1 )
777 {
778 lower = "xerox";
779 }
780
781 return lower;
782 }
783
784
785 // ------------------------------------------------------
786 // ConvertToModelName
787 //
788 // This function is responsible for ensuring that subsequent
789 // string operations don't fail because of differing capitalization
790 // schemes and the like
791 // ------------------------------------------------------
792
793 CString
794 CThirdPage::ConvertToModelName( const CString & name )
795 {
796 //
797 // convert it to lowercase
798 //
799 CString lower = name;
800 lower.MakeLower();
801
802 return lower;
803 }
804
805
806 // ------------------------------------------------------
807 // NormalizeManufacturerName
808 //
809 // This function is responsible for tweaking the manufacturer
810 // name so that there are no aliases for vendors
811 //
812 CString
813 CThirdPage::NormalizeManufacturerName( const CString & name )
814 {
815 CString normalized = name;
816
817 //
818 // now we're going to check to see if the string says "hewlett-packard",
819 // because sometimes they refer to themselves as "hewlett-packard", and
820 // sometimes they refer to themselves as "hp".
821 //
822 if ( normalized == L"Hewlett-Packard")
823 {
824 normalized = "HP";
825 }
826
827 return normalized;
828 }
829
830
831 // -------------------------------------------------------
832 // MatchPrinter
833 //
834 // This function is responsible for matching a printer
835 // to a list of manufacturers and models. It calls
836 // MatchManufacturer and MatchModel in turn.
837 //
838
839 OSStatus CThirdPage::MatchPrinter(Manufacturers & manufacturers, Printer * printer, Service * service)
840 {
841 CString normalizedProductName;
842 Manufacturer * manufacturer = NULL;
843 Model * model = NULL;
844 bool found = false;
845 CString text;
846 OSStatus err = kNoErr;
847
848 //
849 // first look to see if we have a usb_MFG descriptor
850 //
851 if (service->usb_MFG.GetLength() > 0)
852 {
853 manufacturer = MatchManufacturer( manufacturers, ConvertToManufacturerName ( service->usb_MFG ) );
854 }
855
856 if ( manufacturer == NULL )
857 {
858 service->product.Remove('(');
859 service->product.Remove(')');
860
861 manufacturer = MatchManufacturer( manufacturers, ConvertToManufacturerName ( service->product ) );
862 }
863
864 //
865 // if we found the manufacturer, then start looking for the model
866 //
867 if ( manufacturer != NULL )
868 {
869 if (service->usb_MDL.GetLength() > 0)
870 {
871 model = MatchModel ( manufacturer, ConvertToModelName ( service->usb_MDL ) );
872 }
873
874 if ( model == NULL )
875 {
876 service->product.Remove('(');
877 service->product.Remove(')');
878
879 model = MatchModel ( manufacturer, ConvertToModelName ( service->product ) );
880 }
881
882 if ( model != NULL )
883 {
884 SelectMatch(printer, service, manufacturer, model);
885 found = true;
886 }
887 }
888
889 //
890 // display a message to the user based on whether we could match
891 // this printer
892 //
893 if (found)
894 {
895 text.LoadString(IDS_PRINTER_MATCH_GOOD);
896 }
897 else
898 {
899 text.LoadString(IDS_PRINTER_MATCH_BAD);
900
901 //
902 // if there was any crud in this list from before, get rid of it now
903 //
904 m_modelListCtrl.DeleteAllItems();
905
906 //
907 // select the manufacturer if we found one
908 //
909 if (manufacturer != NULL)
910 {
911 LVFINDINFO info;
912 int nIndex;
913
914 //
915 // select the manufacturer
916 //
917 info.flags = LVFI_STRING;
918 info.psz = manufacturer->name;
919
920 nIndex = m_manufacturerListCtrl.FindItem(&info);
921
922 if (nIndex != -1)
923 {
924 m_manufacturerListCtrl.SetItemState(nIndex, LVIS_SELECTED, LVIS_SELECTED);
925 m_manufacturerListCtrl.EnsureVisible(nIndex, FALSE);
926 }
927 }
928 }
929
930 m_printerSelectionText.SetWindowText(text);
931
932 return err;
933 }
934
935
936 // ------------------------------------------------------
937 // MatchManufacturer
938 //
939 // This function is responsible for finding a manufacturer
940 // object from a string name. It does a CString::Find, which
941 // is like strstr, so it doesn't have to do an exact match
942 //
943 // If it can't find a match, NULL is returned
944 // ------------------------------------------------------
945
946 Manufacturer*
947 CThirdPage::MatchManufacturer( Manufacturers & manufacturers, const CString & name)
948 {
949 Manufacturers::iterator iter;
950
951 for (iter = manufacturers.begin(); iter != manufacturers.end(); iter++)
952 {
953 //
954 // we're going to convert all the manufacturer names to lower case,
955 // so we match the name passed in.
956 //
957 CString lower = iter->second->name;
958 lower.MakeLower();
959
960 //
961 // now try and find the lowered string in the name passed in.
962 //
963 if (name.Find(lower) != -1)
964 {
965 return iter->second;
966 }
967 }
968
969 return NULL;
970 }
971
972
973 // -------------------------------------------------------
974 // MatchModel
975 //
976 // This function is responsible for matching a model from
977 // a name. It does a CString::Find(), which works like strstr,
978 // so it doesn't rely on doing an exact string match.
979 //
980
981 Model*
982 CThirdPage::MatchModel(Manufacturer * manufacturer, const CString & name)
983 {
984 Models::iterator iter;
985
986 iter = manufacturer->models.begin();
987
988 for (iter = manufacturer->models.begin(); iter != manufacturer->models.end(); iter++)
989 {
990 Model * model = *iter;
991
992 //
993 // convert the model name to lower case
994 //
995 CString lowered = model->name;
996 lowered.MakeLower();
997
998 if (lowered.Find( name ) != -1)
999 {
1000 return model;
1001 }
1002
1003 //
1004 // <rdar://problem/3841218>
1005 // try removing the first substring and search again
1006 //
1007
1008 if ( name.Find(' ') != -1 )
1009 {
1010 CString altered = name;
1011 altered.Delete( 0, altered.Find(' ') + 1 );
1012
1013 if ( lowered.Find( altered ) != -1 )
1014 {
1015 return model;
1016 }
1017 }
1018 }
1019
1020 return NULL;
1021 }
1022
1023
1024
1025 // -----------------------------------------------------------
1026 // OnInitPage
1027 //
1028 // This function is responsible for doing initialization that
1029 // only occurs once during a run of the wizard
1030 //
1031
1032 OSStatus CThirdPage::OnInitPage()
1033 {
1034 static const int bufferSize = 32768;
1035 TCHAR windowsDirectory[bufferSize];
1036 CString header;
1037 CString ntPrint;
1038 OSStatus err;
1039 BOOL ok;
1040
1041 // Load printer icon
1042
1043 check( m_printerImage == NULL );
1044
1045 m_printerImage = (CStatic*) GetDlgItem( IDR_MANIFEST );
1046 check( m_printerImage );
1047
1048 if ( m_printerImage != NULL )
1049 {
1050 m_printerImage->SetIcon( LoadIcon( GetNonLocalizedResources(), MAKEINTRESOURCE( IDI_PRINTER ) ) );
1051 }
1052
1053 //
1054 // The CTreeCtrl widget automatically sends a selection changed
1055 // message which initially we want to ignore, because the user
1056 // hasn't selected anything
1057 //
1058 // this flag gets reset in the message handler. Every subsequent
1059 // message gets handled.
1060 //
1061
1062 //
1063 // we have to make sure that we only do this once. Typically,
1064 // we would do this in something like OnInitDialog, but we don't
1065 // have this in Wizards, because the window is a PropertySheet.
1066 // We're considered fully initialized when we receive the first
1067 // selection notice
1068 //
1069 header.LoadString(IDS_MANUFACTURER_HEADING);
1070 m_manufacturerListCtrl.InsertColumn(0, header, LVCFMT_LEFT, 138);
1071 m_manufacturerSelected = NULL;
1072
1073 header.LoadString(IDS_MODEL_HEADING);
1074 m_modelListCtrl.InsertColumn(0, header, LVCFMT_LEFT, 247);
1075 m_modelSelected = NULL;
1076
1077 //
1078 // load printers from ntprint.inf
1079 //
1080 ok = GetWindowsDirectory( windowsDirectory, bufferSize );
1081 err = translate_errno( ok, errno_compat(), kUnknownErr );
1082 require_noerr( err, exit );
1083
1084 ntPrint.Format(L"%s\\%s", windowsDirectory, kNTPrintFile);
1085 err = LoadPrintDriverDefsFromFile( m_manufacturers, ntPrint, false );
1086 require_noerr(err, exit);
1087
1088 //
1089 // load printer drivers that have been installed on this machine
1090 //
1091 err = LoadPrintDriverDefs( m_manufacturers );
1092 require_noerr(err, exit);
1093
1094 exit:
1095
1096 return (err);
1097 }
1098
1099
1100 void CThirdPage::DoDataExchange(CDataExchange* pDX)
1101 {
1102 CPropertyPage::DoDataExchange(pDX);
1103 DDX_Control(pDX, IDC_PRINTER_MANUFACTURER, m_manufacturerListCtrl);
1104 DDX_Control(pDX, IDC_PRINTER_MODEL, m_modelListCtrl);
1105 DDX_Control(pDX, IDC_PRINTER_NAME, m_printerName);
1106 DDX_Control(pDX, IDC_DEFAULT_PRINTER, m_defaultPrinterCtrl);
1107 DDX_Control(pDX, IDC_PRINTER_SELECTION_TEXT, m_printerSelectionText);
1108 }
1109
1110
1111 // ----------------------------------------------------------
1112 // OnSetActive
1113 //
1114 // This function is called by MFC after the window has been
1115 // activated.
1116 //
1117
1118 BOOL
1119 CThirdPage::OnSetActive()
1120 {
1121 CPrinterSetupWizardSheet * psheet;
1122 Printer * printer;
1123 Service * service;
1124
1125 psheet = reinterpret_cast<CPrinterSetupWizardSheet*>(GetParent());
1126 require_quiet( psheet, exit );
1127
1128 if ((m_manufacturerListCtrl.GetFirstSelectedItemPosition() != NULL) &&
1129 (m_modelListCtrl.GetFirstSelectedItemPosition() != NULL))
1130 {
1131 psheet->SetWizardButtons( PSWIZB_BACK|PSWIZB_NEXT );
1132 }
1133 else
1134 {
1135 psheet->SetWizardButtons( PSWIZB_BACK );
1136 }
1137
1138 printer = psheet->GetSelectedPrinter();
1139 require_quiet( printer, exit );
1140
1141 service = printer->services.front();
1142 require_quiet( service, exit );
1143
1144 //
1145 // call OnInitPage once
1146 //
1147 if (!m_initialized)
1148 {
1149 OnInitPage();
1150 m_initialized = true;
1151 }
1152
1153 //
1154 // update the UI with the printer name
1155 //
1156 m_printerName.SetWindowText(printer->displayName);
1157
1158 //
1159 // populate the list controls with the manufacturers and models
1160 // from ntprint.inf
1161 //
1162 PopulateUI( m_manufacturers );
1163
1164 //
1165 // and try and match the printer
1166 //
1167 MatchPrinter( m_manufacturers, printer, service );
1168
1169 exit:
1170
1171 return CPropertyPage::OnSetActive();
1172 }
1173
1174
1175 // -------------------------------------------------------
1176 // PopulateUI
1177 //
1178 // This function is called to populate the list of manufacturers
1179 //
1180 OSStatus
1181 CThirdPage::PopulateUI(Manufacturers & manufacturers)
1182 {
1183 Manufacturers::iterator iter;
1184
1185 m_manufacturerListCtrl.DeleteAllItems();
1186
1187 for (iter = manufacturers.begin(); iter != manufacturers.end(); iter++)
1188 {
1189 int nIndex;
1190
1191 Manufacturer * manufacturer = iter->second;
1192
1193 nIndex = m_manufacturerListCtrl.InsertItem(0, manufacturer->name);
1194
1195 m_manufacturerListCtrl.SetItemData(nIndex, (DWORD_PTR) manufacturer);
1196 }
1197
1198 return 0;
1199 }
1200
1201
1202 BEGIN_MESSAGE_MAP(CThirdPage, CPropertyPage)
1203 ON_NOTIFY(LVN_ITEMCHANGED, IDC_PRINTER_MANUFACTURER, OnLvnItemchangedManufacturer)
1204 ON_NOTIFY(LVN_ITEMCHANGED, IDC_PRINTER_MODEL, OnLvnItemchangedPrinterModel)
1205 ON_BN_CLICKED(IDC_DEFAULT_PRINTER, OnBnClickedDefaultPrinter)
1206 ON_BN_CLICKED(IDC_HAVE_DISK, OnBnClickedHaveDisk)
1207 END_MESSAGE_MAP()
1208
1209
1210 // CThirdPage message handlers
1211 void CThirdPage::OnLvnItemchangedManufacturer(NMHDR *pNMHDR, LRESULT *pResult)
1212 {
1213 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
1214
1215 POSITION p = m_manufacturerListCtrl.GetFirstSelectedItemPosition();
1216 int nSelected = m_manufacturerListCtrl.GetNextSelectedItem(p);
1217
1218 if (nSelected != -1)
1219 {
1220 m_manufacturerSelected = (Manufacturer*) m_manufacturerListCtrl.GetItemData(nSelected);
1221
1222 m_modelListCtrl.SetRedraw(FALSE);
1223
1224 m_modelListCtrl.DeleteAllItems();
1225 m_modelSelected = NULL;
1226
1227 Models::iterator iter;
1228
1229 for (iter = m_manufacturerSelected->models.begin(); iter != m_manufacturerSelected->models.end(); iter++)
1230 {
1231 Model * model = *iter;
1232
1233 int nItem = m_modelListCtrl.InsertItem(0, model->name);
1234
1235 m_modelListCtrl.SetItemData(nItem, (DWORD_PTR) model);
1236 }
1237
1238 m_modelListCtrl.SetRedraw(TRUE);
1239 }
1240
1241 *pResult = 0;
1242 }
1243
1244 void CThirdPage::OnLvnItemchangedPrinterModel(NMHDR *pNMHDR, LRESULT *pResult)
1245 {
1246 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
1247
1248 CPrinterSetupWizardSheet * psheet;
1249 Printer * printer;
1250 Service * service;
1251
1252 psheet = reinterpret_cast<CPrinterSetupWizardSheet*>(GetParent());
1253 require_quiet( psheet, exit );
1254
1255 printer = psheet->GetSelectedPrinter();
1256 require_quiet( printer, exit );
1257
1258 service = printer->services.front();
1259 require_quiet( service, exit );
1260
1261 check ( m_manufacturerSelected );
1262
1263 POSITION p = m_modelListCtrl.GetFirstSelectedItemPosition();
1264 int nSelected = m_modelListCtrl.GetNextSelectedItem(p);
1265
1266 if (nSelected != -1)
1267 {
1268 m_modelSelected = (Model*) m_modelListCtrl.GetItemData(nSelected);
1269
1270 CopyPrinterSettings( printer, service, m_manufacturerSelected, m_modelSelected );
1271
1272 psheet->SetWizardButtons(PSWIZB_BACK|PSWIZB_NEXT);
1273 }
1274 else
1275 {
1276 psheet->SetWizardButtons(PSWIZB_BACK);
1277 }
1278
1279 exit:
1280
1281 *pResult = 0;
1282 }
1283
1284
1285 void CThirdPage::OnBnClickedDefaultPrinter()
1286 {
1287 CPrinterSetupWizardSheet * psheet;
1288 Printer * printer;
1289
1290 psheet = reinterpret_cast<CPrinterSetupWizardSheet*>(GetParent());
1291 require_quiet( psheet, exit );
1292
1293 printer = psheet->GetSelectedPrinter();
1294 require_quiet( printer, exit );
1295
1296 printer->deflt = m_defaultPrinterCtrl.GetState() ? true : false;
1297
1298 exit:
1299
1300 return;
1301 }
1302
1303 void CThirdPage::OnBnClickedHaveDisk()
1304 {
1305 CPrinterSetupWizardSheet * psheet;
1306 Printer * printer;
1307 Service * service;
1308
1309 CFileDialog dlg(TRUE, NULL, NULL, OFN_HIDEREADONLY|OFN_FILEMUSTEXIST, L"Setup Information (*.inf)|*.inf||", this);
1310
1311 psheet = reinterpret_cast<CPrinterSetupWizardSheet*>(GetParent());
1312 require_quiet( psheet, exit );
1313
1314 printer = psheet->GetSelectedPrinter();
1315 require_quiet( printer, exit );
1316
1317 service = printer->services.front();
1318 require_quiet( service, exit );
1319
1320 if ( dlg.DoModal() == IDOK )
1321 {
1322 Manufacturers manufacturers;
1323 CString filename = dlg.GetPathName();
1324
1325 LoadPrintDriverDefsFromFile( manufacturers, filename, true );
1326
1327 PopulateUI( manufacturers );
1328
1329 MatchPrinter( manufacturers, printer, service );
1330 }
1331
1332 exit:
1333
1334 return;
1335 }