diff options
Diffstat (limited to 'embedding/components/printingui/win/nsPrintDialogUtil.cpp')
-rw-r--r-- | embedding/components/printingui/win/nsPrintDialogUtil.cpp | 854 |
1 files changed, 854 insertions, 0 deletions
diff --git a/embedding/components/printingui/win/nsPrintDialogUtil.cpp b/embedding/components/printingui/win/nsPrintDialogUtil.cpp new file mode 100644 index 000000000..896c58e85 --- /dev/null +++ b/embedding/components/printingui/win/nsPrintDialogUtil.cpp @@ -0,0 +1,854 @@ +/* -*- 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 char* kAsLaidOutOnScreenStr = "As &laid out on the screen"; +static const char* kTheSelectedFrameStr = "The selected &frame"; +static const char* kEachFrameSeparately = "&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 + nsXPIDLString valUni; + nsAutoString key; + key.AssignWithConversion(aKey); + nsresult rv = aStrBundle->GetStringFromName(key.get(), getter_Copies(valUni)); + if (NS_SUCCEEDED(rv) && valUni) { + oVal.Assign(valUni); + } else { + oVal.Truncate(); + } + return rv; +} + +//-------------------------------------------------------- +// Set a multi-byte string in the control +static void SetTextOnWnd(HWND aControl, const nsString& aStr) +{ + nsAutoCString text; + if (NS_SUCCEEDED(NS_CopyUnicodeToNative(aStr, text))) { + ::SetWindowText(aControl, text.get()); + } +} + +//-------------------------------------------------------- +// 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(LPCTSTR aType, + DWORD aStyle, + HINSTANCE aHInst, + HWND aHdlg, + int aId, + const nsAString& aStr, + const nsIntRect& aRect) +{ + nsAutoCString str; + if (NS_FAILED(NS_CopyUnicodeToNative(aStr, str))) + return nullptr; + + HWND hWnd = ::CreateWindow (aType, str.get(), + 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, + const char* aStr, + const nsIntRect& aRect) +{ + nsString cStr; + cStr.AssignWithConversion(aStr); + return CreateControl("BUTTON", BS_RADIOBUTTON, aHInst, aHdlg, aId, cStr, aRect); +} + +//-------------------------------------------------------- +// Create a Group Box +static HWND CreateGroupBox(HINSTANCE aHInst, + HWND aHdlg, + int aId, + const nsAString& aStr, + const nsIntRect& aRect) +{ + return CreateControl("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, NS_LITERAL_STRING("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 nsXPIDLString& 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(nsXPIDLString &printerName) +{ + nsCOMPtr<nsIPrinterEnumerator> prtEnum = do_GetService("@mozilla.org/gfx/printerenumerator;1"); + if (prtEnum) { + prtEnum->GetDefaultPrinterName(getter_Copies(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 + nsXPIDLString printerName; + aPrintSettings->GetPrinterName(getter_Copies(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, (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(fileName); + aPrintSettings->SetPrintToFile(true); + } else { + // clear "print to file" info + aPrintSettings->SetPrintToFile(false); + aPrintSettings->SetToFileName(nullptr); + } + + nsCOMPtr<nsIPrintSettingsWin> psWin(do_QueryInterface(aPrintSettings)); + if (!psWin) { + return NS_ERROR_FAILURE; + } + + // Setup local Data members + psWin->SetDeviceName(device); + psWin->SetDriverName(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(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; +} + |