/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: Mozilla-sample-code 1.0 * * Copyright (c) 2002 Netscape Communications Corporation and * other contributors * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this Mozilla sample software and associated documentation files * (the "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to permit * persons to whom the Software is furnished to do so, subject to the * following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * Contributor(s): * * ***** END LICENSE BLOCK ***** */ #ifdef MOZ_REQUIRE_CURRENT_SDK #undef WINVER #define WINVER 0x0500 #undef _WIN32_WINNT #define _WIN32_WINNT 0x0500 #endif /* ------------------------------------------------------------------- To Build This: You need to add this to the the makefile.win in mozilla/content/base/src: .\$(OBJDIR)\nsFlyOwnPrintDialog.obj \ And this to the makefile.win in mozilla/content/build: WIN_LIBS= \ winspool.lib \ comctl32.lib \ comdlg32.lib ---------------------------------------------------------------------- */ #include "prmem.h" #include "plstr.h" #include #include #include #include #include "nsIWebBrowserPrint.h" #include "nsString.h" #include "nsIServiceManager.h" #include "nsReadableUtils.h" #include "nsIWidget.h" #include "nsIPrintSettings.h" #include "nsIPrintSettingsWin.h" #include "nsUnitConversion.h" #include "nsIPrintOptions.h" #include "nsGfxCIID.h" #include "nsIPrefService.h" #include "nsIPrefBranch.h" static NS_DEFINE_IID(kPrinterEnumeratorCID, NS_PRINTER_ENUMERATOR_CID); #include "nsRect.h" #include "nsCRT.h" #include "prenv.h" /* for PR_GetEnv */ #include #include // For Localization #include "nsIStringBundle.h" // This is for extending the dialog #include static NS_DEFINE_CID(kStringBundleServiceCID, NS_STRINGBUNDLESERVICE_CID); // For PrintDlgEx // needed because there are unicode/ansi versions of this routine // and we need to make sure we get the correct one. #ifdef UNICODE #define GetPrintDlgExQuoted "PrintDlgExW" #else #define GetPrintDlgExQuoted "PrintDlgExA" #endif // 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 PRPackedBool gDialogWasExtended = PR_FALSE; #define PRINTDLG_PROPERTIES "chrome://global/locale/printdialog.properties" static HWND gParentWnd = NULL; //****************************************************** // Define native paper sizes //****************************************************** typedef struct { short mPaperSize; // native enum double mWidth; double mHeight; PRBool mIsInches; } NativePaperSizes; // There are around 40 default print sizes defined by Windows const NativePaperSizes kPaperSizes[] = { {DMPAPER_LETTER, 8.5, 11.0, PR_TRUE}, {DMPAPER_LEGAL, 8.5, 14.0, PR_TRUE}, {DMPAPER_A4, 210.0, 297.0, PR_FALSE}, {DMPAPER_TABLOID, 11.0, 17.0, PR_TRUE}, {DMPAPER_LEDGER, 17.0, 11.0, PR_TRUE}, {DMPAPER_STATEMENT, 5.5, 8.5, PR_TRUE}, {DMPAPER_EXECUTIVE, 7.25, 10.5, PR_TRUE}, {DMPAPER_A3, 297.0, 420.0, PR_FALSE}, {DMPAPER_A5, 148.0, 210.0, PR_FALSE}, {DMPAPER_CSHEET, 17.0, 22.0, PR_TRUE}, {DMPAPER_DSHEET, 22.0, 34.0, PR_TRUE}, {DMPAPER_ESHEET, 34.0, 44.0, PR_TRUE}, {DMPAPER_LETTERSMALL, 8.5, 11.0, PR_TRUE}, {DMPAPER_A4SMALL, 210.0, 297.0, PR_FALSE}, {DMPAPER_B4, 250.0, 354.0, PR_FALSE}, {DMPAPER_B5, 182.0, 257.0, PR_FALSE}, {DMPAPER_FOLIO, 8.5, 13.0, PR_TRUE}, {DMPAPER_QUARTO, 215.0, 275.0, PR_FALSE}, {DMPAPER_10X14, 10.0, 14.0, PR_TRUE}, {DMPAPER_11X17, 11.0, 17.0, PR_TRUE}, {DMPAPER_NOTE, 8.5, 11.0, PR_TRUE}, {DMPAPER_ENV_9, 3.875, 8.875, PR_TRUE}, {DMPAPER_ENV_10, 40.125, 9.5, PR_TRUE}, {DMPAPER_ENV_11, 4.5, 10.375, PR_TRUE}, {DMPAPER_ENV_12, 4.75, 11.0, PR_TRUE}, {DMPAPER_ENV_14, 5.0, 11.5, PR_TRUE}, {DMPAPER_ENV_DL, 110.0, 220.0, PR_FALSE}, {DMPAPER_ENV_C5, 162.0, 229.0, PR_FALSE}, {DMPAPER_ENV_C3, 324.0, 458.0, PR_FALSE}, {DMPAPER_ENV_C4, 229.0, 324.0, PR_FALSE}, {DMPAPER_ENV_C6, 114.0, 162.0, PR_FALSE}, {DMPAPER_ENV_C65, 114.0, 229.0, PR_FALSE}, {DMPAPER_ENV_B4, 250.0, 353.0, PR_FALSE}, {DMPAPER_ENV_B5, 176.0, 250.0, PR_FALSE}, {DMPAPER_ENV_B6, 176.0, 125.0, PR_FALSE}, {DMPAPER_ENV_ITALY, 110.0, 230.0, PR_FALSE}, {DMPAPER_ENV_MONARCH, 3.875, 7.5, PR_TRUE}, {DMPAPER_ENV_PERSONAL, 3.625, 6.5, PR_TRUE}, {DMPAPER_FANFOLD_US, 14.875, 11.0, PR_TRUE}, {DMPAPER_FANFOLD_STD_GERMAN, 8.5, 12.0, PR_TRUE}, {DMPAPER_FANFOLD_LGL_GERMAN, 8.5, 13.0, PR_TRUE}, }; const PRInt32 kNumPaperSizes = 41; //---------------------------------------------------------------------------------- static PRBool CheckForExtendedDialog() { #ifdef MOZ_REQUIRE_CURRENT_SDK HMODULE lib = GetModuleHandle("comdlg32.dll"); if ( lib ) { return GetProcAddress(lib, GetPrintDlgExQuoted); } #endif return PR_FALSE; } //---------------------------------------------------------------------------------- // Map an incoming size to a Windows Native enum in the DevMode static void MapPaperSizeToNativeEnum(LPDEVMODE aDevMode, PRInt16 aType, double aW, double aH) { #ifdef DEBUG_rods BOOL doingOrientation = aDevMode->dmFields & DM_ORIENTATION; BOOL doingPaperSize = aDevMode->dmFields & DM_PAPERSIZE; BOOL doingPaperLength = aDevMode->dmFields & DM_PAPERLENGTH; BOOL doingPaperWidth = aDevMode->dmFields & DM_PAPERWIDTH; #endif const double kThreshold = 0.05; PRBool foundEnum = PR_FALSE; for (PRInt32 i=0;i width-kThreshold && aH < height+kThreshold && aH > height-kThreshold) { aDevMode->dmPaperSize = kPaperSizes[i].mPaperSize; aDevMode->dmFields &= ~DM_PAPERLENGTH; aDevMode->dmFields &= ~DM_PAPERWIDTH; aDevMode->dmFields |= DM_PAPERSIZE; return; } } short width = 0; short height = 0; if (aType == nsIPrintSettings::kPaperSizeInches) { width = short(NS_TWIPS_TO_MILLIMETERS(NS_INCHES_TO_TWIPS(float(aW))) / 10); height = short(NS_TWIPS_TO_MILLIMETERS(NS_INCHES_TO_TWIPS(float(aH))) / 10); } else if (aType == nsIPrintSettings::kPaperSizeMillimeters) { width = short(aW / 10.0); height = short(aH / 10.0); } else { return; // don't set anything } // width and height is in aDevMode->dmPaperSize = 0; aDevMode->dmPaperWidth = width; aDevMode->dmPaperLength = height; aDevMode->dmFields |= DM_PAPERSIZE; aDevMode->dmFields |= DM_PAPERLENGTH; aDevMode->dmFields |= DM_PAPERWIDTH; } //---------------------------------------------------------------------------------- // Setup Paper Size & Orientation options into the DevMode // static void SetupDevModeFromSettings(LPDEVMODE aDevMode, nsIPrintSettings* aPrintSettings) { // Setup paper size if (aPrintSettings) { PRInt16 type; aPrintSettings->GetPaperSizeType(&type); if (type == nsIPrintSettings::kPaperSizeNativeData) { PRInt16 paperEnum; aPrintSettings->GetPaperData(&paperEnum); aDevMode->dmPaperSize = paperEnum; aDevMode->dmFields &= ~DM_PAPERLENGTH; aDevMode->dmFields &= ~DM_PAPERWIDTH; aDevMode->dmFields |= DM_PAPERSIZE; } else { PRInt16 unit; double width, height; aPrintSettings->GetPaperSizeUnit(&unit); aPrintSettings->GetPaperWidth(&width); aPrintSettings->GetPaperHeight(&height); MapPaperSizeToNativeEnum(aDevMode, unit, width, height); } // Setup Orientation PRInt32 orientation; aPrintSettings->GetOrientation(&orientation); aDevMode->dmOrientation = orientation == nsIPrintSettings::kPortraitOrientation?DMORIENT_PORTRAIT:DMORIENT_LANDSCAPE; aDevMode->dmFields |= DM_ORIENTATION; // Setup Number of Copies PRInt32 copies; aPrintSettings->GetNumCopies(&copies); aDevMode->dmCopies = copies; aDevMode->dmFields |= DM_COPIES; } } //---------------------------------------------------------------------------------- // Helper Function - Free and reallocate the string static nsresult SetPrintSettingsFromDevMode(nsIPrintSettings* aPrintSettings, LPDEVMODE aDevMode) { if (aPrintSettings == nsnull) { return NS_ERROR_FAILURE; } if (aDevMode->dmFields & DM_ORIENTATION) { PRInt32 orientation = aDevMode->dmOrientation == DMORIENT_PORTRAIT? nsIPrintSettings::kPortraitOrientation:nsIPrintSettings::kLandscapeOrientation; aPrintSettings->SetOrientation(orientation); } // Setup Number of Copies if (aDevMode->dmFields & DM_COPIES) { aPrintSettings->SetNumCopies(PRInt32(aDevMode->dmCopies)); } // Scaling // Since we do the scaling, grab their value and reset back to 100 if (aDevMode->dmFields & DM_SCALE) { double origScale = 1.0; aPrintSettings->GetScaling(&origScale); double scale = double(aDevMode->dmScale) / 100.0f; if (origScale == 1.0 || scale != 1.0) { aPrintSettings->SetScaling(scale); } aDevMode->dmScale = 100; // To turn this on you must change where the mPrt->mShrinkToFit is being set in the DocumentViewer //aPrintSettings->SetShrinkToFit(PR_FALSE); } if (aDevMode->dmFields & DM_PAPERSIZE) { aPrintSettings->SetPaperSizeType(nsIPrintSettings::kPaperSizeNativeData); aPrintSettings->SetPaperData(aDevMode->dmPaperSize); } else if (aDevMode->dmFields & DM_PAPERLENGTH && aDevMode->dmFields & DM_PAPERWIDTH) { PRBool found = PR_FALSE; for (PRInt32 i=0;idmPaperSize) { aPrintSettings->SetPaperSizeType(nsIPrintSettings::kPaperSizeDefined); aPrintSettings->SetPaperWidth(kPaperSizes[i].mWidth); aPrintSettings->SetPaperHeight(kPaperSizes[i].mHeight); aPrintSettings->SetPaperSizeUnit(kPaperSizes[i].mIsInches?nsIPrintSettings::kPaperSizeInches:nsIPrintSettings::kPaperSizeInches); found = PR_TRUE; break; } } if (!found) { return NS_ERROR_FAILURE; } } else { return NS_ERROR_FAILURE; } return NS_OK; } //---------------------------------------------------------------------------------- // 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 bundle; // Create bundle nsCOMPtr stringService = do_GetService(kStringBundleServiceCID, &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; } //-------------------------------------------------------- static char* GetACPString(const nsAString& aStr) { int acplen = aStr.Length() * 2 + 1; char * acp = new char[acplen]; if(acp) { int outlen = ::WideCharToMultiByte( CP_ACP, 0, PromiseFlatString(aStr).get(), aStr.Length(), acp, acplen, NULL, NULL); if ( outlen > 0) acp[outlen] = '\0'; // null terminate } return acp; } //-------------------------------------------------------- // Set a multi-byte string in the control static void SetTextOnWnd(HWND aControl, const nsString& aStr) { char* pStr = GetACPString(aStr); if (pStr) { ::SetWindowText(aControl, pStr); delete [] pStr; } } //-------------------------------------------------------- // 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, PRBool aIsSet, PRBool isEnabled = PR_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 strBundle; if (NS_SUCCEEDED(GetLocalizedBundle(PRINTDLG_PROPERTIES, getter_AddRefs(strBundle)))) { PRInt32 i = 0; while (gAllPropKeys[i].mKeyStr != NULL) { SetText(hdlg, gAllPropKeys[i].mKeyId, strBundle, gAllPropKeys[i].mKeyStr); i++; } } // Set up radio buttons if (aHowToEnableFrameUI == nsIPrintSettings::kFrameEnableAll) { SetRadio(hdlg, rad4, PR_FALSE); SetRadio(hdlg, rad5, PR_TRUE); SetRadio(hdlg, rad6, PR_FALSE); // set default so user doesn't have to actually press on it gFrameSelectedRadioBtn = rad5; } else if (aHowToEnableFrameUI == nsIPrintSettings::kFrameEnableAsIsAndEach) { SetRadio(hdlg, rad4, PR_FALSE); SetRadio(hdlg, rad5, PR_FALSE, PR_FALSE); SetRadio(hdlg, rad6, PR_TRUE); // set default so user doesn't have to actually press on it gFrameSelectedRadioBtn = rad6; } else { // nsIPrintSettings::kFrameEnableNone // we are using this function to disabe the group box SetRadio(hdlg, grp3, PR_FALSE, PR_FALSE); // now disable radiobuttons SetRadio(hdlg, rad4, PR_FALSE, PR_FALSE); SetRadio(hdlg, rad5, PR_FALSE, PR_FALSE); SetRadio(hdlg, rad6, PR_FALSE, PR_FALSE); } } //-------------------------------------------------------- // 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 == NULL) return 0L; PRInt16 howToEnableFrameUI = (PRInt16)printDlg->lCustData; HINSTANCE hInst = (HINSTANCE)::GetWindowLong(hdlg, GWL_HINSTANCE); if (hInst == NULL) 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 == NULL) return 0L; RECT dlgRect; GetLocalRect(wnd, dlgRect, hdlg); wnd = ::GetDlgItem(hdlg, rad1); // this is the top control "All" if (wnd == NULL) return 0L; RECT rad1Rect; GetLocalRect(wnd, rad1Rect, hdlg); wnd = ::GetDlgItem(hdlg, rad2); // this is the bottom control "Selection" if (wnd == NULL) return 0L; RECT rad2Rect; GetLocalRect(wnd, rad2Rect, hdlg); wnd = ::GetDlgItem(hdlg, rad3); // this is the middle control "Pages" if (wnd == NULL) return 0L; RECT rad3Rect; GetLocalRect(wnd, rad3Rect, hdlg); HWND okWnd = ::GetDlgItem(hdlg, IDOK); if (okWnd == NULL) return 0L; RECT okRect; GetLocalRect(okWnd, okRect, hdlg); wnd = ::GetDlgItem(hdlg, grp4); // this is the "Print range" groupbox if (wnd == NULL) 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) nsRect 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 == NULL) return 0L; y += radHgt + rbGap; rect.SetRect(rad1Rect.left, y, rbWidth, radHgt); HWND rad5Wnd = CreateRadioBtn(hInst, hdlg, rad5, kTheSelectedFrameStr, rect); if (rad5Wnd == NULL) { 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 == NULL) { 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 == NULL) { 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 it's 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, NULL, 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, NULL, okRect.left, dlgHgt-bottomGap-okHgt, 0, 0, SWP_NOSIZE|SWP_NOREDRAW|SWP_NOZORDER); HWND cancelWnd = ::GetDlgItem(hdlg, IDCANCEL); if (cancelWnd == NULL) return 0L; RECT cancelRect; GetLocalRect(cancelWnd, cancelRect, hdlg); int cancelHgt = cancelRect.bottom - cancelRect.top + 1; ::SetWindowPos(cancelWnd, NULL, 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 = PR_TRUE; } return 0L; } //---------------------------------------------------------------------------------- // Returns a Global Moveable Memory Handle to a DevMode // from the Printer byt the name of aPrintName static HGLOBAL CreateGlobalDevModeAndInit(LPTSTR aPrintName, nsIPrintSettings* aPS) { HGLOBAL hGlobalDevMode = NULL; nsresult rv = NS_ERROR_FAILURE; HANDLE hPrinter = NULL; BOOL status = ::OpenPrinter(aPrintName, &hPrinter, NULL); if (status) { LPDEVMODE pNewDevMode; DWORD dwNeeded, dwRet; nsString prtName; #ifdef UNICODE prtName.AppendWithConversion((PRUnichar *)aPrintName); #else prtName.AssignWithConversion((char*)aPrintName); #endif // Allocate a buffer of the correct size. dwNeeded = ::DocumentProperties(gParentWnd, hPrinter, aPrintName, NULL, NULL, 0); pNewDevMode = (LPDEVMODE)malloc(dwNeeded); if (!pNewDevMode) return NULL; hGlobalDevMode = (HGLOBAL)::GlobalAlloc(GHND, dwNeeded); if (!hGlobalDevMode) { free(pNewDevMode); } dwRet = ::DocumentProperties(gParentWnd, hPrinter, aPrintName, pNewDevMode, NULL, DM_OUT_BUFFER); if (dwRet != IDOK) { free(pNewDevMode); ::GlobalFree(hGlobalDevMode); ::ClosePrinter(hPrinter); return NULL; } // Lock memory and copy contents from DEVMODE (current printer) // to Global Memory DEVMODE LPDEVMODE devMode = (DEVMODE *)::GlobalLock(hGlobalDevMode); if (devMode) { memcpy(devMode, pNewDevMode, dwNeeded); // Initialize values from the PrintSettings SetupDevModeFromSettings(devMode, aPS); ::GlobalUnlock(hGlobalDevMode); } else { ::GlobalFree(hGlobalDevMode); hGlobalDevMode = NULL; } free(pNewDevMode); ::ClosePrinter(hPrinter); } else { return NULL; } return hGlobalDevMode; } //------------------------------------------------------------------ // helper static PRUnichar * GetDefaultPrinterNameFromGlobalPrinters() { nsresult rv; PRUnichar * printerName = nsnull; nsCOMPtr prtEnum = do_GetService(kPrinterEnumeratorCID, &rv); if (prtEnum) { prtEnum->GetDefaultPrinterName(&printerName); } return printerName; } // Determine whether we have a completely native dialog // or whether we cshould extend it static PRBool ShouldExtendPrintDialog() { nsresult rv; nsCOMPtr prefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, PR_TRUE); nsCOMPtr prefBranch; rv = prefs->GetBranch(nsnull, getter_AddRefs(prefBranch)); NS_ENSURE_SUCCESS(rv, PR_TRUE); PRBool result; rv = prefBranch->GetBoolPref("print.extend_native_print_dialog", &result); NS_ENSURE_SUCCESS(rv, PR_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); nsresult rv = NS_ERROR_FAILURE; gDialogWasExtended = PR_FALSE; HGLOBAL hGlobalDevMode = NULL; HGLOBAL hDevNames = NULL; // Get the Print Name to be used PRUnichar * printerName; aPrintSettings->GetPrinterName(&printerName); // If there is no name then use the default printer if (!printerName || (printerName && !*printerName)) { printerName = GetDefaultPrinterNameFromGlobalPrinters(); } NS_ASSERTION(printerName, "We have to have a printer name"); if (!printerName) return NS_ERROR_FAILURE; // Now create a DEVNAMES struct so the the dialog is initialized correctly. PRUint32 len = nsCRT::strlen(printerName); hDevNames = (HGLOBAL)::GlobalAlloc(GHND, len+sizeof(DEVNAMES)+1); DEVNAMES* pDevNames = (DEVNAMES*)::GlobalLock(hDevNames); pDevNames->wDriverOffset = sizeof(DEVNAMES); pDevNames->wDeviceOffset = sizeof(DEVNAMES); pDevNames->wOutputOffset = sizeof(DEVNAMES)+len+1; pDevNames->wDefault = 0; char* device = &(((char*)pDevNames)[pDevNames->wDeviceOffset]); strcpy(device, NS_ConvertUCS2toUTF8(printerName).get()); ::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: We only need to free hGlobalDevMode when the dialog is cancelled // When the user prints, it comes back in the printdlg struct and // is used and cleaned up later #ifdef UNICODE hGlobalDevMode = CreateGlobalDevModeAndInit(printerName, aPrintSettings); #else hGlobalDevMode = CreateGlobalDevModeAndInit(NS_CONST_CAST(char*, NS_ConvertUCS2toUTF8(printerName).get()), aPrintSettings); #endif // Prepare to Display the Print Dialog PRINTDLG prntdlg; memset(&prntdlg, 0, sizeof(PRINTDLG)); prntdlg.lStructSize = sizeof(prntdlg); prntdlg.hwndOwner = aHWnd; prntdlg.hDevMode = hGlobalDevMode; prntdlg.hDevNames = hDevNames; prntdlg.hDC = NULL; prntdlg.Flags = PD_ALLPAGES | PD_RETURNIC | PD_HIDEPRINTTOFILE | PD_USEDEVMODECOPIESANDCOLLATE; // if there is a current selection then enable the "Selection" radio button PRInt16 howToEnableFrameUI = nsIPrintSettings::kFrameEnableNone; PRBool isOn; aPrintSettings->GetPrintOptions(nsIPrintSettings::kEnableSelectionRB, &isOn); if (!isOn) { prntdlg.Flags |= PD_NOSELECTION; } aPrintSettings->GetHowToEnableFrameUI(&howToEnableFrameUI); prntdlg.nFromPage = 0xFFFF; prntdlg.nToPage = 0xFFFF; prntdlg.nMinPage = 1; prntdlg.nMaxPage = 0xFFFF; prntdlg.nCopies = 1; prntdlg.lpfnSetupHook = NULL; prntdlg.lpSetupTemplateName = NULL; prntdlg.hPrintTemplate = NULL; prntdlg.hSetupTemplate = NULL; prntdlg.hInstance = NULL; prntdlg.lpPrintTemplateName = NULL; if (!ShouldExtendPrintDialog()) { prntdlg.lCustData = NULL; prntdlg.lpfnPrintHook = NULL; } 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 = ::PrintDlg(&prntdlg); if (TRUE == result) { if (aPrintSettings && prntdlg.hDevMode != NULL) { // Transfer the settings from the native data to the PrintSettings LPDEVMODE devMode = (LPDEVMODE)::GlobalLock(prntdlg.hDevMode); SetPrintSettingsFromDevMode(aPrintSettings, devMode); ::GlobalUnlock(prntdlg.hDevMode); } DEVNAMES *devnames = (DEVNAMES *)::GlobalLock(prntdlg.hDevNames); if ( NULL != devnames ) { char* device = &(((char *)devnames)[devnames->wDeviceOffset]); char* driver = &(((char *)devnames)[devnames->wDriverOffset]); nsCOMPtr psWin(do_QueryInterface(aPrintSettings)); // Setup local Data members psWin->SetDeviceName(device); psWin->SetDriverName(driver); #if defined(DEBUG_rods) || defined(DEBUG_dcone) printf("printer: driver %s, device %s flags: %d\n", driver, device, prntdlg.Flags); #endif // fill the print options with the info from the dialog if (aPrintSettings != nsnull) { nsString printerName; printerName.AssignWithConversion(device); aPrintSettings->SetPrinterName(printerName.get()); 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); } } ::GlobalUnlock(prntdlg.hDevNames); #if defined(DEBUG_rods) || defined(DEBUG_dcone) PRBool printSelection = prntdlg.Flags & PD_SELECTION; PRBool printAllPages = prntdlg.Flags & PD_ALLPAGES; PRBool printNumPages = prntdlg.Flags & PD_PAGENUMS; PRInt32 fromPageNum = 0; PRInt32 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 LPDEVMODE devMode = (LPDEVMODE)::GlobalLock(prntdlg.hDevMode); psWin->SetDevMode(devMode); SetPrintSettingsFromDevMode(aPrintSettings, devMode); ::GlobalUnlock(prntdlg.hDevMode); } } else { aPrintSettings->SetIsCancelled(PR_TRUE); ::GlobalFree(hGlobalDevMode); return NS_ERROR_ABORT; } return NS_OK; } #ifdef MOZ_REQUIRE_CURRENT_SDK //------------------------------------------------------------------ // Callback for Property Sheet static BOOL APIENTRY PropSheetCallBack(HWND hdlg, UINT uiMsg, UINT wParam, LONG 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) { // Create the groupbox and Radiobuttons on the "Options" Property Sheet // We temporarily borrowed the global value for initialization // now clear it before the dialog appears PRInt16 howToEnableFrameUI = gFrameSelectedRadioBtn; gFrameSelectedRadioBtn = 0; HINSTANCE hInst = (HINSTANCE)::GetWindowLong(hdlg, GWL_HINSTANCE); if (hInst == NULL) return 0L; // Get default font for the dialog & then its font metrics // we need the text height to determine the height of the radio buttons TEXTMETRIC metrics; HFONT hFont = (HFONT)::SendMessage(hdlg, WM_GETFONT, (WPARAM)0, (LPARAM)0); HDC localDC = ::GetDC(hdlg); ::SelectObject(localDC, (HGDIOBJ)hFont); ::GetTextMetrics(localDC, &metrics); ::ReleaseDC(hdlg, localDC); // calculate various different "gaps" for layout purposes RECT dlgr; ::GetWindowRect(hdlg, &dlgr); int horzGap = 5; // generic horz gap int vertGap = 5; // generic vert gap int rbGap = metrics.tmHeight / 2; // gap between radiobtns int top = vertGap*2; // start at the top int radHgt = metrics.tmHeight; // top of new group box int y = top; // starting pos of first radio int x = horzGap*2; int rbWidth = dlgr.right - dlgr.left - (5*horzGap); int grpWidth = dlgr.right - dlgr.left - (2*horzGap); nsRect rect; // Create and position the radio buttons // // If any one control cannot be created then // hide the others and bail out // x += horzGap*2; y += vertGap + metrics.tmHeight; rect.SetRect(x, y, rbWidth,radHgt); HWND rad4Wnd = CreateRadioBtn(hInst, hdlg, rad4, kAsLaidOutOnScreenStr, rect); if (rad4Wnd == NULL) return 0L; y += radHgt + rbGap; rect.SetRect(x, y, rbWidth, radHgt); HWND rad5Wnd = CreateRadioBtn(hInst, hdlg, rad5, kTheSelectedFrameStr, rect); if (rad5Wnd == NULL) { Show(rad4Wnd, FALSE); // hide return 0L; } y += radHgt + rbGap; rect.SetRect(x, y, rbWidth, radHgt); HWND rad6Wnd = CreateRadioBtn(hInst, hdlg, rad6, kEachFrameSeparately, rect); if (rad6Wnd == NULL) { Show(rad4Wnd, FALSE); // hide Show(rad5Wnd, FALSE); // hide return 0L; } y += radHgt + (vertGap*2); x -= horzGap*2; // Create and position the group box rect.SetRect (x, top, grpWidth, y-top+1); HWND grpBoxWnd = CreateGroupBox(hInst, hdlg, grp3, NS_LITERAL_STRING("Print Frame"), rect); if (grpBoxWnd == NULL) { Show(rad4Wnd, FALSE); // hide Show(rad5Wnd, FALSE); // hide Show(rad6Wnd, FALSE); // hide return 0L; } // localize and initialize the groupbox and radiobuttons InitializeExtendedDialog(hdlg, howToEnableFrameUI); // Looks like we were able to extend the dialog gDialogWasExtended = PR_TRUE; } return 0L; } //------------------------------------------------------------------ // Creates the "Options" Property Sheet static HPROPSHEETPAGE ExtendPrintDialog(HWND aHWnd, char* aTitle) { // The resource "OPTPROPSHEET" comes out of the widget/build/widget.rc file HINSTANCE hInst = (HINSTANCE)::GetWindowLong(aHWnd, GWL_HINSTANCE); PROPSHEETPAGE psp; memset(&psp, 0, sizeof(PROPSHEETPAGE)); psp.dwSize = sizeof(PROPSHEETPAGE); psp.dwFlags = PSP_USETITLE | PSP_PREMATURE; psp.hInstance = hInst; psp.pszTemplate = "OPTPROPSHEET"; psp.pfnDlgProc = PropSheetCallBack; psp.pszTitle = aTitle?aTitle:"Options"; HPROPSHEETPAGE newPropSheet = ::CreatePropertySheetPage(&psp); return newPropSheet; } //------------------------------------------------------------------ // Displays the native Print Dialog static nsresult ShowNativePrintDialogEx(HWND aHWnd, nsIPrintSettings* aPrintSettings) { NS_ENSURE_ARG_POINTER(aHWnd); NS_ENSURE_ARG_POINTER(aPrintSettings); nsresult rv = NS_ERROR_FAILURE; gDialogWasExtended = PR_FALSE; // 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: We only need to free hGlobalDevMode when the dialog is cancelled // When the user prints, it comes back in the printdlg struct and // is used and cleaned up later PRUnichar * printerName; aPrintSettings->GetPrinterName(&printerName); HGLOBAL hGlobalDevMode = NULL; if (printerName) { #ifdef UNICODE hGlobalDevMode = CreateGlobalDevModeAndInit(printerName, aPrintSettings); #else hGlobalDevMode = CreateGlobalDevModeAndInit(NS_CONST_CAST(char*, NS_ConvertUCS2toUTF8(printerName).get()), aPrintSettings); #endif } // Prepare to Display the Print Dialog PRINTDLGEX prntdlg; memset(&prntdlg, 0, sizeof(PRINTDLGEX)); prntdlg.lStructSize = sizeof(prntdlg); prntdlg.hwndOwner = aHWnd; prntdlg.hDevMode = hGlobalDevMode; prntdlg.Flags = PD_ALLPAGES | PD_RETURNDC | PD_HIDEPRINTTOFILE | PD_USEDEVMODECOPIESANDCOLLATE | PD_NOCURRENTPAGE; prntdlg.nStartPage = START_PAGE_GENERAL; // if there is a current selection then enable the "Selection" radio button PRInt16 howToEnableFrameUI = nsIPrintSettings::kFrameEnableNone; if (aPrintSettings != nsnull) { PRBool isOn; aPrintSettings->GetPrintOptions(nsIPrintSettings::kEnableSelectionRB, &isOn); if (!isOn) { prntdlg.Flags |= PD_NOSELECTION; } aPrintSettings->GetHowToEnableFrameUI(&howToEnableFrameUI); } // At the moment we can only support one page range // from all the documentation I can find, it appears that this // will get cleanup automatically when the struct goes away const int kNumPageRanges = 1; LPPRINTPAGERANGE pPageRanges = (LPPRINTPAGERANGE) GlobalAlloc(GPTR, kNumPageRanges * sizeof(PRINTPAGERANGE)); if (!pPageRanges) return E_OUTOFMEMORY; prntdlg.nPageRanges = 0; prntdlg.nMaxPageRanges = kNumPageRanges; prntdlg.lpPageRanges = pPageRanges; prntdlg.nMinPage = 1; prntdlg.nMaxPage = 0xFFFF; prntdlg.nCopies = 1; if (ShouldExtendPrintDialog()) { // lLcalize the Property Sheet (Tab) title char* pTitle = NULL; nsString optionsStr; if (NS_SUCCEEDED(GetLocalizedString(strBundle, "options", optionsStr))) { pTitle = GetACPString(optionsStr); } // Temporarily borrow this variable for setting up the radiobuttons // if we don't use this, we will need to define a new global var gFrameSelectedRadioBtn = howToEnableFrameUI; HPROPSHEETPAGE psp[1]; psp[0] = ExtendPrintDialog(aHWnd, pTitle); prntdlg.nPropertyPages = 1; prntdlg.lphPropertyPages = psp; } HRESULT result = ::PrintDlgEx(&prntdlg); if (S_OK == result && (prntdlg.dwResultAction == PD_RESULT_PRINT)) { if (aPrintSettings && prntdlg.hDevMode != NULL) { LPDEVMODE devMode = (LPDEVMODE)::GlobalLock(prntdlg.hDevMode); SetPrintSettingsFromDevMode(aPrintSettings, devMode); ::GlobalUnlock(prntdlg.hDevMode); } DEVNAMES *devnames = (DEVNAMES *)::GlobalLock(prntdlg.hDevNames); if ( NULL != devnames ) { char* device = &(((char *)devnames)[devnames->wDeviceOffset]); char* driver = &(((char *)devnames)[devnames->wDriverOffset]); nsCOMPtr psWin(do_QueryInterface(aPrintSettings)); // Setup local Data members psWin->SetDeviceName(device); psWin->SetDriverName(driver); #if defined(DEBUG_rods) || defined(DEBUG_dcone) printf("printer: driver %s, device %s flags: %d\n", driver, device, prntdlg.Flags); #endif ::GlobalUnlock(prntdlg.hDevNames); // fill the print options with the info from the dialog if (aPrintSettings != nsnull) { if (prntdlg.Flags & PD_SELECTION) { aPrintSettings->SetPrintRange(nsIPrintSettings::kRangeSelection); } else if (prntdlg.Flags & PD_PAGENUMS) { aPrintSettings->SetPrintRange(nsIPrintSettings::kRangeSpecifiedPageRange); aPrintSettings->SetStartPageRange(pPageRanges->nFromPage); aPrintSettings->SetEndPageRange(pPageRanges->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); } } #if defined(DEBUG_rods) || defined(DEBUG_dcone) PRBool printSelection = prntdlg.Flags & PD_SELECTION; PRBool printAllPages = prntdlg.Flags & PD_ALLPAGES; PRBool printNumPages = prntdlg.Flags & PD_PAGENUMS; PRInt32 fromPageNum = 0; PRInt32 toPageNum = 0; if (printNumPages) { fromPageNum = pPageRanges->nFromPage; toPageNum = pPageRanges->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 LPDEVMODE devMode = (LPDEVMODE)::GlobalLock(prntdlg.hDevMode); psWin->SetDevMode(devMode); SetPrintSettingsFromDevMode(aPrintSettings, devMode); ::GlobalUnlock(prntdlg.hDevMode); } } else { if (hGlobalDevMode) ::GlobalFree(hGlobalDevMode); return NS_ERROR_ABORT; } ::GlobalFree(pPageRanges); return NS_OK; } #endif // MOZ_REQUIRE_CURRENT_SDK //------------------------------------------------------------------ static void PrepareForPrintDialog(nsIWebBrowserPrint* aWebBrowserPrint, nsIPrintSettings* aPS) { NS_ASSERTION(aWebBrowserPrint, "Can't be null"); NS_ASSERTION(aPS, "Can't be null"); PRBool isFramesetDocument; PRBool isFramesetFrameSelected; PRBool isIFrameSelected; PRBool 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) { nsresult rv = NS_ERROR_FAILURE; PrepareForPrintDialog(aWebBrowserPrint, aPrintSettings); #ifdef MOZ_REQUIRE_CURRENT_SDK if (CheckForExtendedDialog()) { rv = ShowNativePrintDialogEx(aHWnd, aPrintSettings); } else { rv = ShowNativePrintDialog(aHWnd, aPrintSettings); } #else rv = ShowNativePrintDialog(aHWnd, aPrintSettings); #endif return rv; }