Home | History | Annotate | Download | only in printing
      1 // Copyright 2014 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_system_dialog_win.h"
      6 
      7 #include "base/message_loop/message_loop.h"
      8 #include "printing/backend/win_helper.h"
      9 #include "printing/print_settings_initializer_win.h"
     10 #include "skia/ext/platform_device.h"
     11 
     12 namespace printing {
     13 
     14 PrintingContextSytemDialogWin::PrintingContextSytemDialogWin(Delegate* delegate)
     15     : PrintingContextWin(delegate), dialog_box_(NULL) {
     16 }
     17 
     18 PrintingContextSytemDialogWin::~PrintingContextSytemDialogWin() {
     19 }
     20 
     21 void PrintingContextSytemDialogWin::AskUserForSettings(
     22     int max_pages,
     23     bool has_selection,
     24     const PrintSettingsCallback& callback) {
     25   DCHECK(!in_print_job_);
     26   dialog_box_dismissed_ = false;
     27 
     28   HWND window = GetRootWindow(delegate_->GetParentView());
     29   DCHECK(window);
     30 
     31   // Show the OS-dependent dialog box.
     32   // If the user press
     33   // - OK, the settings are reset and reinitialized with the new settings. OK
     34   // is
     35   //   returned.
     36   // - Apply then Cancel, the settings are reset and reinitialized with the
     37   // new
     38   //   settings. CANCEL is returned.
     39   // - Cancel, the settings are not changed, the previous setting, if it was
     40   //   initialized before, are kept. CANCEL is returned.
     41   // On failure, the settings are reset and FAILED is returned.
     42   PRINTDLGEX dialog_options = {sizeof(PRINTDLGEX)};
     43   dialog_options.hwndOwner = window;
     44   // Disable options we don't support currently.
     45   // TODO(maruel):  Reuse the previously loaded settings!
     46   dialog_options.Flags = PD_RETURNDC | PD_USEDEVMODECOPIESANDCOLLATE |
     47                          PD_NOCURRENTPAGE | PD_HIDEPRINTTOFILE;
     48   if (!has_selection)
     49     dialog_options.Flags |= PD_NOSELECTION;
     50 
     51   PRINTPAGERANGE ranges[32];
     52   dialog_options.nStartPage = START_PAGE_GENERAL;
     53   if (max_pages) {
     54     // Default initialize to print all the pages.
     55     memset(ranges, 0, sizeof(ranges));
     56     ranges[0].nFromPage = 1;
     57     ranges[0].nToPage = max_pages;
     58     dialog_options.nPageRanges = 1;
     59     dialog_options.nMaxPageRanges = arraysize(ranges);
     60     dialog_options.nMinPage = 1;
     61     dialog_options.nMaxPage = max_pages;
     62     dialog_options.lpPageRanges = ranges;
     63   } else {
     64     // No need to bother, we don't know how many pages are available.
     65     dialog_options.Flags |= PD_NOPAGENUMS;
     66   }
     67 
     68   if (ShowPrintDialog(&dialog_options) != S_OK) {
     69     ResetSettings();
     70     callback.Run(FAILED);
     71   }
     72 
     73   // TODO(maruel):  Support PD_PRINTTOFILE.
     74   callback.Run(ParseDialogResultEx(dialog_options));
     75 }
     76 
     77 void PrintingContextSytemDialogWin::Cancel() {
     78   PrintingContextWin::Cancel();
     79   if (dialog_box_) {
     80     DestroyWindow(dialog_box_);
     81     dialog_box_dismissed_ = true;
     82   }
     83 }
     84 
     85 HRESULT PrintingContextSytemDialogWin::ShowPrintDialog(PRINTDLGEX* options) {
     86   // Note that this cannot use ui::BaseShellDialog as the print dialog is
     87   // system modal: opening it from a background thread can cause Windows to
     88   // get the wrong Z-order which will make the print dialog appear behind the
     89   // browser frame (but still being modal) so neither the browser frame nor
     90   // the print dialog will get any input. See http://crbug.com/342697
     91   // http://crbug.com/180997 for details.
     92   base::MessageLoop::ScopedNestableTaskAllower allow(
     93       base::MessageLoop::current());
     94 
     95   return PrintDlgEx(options);
     96 }
     97 
     98 bool PrintingContextSytemDialogWin::InitializeSettings(
     99     const DEVMODE& dev_mode,
    100     const std::wstring& new_device_name,
    101     const PRINTPAGERANGE* ranges,
    102     int number_ranges,
    103     bool selection_only) {
    104   DCHECK(GetDeviceCaps(context(), CLIPCAPS));
    105   DCHECK(GetDeviceCaps(context(), RASTERCAPS) & RC_STRETCHDIB);
    106   DCHECK(GetDeviceCaps(context(), RASTERCAPS) & RC_BITMAP64);
    107   // Some printers don't advertise these.
    108   // DCHECK(GetDeviceCaps(context(), RASTERCAPS) & RC_SCALING);
    109   // DCHECK(GetDeviceCaps(context(), SHADEBLENDCAPS) & SB_CONST_ALPHA);
    110   // DCHECK(GetDeviceCaps(context(), SHADEBLENDCAPS) & SB_PIXEL_ALPHA);
    111 
    112   // StretchDIBits() support is needed for printing.
    113   if (!(GetDeviceCaps(context(), RASTERCAPS) & RC_STRETCHDIB) ||
    114       !(GetDeviceCaps(context(), RASTERCAPS) & RC_BITMAP64)) {
    115     NOTREACHED();
    116     ResetSettings();
    117     return false;
    118   }
    119 
    120   DCHECK(!in_print_job_);
    121   DCHECK(context());
    122   PageRanges ranges_vector;
    123   if (!selection_only) {
    124     // Convert the PRINTPAGERANGE array to a PrintSettings::PageRanges vector.
    125     ranges_vector.reserve(number_ranges);
    126     for (int i = 0; i < number_ranges; ++i) {
    127       PageRange range;
    128       // Transfer from 1-based to 0-based.
    129       range.from = ranges[i].nFromPage - 1;
    130       range.to = ranges[i].nToPage - 1;
    131       ranges_vector.push_back(range);
    132     }
    133   }
    134 
    135   settings_.set_ranges(ranges_vector);
    136   settings_.set_device_name(new_device_name);
    137   settings_.set_selection_only(selection_only);
    138   PrintSettingsInitializerWin::InitPrintSettings(
    139       context(), dev_mode, &settings_);
    140 
    141   return true;
    142 }
    143 
    144 PrintingContext::Result PrintingContextSytemDialogWin::ParseDialogResultEx(
    145     const PRINTDLGEX& dialog_options) {
    146   // If the user clicked OK or Apply then Cancel, but not only Cancel.
    147   if (dialog_options.dwResultAction != PD_RESULT_CANCEL) {
    148     // Start fresh.
    149     ResetSettings();
    150 
    151     DEVMODE* dev_mode = NULL;
    152     if (dialog_options.hDevMode) {
    153       dev_mode =
    154           reinterpret_cast<DEVMODE*>(GlobalLock(dialog_options.hDevMode));
    155       DCHECK(dev_mode);
    156     }
    157 
    158     std::wstring device_name;
    159     if (dialog_options.hDevNames) {
    160       DEVNAMES* dev_names =
    161           reinterpret_cast<DEVNAMES*>(GlobalLock(dialog_options.hDevNames));
    162       DCHECK(dev_names);
    163       if (dev_names) {
    164         device_name = reinterpret_cast<const wchar_t*>(dev_names) +
    165                       dev_names->wDeviceOffset;
    166         GlobalUnlock(dialog_options.hDevNames);
    167       }
    168     }
    169 
    170     bool success = false;
    171     if (dev_mode && !device_name.empty()) {
    172       set_context(dialog_options.hDC);
    173       PRINTPAGERANGE* page_ranges = NULL;
    174       DWORD num_page_ranges = 0;
    175       bool print_selection_only = false;
    176       if (dialog_options.Flags & PD_PAGENUMS) {
    177         page_ranges = dialog_options.lpPageRanges;
    178         num_page_ranges = dialog_options.nPageRanges;
    179       }
    180       if (dialog_options.Flags & PD_SELECTION) {
    181         print_selection_only = true;
    182       }
    183       success = InitializeSettings(*dev_mode,
    184                                    device_name,
    185                                    page_ranges,
    186                                    num_page_ranges,
    187                                    print_selection_only);
    188     }
    189 
    190     if (!success && dialog_options.hDC) {
    191       DeleteDC(dialog_options.hDC);
    192       set_context(NULL);
    193     }
    194 
    195     if (dev_mode) {
    196       GlobalUnlock(dialog_options.hDevMode);
    197     }
    198   } else {
    199     if (dialog_options.hDC) {
    200       DeleteDC(dialog_options.hDC);
    201     }
    202   }
    203 
    204   if (dialog_options.hDevMode != NULL)
    205     GlobalFree(dialog_options.hDevMode);
    206   if (dialog_options.hDevNames != NULL)
    207     GlobalFree(dialog_options.hDevNames);
    208 
    209   switch (dialog_options.dwResultAction) {
    210     case PD_RESULT_PRINT:
    211       return context() ? OK : FAILED;
    212     case PD_RESULT_APPLY:
    213       return context() ? CANCEL : FAILED;
    214     case PD_RESULT_CANCEL:
    215       return CANCEL;
    216     default:
    217       return FAILED;
    218   }
    219 }
    220 
    221 PrintingContext::Result PrintingContextSytemDialogWin::ParseDialogResult(
    222     const PRINTDLG& dialog_options) {
    223   // If the user clicked OK or Apply then Cancel, but not only Cancel.
    224   // Start fresh.
    225   ResetSettings();
    226 
    227   DEVMODE* dev_mode = NULL;
    228   if (dialog_options.hDevMode) {
    229     dev_mode = reinterpret_cast<DEVMODE*>(GlobalLock(dialog_options.hDevMode));
    230     DCHECK(dev_mode);
    231   }
    232 
    233   std::wstring device_name;
    234   if (dialog_options.hDevNames) {
    235     DEVNAMES* dev_names =
    236         reinterpret_cast<DEVNAMES*>(GlobalLock(dialog_options.hDevNames));
    237     DCHECK(dev_names);
    238     if (dev_names) {
    239       device_name = reinterpret_cast<const wchar_t*>(
    240           reinterpret_cast<const wchar_t*>(dev_names) +
    241           dev_names->wDeviceOffset);
    242       GlobalUnlock(dialog_options.hDevNames);
    243     }
    244   }
    245 
    246   bool success = false;
    247   if (dev_mode && !device_name.empty()) {
    248     set_context(dialog_options.hDC);
    249     success = InitializeSettings(*dev_mode, device_name, NULL, 0, false);
    250   }
    251 
    252   if (!success && dialog_options.hDC) {
    253     DeleteDC(dialog_options.hDC);
    254     set_context(NULL);
    255   }
    256 
    257   if (dev_mode) {
    258     GlobalUnlock(dialog_options.hDevMode);
    259   }
    260 
    261   if (dialog_options.hDevMode != NULL)
    262     GlobalFree(dialog_options.hDevMode);
    263   if (dialog_options.hDevNames != NULL)
    264     GlobalFree(dialog_options.hDevNames);
    265 
    266   return context() ? OK : FAILED;
    267 }
    268 
    269 }  // namespace printing
    270