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
) {
75 // TODO(maruel): Support PD_PRINTTOFILE.
76 callback
.Run(ParseDialogResultEx(dialog_options
));
79 HRESULT
PrintingContextSytemDialogWin::ShowPrintDialog(PRINTDLGEX
* options
) {
80 // Runs always on the UI thread.
81 static bool is_dialog_shown
= false;
84 // Block opening dialog from nested task. It crashes PrintDlgEx.
85 base::AutoReset
<bool> auto_reset(&is_dialog_shown
, true);
87 // 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());
96 return PrintDlgEx(options
);
99 bool PrintingContextSytemDialogWin::InitializeSettingsWithRanges(
100 const DEVMODE
& dev_mode
,
101 const std::wstring
& new_device_name
,
102 const PRINTPAGERANGE
* 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);
113 // StretchDIBits() support is needed for printing.
114 if (!(GetDeviceCaps(context(), RASTERCAPS
) & RC_STRETCHDIB
) ||
115 !(GetDeviceCaps(context(), RASTERCAPS
) & RC_BITMAP64
)) {
121 DCHECK(!in_print_job_
);
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
) {
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
);
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_
);
145 PrintingContext::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
) {
152 DEVMODE
* dev_mode
= NULL
;
153 if (dialog_options
.hDevMode
) {
155 reinterpret_cast<DEVMODE
*>(GlobalLock(dialog_options
.hDevMode
));
159 std::wstring device_name
;
160 if (dialog_options
.hDevNames
) {
161 DEVNAMES
* dev_names
=
162 reinterpret_cast<DEVNAMES
*>(GlobalLock(dialog_options
.hDevNames
));
165 device_name
= reinterpret_cast<const wchar_t*>(dev_names
) +
166 dev_names
->wDeviceOffset
;
167 GlobalUnlock(dialog_options
.hDevNames
);
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
;
181 if (dialog_options
.Flags
& PD_SELECTION
) {
182 print_selection_only
= true;
185 InitializeSettingsWithRanges(*dev_mode
, device_name
, page_ranges
,
186 num_page_ranges
, print_selection_only
);
189 if (!success
&& dialog_options
.hDC
) {
190 DeleteDC(dialog_options
.hDC
);
195 GlobalUnlock(dialog_options
.hDevMode
);
198 if (dialog_options
.hDC
) {
199 DeleteDC(dialog_options
.hDC
);
203 if (dialog_options
.hDevMode
!= NULL
)
204 GlobalFree(dialog_options
.hDevMode
);
205 if (dialog_options
.hDevNames
!= NULL
)
206 GlobalFree(dialog_options
.hDevNames
);
208 switch (dialog_options
.dwResultAction
) {
209 case PD_RESULT_PRINT
:
210 return context() ? OK
: FAILED
;
211 case PD_RESULT_APPLY
:
212 return context() ? CANCEL
: FAILED
;
213 case PD_RESULT_CANCEL
:
220 PrintingContext::Result
PrintingContextSytemDialogWin::ParseDialogResult(
221 const PRINTDLG
& dialog_options
) {
222 // If the user clicked OK or Apply then Cancel, but not only Cancel.
226 DEVMODE
* dev_mode
= NULL
;
227 if (dialog_options
.hDevMode
) {
228 dev_mode
= reinterpret_cast<DEVMODE
*>(GlobalLock(dialog_options
.hDevMode
));
232 std::wstring device_name
;
233 if (dialog_options
.hDevNames
) {
234 DEVNAMES
* dev_names
=
235 reinterpret_cast<DEVNAMES
*>(GlobalLock(dialog_options
.hDevNames
));
238 device_name
= reinterpret_cast<const wchar_t*>(
239 reinterpret_cast<const wchar_t*>(dev_names
) +
240 dev_names
->wDeviceOffset
);
241 GlobalUnlock(dialog_options
.hDevNames
);
245 bool success
= false;
246 if (dev_mode
&& !device_name
.empty()) {
247 set_context(dialog_options
.hDC
);
249 InitializeSettingsWithRanges(*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