forked from mirrors/gecko-dev
		
	 bd27b86da3
			
		
	
	
		bd27b86da3
		
	
	
	
	
		
			
			MozReview-Commit-ID: AqnztoUbsmk --HG-- extra : rebase_source : 76a232a08b42ed73b4922c03bc0f2e9d1769203b
		
			
				
	
	
		
			902 lines
		
	
	
	
		
			28 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			902 lines
		
	
	
	
		
			28 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| /* -------------------------------------------------------------------
 | |
| To Build This:
 | |
| 
 | |
|   You need to add this to the the makefile.win in mozilla/dom/base:
 | |
| 
 | |
| 	.\$(OBJDIR)\nsFlyOwnPrintDialog.obj	\
 | |
| 
 | |
| 
 | |
|   And this to the makefile.win in mozilla/content/build:
 | |
| 
 | |
| WIN_LIBS=                                       \
 | |
|         winspool.lib                           \
 | |
|         comctl32.lib                           \
 | |
|         comdlg32.lib
 | |
| 
 | |
| ---------------------------------------------------------------------- */
 | |
| 
 | |
| #include "plstr.h"
 | |
| #include <windows.h>
 | |
| #include <tchar.h>
 | |
| 
 | |
| #include <unknwn.h>
 | |
| #include <commdlg.h>
 | |
| 
 | |
| #include "nsIWebBrowserPrint.h"
 | |
| #include "nsString.h"
 | |
| #include "nsIServiceManager.h"
 | |
| #include "nsReadableUtils.h"
 | |
| #include "nsIPrintSettings.h"
 | |
| #include "nsIPrintSettingsWin.h"
 | |
| #include "nsIPrinterEnumerator.h"
 | |
| 
 | |
| #include "nsRect.h"
 | |
| 
 | |
| #include "nsIPrefService.h"
 | |
| #include "nsIPrefBranch.h"
 | |
| 
 | |
| #include "nsCRT.h"
 | |
| #include "prenv.h" /* for PR_GetEnv */
 | |
| 
 | |
| #include <windows.h>
 | |
| #include <winspool.h>
 | |
| 
 | |
| // For Localization
 | |
| #include "nsIStringBundle.h"
 | |
| 
 | |
| // For NS_CopyUnicodeToNative
 | |
| #include "nsNativeCharsetUtils.h"
 | |
| 
 | |
| // This is for extending the dialog
 | |
| #include <dlgs.h>
 | |
| 
 | |
| #include "nsWindowsHelpers.h"
 | |
| #include "WinUtils.h"
 | |
| 
 | |
| // Default labels for the radio buttons
 | |
| static const wchar_t* kAsLaidOutOnScreenStr = L"As &laid out on the screen";
 | |
| static const wchar_t* kTheSelectedFrameStr = L"The selected &frame";
 | |
| static const wchar_t* kEachFrameSeparately = L"&Each frame separately";
 | |
| 
 | |
| //-----------------------------------------------
 | |
| // Global Data
 | |
| //-----------------------------------------------
 | |
| // Identifies which new radio btn was cliked on
 | |
| static UINT gFrameSelectedRadioBtn = 0;
 | |
| 
 | |
| // Indicates whether the native print dialog was successfully extended
 | |
| static bool gDialogWasExtended = false;
 | |
| 
 | |
| #define PRINTDLG_PROPERTIES "chrome://global/locale/printdialog.properties"
 | |
| 
 | |
| static HWND gParentWnd = nullptr;
 | |
| 
 | |
| //----------------------------------------------------------------------------------
 | |
| // Return localized bundle for resource strings
 | |
| static nsresult
 | |
| GetLocalizedBundle(const char* aPropFileName, nsIStringBundle** aStrBundle)
 | |
| {
 | |
|   NS_ENSURE_ARG_POINTER(aPropFileName);
 | |
|   NS_ENSURE_ARG_POINTER(aStrBundle);
 | |
| 
 | |
|   nsresult rv;
 | |
|   nsCOMPtr<nsIStringBundle> bundle;
 | |
| 
 | |
|   // Create bundle
 | |
|   nsCOMPtr<nsIStringBundleService> stringService =
 | |
|     do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
 | |
|   if (NS_SUCCEEDED(rv) && stringService) {
 | |
|     rv = stringService->CreateBundle(aPropFileName, aStrBundle);
 | |
|   }
 | |
| 
 | |
|   return rv;
 | |
| }
 | |
| 
 | |
| //--------------------------------------------------------
 | |
| // Return localized string
 | |
| static nsresult
 | |
| GetLocalizedString(nsIStringBundle* aStrBundle,
 | |
|                    const char* aKey,
 | |
|                    nsString& oVal)
 | |
| {
 | |
|   NS_ENSURE_ARG_POINTER(aStrBundle);
 | |
|   NS_ENSURE_ARG_POINTER(aKey);
 | |
| 
 | |
|   // Determine default label from string bundle
 | |
|   nsAutoString valUni;
 | |
|   nsresult rv = aStrBundle->GetStringFromName(aKey, valUni);
 | |
|   if (NS_SUCCEEDED(rv)) {
 | |
|     oVal.Assign(valUni);
 | |
|   } else {
 | |
|     oVal.Truncate();
 | |
|   }
 | |
|   return rv;
 | |
| }
 | |
| 
 | |
| //--------------------------------------------------------
 | |
| // Set a multi-byte string in the control
 | |
| static void
 | |
| SetTextOnWnd(HWND aControl, const nsAString& aStr)
 | |
| {
 | |
|   ::SetWindowTextW(aControl,
 | |
|                    reinterpret_cast<const wchar_t*>(aStr.BeginReading()));
 | |
| }
 | |
