1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "printing/printing_context_win.h" 6 7 #include <winspool.h> 8 9 #include <algorithm> 10 11 #include "base/i18n/file_util_icu.h" 12 #include "base/i18n/time_formatting.h" 13 #include "base/message_loop/message_loop.h" 14 #include "base/metrics/histogram.h" 15 #include "base/strings/utf_string_conversions.h" 16 #include "base/time/time.h" 17 #include "base/values.h" 18 #include "base/win/metro.h" 19 #include "printing/backend/print_backend.h" 20 #include "printing/backend/printing_info_win.h" 21 #include "printing/backend/win_helper.h" 22 #include "printing/print_job_constants.h" 23 #include "printing/print_settings_initializer_win.h" 24 #include "printing/printed_document.h" 25 #include "printing/printing_utils.h" 26 #include "printing/units.h" 27 #include "skia/ext/platform_device.h" 28 #include "win8/util/win8_util.h" 29 30 #if defined(USE_AURA) 31 #include "ui/aura/remote_root_window_host_win.h" 32 #include "ui/aura/root_window.h" 33 #include "ui/aura/window.h" 34 #endif 35 36 using base::Time; 37 38 namespace { 39 40 HWND GetRootWindow(gfx::NativeView view) { 41 HWND window = NULL; 42 #if defined(USE_AURA) 43 if (view) 44 window = view->GetDispatcher()->host()->GetAcceleratedWidget(); 45 #else 46 if (view && IsWindow(view)) { 47 window = GetAncestor(view, GA_ROOTOWNER); 48 } 49 #endif 50 if (!window) { 51 // TODO(maruel): bug 1214347 Get the right browser window instead. 52 return GetDesktopWindow(); 53 } 54 return window; 55 } 56 57 } // anonymous namespace 58 59 namespace printing { 60 61 class PrintingContextWin::CallbackHandler : public IPrintDialogCallback, 62 public IObjectWithSite { 63 public: 64 CallbackHandler(PrintingContextWin& owner, HWND owner_hwnd) 65 : owner_(owner), 66 owner_hwnd_(owner_hwnd), 67 services_(NULL) { 68 } 69 70 ~CallbackHandler() { 71 if (services_) 72 services_->Release(); 73 } 74 75 IUnknown* ToIUnknown() { 76 return static_cast<IUnknown*>(static_cast<IPrintDialogCallback*>(this)); 77 } 78 79 // IUnknown 80 virtual HRESULT WINAPI QueryInterface(REFIID riid, void**object) { 81 if (riid == IID_IUnknown) { 82 *object = ToIUnknown(); 83 } else if (riid == IID_IPrintDialogCallback) { 84 *object = static_cast<IPrintDialogCallback*>(this); 85 } else if (riid == IID_IObjectWithSite) { 86 *object = static_cast<IObjectWithSite*>(this); 87 } else { 88 return E_NOINTERFACE; 89 } 90 return S_OK; 91 } 92 93 // No real ref counting. 94 virtual ULONG WINAPI AddRef() { 95 return 1; 96 } 97 virtual ULONG WINAPI Release() { 98 return 1; 99 } 100 101 // IPrintDialogCallback methods 102 virtual HRESULT WINAPI InitDone() { 103 return S_OK; 104 } 105 106 virtual HRESULT WINAPI SelectionChange() { 107 if (services_) { 108 // TODO(maruel): Get the devmode for the new printer with 109 // services_->GetCurrentDevMode(&devmode, &size), send that information 110 // back to our client and continue. The client needs to recalculate the 111 // number of rendered pages and send back this information here. 112 } 113 return S_OK; 114 } 115 116 virtual HRESULT WINAPI HandleMessage(HWND dialog, 117 UINT message, 118 WPARAM wparam, 119 LPARAM lparam, 120 LRESULT* result) { 121 // Cheap way to retrieve the window handle. 122 if (!owner_.dialog_box_) { 123 // The handle we receive is the one of the groupbox in the General tab. We 124 // need to get the grand-father to get the dialog box handle. 125 owner_.dialog_box_ = GetAncestor(dialog, GA_ROOT); 126 // Trick to enable the owner window. This can cause issues with navigation 127 // events so it may have to be disabled if we don't fix the side-effects. 128 EnableWindow(owner_hwnd_, TRUE); 129 } 130 return S_FALSE; 131 } 132 133 virtual HRESULT WINAPI SetSite(IUnknown* site) { 134 if (!site) { 135 DCHECK(services_); 136 services_->Release(); 137 services_ = NULL; 138 // The dialog box is destroying, PrintJob::Worker don't need the handle 139 // anymore. 140 owner_.dialog_box_ = NULL; 141 } else { 142 DCHECK(services_ == NULL); 143 HRESULT hr = site->QueryInterface(IID_IPrintDialogServices, 144 reinterpret_cast<void**>(&services_)); 145 DCHECK(SUCCEEDED(hr)); 146 } 147 return S_OK; 148 } 149 150 virtual HRESULT WINAPI GetSite(REFIID riid, void** site) { 151 return E_NOTIMPL; 152 } 153 154 private: 155 PrintingContextWin& owner_; 156 HWND owner_hwnd_; 157 IPrintDialogServices* services_; 158 159 DISALLOW_COPY_AND_ASSIGN(CallbackHandler); 160 }; 161 162 // static 163 PrintingContext* PrintingContext::Create(const std::string& app_locale) { 164 return static_cast<PrintingContext*>(new PrintingContextWin(app_locale)); 165 } 166 167 PrintingContextWin::PrintingContextWin(const std::string& app_locale) 168 : PrintingContext(app_locale), 169 context_(NULL), 170 dialog_box_(NULL), 171 print_dialog_func_(&PrintDlgEx) { 172 } 173 174 PrintingContextWin::~PrintingContextWin() { 175 ReleaseContext(); 176 } 177 178 void PrintingContextWin::AskUserForSettings( 179 gfx::NativeView view, int max_pages, bool has_selection, 180 const PrintSettingsCallback& callback) { 181 DCHECK(!in_print_job_); 182 // TODO(scottmg): Possibly this has to move into the threaded runner too? 183 if (win8::IsSingleWindowMetroMode()) { 184 // The system dialog can not be opened while running in Metro. 185 // But we can programatically launch the Metro print device charm though. 186 HMODULE metro_module = base::win::GetMetroModule(); 187 if (metro_module != NULL) { 188 typedef void (*MetroShowPrintUI)(); 189 MetroShowPrintUI metro_show_print_ui = 190 reinterpret_cast<MetroShowPrintUI>( 191 ::GetProcAddress(metro_module, "MetroShowPrintUI")); 192 if (metro_show_print_ui) { 193 // TODO(mad): Remove this once we can send user metrics from the metro 194 // driver. crbug.com/142330 195 UMA_HISTOGRAM_ENUMERATION("Metro.Print", 1, 2); 196 metro_show_print_ui(); 197 } 198 } 199 return callback.Run(CANCEL); 200 } 201 dialog_box_dismissed_ = false; 202 203 HWND window = GetRootWindow(view); 204 DCHECK(window); 205 206 // Show the OS-dependent dialog box. 207 // If the user press 208 // - OK, the settings are reset and reinitialized with the new settings. OK is 209 // returned. 210 // - Apply then Cancel, the settings are reset and reinitialized with the new 211 // settings. CANCEL is returned. 212 // - Cancel, the settings are not changed, the previous setting, if it was 213 // initialized before, are kept. CANCEL is returned. 214 // On failure, the settings are reset and FAILED is returned. 215 PRINTDLGEX* dialog_options = 216 reinterpret_cast<PRINTDLGEX*>(malloc(sizeof(PRINTDLGEX))); 217 ZeroMemory(dialog_options, sizeof(PRINTDLGEX)); 218 dialog_options->lStructSize = sizeof(PRINTDLGEX); 219 dialog_options->hwndOwner = window; 220 // Disable options we don't support currently. 221 // TODO(maruel): Reuse the previously loaded settings! 222 dialog_options->Flags = PD_RETURNDC | PD_USEDEVMODECOPIESANDCOLLATE | 223 PD_NOCURRENTPAGE | PD_HIDEPRINTTOFILE; 224 if (!has_selection) 225 dialog_options->Flags |= PD_NOSELECTION; 226 227 const size_t max_page_ranges = 32; 228 PRINTPAGERANGE* ranges = new PRINTPAGERANGE[max_page_ranges]; 229 dialog_options->lpPageRanges = ranges; 230 dialog_options->nStartPage = START_PAGE_GENERAL; 231 if (max_pages) { 232 // Default initialize to print all the pages. 233 memset(ranges, 0, sizeof(ranges)); 234 ranges[0].nFromPage = 1; 235 ranges[0].nToPage = max_pages; 236 dialog_options->nPageRanges = 1; 237 dialog_options->nMaxPageRanges = max_page_ranges; 238 dialog_options->nMinPage = 1; 239 dialog_options->nMaxPage = max_pages; 240 } else { 241 // No need to bother, we don't know how many pages are available. 242 dialog_options->Flags |= PD_NOPAGENUMS; 243 } 244 245 callback_ = callback; 246 print_settings_dialog_ = new ui::PrintSettingsDialogWin(this); 247 print_settings_dialog_->GetPrintSettings( 248 print_dialog_func_, window, dialog_options); 249 } 250 251 PrintingContext::Result PrintingContextWin::UseDefaultSettings() { 252 DCHECK(!in_print_job_); 253 254 PRINTDLG dialog_options = { sizeof(PRINTDLG) }; 255 dialog_options.Flags = PD_RETURNDC | PD_RETURNDEFAULT; 256 if (PrintDlg(&dialog_options)) 257 return ParseDialogResult(dialog_options); 258 259 // No default printer configured, do we have any printers at all? 260 DWORD bytes_needed = 0; 261 DWORD count_returned = 0; 262 (void)::EnumPrinters(PRINTER_ENUM_LOCAL|PRINTER_ENUM_CONNECTIONS, 263 NULL, 2, NULL, 0, &bytes_needed, &count_returned); 264 if (bytes_needed) { 265 DCHECK(bytes_needed >= count_returned * sizeof(PRINTER_INFO_2)); 266 scoped_ptr<BYTE[]> printer_info_buffer(new BYTE[bytes_needed]); 267 BOOL ret = ::EnumPrinters(PRINTER_ENUM_LOCAL|PRINTER_ENUM_CONNECTIONS, 268 NULL, 2, printer_info_buffer.get(), 269 bytes_needed, &bytes_needed, 270 &count_returned); 271 if (ret && count_returned) { // have printers 272 // Open the first successfully found printer. 273 for (DWORD count = 0; count < count_returned; ++count) { 274 PRINTER_INFO_2* info_2 = reinterpret_cast<PRINTER_INFO_2*>( 275 printer_info_buffer.get() + count * sizeof(PRINTER_INFO_2)); 276 std::wstring printer_name = info_2->pPrinterName; 277 if (info_2->pDevMode == NULL || printer_name.length() == 0) 278 continue; 279 if (!AllocateContext(printer_name, info_2->pDevMode, &context_)) 280 break; 281 if (InitializeSettings(*info_2->pDevMode, printer_name, 282 NULL, 0, false)) { 283 break; 284 } 285 ReleaseContext(); 286 } 287 if (context_) 288 return OK; 289 } 290 } 291 292 ResetSettings(); 293 return FAILED; 294 } 295 296 gfx::Size PrintingContextWin::GetPdfPaperSizeDeviceUnits() { 297 // Default fallback to Letter size. 298 gfx::SizeF paper_size(kLetterWidthInch, kLetterHeightInch); 299 300 // Get settings from locale. Paper type buffer length is at most 4. 301 const int paper_type_buffer_len = 4; 302 wchar_t paper_type_buffer[paper_type_buffer_len] = {0}; 303 GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IPAPERSIZE, paper_type_buffer, 304 paper_type_buffer_len); 305 if (wcslen(paper_type_buffer)) { // The call succeeded. 306 int paper_code = _wtoi(paper_type_buffer); 307 switch (paper_code) { 308 case DMPAPER_LEGAL: 309 paper_size.SetSize(kLegalWidthInch, kLegalHeightInch); 310 break; 311 case DMPAPER_A4: 312 paper_size.SetSize(kA4WidthInch, kA4HeightInch); 313 break; 314 case DMPAPER_A3: 315 paper_size.SetSize(kA3WidthInch, kA3HeightInch); 316 break; 317 default: // DMPAPER_LETTER is used for default fallback. 318 break; 319 } 320 } 321 return gfx::Size( 322 paper_size.width() * settings_.device_units_per_inch(), 323 paper_size.height() * settings_.device_units_per_inch()); 324 } 325 326 PrintingContext::Result PrintingContextWin::UpdatePrinterSettings( 327 bool external_preview) { 328 DCHECK(!in_print_job_); 329 DCHECK(!external_preview) << "Not implemented"; 330 331 ScopedPrinterHandle printer; 332 LPWSTR device_name_wide = 333 const_cast<wchar_t*>(settings_.device_name().c_str()); 334 if (!printer.OpenPrinter(device_name_wide)) 335 return OnError(); 336 337 // Make printer changes local to Chrome. 338 // See MSDN documentation regarding DocumentProperties. 339 scoped_ptr<uint8[]> buffer; 340 DEVMODE* dev_mode = NULL; 341 LONG buffer_size = DocumentProperties(NULL, printer, device_name_wide, 342 NULL, NULL, 0); 343 if (buffer_size > 0) { 344 buffer.reset(new uint8[buffer_size]); 345 memset(buffer.get(), 0, buffer_size); 346 if (DocumentProperties(NULL, printer, device_name_wide, 347 reinterpret_cast<PDEVMODE>(buffer.get()), NULL, 348 DM_OUT_BUFFER) == IDOK) { 349 dev_mode = reinterpret_cast<PDEVMODE>(buffer.get()); 350 } 351 } 352 if (dev_mode == NULL) { 353 buffer.reset(); 354 return OnError(); 355 } 356 357 if (settings_.color() == GRAY) 358 dev_mode->dmColor = DMCOLOR_MONOCHROME; 359 else 360 dev_mode->dmColor = DMCOLOR_COLOR; 361 362 dev_mode->dmCopies = std::max(settings_.copies(), 1); 363 if (dev_mode->dmCopies > 1) { // do not change collate unless multiple copies 364 dev_mode->dmCollate = settings_.collate() ? DMCOLLATE_TRUE : 365 DMCOLLATE_FALSE; 366 } 367 switch (settings_.duplex_mode()) { 368 case LONG_EDGE: 369 dev_mode->dmDuplex = DMDUP_VERTICAL; 370 break; 371 case SHORT_EDGE: 372 dev_mode->dmDuplex = DMDUP_HORIZONTAL; 373 break; 374 case SIMPLEX: 375 dev_mode->dmDuplex = DMDUP_SIMPLEX; 376 break; 377 default: // UNKNOWN_DUPLEX_MODE 378 break; 379 } 380 dev_mode->dmOrientation = settings_.landscape() ? DMORIENT_LANDSCAPE : 381 DMORIENT_PORTRAIT; 382 383 // Update data using DocumentProperties. 384 if (DocumentProperties(NULL, printer, device_name_wide, dev_mode, dev_mode, 385 DM_IN_BUFFER | DM_OUT_BUFFER) != IDOK) { 386 return OnError(); 387 } 388 389 // Set printer then refresh printer settings. 390 if (!AllocateContext(settings_.device_name(), dev_mode, &context_)) { 391 return OnError(); 392 } 393 PrintSettingsInitializerWin::InitPrintSettings(context_, *dev_mode, 394 &settings_); 395 return OK; 396 } 397 398 PrintingContext::Result PrintingContextWin::InitWithSettings( 399 const PrintSettings& settings) { 400 DCHECK(!in_print_job_); 401 402 settings_ = settings; 403 404 // TODO(maruel): settings_.ToDEVMODE() 405 ScopedPrinterHandle printer; 406 if (!printer.OpenPrinter(settings_.device_name().c_str())) { 407 return FAILED; 408 } 409 410 Result status = OK; 411 412 if (!GetPrinterSettings(printer, settings_.device_name())) 413 status = FAILED; 414 415 if (status != OK) 416 ResetSettings(); 417 return status; 418 } 419 420 PrintingContext::Result PrintingContextWin::NewDocument( 421 const base::string16& document_name) { 422 DCHECK(!in_print_job_); 423 if (!context_) 424 return OnError(); 425 426 // Set the flag used by the AbortPrintJob dialog procedure. 427 abort_printing_ = false; 428 429 in_print_job_ = true; 430 431 // Register the application's AbortProc function with GDI. 432 if (SP_ERROR == SetAbortProc(context_, &AbortProc)) 433 return OnError(); 434 435 DCHECK(SimplifyDocumentTitle(document_name) == document_name); 436 DOCINFO di = { sizeof(DOCINFO) }; 437 const std::wstring& document_name_wide = UTF16ToWide(document_name); 438 di.lpszDocName = document_name_wide.c_str(); 439 440 // Is there a debug dump directory specified? If so, force to print to a file. 441 base::FilePath debug_dump_path = PrintedDocument::debug_dump_path(); 442 if (!debug_dump_path.empty()) { 443 // Create a filename. 444 std::wstring filename; 445 Time now(Time::Now()); 446 filename = base::TimeFormatShortDateNumeric(now); 447 filename += L"_"; 448 filename += base::TimeFormatTimeOfDay(now); 449 filename += L"_"; 450 filename += UTF16ToWide(document_name); 451 filename += L"_"; 452 filename += L"buffer.prn"; 453 file_util::ReplaceIllegalCharactersInPath(&filename, '_'); 454 debug_dump_path.Append(filename); 455 di.lpszOutput = debug_dump_path.value().c_str(); 456 } 457 458 // No message loop running in unit tests. 459 DCHECK(!base::MessageLoop::current() || 460 !base::MessageLoop::current()->NestableTasksAllowed()); 461 462 // Begin a print job by calling the StartDoc function. 463 // NOTE: StartDoc() starts a message loop. That causes a lot of problems with 464 // IPC. Make sure recursive task processing is disabled. 465 if (StartDoc(context_, &di) <= 0) 466 return OnError(); 467 468 return OK; 469 } 470 471 PrintingContext::Result PrintingContextWin::NewPage() { 472 if (abort_printing_) 473 return CANCEL; 474 DCHECK(context_); 475 DCHECK(in_print_job_); 476 477 // Intentional No-op. NativeMetafile::SafePlayback takes care of calling 478 // ::StartPage(). 479 480 return OK; 481 } 482 483 PrintingContext::Result PrintingContextWin::PageDone() { 484 if (abort_printing_) 485 return CANCEL; 486 DCHECK(in_print_job_); 487 488 // Intentional No-op. NativeMetafile::SafePlayback takes care of calling 489 // ::EndPage(). 490 491 return OK; 492 } 493 494 PrintingContext::Result PrintingContextWin::DocumentDone() { 495 if (abort_printing_) 496 return CANCEL; 497 DCHECK(in_print_job_); 498 DCHECK(context_); 499 500 // Inform the driver that document has ended. 501 if (EndDoc(context_) <= 0) 502 return OnError(); 503 504 ResetSettings(); 505 return OK; 506 } 507 508 void PrintingContextWin::Cancel() { 509 abort_printing_ = true; 510 in_print_job_ = false; 511 if (context_) 512 CancelDC(context_); 513 if (dialog_box_) { 514 DestroyWindow(dialog_box_); 515 dialog_box_dismissed_ = true; 516 } 517 } 518 519 void PrintingContextWin::ReleaseContext() { 520 if (context_) { 521 DeleteDC(context_); 522 context_ = NULL; 523 } 524 } 525 526 gfx::NativeDrawingContext PrintingContextWin::context() const { 527 return context_; 528 } 529 530 void PrintingContextWin::PrintSettingsConfirmed(PRINTDLGEX* dialog_options) { 531 // TODO(maruel): Support PD_PRINTTOFILE. 532 callback_.Run(ParseDialogResultEx(*dialog_options)); 533 delete [] dialog_options->lpPageRanges; 534 free(dialog_options); 535 } 536 537 void PrintingContextWin::PrintSettingsCancelled(PRINTDLGEX* dialog_options) { 538 ResetSettings(); 539 callback_.Run(FAILED); 540 delete [] dialog_options->lpPageRanges; 541 free(dialog_options); 542 } 543 544 // static 545 BOOL PrintingContextWin::AbortProc(HDC hdc, int nCode) { 546 if (nCode) { 547 // TODO(maruel): Need a way to find the right instance to set. Should 548 // leverage PrintJobManager here? 549 // abort_printing_ = true; 550 } 551 return true; 552 } 553 554 bool PrintingContextWin::InitializeSettings(const DEVMODE& dev_mode, 555 const std::wstring& new_device_name, 556 const PRINTPAGERANGE* ranges, 557 int number_ranges, 558 bool selection_only) { 559 skia::InitializeDC(context_); 560 DCHECK(GetDeviceCaps(context_, CLIPCAPS)); 561 DCHECK(GetDeviceCaps(context_, RASTERCAPS) & RC_STRETCHDIB); 562 DCHECK(GetDeviceCaps(context_, RASTERCAPS) & RC_BITMAP64); 563 // Some printers don't advertise these. 564 // DCHECK(GetDeviceCaps(context_, RASTERCAPS) & RC_SCALING); 565 // DCHECK(GetDeviceCaps(context_, SHADEBLENDCAPS) & SB_CONST_ALPHA); 566 // DCHECK(GetDeviceCaps(context_, SHADEBLENDCAPS) & SB_PIXEL_ALPHA); 567 568 // StretchDIBits() support is needed for printing. 569 if (!(GetDeviceCaps(context_, RASTERCAPS) & RC_STRETCHDIB) || 570 !(GetDeviceCaps(context_, RASTERCAPS) & RC_BITMAP64)) { 571 NOTREACHED(); 572 ResetSettings(); 573 return false; 574 } 575 576 DCHECK(!in_print_job_); 577 DCHECK(context_); 578 PageRanges ranges_vector; 579 if (!selection_only) { 580 // Convert the PRINTPAGERANGE array to a PrintSettings::PageRanges vector. 581 ranges_vector.reserve(number_ranges); 582 for (int i = 0; i < number_ranges; ++i) { 583 PageRange range; 584 // Transfer from 1-based to 0-based. 585 range.from = ranges[i].nFromPage - 1; 586 range.to = ranges[i].nToPage - 1; 587 ranges_vector.push_back(range); 588 } 589 } 590 591 settings_.set_ranges(ranges_vector); 592 settings_.set_device_name(new_device_name); 593 settings_.set_selection_only(selection_only); 594 PrintSettingsInitializerWin::InitPrintSettings(context_, dev_mode, 595 &settings_); 596 597 return true; 598 } 599 600 bool PrintingContextWin::GetPrinterSettings(HANDLE printer, 601 const std::wstring& device_name) { 602 DCHECK(!in_print_job_); 603 604 UserDefaultDevMode user_settings; 605 606 if (!user_settings.Init(printer) || 607 !AllocateContext(device_name, user_settings.get(), &context_)) { 608 ResetSettings(); 609 return false; 610 } 611 612 return InitializeSettings(*user_settings.get(), device_name, NULL, 0, false); 613 } 614 615 // static 616 bool PrintingContextWin::AllocateContext(const std::wstring& device_name, 617 const DEVMODE* dev_mode, 618 gfx::NativeDrawingContext* context) { 619 *context = CreateDC(L"WINSPOOL", device_name.c_str(), NULL, dev_mode); 620 DCHECK(*context); 621 return *context != NULL; 622 } 623 624 PrintingContext::Result PrintingContextWin::ParseDialogResultEx( 625 const PRINTDLGEX& dialog_options) { 626 // If the user clicked OK or Apply then Cancel, but not only Cancel. 627 if (dialog_options.dwResultAction != PD_RESULT_CANCEL) { 628 // Start fresh. 629 ResetSettings(); 630 631 DEVMODE* dev_mode = NULL; 632 if (dialog_options.hDevMode) { 633 dev_mode = 634 reinterpret_cast<DEVMODE*>(GlobalLock(dialog_options.hDevMode)); 635 DCHECK(dev_mode); 636 } 637 638 std::wstring device_name; 639 if (dialog_options.hDevNames) { 640 DEVNAMES* dev_names = 641 reinterpret_cast<DEVNAMES*>(GlobalLock(dialog_options.hDevNames)); 642 DCHECK(dev_names); 643 if (dev_names) { 644 device_name = reinterpret_cast<const wchar_t*>(dev_names) + 645 dev_names->wDeviceOffset; 646 GlobalUnlock(dialog_options.hDevNames); 647 } 648 } 649 650 bool success = false; 651 if (dev_mode && !device_name.empty()) { 652 context_ = dialog_options.hDC; 653 PRINTPAGERANGE* page_ranges = NULL; 654 DWORD num_page_ranges = 0; 655 bool print_selection_only = false; 656 if (dialog_options.Flags & PD_PAGENUMS) { 657 page_ranges = dialog_options.lpPageRanges; 658 num_page_ranges = dialog_options.nPageRanges; 659 } 660 if (dialog_options.Flags & PD_SELECTION) { 661 print_selection_only = true; 662 } 663 success = InitializeSettings(*dev_mode, 664 device_name, 665 page_ranges, 666 num_page_ranges, 667 print_selection_only); 668 } 669 670 if (!success && dialog_options.hDC) { 671 DeleteDC(dialog_options.hDC); 672 context_ = NULL; 673 } 674 675 if (dev_mode) { 676 GlobalUnlock(dialog_options.hDevMode); 677 } 678 } else { 679 if (dialog_options.hDC) { 680 DeleteDC(dialog_options.hDC); 681 } 682 } 683 684 if (dialog_options.hDevMode != NULL) 685 GlobalFree(dialog_options.hDevMode); 686 if (dialog_options.hDevNames != NULL) 687 GlobalFree(dialog_options.hDevNames); 688 689 switch (dialog_options.dwResultAction) { 690 case PD_RESULT_PRINT: 691 return context_ ? OK : FAILED; 692 case PD_RESULT_APPLY: 693 return context_ ? CANCEL : FAILED; 694 case PD_RESULT_CANCEL: 695 return CANCEL; 696 default: 697 return FAILED; 698 } 699 } 700 701 PrintingContext::Result PrintingContextWin::ParseDialogResult( 702 const PRINTDLG& dialog_options) { 703 // If the user clicked OK or Apply then Cancel, but not only Cancel. 704 // Start fresh. 705 ResetSettings(); 706 707 DEVMODE* dev_mode = NULL; 708 if (dialog_options.hDevMode) { 709 dev_mode = 710 reinterpret_cast<DEVMODE*>(GlobalLock(dialog_options.hDevMode)); 711 DCHECK(dev_mode); 712 } 713 714 std::wstring device_name; 715 if (dialog_options.hDevNames) { 716 DEVNAMES* dev_names = 717 reinterpret_cast<DEVNAMES*>(GlobalLock(dialog_options.hDevNames)); 718 DCHECK(dev_names); 719 if (dev_names) { 720 device_name = 721 reinterpret_cast<const wchar_t*>( 722 reinterpret_cast<const wchar_t*>(dev_names) + 723 dev_names->wDeviceOffset); 724 GlobalUnlock(dialog_options.hDevNames); 725 } 726 } 727 728 bool success = false; 729 if (dev_mode && !device_name.empty()) { 730 context_ = dialog_options.hDC; 731 success = InitializeSettings(*dev_mode, device_name, NULL, 0, false); 732 } 733 734 if (!success && dialog_options.hDC) { 735 DeleteDC(dialog_options.hDC); 736 context_ = NULL; 737 } 738 739 if (dev_mode) { 740 GlobalUnlock(dialog_options.hDevMode); 741 } 742 743 if (dialog_options.hDevMode != NULL) 744 GlobalFree(dialog_options.hDevMode); 745 if (dialog_options.hDevNames != NULL) 746 GlobalFree(dialog_options.hDevNames); 747 748 return context_ ? OK : FAILED; 749 } 750 751 } // namespace printing 752