1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 /* -------------------------------------------------------------------
7 To Build This:
8
9 You need to add this to the the makefile.win in mozilla/dom/base:
10
11 .\$(OBJDIR)\nsFlyOwnPrintDialog.obj \
12
13
14 And this to the makefile.win in mozilla/content/build:
15
16 WIN_LIBS= \
17 winspool.lib \
18 comctl32.lib \
19 comdlg32.lib
20
21 ---------------------------------------------------------------------- */
22
23 #include "plstr.h"
24 #include <windows.h>
25 #include <tchar.h>
26
27 #include <unknwn.h>
28 #include <commdlg.h>
29
30 #include "nsIWebBrowserPrint.h"
31 #include "nsString.h"
32 #include "nsIServiceManager.h"
33 #include "nsReadableUtils.h"
34 #include "nsIPrintSettings.h"
35 #include "nsIPrintSettingsWin.h"
36 #include "nsIPrinterEnumerator.h"
37
38 #include "nsRect.h"
39
40 #include "nsIPrefService.h"
41 #include "nsIPrefBranch.h"
42
43 #include "nsCRT.h"
44 #include "prenv.h" /* for PR_GetEnv */
45
46 #include <windows.h>
47 #include <winspool.h>
48
49 // For Localization
50 #include "nsIStringBundle.h"
51
52 // For NS_CopyUnicodeToNative
53 #include "nsNativeCharsetUtils.h"
54
55 // This is for extending the dialog
56 #include <dlgs.h>
57
58 #include "nsWindowsHelpers.h"
59 #include "WinUtils.h"
60
61 // Default labels for the radio buttons
62 static const char* kAsLaidOutOnScreenStr = "As &laid out on the screen";
63 static const char* kTheSelectedFrameStr = "The selected &frame";
64 static const char* kEachFrameSeparately = "&Each frame separately";
65
66
67 //-----------------------------------------------
68 // Global Data
69 //-----------------------------------------------
70 // Identifies which new radio btn was cliked on
71 static UINT gFrameSelectedRadioBtn = 0;
72
73 // Indicates whether the native print dialog was successfully extended
74 static bool gDialogWasExtended = false;
75
76 #define PRINTDLG_PROPERTIES "chrome://global/locale/printdialog.properties"
77
78 static HWND gParentWnd = nullptr;
79
80 //----------------------------------------------------------------------------------
81 // Return localized bundle for resource strings
82 static nsresult
GetLocalizedBundle(const char * aPropFileName,nsIStringBundle ** aStrBundle)83 GetLocalizedBundle(const char * aPropFileName, nsIStringBundle** aStrBundle)
84 {
85 NS_ENSURE_ARG_POINTER(aPropFileName);
86 NS_ENSURE_ARG_POINTER(aStrBundle);
87
88 nsresult rv;
89 nsCOMPtr<nsIStringBundle> bundle;
90
91
92 // Create bundle
93 nsCOMPtr<nsIStringBundleService> stringService =
94 do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
95 if (NS_SUCCEEDED(rv) && stringService) {
96 rv = stringService->CreateBundle(aPropFileName, aStrBundle);
97 }
98
99 return rv;
100 }
101
102 //--------------------------------------------------------
103 // Return localized string
104 static nsresult
GetLocalizedString(nsIStringBundle * aStrBundle,const char * aKey,nsString & oVal)105 GetLocalizedString(nsIStringBundle* aStrBundle, const char* aKey, nsString& oVal)
106 {
107 NS_ENSURE_ARG_POINTER(aStrBundle);
108 NS_ENSURE_ARG_POINTER(aKey);
109
110 // Determine default label from string bundle
111 nsXPIDLString valUni;
112 nsAutoString key;
113 key.AssignWithConversion(aKey);
114 nsresult rv = aStrBundle->GetStringFromName(key.get(), getter_Copies(valUni));
115 if (NS_SUCCEEDED(rv) && valUni) {
116 oVal.Assign(valUni);
117 } else {
118 oVal.Truncate();
119 }
120 return rv;
121 }
122
123 //--------------------------------------------------------
124 // Set a multi-byte string in the control
SetTextOnWnd(HWND aControl,const nsString & aStr)125 static void SetTextOnWnd(HWND aControl, const nsString& aStr)
126 {
127 nsAutoCString text;
128 if (NS_SUCCEEDED(NS_CopyUnicodeToNative(aStr, text))) {
129 ::SetWindowText(aControl, text.get());
130 }
131 }
132
133 //--------------------------------------------------------
134 // Will get the control and localized string by "key"
SetText(HWND aParent,UINT aId,nsIStringBundle * aStrBundle,const char * aKey)135 static void SetText(HWND aParent,
136 UINT aId,
137 nsIStringBundle* aStrBundle,
138 const char* aKey)
139 {
140 HWND wnd = GetDlgItem (aParent, aId);
141 if (!wnd) {
142 return;
143 }
144 nsAutoString str;
145 nsresult rv = GetLocalizedString(aStrBundle, aKey, str);
146 if (NS_SUCCEEDED(rv)) {
147 SetTextOnWnd(wnd, str);
148 }
149 }
150
151 //--------------------------------------------------------
SetRadio(HWND aParent,UINT aId,bool aIsSet,bool isEnabled=true)152 static void SetRadio(HWND aParent,
153 UINT aId,
154 bool aIsSet,
155 bool isEnabled = true)
156 {
157 HWND wnd = ::GetDlgItem (aParent, aId);
158 if (!wnd) {
159 return;
160 }
161 if (!isEnabled) {
162 ::EnableWindow(wnd, FALSE);
163 return;
164 }
165 ::EnableWindow(wnd, TRUE);
166 ::SendMessage(wnd, BM_SETCHECK, (WPARAM)aIsSet, (LPARAM)0);
167 }
168
169 //--------------------------------------------------------
SetRadioOfGroup(HWND aDlg,int aRadId)170 static void SetRadioOfGroup(HWND aDlg, int aRadId)
171 {
172 int radioIds[] = {rad4, rad5, rad6};
173 int numRads = 3;
174
175 for (int i=0;i<numRads;i++) {
176 HWND radWnd = ::GetDlgItem(aDlg, radioIds[i]);
177 if (radWnd != nullptr) {
178 ::SendMessage(radWnd, BM_SETCHECK, (WPARAM)(radioIds[i] == aRadId), (LPARAM)0);
179 }
180 }
181 }
182
183 //--------------------------------------------------------
184 typedef struct {
185 const char * mKeyStr;
186 long mKeyId;
187 } PropKeyInfo;
188
189 // These are the control ids used in the dialog and
190 // defined by MS-Windows in commdlg.h
191 static PropKeyInfo gAllPropKeys[] = {
192 {"printFramesTitleWindows", grp3},
193 {"asLaidOutWindows", rad4},
194 {"selectedFrameWindows", rad5},
195 {"separateFramesWindows", rad6},
196 {nullptr, 0}};
197
198 //--------------------------------------------------------
199 //--------------------------------------------------------
200 //--------------------------------------------------------
201 //--------------------------------------------------------
202 // Get the absolute coords of the child windows relative
203 // to its parent window
GetLocalRect(HWND aWnd,RECT & aRect,HWND aParent)204 static void GetLocalRect(HWND aWnd, RECT& aRect, HWND aParent)
205 {
206 ::GetWindowRect(aWnd, &aRect);
207
208 // MapWindowPoints converts screen coordinates to client coordinates.
209 // It works correctly in both left-to-right and right-to-left windows.
210 ::MapWindowPoints(nullptr, aParent, (LPPOINT)&aRect, 2);
211 }
212
213 //--------------------------------------------------------
214 // Show or Hide the control
Show(HWND aWnd,bool bState)215 static void Show(HWND aWnd, bool bState)
216 {
217 if (aWnd) {
218 ::ShowWindow(aWnd, bState?SW_SHOW:SW_HIDE);
219 }
220 }
221
222 //--------------------------------------------------------
223 // Create a child window "control"
CreateControl(LPCTSTR aType,DWORD aStyle,HINSTANCE aHInst,HWND aHdlg,int aId,const nsAString & aStr,const nsIntRect & aRect)224 static HWND CreateControl(LPCTSTR aType,
225 DWORD aStyle,
226 HINSTANCE aHInst,
227 HWND aHdlg,
228 int aId,
229 const nsAString& aStr,
230 const nsIntRect& aRect)
231 {
232 nsAutoCString str;
233 if (NS_FAILED(NS_CopyUnicodeToNative(aStr, str)))
234 return nullptr;
235
236 HWND hWnd = ::CreateWindow (aType, str.get(),
237 WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE | aStyle,
238 aRect.x, aRect.y, aRect.width, aRect.height,
239 (HWND)aHdlg, (HMENU)(intptr_t)aId,
240 aHInst, nullptr);
241 if (hWnd == nullptr) return nullptr;
242
243 // get the native font for the dialog and
244 // set it into the new control
245 HFONT hFont = (HFONT)::SendMessage(aHdlg, WM_GETFONT, (WPARAM)0, (LPARAM)0);
246 if (hFont != nullptr) {
247 ::SendMessage(hWnd, WM_SETFONT, (WPARAM) hFont, (LPARAM)0);
248 }
249 return hWnd;
250 }
251
252 //--------------------------------------------------------
253 // Create a Radio Button
CreateRadioBtn(HINSTANCE aHInst,HWND aHdlg,int aId,const char * aStr,const nsIntRect & aRect)254 static HWND CreateRadioBtn(HINSTANCE aHInst,
255 HWND aHdlg,
256 int aId,
257 const char* aStr,
258 const nsIntRect& aRect)
259 {
260 nsString cStr;
261 cStr.AssignWithConversion(aStr);
262 return CreateControl("BUTTON", BS_RADIOBUTTON, aHInst, aHdlg, aId, cStr, aRect);
263 }
264
265 //--------------------------------------------------------
266 // Create a Group Box
CreateGroupBox(HINSTANCE aHInst,HWND aHdlg,int aId,const nsAString & aStr,const nsIntRect & aRect)267 static HWND CreateGroupBox(HINSTANCE aHInst,
268 HWND aHdlg,
269 int aId,
270 const nsAString& aStr,
271 const nsIntRect& aRect)
272 {
273 return CreateControl("BUTTON", BS_GROUPBOX, aHInst, aHdlg, aId, aStr, aRect);
274 }
275
276 //--------------------------------------------------------
277 // Localizes and initializes the radio buttons and group
InitializeExtendedDialog(HWND hdlg,int16_t aHowToEnableFrameUI)278 static void InitializeExtendedDialog(HWND hdlg, int16_t aHowToEnableFrameUI)
279 {
280 MOZ_ASSERT(aHowToEnableFrameUI != nsIPrintSettings::kFrameEnableNone,
281 "should not be called");
282
283 // Localize the new controls in the print dialog
284 nsCOMPtr<nsIStringBundle> strBundle;
285 if (NS_SUCCEEDED(GetLocalizedBundle(PRINTDLG_PROPERTIES, getter_AddRefs(strBundle)))) {
286 int32_t i = 0;
287 while (gAllPropKeys[i].mKeyStr != nullptr) {
288 SetText(hdlg, gAllPropKeys[i].mKeyId, strBundle, gAllPropKeys[i].mKeyStr);
289 i++;
290 }
291 }
292
293 // Set up radio buttons
294 if (aHowToEnableFrameUI == nsIPrintSettings::kFrameEnableAll) {
295 SetRadio(hdlg, rad4, false);
296 SetRadio(hdlg, rad5, true);
297 SetRadio(hdlg, rad6, false);
298 // set default so user doesn't have to actually press on it
299 gFrameSelectedRadioBtn = rad5;
300
301 } else { // nsIPrintSettings::kFrameEnableAsIsAndEach
302 SetRadio(hdlg, rad4, false);
303 SetRadio(hdlg, rad5, false, false);
304 SetRadio(hdlg, rad6, true);
305 // set default so user doesn't have to actually press on it
306 gFrameSelectedRadioBtn = rad6;
307 }
308 }
309
310
311 //--------------------------------------------------------
312 // Special Hook Procedure for handling the print dialog messages
PrintHookProc(HWND hdlg,UINT uiMsg,WPARAM wParam,LPARAM lParam)313 static UINT CALLBACK PrintHookProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
314 {
315
316 if (uiMsg == WM_COMMAND) {
317 UINT id = LOWORD(wParam);
318 if (id == rad4 || id == rad5 || id == rad6) {
319 gFrameSelectedRadioBtn = id;
320 SetRadioOfGroup(hdlg, id);
321 }
322
323 } else if (uiMsg == WM_INITDIALOG) {
324 PRINTDLG * printDlg = (PRINTDLG *)lParam;
325 if (printDlg == nullptr) return 0L;
326
327 int16_t howToEnableFrameUI = (int16_t)printDlg->lCustData;
328 // don't add frame options if they would be disabled anyway
329 // because there are no frames
330 if (howToEnableFrameUI == nsIPrintSettings::kFrameEnableNone)
331 return TRUE;
332
333 HINSTANCE hInst = (HINSTANCE)::GetWindowLongPtr(hdlg, GWLP_HINSTANCE);
334 if (hInst == nullptr) return 0L;
335
336 // Start by getting the local rects of several of the controls
337 // so we can calculate where the new controls are
338 HWND wnd = ::GetDlgItem(hdlg, grp1);
339 if (wnd == nullptr) return 0L;
340 RECT dlgRect;
341 GetLocalRect(wnd, dlgRect, hdlg);
342
343 wnd = ::GetDlgItem(hdlg, rad1); // this is the top control "All"
344 if (wnd == nullptr) return 0L;
345 RECT rad1Rect;
346 GetLocalRect(wnd, rad1Rect, hdlg);
347
348 wnd = ::GetDlgItem(hdlg, rad2); // this is the bottom control "Selection"
349 if (wnd == nullptr) return 0L;
350 RECT rad2Rect;
351 GetLocalRect(wnd, rad2Rect, hdlg);
352
353 wnd = ::GetDlgItem(hdlg, rad3); // this is the middle control "Pages"
354 if (wnd == nullptr) return 0L;
355 RECT rad3Rect;
356 GetLocalRect(wnd, rad3Rect, hdlg);
357
358 HWND okWnd = ::GetDlgItem(hdlg, IDOK);
359 if (okWnd == nullptr) return 0L;
360 RECT okRect;
361 GetLocalRect(okWnd, okRect, hdlg);
362
363 wnd = ::GetDlgItem(hdlg, grp4); // this is the "Print range" groupbox
364 if (wnd == nullptr) return 0L;
365 RECT prtRect;
366 GetLocalRect(wnd, prtRect, hdlg);
367
368
369 // calculate various different "gaps" for layout purposes
370
371 int rbGap = rad3Rect.top - rad1Rect.bottom; // gap between radiobtns
372 int grpBotGap = dlgRect.bottom - rad2Rect.bottom; // gap from bottom rb to bottom of grpbox
373 int grpGap = dlgRect.top - prtRect.bottom ; // gap between group boxes
374 int top = dlgRect.bottom + grpGap;
375 int radHgt = rad1Rect.bottom - rad1Rect.top + 1; // top of new group box
376 int y = top+(rad1Rect.top-dlgRect.top); // starting pos of first radio
377 int rbWidth = dlgRect.right - rad1Rect.left - 5; // measure from rb left to the edge of the groupbox
378 // (5 is arbitrary)
379 nsIntRect rect;
380
381 // Create and position the radio buttons
382 //
383 // If any one control cannot be created then
384 // hide the others and bail out
385 //
386 rect.SetRect(rad1Rect.left, y, rbWidth,radHgt);
387 HWND rad4Wnd = CreateRadioBtn(hInst, hdlg, rad4, kAsLaidOutOnScreenStr, rect);
388 if (rad4Wnd == nullptr) return 0L;
389 y += radHgt + rbGap;
390
391 rect.SetRect(rad1Rect.left, y, rbWidth, radHgt);
392 HWND rad5Wnd = CreateRadioBtn(hInst, hdlg, rad5, kTheSelectedFrameStr, rect);
393 if (rad5Wnd == nullptr) {
394 Show(rad4Wnd, FALSE); // hide
395 return 0L;
396 }
397 y += radHgt + rbGap;
398
399 rect.SetRect(rad1Rect.left, y, rbWidth, radHgt);
400 HWND rad6Wnd = CreateRadioBtn(hInst, hdlg, rad6, kEachFrameSeparately, rect);
401 if (rad6Wnd == nullptr) {
402 Show(rad4Wnd, FALSE); // hide
403 Show(rad5Wnd, FALSE); // hide
404 return 0L;
405 }
406 y += radHgt + grpBotGap;
407
408 // Create and position the group box
409 rect.SetRect (dlgRect.left, top, dlgRect.right-dlgRect.left+1, y-top+1);
410 HWND grpBoxWnd = CreateGroupBox(hInst, hdlg, grp3, NS_LITERAL_STRING("Print Frame"), rect);
411 if (grpBoxWnd == nullptr) {
412 Show(rad4Wnd, FALSE); // hide
413 Show(rad5Wnd, FALSE); // hide
414 Show(rad6Wnd, FALSE); // hide
415 return 0L;
416 }
417
418 // Here we figure out the old height of the dlg
419 // then figure its gap from the old grpbx to the bottom
420 // then size the dlg
421 RECT pr, cr;
422 ::GetWindowRect(hdlg, &pr);
423 ::GetClientRect(hdlg, &cr);
424
425 int dlgHgt = (cr.bottom - cr.top) + 1;
426 int bottomGap = dlgHgt - okRect.bottom;
427 pr.bottom += (dlgRect.bottom-dlgRect.top) + grpGap + 1 - (dlgHgt-dlgRect.bottom) + bottomGap;
428
429 ::SetWindowPos(hdlg, nullptr, pr.left, pr.top, pr.right-pr.left+1, pr.bottom-pr.top+1,
430 SWP_NOMOVE|SWP_NOREDRAW|SWP_NOZORDER);
431
432 // figure out the new height of the dialog
433 ::GetClientRect(hdlg, &cr);
434 dlgHgt = (cr.bottom - cr.top) + 1;
435
436 // Reposition the OK and Cancel btns
437 int okHgt = okRect.bottom - okRect.top + 1;
438 ::SetWindowPos(okWnd, nullptr, okRect.left, dlgHgt-bottomGap-okHgt, 0, 0,
439 SWP_NOSIZE|SWP_NOREDRAW|SWP_NOZORDER);
440
441 HWND cancelWnd = ::GetDlgItem(hdlg, IDCANCEL);
442 if (cancelWnd == nullptr) return 0L;
443
444 RECT cancelRect;
445 GetLocalRect(cancelWnd, cancelRect, hdlg);
446 int cancelHgt = cancelRect.bottom - cancelRect.top + 1;
447 ::SetWindowPos(cancelWnd, nullptr, cancelRect.left, dlgHgt-bottomGap-cancelHgt, 0, 0,
448 SWP_NOSIZE|SWP_NOREDRAW|SWP_NOZORDER);
449
450 // localize and initialize the groupbox and radiobuttons
451 InitializeExtendedDialog(hdlg, howToEnableFrameUI);
452
453 // Looks like we were able to extend the dialog
454 gDialogWasExtended = true;
455 return TRUE;
456 }
457 return 0L;
458 }
459
460 //----------------------------------------------------------------------------------
461 // Returns a Global Moveable Memory Handle to a DevMode
462 // from the Printer by the name of aPrintName
463 //
464 // NOTE:
465 // This function assumes that aPrintName has already been converted from
466 // unicode
467 //
468 static nsReturnRef<nsHGLOBAL>
CreateGlobalDevModeAndInit(const nsXPIDLString & aPrintName,nsIPrintSettings * aPS)469 CreateGlobalDevModeAndInit(const nsXPIDLString& aPrintName,
470 nsIPrintSettings* aPS)
471 {
472 nsHPRINTER hPrinter = nullptr;
473 // const cast kludge for silly Win32 api's
474 LPWSTR printName = const_cast<wchar_t*>(static_cast<const wchar_t*>(aPrintName.get()));
475 BOOL status = ::OpenPrinterW(printName, &hPrinter, nullptr);
476 if (!status) {
477 return nsReturnRef<nsHGLOBAL>();
478 }
479
480 // Make sure hPrinter is closed on all paths
481 nsAutoPrinter autoPrinter(hPrinter);
482
483 // Get the buffer size
484 LONG needed = ::DocumentPropertiesW(gParentWnd, hPrinter, printName, nullptr,
485 nullptr, 0);
486 if (needed < 0) {
487 return nsReturnRef<nsHGLOBAL>();
488 }
489
490 // Allocate a buffer of the correct size.
491 nsAutoDevMode newDevMode((LPDEVMODEW)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY,
492 needed));
493 if (!newDevMode) {
494 return nsReturnRef<nsHGLOBAL>();
495 }
496
497 nsHGLOBAL hDevMode = ::GlobalAlloc(GHND, needed);
498 nsAutoGlobalMem globalDevMode(hDevMode);
499 if (!hDevMode) {
500 return nsReturnRef<nsHGLOBAL>();
501 }
502
503 LONG ret = ::DocumentPropertiesW(gParentWnd, hPrinter, printName, newDevMode,
504 nullptr, DM_OUT_BUFFER);
505 if (ret != IDOK) {
506 return nsReturnRef<nsHGLOBAL>();
507 }
508
509 // Lock memory and copy contents from DEVMODE (current printer)
510 // to Global Memory DEVMODE
511 LPDEVMODEW devMode = (DEVMODEW *)::GlobalLock(hDevMode);
512 if (!devMode) {
513 return nsReturnRef<nsHGLOBAL>();
514 }
515
516 memcpy(devMode, newDevMode.get(), needed);
517 // Initialize values from the PrintSettings
518 nsCOMPtr<nsIPrintSettingsWin> psWin = do_QueryInterface(aPS);
519 MOZ_ASSERT(psWin);
520 psWin->CopyToNative(devMode);
521
522 // Sets back the changes we made to the DevMode into the Printer Driver
523 ret = ::DocumentPropertiesW(gParentWnd, hPrinter, printName, devMode, devMode,
524 DM_IN_BUFFER | DM_OUT_BUFFER);
525 if (ret != IDOK) {
526 ::GlobalUnlock(hDevMode);
527 return nsReturnRef<nsHGLOBAL>();
528 }
529
530 ::GlobalUnlock(hDevMode);
531
532 return globalDevMode.out();
533 }
534
535 //------------------------------------------------------------------
536 // helper
GetDefaultPrinterNameFromGlobalPrinters(nsXPIDLString & printerName)537 static void GetDefaultPrinterNameFromGlobalPrinters(nsXPIDLString &printerName)
538 {
539 nsCOMPtr<nsIPrinterEnumerator> prtEnum = do_GetService("@mozilla.org/gfx/printerenumerator;1");
540 if (prtEnum) {
541 prtEnum->GetDefaultPrinterName(getter_Copies(printerName));
542 }
543 }
544
545 // Determine whether we have a completely native dialog
546 // or whether we cshould extend it
ShouldExtendPrintDialog()547 static bool ShouldExtendPrintDialog()
548 {
549 nsresult rv;
550 nsCOMPtr<nsIPrefService> prefs =
551 do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
552 NS_ENSURE_SUCCESS(rv, true);
553 nsCOMPtr<nsIPrefBranch> prefBranch;
554 rv = prefs->GetBranch(nullptr, getter_AddRefs(prefBranch));
555 NS_ENSURE_SUCCESS(rv, true);
556
557 bool result;
558 rv = prefBranch->GetBoolPref("print.extend_native_print_dialog", &result);
559 NS_ENSURE_SUCCESS(rv, true);
560 return result;
561 }
562
563 //------------------------------------------------------------------
564 // Displays the native Print Dialog
565 static nsresult
ShowNativePrintDialog(HWND aHWnd,nsIPrintSettings * aPrintSettings)566 ShowNativePrintDialog(HWND aHWnd,
567 nsIPrintSettings* aPrintSettings)
568 {
569 //NS_ENSURE_ARG_POINTER(aHWnd);
570 NS_ENSURE_ARG_POINTER(aPrintSettings);
571
572 gDialogWasExtended = false;
573
574 // Get the Print Name to be used
575 nsXPIDLString printerName;
576 aPrintSettings->GetPrinterName(getter_Copies(printerName));
577
578 // If there is no name then use the default printer
579 if (printerName.IsEmpty()) {
580 GetDefaultPrinterNameFromGlobalPrinters(printerName);
581 } else {
582 HANDLE hPrinter = nullptr;
583 if(!::OpenPrinterW(const_cast<wchar_t*>(static_cast<const wchar_t*>(printerName.get())),
584 &hPrinter, nullptr)) {
585 // If the last used printer is not found, we should use default printer.
586 GetDefaultPrinterNameFromGlobalPrinters(printerName);
587 } else {
588 ::ClosePrinter(hPrinter);
589 }
590 }
591
592 // Now create a DEVNAMES struct so the the dialog is initialized correctly.
593
594 uint32_t len = printerName.Length();
595 nsHGLOBAL hDevNames = ::GlobalAlloc(GHND, sizeof(wchar_t) * (len + 1)
596 + sizeof(DEVNAMES));
597 nsAutoGlobalMem autoDevNames(hDevNames);
598 if (!hDevNames) {
599 return NS_ERROR_OUT_OF_MEMORY;
600 }
601
602 DEVNAMES* pDevNames = (DEVNAMES*)::GlobalLock(hDevNames);
603 if (!pDevNames) {
604 return NS_ERROR_FAILURE;
605 }
606 pDevNames->wDriverOffset = sizeof(DEVNAMES)/sizeof(wchar_t);
607 pDevNames->wDeviceOffset = sizeof(DEVNAMES)/sizeof(wchar_t);
608 pDevNames->wOutputOffset = sizeof(DEVNAMES)/sizeof(wchar_t)+len;
609 pDevNames->wDefault = 0;
610
611 memcpy(pDevNames+1, printerName, (len + 1) * sizeof(wchar_t));
612 ::GlobalUnlock(hDevNames);
613
614 // Create a Moveable Memory Object that holds a new DevMode
615 // from the Printer Name
616 // The PRINTDLG.hDevMode requires that it be a moveable memory object
617 // NOTE: autoDevMode is automatically freed when any error occurred
618 nsAutoGlobalMem autoDevMode(CreateGlobalDevModeAndInit(printerName, aPrintSettings));
619
620 // Prepare to Display the Print Dialog
621 PRINTDLGW prntdlg;
622 memset(&prntdlg, 0, sizeof(PRINTDLGW));
623
624 prntdlg.lStructSize = sizeof(prntdlg);
625 prntdlg.hwndOwner = aHWnd;
626 prntdlg.hDevMode = autoDevMode.get();
627 prntdlg.hDevNames = hDevNames;
628 prntdlg.hDC = nullptr;
629 prntdlg.Flags = PD_ALLPAGES | PD_RETURNIC |
630 PD_USEDEVMODECOPIESANDCOLLATE | PD_COLLATE;
631
632 // if there is a current selection then enable the "Selection" radio button
633 int16_t howToEnableFrameUI = nsIPrintSettings::kFrameEnableNone;
634 bool isOn;
635 aPrintSettings->GetPrintOptions(nsIPrintSettings::kEnableSelectionRB, &isOn);
636 if (!isOn) {
637 prntdlg.Flags |= PD_NOSELECTION;
638 }
639 aPrintSettings->GetHowToEnableFrameUI(&howToEnableFrameUI);
640
641 int32_t pg = 1;
642 aPrintSettings->GetStartPageRange(&pg);
643 prntdlg.nFromPage = pg;
644
645 aPrintSettings->GetEndPageRange(&pg);
646 prntdlg.nToPage = pg;
647
648 prntdlg.nMinPage = 1;
649 prntdlg.nMaxPage = 0xFFFF;
650 prntdlg.nCopies = 1;
651 prntdlg.lpfnSetupHook = nullptr;
652 prntdlg.lpSetupTemplateName = nullptr;
653 prntdlg.hPrintTemplate = nullptr;
654 prntdlg.hSetupTemplate = nullptr;
655
656 prntdlg.hInstance = nullptr;
657 prntdlg.lpPrintTemplateName = nullptr;
658
659 if (!ShouldExtendPrintDialog()) {
660 prntdlg.lCustData = 0;
661 prntdlg.lpfnPrintHook = nullptr;
662 } else {
663 // Set up print dialog "hook" procedure for extending the dialog
664 prntdlg.lCustData = (DWORD)howToEnableFrameUI;
665 prntdlg.lpfnPrintHook = (LPPRINTHOOKPROC)PrintHookProc;
666 prntdlg.Flags |= PD_ENABLEPRINTHOOK;
667 }
668
669 BOOL result;
670 {
671 mozilla::widget::WinUtils::AutoSystemDpiAware dpiAwareness;
672 result = ::PrintDlgW(&prntdlg);
673 }
674
675 if (TRUE == result) {
676 // check to make sure we don't have any nullptr pointers
677 NS_ENSURE_TRUE(aPrintSettings && prntdlg.hDevMode, NS_ERROR_FAILURE);
678
679 if (prntdlg.hDevNames == nullptr) {
680 return NS_ERROR_FAILURE;
681 }
682 // Lock the deviceNames and check for nullptr
683 DEVNAMES *devnames = (DEVNAMES *)::GlobalLock(prntdlg.hDevNames);
684 if (devnames == nullptr) {
685 return NS_ERROR_FAILURE;
686 }
687
688 char16_t* device = &(((char16_t *)devnames)[devnames->wDeviceOffset]);
689 char16_t* driver = &(((char16_t *)devnames)[devnames->wDriverOffset]);
690
691 // Check to see if the "Print To File" control is checked
692 // then take the name from devNames and set it in the PrintSettings
693 //
694 // NOTE:
695 // As per Microsoft SDK documentation the returned value offset from
696 // devnames->wOutputOffset is either "FILE:" or nullptr
697 // if the "Print To File" checkbox is checked it MUST be "FILE:"
698 // We assert as an extra safety check.
699 if (prntdlg.Flags & PD_PRINTTOFILE) {
700 char16ptr_t fileName = &(((wchar_t *)devnames)[devnames->wOutputOffset]);
701 NS_ASSERTION(wcscmp(fileName, L"FILE:") == 0, "FileName must be `FILE:`");
702 aPrintSettings->SetToFileName(fileName);
703 aPrintSettings->SetPrintToFile(true);
704 } else {
705 // clear "print to file" info
706 aPrintSettings->SetPrintToFile(false);
707 aPrintSettings->SetToFileName(nullptr);
708 }
709
710 nsCOMPtr<nsIPrintSettingsWin> psWin(do_QueryInterface(aPrintSettings));
711 if (!psWin) {
712 return NS_ERROR_FAILURE;
713 }
714
715 // Setup local Data members
716 psWin->SetDeviceName(device);
717 psWin->SetDriverName(driver);
718
719 #if defined(DEBUG_rods) || defined(DEBUG_dcone)
720 wprintf(L"printer: driver %s, device %s flags: %d\n", driver, device, prntdlg.Flags);
721 #endif
722 // fill the print options with the info from the dialog
723
724 aPrintSettings->SetPrinterName(device);
725
726 if (prntdlg.Flags & PD_SELECTION) {
727 aPrintSettings->SetPrintRange(nsIPrintSettings::kRangeSelection);
728
729 } else if (prntdlg.Flags & PD_PAGENUMS) {
730 aPrintSettings->SetPrintRange(nsIPrintSettings::kRangeSpecifiedPageRange);
731 aPrintSettings->SetStartPageRange(prntdlg.nFromPage);
732 aPrintSettings->SetEndPageRange(prntdlg.nToPage);
733
734 } else { // (prntdlg.Flags & PD_ALLPAGES)
735 aPrintSettings->SetPrintRange(nsIPrintSettings::kRangeAllPages);
736 }
737
738 if (howToEnableFrameUI != nsIPrintSettings::kFrameEnableNone) {
739 // make sure the dialog got extended
740 if (gDialogWasExtended) {
741 // check to see about the frame radio buttons
742 switch (gFrameSelectedRadioBtn) {
743 case rad4:
744 aPrintSettings->SetPrintFrameType(nsIPrintSettings::kFramesAsIs);
745 break;
746 case rad5:
747 aPrintSettings->SetPrintFrameType(nsIPrintSettings::kSelectedFrame);
748 break;
749 case rad6:
750 aPrintSettings->SetPrintFrameType(nsIPrintSettings::kEachFrameSep);
751 break;
752 } // switch
753 } else {
754 // if it didn't get extended then have it default to printing
755 // each frame separately
756 aPrintSettings->SetPrintFrameType(nsIPrintSettings::kEachFrameSep);
757 }
758 } else {
759 aPrintSettings->SetPrintFrameType(nsIPrintSettings::kNoFrames);
760 }
761 // Unlock DeviceNames
762 ::GlobalUnlock(prntdlg.hDevNames);
763
764 // Transfer the settings from the native data to the PrintSettings
765 LPDEVMODEW devMode = (LPDEVMODEW)::GlobalLock(prntdlg.hDevMode);
766 if (!devMode || !prntdlg.hDC) {
767 return NS_ERROR_FAILURE;
768 }
769 psWin->SetDevMode(devMode); // copies DevMode
770 psWin->CopyFromNative(prntdlg.hDC, devMode);
771 ::GlobalUnlock(prntdlg.hDevMode);
772 ::DeleteDC(prntdlg.hDC);
773
774 #if defined(DEBUG_rods) || defined(DEBUG_dcone)
775 bool printSelection = prntdlg.Flags & PD_SELECTION;
776 bool printAllPages = prntdlg.Flags & PD_ALLPAGES;
777 bool printNumPages = prntdlg.Flags & PD_PAGENUMS;
778 int32_t fromPageNum = 0;
779 int32_t toPageNum = 0;
780
781 if (printNumPages) {
782 fromPageNum = prntdlg.nFromPage;
783 toPageNum = prntdlg.nToPage;
784 }
785 if (printSelection) {
786 printf("Printing the selection\n");
787
788 } else if (printAllPages) {
789 printf("Printing all the pages\n");
790
791 } else {
792 printf("Printing from page no. %d to %d\n", fromPageNum, toPageNum);
793 }
794 #endif
795
796 } else {
797 ::SetFocus(aHWnd);
798 aPrintSettings->SetIsCancelled(true);
799 return NS_ERROR_ABORT;
800 }
801
802 return NS_OK;
803 }
804
805 //------------------------------------------------------------------
806 static void
PrepareForPrintDialog(nsIWebBrowserPrint * aWebBrowserPrint,nsIPrintSettings * aPS)807 PrepareForPrintDialog(nsIWebBrowserPrint* aWebBrowserPrint, nsIPrintSettings* aPS)
808 {
809 NS_ASSERTION(aWebBrowserPrint, "Can't be null");
810 NS_ASSERTION(aPS, "Can't be null");
811
812 bool isFramesetDocument;
813 bool isFramesetFrameSelected;
814 bool isIFrameSelected;
815 bool isRangeSelection;
816
817 aWebBrowserPrint->GetIsFramesetDocument(&isFramesetDocument);
818 aWebBrowserPrint->GetIsFramesetFrameSelected(&isFramesetFrameSelected);
819 aWebBrowserPrint->GetIsIFrameSelected(&isIFrameSelected);
820 aWebBrowserPrint->GetIsRangeSelection(&isRangeSelection);
821
822 // Setup print options for UI
823 if (isFramesetDocument) {
824 if (isFramesetFrameSelected) {
825 aPS->SetHowToEnableFrameUI(nsIPrintSettings::kFrameEnableAll);
826 } else {
827 aPS->SetHowToEnableFrameUI(nsIPrintSettings::kFrameEnableAsIsAndEach);
828 }
829 } else {
830 aPS->SetHowToEnableFrameUI(nsIPrintSettings::kFrameEnableNone);
831 }
832
833 // Now determine how to set up the Frame print UI
834 aPS->SetPrintOptions(nsIPrintSettings::kEnableSelectionRB, isRangeSelection || isIFrameSelected);
835
836 }
837
838 //----------------------------------------------------------------------------------
839 //-- Show Print Dialog
840 //----------------------------------------------------------------------------------
NativeShowPrintDialog(HWND aHWnd,nsIWebBrowserPrint * aWebBrowserPrint,nsIPrintSettings * aPrintSettings)841 nsresult NativeShowPrintDialog(HWND aHWnd,
842 nsIWebBrowserPrint* aWebBrowserPrint,
843 nsIPrintSettings* aPrintSettings)
844 {
845 PrepareForPrintDialog(aWebBrowserPrint, aPrintSettings);
846
847 nsresult rv = ShowNativePrintDialog(aHWnd, aPrintSettings);
848 if (aHWnd) {
849 ::DestroyWindow(aHWnd);
850 }
851
852 return rv;
853 }
854
855