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.
5 #include "printing/printing_context_system_dialog_win.h"
7 #include "base/auto_reset.h"
8 #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"
15 PrintingContextSytemDialogWin::PrintingContextSytemDialogWin(Delegate
* delegate
)
16 : PrintingContextWin(delegate
) {
19 PrintingContextSytemDialogWin::~PrintingContextSytemDialogWin() {
22 void PrintingContextSytemDialogWin::AskUserForSettings(
26 const PrintSettingsCallback
& callback
) {
27 DCHECK(!in_print_job_
);
29 HWND window
= GetRootWindow(delegate_
->GetParentView());
32 // Show the OS-dependent dialog box.
34 // - OK, the settings are reset and reinitialized with the new settings. OK
37 // - Apply then Cancel, the settings are reset and reinitialized with the
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
;
50 dialog_options
.Flags
|= PD_NOSELECTION
;
52 PRINTPAGERANGE ranges
[32];
53 dialog_options
.nStartPage
= START_PAGE_GENERAL
;
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
;
65 // No need to bother, we don't know how many pages are available.
66 dialog_options
.Flags
|= PD_NOPAGENUMS
;
69 if (ShowPrintDialog(&dialog_options
) != S_OK
) {
74 // TODO(maruel): Support PD_PRINTTOFILE.
75 callback
.Run(ParseDialogResultEx(dialog_options
));
78 HRESULT
PrintingContextSytemDialogWin::ShowPrintDialog(PRINTDLGEX
* options
) {
79 // Runs always on the UI thread.
80 static bool is_dialog_shown
= false;
83 // Block opening dialog from nested task. It crashes PrintDlgEx.
84 base::AutoReset
<bool> auto_reset(&is_dialog_shown
, true);
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());
95 return PrintDlgEx(options
);
98 bool PrintingContextSytemDialogWin::InitializeSettings(
99 const DEVMODE
& dev_mode
,
100 const std::wstring
& new_device_name
,
101 const PRINTPAGERANGE
* 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);
112 // StretchDIBits() support is needed for printing.
113 if (!(GetDeviceCaps(context(), RASTERCAPS
) & RC_STRETCHDIB
) ||
114 !(GetDeviceCaps(context(), RASTERCAPS
) & RC_BITMAP64
)) {
120 DCHECK(!in_print_job_
);
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
) {
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
);
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_
);
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
) {
151 DEVMODE
* dev_mode
= NULL
;
152 if (dialog_options
.hDevMode
) {
154 reinterpret_cast<DEVMODE
*>(GlobalLock(dialog_options
.hDevMode
));
158 std::wstring device_name
;
159 if (dialog_options
.hDevNames
) {
160 DEVNAMES
* dev_names
=
161 reinterpret_cast<DEVNAMES
*>(GlobalLock(dialog_options
.hDevNames
));
164 device_name
= reinterpret_cast<const wchar_t*>(dev_names
) +
165 dev_names
->wDeviceOffset
;
166 GlobalUnlock(dialog_options
.hDevNames
);
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
;
180 if (dialog_options
.Flags
& PD_SELECTION
) {
181 print_selection_only
= true;
183 success
= InitializeSettings(*dev_mode
,
187 print_selection_only
);
190 if (!success
&& dialog_options
.hDC
) {
191 DeleteDC(dialog_options
.hDC
);
196 GlobalUnlock(dialog_options
.hDevMode
);
199 if (dialog_options
.hDC
) {
200 DeleteDC(dialog_options
.hDC
);
204 if (dialog_options
.hDevMode
!= NULL
)
205 GlobalFree(dialog_options
.hDevMode
);
206 if (dialog_options
.hDevNames
!= NULL
)
207 GlobalFree(dialog_options
.hDevNames
);
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
:
221 PrintingContext::Result
PrintingContextSytemDialogWin::ParseDialogResult(
222 const PRINTDLG
& dialog_options
) {
223 // If the user clicked OK or Apply then Cancel, but not only Cancel.
227 DEVMODE
* dev_mode
= NULL
;
228 if (dialog_options
.hDevMode
) {
229 dev_mode
= reinterpret_cast<DEVMODE
*>(GlobalLock(dialog_options
.hDevMode
));
233 std::wstring device_name
;
234 if (dialog_options
.hDevNames
) {
235 DEVNAMES
* dev_names
=
236 reinterpret_cast<DEVNAMES
*>(GlobalLock(dialog_options
.hDevNames
));
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
);
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);
252 if (!success
&& dialog_options
.hDC
) {
253 DeleteDC(dialog_options
.hDC
);
258 GlobalUnlock(dialog_options
.hDevMode
);
261 if (dialog_options
.hDevMode
!= NULL
)
262 GlobalFree(dialog_options
.hDevMode
);
263 if (dialog_options
.hDevNames
!= NULL
)
264 GlobalFree(dialog_options
.hDevNames
);
266 return context() ? OK
: FAILED
;
269 } // namespace printing