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