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