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