| 
 | |
| //--------------------------------------------------------
 | |
| // Will get the control and localized string by "key"
 | |
| static void
 | |
| SetText(HWND aParent, UINT aId, nsIStringBundle* aStrBundle, const char* aKey)
 | |
| {
 | |
|   HWND wnd = GetDlgItem(aParent, aId);
 | |
|   if (!wnd) {
 | |
|     return;
 | |
|   }
 | |
|   nsAutoString str;
 | |
|   nsresult rv = GetLocalizedString(aStrBundle, aKey, str);
 | |
|   if (NS_SUCCEEDED(rv)) {
 | |
|     SetTextOnWnd(wnd, str);
 | |
|   }
 | |
| }
 | |
| 
 | |
| //--------------------------------------------------------
 | |
| static void
 | |
| SetRadio(HWND aParent, UINT aId, bool aIsSet, bool isEnabled = true)
 | |
| {
 | |
|   HWND wnd = ::GetDlgItem(aParent, aId);
 | |
|   if (!wnd) {
 | |
|     return;
 | |
|   }
 | |
|   if (!isEnabled) {
 | |
|     ::EnableWindow(wnd, FALSE);
 | |
|     return;
 | |
|   }
 | |
|   ::EnableWindow(wnd, TRUE);
 | |
|   ::SendMessage(wnd, BM_SETCHECK, (WPARAM)aIsSet, (LPARAM)0);
 | |
| }
 | |
| 
 | |
| //--------------------------------------------------------
 | |
| static void
 | |
| SetRadioOfGroup(HWND aDlg, int aRadId)
 | |
