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 58IMPLEMENT_DYNAMIC(CThirdPage, CPropertyPage) 59CThirdPage::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 136exit: 137 138 return; 139} 140 141 142void 143CThirdPage::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 157CThirdPage::~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// ---------------------------------------------------- 171void 172CThirdPage::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 217void 218CThirdPage::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 232void 233CThirdPage::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// -------------------------------------------------------- 300BOOL 301CThirdPage::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// -------------------------------------------------------- 315void 316CThirdPage::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 362OSStatus 363CThirdPage::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 540exit: 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// ------------------------------------------------------- 559OSStatus 560CThirdPage::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 676exit: 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// ------------------------------------------------------- 694OSStatus 695CThirdPage::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 811exit: 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// 824CString 825CThirdPage::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 864CString 865CThirdPage::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// 882CString 883CThirdPage::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 908OSStatus 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 1090Manufacturer* 1091CThirdPage::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 1124Model* 1125CThirdPage::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// 1173BOOL 1174CThirdPage::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 1206exit: 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 1218OSStatus 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 1262void 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 1280BOOL 1281CThirdPage::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 1350exit: 1351 1352 return CPropertyPage::OnSetActive(); 1353} 1354 1355BOOL 1356CThirdPage::OnKillActive() 1357{ 1358 CPrinterSetupWizardSheet * psheet; 1359 1360 psheet = reinterpret_cast<CPrinterSetupWizardSheet*>(GetParent()); 1361 require_quiet( psheet, exit ); 1362 1363 psheet->SetLastPage(this); 1364 1365exit: 1366 1367 return CPropertyPage::OnKillActive(); 1368} 1369 1370// ------------------------------------------------------- 1371// PopulateUI 1372// 1373// This function is called to populate the list of manufacturers 1374// 1375OSStatus 1376CThirdPage::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 1398BEGIN_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) 1403END_MESSAGE_MAP() 1404 1405// CThirdPage message handlers 1406void 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 1441void 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 1476exit: 1477 1478 *pResult = 0; 1479} 1480 1481void 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 1494exit: 1495 1496 return; 1497} 1498 1499void 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 1561exit: 1562 1563 FreeManufacturers( manufacturers ); 1564 return; 1565} 1566 1567 1568void 1569CThirdPage::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} 1584