| {
 | |
|   int radioIds[] = { rad4, rad5, rad6 };
 | |
|   int numRads = 3;
 | |
| 
 | |
|   for (int i = 0; i < numRads; i++) {
 | |
|     HWND radWnd = ::GetDlgItem(aDlg, radioIds[i]);
 | |
|     if (radWnd != nullptr) {
 | |
|       ::SendMessage(
 | |
|         radWnd, BM_SETCHECK, (WPARAM)(radioIds[i] == aRadId), (LPARAM)0);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| //--------------------------------------------------------
 | |
| typedef struct
 | |
| {
 | |
|   const char* mKeyStr;
 | |
|   long mKeyId;
 | |
| } PropKeyInfo;
 | |
| 
 | |
| // These are the control ids used in the dialog and
 | |
| // defined by MS-Windows in commdlg.h
 | |
| static PropKeyInfo gAllPropKeys[] = { { "printFramesTitleWindows", grp3 },
 | |
|                                       { "asLaidOutWindows", rad4 },
 | |
|                                       { "selectedFrameWindows", rad5 },
 | |
|                                       { "separateFramesWindows", rad6 },
 | |
|                                       { nullptr, 0 } };
 | |
| 
 | |
| //--------------------------------------------------------
 | |
| //--------------------------------------------------------
 | |
| //--------------------------------------------------------
 | |
| //--------------------------------------------------------
 | |
| // Get the absolute coords of the child windows relative
 | |
| // to its parent window
 | |
| static void
 | |
| GetLocalRect(HWND aWnd, RECT& aRect, HWND aParent)
 | |
| {
 | |
|   ::GetWindowRect(aWnd, &aRect);
 | |
| 
 | |
|   // MapWindowPoints converts screen coordinates to client coordinates.
 | |
|   // It works correctly in both left-to-right and right-to-left windows.
 | |
|   ::MapWindowPoints(nullptr, aParent, (LPPOINT)&aRect, 2);
 | |
| }
 | |
| 
 | |
| //--------------------------------------------------------
 | |
| // Show or Hide the control
 | |
| static void
 | |
| Show(HWND aWnd, bool bState)
 | |
| {
 | |
|   if (aWnd) {
 | |
|     ::ShowWindow(aWnd, bState ? SW_SHOW : SW_HIDE);
 | |
|   }
 | |
| }
 | |
| 
 | |
| //--------------------------------------------------------
 | |
| // Create a child window "control"
 | |
| static HWND
 | |
| CreateControl(LPCWSTR aType,
 | |
|               DWORD aStyle,
 | |
|               HINSTANCE aHInst,
 | |
|               HWND aHdlg,
 | |
|               int aId,
 | |
|               LPCWSTR aStr,
 | |
|               const nsIntRect& aRect)
 | |
| {
 | |
|   HWND hWnd = ::CreateWindowW(aType,
 | |
|                               aStr,
 | |
|                               WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE | aStyle,
 | |
|                               aRect.X(),
 | |
|                               aRect.Y(),
 | |
|                               aRect.Width(),
 | |
|                               aRect.Height(),
 | |
|                               (HWND)aHdlg,
 | |
|                               (HMENU)(intptr_t)aId,
 | |
|                               aHInst,
 | |
|                               nullptr);
 | |
|   if (hWnd == nullptr)
 | |
|     return nullptr;
 | |
| 
 | |
|   // get the native font for the dialog and
 | |
|   // set it into the new control
 | |
|   HFONT hFont = (HFONT)::SendMessage(aHdlg, WM_GETFONT, (WPARAM)0, (LPARAM)0);
 | |
|   if (hFont != nullptr) {
 | |
|     ::SendMessage(hWnd, WM_SETFONT, (WPARAM)hFont, (LPARAM)0);
 | |
|   }
 | |
|   return hWnd;
 | |
| }
 | |
| 
 | |
| //--------------------------------------------------------
 | |
| // Create a Radio Button
 | |
| static HWND
 | |
| CreateRadioBtn(HINSTANCE aHInst,
 | |
|                HWND aHdlg,
 | |
|                int aId,
 | |
|                LPCWSTR aStr,
 | |
|                const nsIntRect& aRect)
 | |
| {
 | |
|   return CreateControl(
 | |
|     L"BUTTON", BS_RADIOBUTTON, aHInst, aHdlg, aId, aStr, aRect);
 | |
| }
 | |
| 
 | |
| //--------------------------------------------------------
 | |
| // Create a Group Box
 | |
| static HWND
 | |
| CreateGroupBox(HINSTANCE aHInst,
 | |
|                HWND aHdlg,
 | |
|                int aId,
 | |
|                LPCWSTR aStr,
 | |
|                const nsIntRect& aRect)
 | |
| {
 | |
|   return CreateControl(L"BUTTON", BS_GROUPBOX, aHInst, aHdlg, aId, aStr, aRect);
 | |
| }
 | |
| 
 | |
| //--------------------------------------------------------
 | |
| // Localizes and initializes the radio buttons and group
 | |
| static void
 | |
| InitializeExtendedDialog(HWND hdlg, int16_t aHowToEnableFrameUI)
 | |
| {
 | |
|   MOZ_ASSERT(aHowToEnableFrameUI != nsIPrintSettings::kFrameEnableNone,
 | |
|              "should not be called");
 | |
| 
 | |
|   // Localize the new controls in the print dialog
 | |
|   nsCOMPtr<nsIStringBundle> strBundle;
 | |
|   if (NS_SUCCEEDED(
 | |
|         GetLocalizedBundle(PRINTDLG_PROPERTIES, getter_AddRefs(strBundle)))) {
 | |
|     int32_t i = 0;
 | |
|     while (gAllPropKeys[i].mKeyStr != nullptr) {
 | |
|       SetText(hdlg, gAllPropKeys[i].mKeyId, strBundle, gAllPropKeys[i].mKeyStr);
 | |
|       i++;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Set up radio buttons
 | |
|   if (aHowToEnableFrameUI == nsIPrintSettings::kFrameEnableAll) {
 | |
|     SetRadio(hdlg, rad4, false);
 | |
|     SetRadio(hdlg, rad5, true);
 | |
|     SetRadio(hdlg, rad6, false);
 | |
|     // set default so user doesn't have to actually press on it
 | |
|     gFrameSelectedRadioBtn = rad5;
 | |
| 
 | |
|   } else { // nsIPrintSettings::kFrameEnableAsIsAndEach
 | |
|     SetRadio(hdlg, rad4, false);
 | |
|     SetRadio(hdlg, rad5, false, false);
 | |
|     SetRadio(hdlg, rad6, true);
 | |
|     // set default so user doesn't have to actually press on it
 | |
|     gFrameSelectedRadioBtn = rad6;
 | |
|   }
 | |
| }
 | |
| 
 | |
| //--------------------------------------------------------
 | |
| // Special Hook Procedure for handling the print dialog messages
 | |
| static UINT CALLBACK
 | |
| PrintHookProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
 | |
| {
 | |
| 
 | |
|   if (uiMsg == WM_COMMAND) {
 | |
|     UINT id = LOWORD(wParam);
 | |
|     if (id == rad4 || id == rad5 || id == rad6) {
 | |
|       gFrameSelectedRadioBtn = id;
 | |
|       SetRadioOfGroup(hdlg, id);
 | |
|     }
 | |
| 
 | |
|   } else if (uiMsg == WM_INITDIALOG) {
 | |
|     PRINTDLG* printDlg = (PRINTDLG*)lParam;
 | |
|     if (printDlg == nullptr)
 | |
|       return 0L;
 | |
| 
 | |
|     int16_t howToEnableFrameUI = (int16_t)printDlg->lCustData;
 | |
|     // don't add frame options if they would be disabled anyway
 | |
|     // because there are no frames
 | |
|     if (howToEnableFrameUI == nsIPrintSettings::kFrameEnableNone)
 | |
|       return TRUE;
 | |
| 
 | |
|     HINSTANCE hInst = (HINSTANCE)::GetWindowLongPtr(hdlg, GWLP_HINSTANCE);
 | |
|     if (hInst == nullptr)
 | |
|       return 0L;
 | |
| 
 | |
|     // Start by getting the local rects of several of the controls
 | |
|     // so we can calculate where the new controls are
 | |
|     HWND wnd = ::GetDlgItem(hdlg, grp1);
 | |
|     if (wnd == nullptr)
 | |
|       return 0L;
 | |
|     RECT dlgRect;
 | |
|     GetLocalRect(wnd, dlgRect, hdlg);
 | |
| 
 | |
|     wnd = ::GetDlgItem(hdlg, rad1); // this is the top control "All"
 | |
|     if (wnd == nullptr)
 | |
|       return 0L;
 | |
|     RECT rad1Rect;
 | |
|     GetLocalRect(wnd, rad1Rect, hdlg);
 | |
| 
 | |
|     wnd = ::GetDlgItem(hdlg, rad2); // this is the bottom control "Selection"
 | |
|     if (wnd == nullptr)
 | |
|       return 0L;
 | |
|     RECT rad2Rect;
 | |
|     GetLocalRect(wnd, rad2Rect, hdlg);
 | |
| 
 | |
|     wnd = ::GetDlgItem(hdlg, rad3); // this is the middle control "Pages"
 | |
|     if (wnd == nullptr)
 | |
|       return 0L;
 | |
|     RECT rad3Rect;
 | |
|     GetLocalRect(wnd, rad3Rect, hdlg);
 | |
| 
 | |
|     HWND okWnd = ::GetDlgItem(hdlg, IDOK);
 | |
|     if (okWnd == nullptr)
 | |
|       return 0L;
 | |
|     RECT okRect;
 | |
|     GetLocalRect(okWnd, okRect, hdlg);
 | |
| 
 | |
|     wnd = ::GetDlgItem(hdlg, grp4); // this is the "Print range" groupbox
 | |
|     if (wnd == nullptr)
 | |
|       return 0L;
 | |
|     RECT prtRect;
 | |
|     GetLocalRect(wnd, prtRect, hdlg);
 | |
| 
 | |
|     // calculate various different "gaps" for layout purposes
 | |
| 
 | |
|     int rbGap = rad3Rect.top - rad1Rect.bottom; // gap between radiobtns
 | |
|     int grpBotGap = dlgRect.bottom -
 | |
|                     rad2Rect.bottom; // gap from bottom rb to bottom of grpbox
 | |
|     int grpGap = dlgRect.top - prtRect.bottom; // gap between group boxes
 | |
|     int top = dlgRect.bottom + grpGap;
 | |
|     int radHgt = rad1Rect.bottom - rad1Rect.top + 1; // top of new group box
 | |
|     int y = top + (rad1Rect.top - dlgRect.top); // starting pos of first radio
 | |
|     int rbWidth = dlgRect.right - rad1Rect.left -
 | |
|                   5; // measure from rb left to the edge of the groupbox
 | |
|                      // (5 is arbitrary)
 | |
|     nsIntRect rect;
 | |
| 
 | |
|     // Create and position the radio buttons
 | |
|     //
 | |
|     // If any one control cannot be created then
 | |
|     // hide the others and bail out
 | |
|     //
 | |
|     rect.SetRect(rad1Rect.left, y, rbWidth, radHgt);
 | |
|     HWND rad4Wnd =
 | |
|       CreateRadioBtn(hInst, hdlg, rad4, kAsLaidOutOnScreenStr, rect);
 | |
|     if (rad4Wnd == nullptr)
 | |
|       return 0L;
 | |
|     y += radHgt + rbGap;
 | |
| 
 | |
|     rect.SetRect(rad1Rect.left, y, rbWidth, radHgt);
 | |
|     HWND rad5Wnd =
 | |
|       CreateRadioBtn(hInst, hdlg, rad5, kTheSelectedFrameStr, rect);
 | |
|     if (rad5Wnd == nullptr) {
 | |
|       Show(rad4Wnd, FALSE); // hide
 | |
|       return 0L;
 | |
|     }
 | |
|     y += radHgt + rbGap;
 | |
| 
 | |
|     rect.SetRect(rad1Rect.left, y, rbWidth, radHgt);
 | |
|     HWND rad6Wnd =
 | |
|       CreateRadioBtn(hInst, hdlg, rad6, kEachFrameSeparately, rect);
 | |
|     if (rad6Wnd == nullptr) {
 | |
|       Show(rad4Wnd, FALSE); // hide
 | |
|       Show(rad5Wnd, FALSE); // hide
 | |
|       return 0L;
 | |
|     }
 | |
|     y += radHgt + grpBotGap;
 | |
| 
 | |
|     // Create and position the group box
 | |
|     rect.SetRect(
 | |
|       dlgRect.left, top, dlgRect.right - dlgRect.left + 1, y - top + 1);
 | |
|     HWND grpBoxWnd = CreateGroupBox(hInst, hdlg, grp3, L"Print Frame", rect);
 | |
|     if (grpBoxWnd == nullptr) {
 | |
|       Show(rad4Wnd, FALSE); // hide
 | |
|       Show(rad5Wnd, FALSE); // hide
 | |
|       Show(rad6Wnd, FALSE); // hide
 | |
|       return 0L;
 | |
|     }
 | |
| 
 | |
|     // Here we figure out the old height of the dlg
 | |
|     // then figure its gap from the old grpbx to the bottom
 | |
|     // then size the dlg
 | |
|     RECT pr, cr;
 | |
|     ::GetWindowRect(hdlg, &pr);
 | |
|     ::GetClientRect(hdlg, &cr);
 | |
| 
 | |
|     int dlgHgt = (cr.bottom - cr.top) + 1;
 | |
|     int bottomGap = dlgHgt - okRect.bottom;
 | |
|     pr.bottom += (dlgRect.bottom - dlgRect.top) + grpGap + 1 -
 | |
|                  (dlgHgt - dlgRect.bottom) + bottomGap;
 | |
| 
 | |
|     ::SetWindowPos(hdlg,
 | |
|                    nullptr,
 | |
|                    pr.left,
 | |
|                    pr.top,
 | |
|                    pr.right - pr.left + 1,
 | |
|                    pr.bottom - pr.top + 1,
 | |
|                    SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
 | |
| 
 | |
|     // figure out the new height of the dialog
 | |
|     ::GetClientRect(hdlg, &cr);
 | |
|     dlgHgt = (cr.bottom - cr.top) + 1;
 | |
| 
 | |
|     // Reposition the OK and Cancel btns
 | |
|     int okHgt = okRect.bottom - okRect.top + 1;
 | |
|     ::SetWindowPos(okWnd,
 | |
|                    nullptr,
 | |
|                    okRect.left,
 | |
|                    dlgHgt - bottomGap - okHgt,
 | |
|                    0,
 | |
|                    0,
 | |
|                    SWP_NOSIZE | SWP_NOREDRAW | SWP_NOZORDER);
 | |
| 
 | |
|     HWND cancelWnd = ::GetDlgItem(hdlg, IDCANCEL);
 | |
|     if (cancelWnd == nullptr)
 | |
|       return 0L;
 | |
| 
 | |
|     RECT cancelRect;
 | |
|     GetLocalRect(cancelWnd, cancelRect, hdlg);
 | |
|     int cancelHgt = cancelRect.bottom - cancelRect.top + 1;
 | |
|     ::SetWindowPos(cancelWnd,
 | |
|                    nullptr,
 | |
|                    cancelRect.left,
 | |
|                    dlgHgt - bottomGap - cancelHgt,
 | |
|                    0,
 | |
|                    0,
 | |
|                    SWP_NOSIZE | SWP_NOREDRAW | SWP_NOZORDER);
 | |
| 
 | |
|     // localize and initialize the groupbox and radiobuttons
 | |
|     InitializeExtendedDialog(hdlg, howToEnableFrameUI);
 | |
| 
 | |
|     // Looks like we were able to extend the dialog
 | |
|     gDialogWasExtended = true;
 | |
|     return TRUE;
 | |
|   }
 | |
|   return 0L;
 | |
| }
 | |
| 
 | |
| //----------------------------------------------------------------------------------
 | |
| // Returns a Global Moveable Memory Handle to a DevMode
 | |
| // from the Printer by the name of aPrintName
 | |
| //
 | |
| // NOTE:
 | |
| //   This function assumes that aPrintName has already been converted from
 | |
| //   unicode
 | |
| //
 | |
| static nsReturnRef<nsHGLOBAL>
 | |
| CreateGlobalDevModeAndInit(const nsString& aPrintName, nsIPrintSettings* aPS)
 | |
| {
 | |
|   nsHPRINTER hPrinter = nullptr;
 | |
|   // const cast kludge for silly Win32 api's
 | |
|   LPWSTR printName =
 | |
|     const_cast<wchar_t*>(static_cast<const wchar_t*>(aPrintName.get()));
 | |
|   BOOL status = ::OpenPrinterW(printName, &hPrinter, nullptr);
 | |
|   if (!status) {
 | |
|     return nsReturnRef<nsHGLOBAL>();
 | |
|   }
 | |
| 
 | |
|   // Make sure hPrinter is closed on all paths
 | |
|   nsAutoPrinter autoPrinter(hPrinter);
 | |
| 
 | |
|   // Get the buffer size
 | |
|   LONG needed =
 | |
|     ::DocumentPropertiesW(gParentWnd, hPrinter, printName, nullptr, nullptr, 0);
 | |
|   if (needed < 0) {
 | |
|     return nsReturnRef<nsHGLOBAL>();
 | |
|   }
 | |
| 
 | |
|   // Allocate a buffer of the correct size.
 | |
|   nsAutoDevMode newDevMode(
 | |
|     (LPDEVMODEW)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, needed));
 | |
|   if (!newDevMode) {
 | |
|     return nsReturnRef<nsHGLOBAL>();
 | |
|   }
 | |
| 
 | |
|   nsHGLOBAL hDevMode = ::GlobalAlloc(GHND, needed);
 | |
|   nsAutoGlobalMem globalDevMode(hDevMode);
 | |
|   if (!hDevMode) {
 | |
|     return nsReturnRef<nsHGLOBAL>();
 | |
|   }
 | |
| 
 | |
|   LONG ret = ::DocumentPropertiesW(
 | |
|     gParentWnd, hPrinter, printName, newDevMode, nullptr, DM_OUT_BUFFER);
 | |
|   if (ret != IDOK) {
 | |
|     return nsReturnRef<nsHGLOBAL>();
 | |
|   }
 | |
| 
 | |
|   // Lock memory and copy contents from DEVMODE (current printer)
 | |
|   // to Global Memory DEVMODE
 | |
|   LPDEVMODEW devMode = (DEVMODEW*)::GlobalLock(hDevMode);
 | |
|   if (!devMode) {
 | |
|     return nsReturnRef<nsHGLOBAL>();
 | |
|   }
 | |
| 
 | |
|   memcpy(devMode, newDevMode.get(), needed);
 | |
|   // Initialize values from the PrintSettings
 | |
|   nsCOMPtr<nsIPrintSettingsWin> psWin = do_QueryInterface(aPS);
 | |
|   MOZ_ASSERT(psWin);
 | |
|   psWin->CopyToNative(devMode);
 | |
| 
 | |
|   // Sets back the changes we made to the DevMode into the Printer Driver
 | |
|   ret = ::DocumentPropertiesW(gParentWnd,
 | |
|                               hPrinter,
 | |
|                               printName,
 | |
|                               devMode,
 | |
|                               devMode,
 | |
|                               DM_IN_BUFFER | DM_OUT_BUFFER);
 | |
|   if (ret != IDOK) {
 | |
|     ::GlobalUnlock(hDevMode);
 | |
|     return nsReturnRef<nsHGLOBAL>();
 | |
|   }
 | |
| 
 | |
|   ::GlobalUnlock(hDevMode);
 | |
| 
 | |
|   return globalDevMode.out();
 | |
| }
 | |
| 
 | |
| //------------------------------------------------------------------
 | |
| // helper
 | |
| static void
 | |
| GetDefaultPrinterNameFromGlobalPrinters(nsAString& printerName)
 | |
| {
 | |
|   nsCOMPtr<nsIPrinterEnumerator> prtEnum =
 | |
|     do_GetService("@mozilla.org/gfx/printerenumerator;1");
 | |
|   if (prtEnum) {
 | |
|     prtEnum->GetDefaultPrinterName(printerName);
 | |
|   }
 | |
| }
 | |
| 
 | |
| // Determine whether we have a completely native dialog
 | |
| // or whether we cshould extend it
 | |
| static bool
 | |
| ShouldExtendPrintDialog()
 | |
| {
 | |
|   nsresult rv;
 | |
|   nsCOMPtr<nsIPrefService> prefs =
 | |
|     do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
 | |
|   NS_ENSURE_SUCCESS(rv, true);
 | |
|   nsCOMPtr<nsIPrefBranch> prefBranch;
 | |
|   rv = prefs->GetBranch(nullptr, getter_AddRefs(prefBranch));
 | |
|   NS_ENSURE_SUCCESS(rv, true);
 | |
| 
 | |
|   bool result;
 | |
|   rv = prefBranch->GetBoolPref("print.extend_native_print_dialog", &result);
 | |
|   NS_ENSURE_SUCCESS(rv, true);
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| //------------------------------------------------------------------
 | |
| // Displays the native Print Dialog
 | |
| static nsresult
 | |
| ShowNativePrintDialog(HWND aHWnd, nsIPrintSettings* aPrintSettings)
 | |
| {
 | |
|   //NS_ENSURE_ARG_POINTER(aHWnd);
 | |
|   NS_ENSURE_ARG_POINTER(aPrintSettings);
 | |
| 
 | |
|   gDialogWasExtended = false;
 | |
| 
 | |
|   // Get the Print Name to be used
 | |
|   nsString printerName;
 | |
|   aPrintSettings->GetPrinterName(printerName);
 | |
| 
 | |
|   // If there is no name then use the default printer
 | |
|   if (printerName.IsEmpty()) {
 | |
|     GetDefaultPrinterNameFromGlobalPrinters(printerName);
 | |
|   } else {
 | |
|     HANDLE hPrinter = nullptr;
 | |
|     if (!::OpenPrinterW(
 | |
|           const_cast<wchar_t*>(static_cast<const wchar_t*>(printerName.get())),
 | |
|           &hPrinter,
 | |
|           nullptr)) {
 | |
|       // If the last used printer is not found, we should use default printer.
 | |
|       GetDefaultPrinterNameFromGlobalPrinters(printerName);
 | |
|     } else {
 | |
|       ::ClosePrinter(hPrinter);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Now create a DEVNAMES struct so the the dialog is initialized correctly.
 | |
| 
 | |
|   uint32_t len = printerName.Length();
 | |
|   nsHGLOBAL hDevNames =
 | |
|     ::GlobalAlloc(GHND, sizeof(wchar_t) * (len + 1) + sizeof(DEVNAMES));
 | |
|   nsAutoGlobalMem autoDevNames(hDevNames);
 | |
|   if (!hDevNames) {
 | |
|     return NS_ERROR_OUT_OF_MEMORY;
 | |
|   }
 | |
| 
 | |
|   DEVNAMES* pDevNames = (DEVNAMES*)::GlobalLock(hDevNames);
 | |
|   if (!pDevNames) {
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
|   pDevNames->wDriverOffset = sizeof(DEVNAMES) / sizeof(wchar_t);
 | |
|   pDevNames->wDeviceOffset = sizeof(DEVNAMES) / sizeof(wchar_t);
 | |
|   pDevNames->wOutputOffset = sizeof(DEVNAMES) / sizeof(wchar_t) + len;
 | |
|   pDevNames->wDefault = 0;
 | |
| 
 | |
|   memcpy(pDevNames + 1, printerName.get(), (len + 1) * sizeof(wchar_t));
 | |
|   ::GlobalUnlock(hDevNames);
 | |
| 
 | |
|   // Create a Moveable Memory Object that holds a new DevMode
 | |
|   // from the Printer Name
 | |
|   // The PRINTDLG.hDevMode requires that it be a moveable memory object
 | |
|   // NOTE: autoDevMode is automatically freed when any error occurred
 | |
|   nsAutoGlobalMem autoDevMode(
 | |
|     CreateGlobalDevModeAndInit(printerName, aPrintSettings));
 | |
| 
 | |
|   // Prepare to Display the Print Dialog
 | |
|   PRINTDLGW prntdlg;
 | |
|   memset(&prntdlg, 0, sizeof(PRINTDLGW));
 | |
| 
 | |
|   prntdlg.lStructSize = sizeof(prntdlg);
 | |
|   prntdlg.hwndOwner = aHWnd;
 | |
|   prntdlg.hDevMode = autoDevMode.get();
 | |
|   prntdlg.hDevNames = hDevNames;
 | |
|   prntdlg.hDC = nullptr;
 | |
|   prntdlg.Flags =
 | |
|     PD_ALLPAGES | PD_RETURNIC | PD_USEDEVMODECOPIESANDCOLLATE | PD_COLLATE;
 | |
| 
 | |
|   // if there is a current selection then enable the "Selection" radio button
 | |
|   int16_t howToEnableFrameUI = nsIPrintSettings::kFrameEnableNone;
 | |
|   bool isOn;
 | |
|   aPrintSettings->GetPrintOptions(nsIPrintSettings::kEnableSelectionRB, &isOn);
 | |
|   if (!isOn) {
 | |
|     prntdlg.Flags |= PD_NOSELECTION;
 | |
|   }
 | |
|   aPrintSettings->GetHowToEnableFrameUI(&howToEnableFrameUI);
 | |
| 
 | |
|   int32_t pg = 1;
 | |
|   aPrintSettings->GetStartPageRange(&pg);
 | |
|   prntdlg.nFromPage = pg;
 | |
| 
 | |
|   aPrintSettings->GetEndPageRange(&pg);
 | |
|   prntdlg.nToPage = pg;
 | |
| 
 | |
|   prntdlg.nMinPage = 1;
 | |
|   prntdlg.nMaxPage = 0xFFFF;
 | |
|   prntdlg.nCopies = 1;
 | |
|   prntdlg.lpfnSetupHook = nullptr;
 | |
|   prntdlg.lpSetupTemplateName = nullptr;
 | |
|   prntdlg.hPrintTemplate = nullptr;
 | |
|   prntdlg.hSetupTemplate = nullptr;
 | |
| 
 | |
|   prntdlg.hInstance = nullptr;
 | |
|   prntdlg.lpPrintTemplateName = nullptr;
 | |
| 
 | |
|   if (!ShouldExtendPrintDialog()) {
 | |
|     prntdlg.lCustData = 0;
 | |
|     prntdlg.lpfnPrintHook = nullptr;
 | |
|   } else {
 | |
|     // Set up print dialog "hook" procedure for extending the dialog
 | |
|     prntdlg.lCustData = (DWORD)howToEnableFrameUI;
 | |
|     prntdlg.lpfnPrintHook = (LPPRINTHOOKPROC)PrintHookProc;
 | |
|     prntdlg.Flags |= PD_ENABLEPRINTHOOK;
 | |
|   }
 | |
| 
 | |
|   BOOL result;
 | |
|   {
 | |
|     mozilla::widget::WinUtils::AutoSystemDpiAware dpiAwareness;
 | |
|     result = ::PrintDlgW(&prntdlg);
 | |
|   }
 | |
| 
 | |
|   if (TRUE == result) {
 | |
|     // check to make sure we don't have any nullptr pointers
 | |
|     NS_ENSURE_TRUE(aPrintSettings && prntdlg.hDevMode, NS_ERROR_FAILURE);
 | |
| 
 | |
|     if (prntdlg.hDevNames == nullptr) {
 | |
|       return NS_ERROR_FAILURE;
 | |
|     }
 | |
|     // Lock the deviceNames and check for nullptr
 | |
|     DEVNAMES* devnames = (DEVNAMES*)::GlobalLock(prntdlg.hDevNames);
 | |
|     if (devnames == nullptr) {
 | |
|       return NS_ERROR_FAILURE;
 | |
|     }
 | |
| 
 | |
|     char16_t* device = &(((char16_t*)devnames)[devnames->wDeviceOffset]);
 | |
|     char16_t* driver = &(((char16_t*)devnames)[devnames->wDriverOffset]);
 | |
| 
 | |
|     // Check to see if the "Print To File" control is checked
 | |
|     // then take the name from devNames and set it in the PrintSettings
 | |
|     //
 | |
|     // NOTE:
 | |
|     // As per Microsoft SDK documentation the returned value offset from
 | |
|     // devnames->wOutputOffset is either "FILE:" or nullptr
 | |
|     // if the "Print To File" checkbox is checked it MUST be "FILE:"
 | |
|     // We assert as an extra safety check.
 | |
|     if (prntdlg.Flags & PD_PRINTTOFILE) {
 | |
|       char16ptr_t fileName = &(((wchar_t*)devnames)[devnames->wOutputOffset]);
 | |
|       NS_ASSERTION(wcscmp(fileName, L"FILE:") == 0, "FileName must be `FILE:`");
 | |
|       aPrintSettings->SetToFileName(nsDependentString(fileName));
 | |
|       aPrintSettings->SetPrintToFile(true);
 | |
|     } else {
 | |
|       // clear "print to file" info
 | |
|       aPrintSettings->SetPrintToFile(false);
 | |
|       aPrintSettings->SetToFileName(EmptyString());
 | |
|     }
 | |
| 
 | |
|     nsCOMPtr<nsIPrintSettingsWin> psWin(do_QueryInterface(aPrintSettings));
 | |
|     if (!psWin) {
 | |
|       return NS_ERROR_FAILURE;
 | |
|     }
 | |
| 
 | |
|     // Setup local Data members
 | |
|     psWin->SetDeviceName(nsDependentString(device));
 | |
|     psWin->SetDriverName(nsDependentString(driver));
 | |
| 
 | |
| #if defined(DEBUG_rods) || defined(DEBUG_dcone)
 | |
|     wprintf(L"printer: driver %s, device %s  flags: %d\n",
 | |
|             driver,
 | |
|             device,
 | |
|             prntdlg.Flags);
 | |
| #endif
 | |
|     // fill the print options with the info from the dialog
 | |
| 
 | |
|     aPrintSettings->SetPrinterName(nsDependentString(device));
 | |
| 
 | |
|     if (prntdlg.Flags & PD_SELECTION) {
 | |
|       aPrintSettings->SetPrintRange(nsIPrintSettings::kRangeSelection);
 | |
| 
 | |
|     } else if (prntdlg.Flags & PD_PAGENUMS) {
 | |
|       aPrintSettings->SetPrintRange(nsIPrintSettings::kRangeSpecifiedPageRange);
 | |
|       aPrintSettings->SetStartPageRange(prntdlg.nFromPage);
 | |
|       aPrintSettings->SetEndPageRange(prntdlg.nToPage);
 | |
| 
 | |
|     } else { // (prntdlg.Flags & PD_ALLPAGES)
 | |
|       aPrintSettings->SetPrintRange(nsIPrintSettings::kRangeAllPages);
 | |
|     }
 | |
| 
 | |
|     if (howToEnableFrameUI != nsIPrintSettings::kFrameEnableNone) {
 | |
|       // make sure the dialog got extended
 | |
|       if (gDialogWasExtended) {
 | |
|         // check to see about the frame radio buttons
 | |
|         switch (gFrameSelectedRadioBtn) {
 | |
|           case rad4:
 | |
|             aPrintSettings->SetPrintFrameType(nsIPrintSettings::kFramesAsIs);
 | |
|             break;
 | |
|           case rad5:
 | |
|             aPrintSettings->SetPrintFrameType(nsIPrintSettings::kSelectedFrame);
 | |
|             break;
 | |
|           case rad6:
 | |
|             aPrintSettings->SetPrintFrameType(nsIPrintSettings::kEachFrameSep);
 | |
|             break;
 | |
|         } // switch
 | |
|       } else {
 | |
|         // if it didn't get extended then have it default to printing
 | |
|         // each frame separately
 | |
|         aPrintSettings->SetPrintFrameType(nsIPrintSettings::kEachFrameSep);
 | |
|       }
 | |
|     } else {
 | |
|       aPrintSettings->SetPrintFrameType(nsIPrintSettings::kNoFrames);
 | |
|     }
 | |
|     // Unlock DeviceNames
 | |
|     ::GlobalUnlock(prntdlg.hDevNames);
 | |
| 
 | |
|     // Transfer the settings from the native data to the PrintSettings
 | |
|     LPDEVMODEW devMode = (LPDEVMODEW)::GlobalLock(prntdlg.hDevMode);
 | |
|     if (!devMode || !prntdlg.hDC) {
 | |
|       return NS_ERROR_FAILURE;
 | |
|     }
 | |
|     psWin->SetDevMode(devMode); // copies DevMode
 | |
|     psWin->CopyFromNative(prntdlg.hDC, devMode);
 | |
|     ::GlobalUnlock(prntdlg.hDevMode);
 | |
|     ::DeleteDC(prntdlg.hDC);
 | |
| 
 | |
| #if defined(DEBUG_rods) || defined(DEBUG_dcone)
 | |
|     bool printSelection = prntdlg.Flags & PD_SELECTION;
 | |
|     bool printAllPages = prntdlg.Flags & PD_ALLPAGES;
 | |
|     bool printNumPages = prntdlg.Flags & PD_PAGENUMS;
 | |
|     int32_t fromPageNum = 0;
 | |
|     int32_t toPageNum = 0;
 | |
| 
 | |
|     if (printNumPages) {
 | |
|       fromPageNum = prntdlg.nFromPage;
 | |
|       toPageNum = prntdlg.nToPage;
 | |
|     }
 | |
|     if (printSelection) {
 | |
|       printf("Printing the selection\n");
 | |
| 
 | |
|     } else if (printAllPages) {
 | |
|       printf("Printing all the pages\n");
 | |
| 
 | |
|     } else {
 | |
|       printf("Printing from page no. %d to %d\n", fromPageNum, toPageNum);
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|   } else {
 | |
|     ::SetFocus(aHWnd);
 | |
|     aPrintSettings->SetIsCancelled(true);
 | |
|     return NS_ERROR_ABORT;
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| //------------------------------------------------------------------
 | |
| static void
 | |
| PrepareForPrintDialog(nsIWebBrowserPrint* aWebBrowserPrint,
 | |
|                       nsIPrintSettings* aPS)
 | |
| {
 | |
|   NS_ASSERTION(aWebBrowserPrint, "Can't be null");
 | |
|   NS_ASSERTION(aPS, "Can't be null");
 | |
| 
 | |
|   bool isFramesetDocument;
 | |
|   bool isFramesetFrameSelected;
 | |
|   bool isIFrameSelected;
 | |
|   bool isRangeSelection;
 | |
| 
 | |
|   aWebBrowserPrint->GetIsFramesetDocument(&isFramesetDocument);
 | |
|   aWebBrowserPrint->GetIsFramesetFrameSelected(&isFramesetFrameSelected);
 | |
|   aWebBrowserPrint->GetIsIFrameSelected(&isIFrameSelected);
 | |
|   aWebBrowserPrint->GetIsRangeSelection(&isRangeSelection);
 | |
| 
 | |
|   // Setup print options for UI
 | |
|   if (isFramesetDocument) {
 | |
|     if (isFramesetFrameSelected) {
 | |
|       aPS->SetHowToEnableFrameUI(nsIPrintSettings::kFrameEnableAll);
 | |
|     } else {
 | |
|       aPS->SetHowToEnableFrameUI(nsIPrintSettings::kFrameEnableAsIsAndEach);
 | |
|     }
 | |
|   } else {
 | |
|     aPS->SetHowToEnableFrameUI(nsIPrintSettings::kFrameEnableNone);
 | |
|   }
 | |
| 
 | |
|   // Now determine how to set up the Frame print UI
 | |
|   aPS->SetPrintOptions(nsIPrintSettings::kEnableSelectionRB,
 | |
|                        isRangeSelection || isIFrameSelected);
 | |
| }
 | |
| 
 | |
| //----------------------------------------------------------------------------------
 | |
| //-- Show Print Dialog
 | |
| //----------------------------------------------------------------------------------
 | |
| nsresult
 | |
| NativeShowPrintDialog(HWND aHWnd,
 | |
|                       nsIWebBrowserPrint* aWebBrowserPrint,
 | |
|                       nsIPrintSettings* aPrintSettings)
 | |
| {
 | |
|   PrepareForPrintDialog(aWebBrowserPrint, aPrintSettings);
 | |
| 
 | |
|   nsresult rv = ShowNativePrintDialog(aHWnd, aPrintSettings);
 | |
|   if (aHWnd) {
 | |
|     ::DestroyWindow(aHWnd);
 | |
|   }
 | |
| 
 | |
|   return rv;
 | |
| }
 |