diff options
Diffstat (limited to 'toolkit/components/printing/content')
-rw-r--r-- | toolkit/components/printing/content/printPageSetup.js | 478 | ||||
-rw-r--r-- | toolkit/components/printing/content/printPageSetup.xul | 234 | ||||
-rw-r--r-- | toolkit/components/printing/content/printPreviewBindings.xml | 415 | ||||
-rw-r--r-- | toolkit/components/printing/content/printPreviewProgress.js | 154 | ||||
-rw-r--r-- | toolkit/components/printing/content/printPreviewProgress.xul | 42 | ||||
-rw-r--r-- | toolkit/components/printing/content/printProgress.js | 282 | ||||
-rw-r--r-- | toolkit/components/printing/content/printProgress.xul | 60 | ||||
-rw-r--r-- | toolkit/components/printing/content/printUtils.js | 710 | ||||
-rw-r--r-- | toolkit/components/printing/content/printdialog.js | 425 | ||||
-rw-r--r-- | toolkit/components/printing/content/printdialog.xul | 126 | ||||
-rw-r--r-- | toolkit/components/printing/content/printjoboptions.js | 401 | ||||
-rw-r--r-- | toolkit/components/printing/content/printjoboptions.xul | 110 | ||||
-rw-r--r-- | toolkit/components/printing/content/simplifyMode.css | 22 |
13 files changed, 3459 insertions, 0 deletions
diff --git a/toolkit/components/printing/content/printPageSetup.js b/toolkit/components/printing/content/printPageSetup.js new file mode 100644 index 000000000..31eb1bdd3 --- /dev/null +++ b/toolkit/components/printing/content/printPageSetup.js @@ -0,0 +1,478 @@ +// -*- indent-tabs-mode: nil; js-indent-level: 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/. */ + +var gDialog; +var paramBlock; +var gPrefs = null; +var gPrintService = null; +var gPrintSettings = null; +var gStringBundle = null; +var gDoingMetric = false; + +var gPrintSettingsInterface = Components.interfaces.nsIPrintSettings; +var gDoDebug = false; + +// --------------------------------------------------- +function initDialog() +{ + gDialog = {}; + + gDialog.orientation = document.getElementById("orientation"); + gDialog.portrait = document.getElementById("portrait"); + gDialog.landscape = document.getElementById("landscape"); + + gDialog.printBG = document.getElementById("printBG"); + + gDialog.shrinkToFit = document.getElementById("shrinkToFit"); + + gDialog.marginGroup = document.getElementById("marginGroup"); + + gDialog.marginPage = document.getElementById("marginPage"); + gDialog.marginTop = document.getElementById("marginTop"); + gDialog.marginBottom = document.getElementById("marginBottom"); + gDialog.marginLeft = document.getElementById("marginLeft"); + gDialog.marginRight = document.getElementById("marginRight"); + + gDialog.topInput = document.getElementById("topInput"); + gDialog.bottomInput = document.getElementById("bottomInput"); + gDialog.leftInput = document.getElementById("leftInput"); + gDialog.rightInput = document.getElementById("rightInput"); + + gDialog.hLeftOption = document.getElementById("hLeftOption"); + gDialog.hCenterOption = document.getElementById("hCenterOption"); + gDialog.hRightOption = document.getElementById("hRightOption"); + + gDialog.fLeftOption = document.getElementById("fLeftOption"); + gDialog.fCenterOption = document.getElementById("fCenterOption"); + gDialog.fRightOption = document.getElementById("fRightOption"); + + gDialog.scalingLabel = document.getElementById("scalingInput"); + gDialog.scalingInput = document.getElementById("scalingInput"); + + gDialog.enabled = false; + + gDialog.strings = new Array; + gDialog.strings["marginUnits.inches"] = document.getElementById("marginUnits.inches").childNodes[0].nodeValue; + gDialog.strings["marginUnits.metric"] = document.getElementById("marginUnits.metric").childNodes[0].nodeValue; + gDialog.strings["customPrompt.title"] = document.getElementById("customPrompt.title").childNodes[0].nodeValue; + gDialog.strings["customPrompt.prompt"] = document.getElementById("customPrompt.prompt").childNodes[0].nodeValue; + +} + +// --------------------------------------------------- +function isListOfPrinterFeaturesAvailable() +{ + var has_printerfeatures = false; + + try { + has_printerfeatures = gPrefs.getBoolPref("print.tmp.printerfeatures." + gPrintSettings.printerName + ".has_special_printerfeatures"); + } catch (ex) { + } + + return has_printerfeatures; +} + +// --------------------------------------------------- +function checkDouble(element) +{ + element.value = element.value.replace(/[^.0-9]/g, ""); +} + +// Theoretical paper width/height. +var gPageWidth = 8.5; +var gPageHeight = 11.0; + +// --------------------------------------------------- +function setOrientation() +{ + var selection = gDialog.orientation.selectedItem; + + var style = "background-color:white;"; + if ((selection == gDialog.portrait && gPageWidth > gPageHeight) || + (selection == gDialog.landscape && gPageWidth < gPageHeight)) { + // Swap width/height. + var temp = gPageHeight; + gPageHeight = gPageWidth; + gPageWidth = temp; + } + var div = gDoingMetric ? 100 : 10; + style += "width:" + gPageWidth/div + unitString() + ";height:" + gPageHeight/div + unitString() + ";"; + gDialog.marginPage.setAttribute( "style", style ); +} + +// --------------------------------------------------- +function unitString() +{ + return (gPrintSettings.paperSizeUnit == gPrintSettingsInterface.kPaperSizeInches) ? "in" : "mm"; +} + +// --------------------------------------------------- +function checkMargin( value, max, other ) +{ + // Don't draw this margin bigger than permitted. + return Math.min(value, max - other.value); +} + +// --------------------------------------------------- +function changeMargin( node ) +{ + // Correct invalid input. + checkDouble(node); + + // Reset the margin height/width for this node. + var val = node.value; + var nodeToStyle; + var attr="width"; + if ( node == gDialog.topInput ) { + nodeToStyle = gDialog.marginTop; + val = checkMargin( val, gPageHeight, gDialog.bottomInput ); + attr = "height"; + } else if ( node == gDialog.bottomInput ) { + nodeToStyle = gDialog.marginBottom; + val = checkMargin( val, gPageHeight, gDialog.topInput ); + attr = "height"; + } else if ( node == gDialog.leftInput ) { + nodeToStyle = gDialog.marginLeft; + val = checkMargin( val, gPageWidth, gDialog.rightInput ); + } else { + nodeToStyle = gDialog.marginRight; + val = checkMargin( val, gPageWidth, gDialog.leftInput ); + } + var style = attr + ":" + (val/10) + unitString() + ";"; + nodeToStyle.setAttribute( "style", style ); +} + +// --------------------------------------------------- +function changeMargins() +{ + changeMargin( gDialog.topInput ); + changeMargin( gDialog.bottomInput ); + changeMargin( gDialog.leftInput ); + changeMargin( gDialog.rightInput ); +} + +// --------------------------------------------------- +function customize( node ) +{ + // If selection is now "Custom..." then prompt user for custom setting. + if ( node.value == 6 ) { + var prompter = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] + .getService( Components.interfaces.nsIPromptService ); + var title = gDialog.strings["customPrompt.title"]; + var promptText = gDialog.strings["customPrompt.prompt"]; + var result = { value: node.custom }; + var ok = prompter.prompt(window, title, promptText, result, null, { value: false } ); + if ( ok ) { + node.custom = result.value; + } + } +} + +// --------------------------------------------------- +function setHeaderFooter( node, value ) +{ + node.value= hfValueToId(value); + if (node.value == 6) { + // Remember current Custom... value. + node.custom = value; + } else { + // Start with empty Custom... value. + node.custom = ""; + } +} + +var gHFValues = new Array; +gHFValues["&T"] = 1; +gHFValues["&U"] = 2; +gHFValues["&D"] = 3; +gHFValues["&P"] = 4; +gHFValues["&PT"] = 5; + +function hfValueToId(val) +{ + if ( val in gHFValues ) { + return gHFValues[val]; + } + if ( val.length ) { + return 6; // Custom... + } + return 0; // --blank-- +} + +function hfIdToValue(node) +{ + var result = ""; + switch ( parseInt( node.value ) ) { + case 0: + break; + case 1: + result = "&T"; + break; + case 2: + result = "&U"; + break; + case 3: + result = "&D"; + break; + case 4: + result = "&P"; + break; + case 5: + result = "&PT"; + break; + case 6: + result = node.custom; + break; + } + return result; +} + +function setPrinterDefaultsForSelectedPrinter() +{ + if (gPrintSettings.printerName == "") { + gPrintSettings.printerName = gPrintService.defaultPrinterName; + } + + // First get any defaults from the printer + gPrintService.initPrintSettingsFromPrinter(gPrintSettings.printerName, gPrintSettings); + + // now augment them with any values from last time + gPrintService.initPrintSettingsFromPrefs(gPrintSettings, true, gPrintSettingsInterface.kInitSaveAll); + + if (gDoDebug) { + dump("pagesetup/setPrinterDefaultsForSelectedPrinter: printerName='"+gPrintSettings.printerName+"', orientation='"+gPrintSettings.orientation+"'\n"); + } +} + +// --------------------------------------------------- +function loadDialog() +{ + var print_orientation = 0; + var print_margin_top = 0.5; + var print_margin_left = 0.5; + var print_margin_bottom = 0.5; + var print_margin_right = 0.5; + + try { + gPrefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch); + + gPrintService = Components.classes["@mozilla.org/gfx/printsettings-service;1"]; + if (gPrintService) { + gPrintService = gPrintService.getService(); + if (gPrintService) { + gPrintService = gPrintService.QueryInterface(Components.interfaces.nsIPrintSettingsService); + } + } + } catch (ex) { + dump("loadDialog: ex="+ex+"\n"); + } + + setPrinterDefaultsForSelectedPrinter(); + + gDialog.printBG.checked = gPrintSettings.printBGColors || gPrintSettings.printBGImages; + + gDialog.shrinkToFit.checked = gPrintSettings.shrinkToFit; + + gDialog.scalingLabel.disabled = gDialog.scalingInput.disabled = gDialog.shrinkToFit.checked; + + var marginGroupLabel = gDialog.marginGroup.label; + if (gPrintSettings.paperSizeUnit == gPrintSettingsInterface.kPaperSizeInches) { + marginGroupLabel = marginGroupLabel.replace(/#1/, gDialog.strings["marginUnits.inches"]); + gDoingMetric = false; + } else { + marginGroupLabel = marginGroupLabel.replace(/#1/, gDialog.strings["marginUnits.metric"]); + // Also, set global page dimensions for A4 paper, in millimeters (assumes portrait at this point). + gPageWidth = 2100; + gPageHeight = 2970; + gDoingMetric = true; + } + gDialog.marginGroup.label = marginGroupLabel; + + print_orientation = gPrintSettings.orientation; + print_margin_top = convertMarginInchesToUnits(gPrintSettings.marginTop, gDoingMetric); + print_margin_left = convertMarginInchesToUnits(gPrintSettings.marginLeft, gDoingMetric); + print_margin_right = convertMarginInchesToUnits(gPrintSettings.marginRight, gDoingMetric); + print_margin_bottom = convertMarginInchesToUnits(gPrintSettings.marginBottom, gDoingMetric); + + if (gDoDebug) { + dump("print_orientation "+print_orientation+"\n"); + + dump("print_margin_top "+print_margin_top+"\n"); + dump("print_margin_left "+print_margin_left+"\n"); + dump("print_margin_right "+print_margin_right+"\n"); + dump("print_margin_bottom "+print_margin_bottom+"\n"); + } + + if (print_orientation == gPrintSettingsInterface.kPortraitOrientation) { + gDialog.orientation.selectedItem = gDialog.portrait; + } else if (print_orientation == gPrintSettingsInterface.kLandscapeOrientation) { + gDialog.orientation.selectedItem = gDialog.landscape; + } + + // Set orientation the first time on a timeout so the dialog sizes to the + // maximum height specified in the .xul file. Otherwise, if the user switches + // from landscape to portrait, the content grows and the buttons are clipped. + setTimeout( setOrientation, 0 ); + + gDialog.topInput.value = print_margin_top.toFixed(1); + gDialog.bottomInput.value = print_margin_bottom.toFixed(1); + gDialog.leftInput.value = print_margin_left.toFixed(1); + gDialog.rightInput.value = print_margin_right.toFixed(1); + changeMargins(); + + setHeaderFooter( gDialog.hLeftOption, gPrintSettings.headerStrLeft ); + setHeaderFooter( gDialog.hCenterOption, gPrintSettings.headerStrCenter ); + setHeaderFooter( gDialog.hRightOption, gPrintSettings.headerStrRight ); + + setHeaderFooter( gDialog.fLeftOption, gPrintSettings.footerStrLeft ); + setHeaderFooter( gDialog.fCenterOption, gPrintSettings.footerStrCenter ); + setHeaderFooter( gDialog.fRightOption, gPrintSettings.footerStrRight ); + + gDialog.scalingInput.value = (gPrintSettings.scaling * 100).toFixed(0); + + // Enable/disable widgets based in the information whether the selected + // printer supports the matching feature or not + if (isListOfPrinterFeaturesAvailable()) { + if (gPrefs.getBoolPref("print.tmp.printerfeatures." + gPrintSettings.printerName + ".can_change_orientation")) + gDialog.orientation.removeAttribute("disabled"); + else + gDialog.orientation.setAttribute("disabled", "true"); + } + + // Give initial focus to the orientation radio group. + // Done on a timeout due to to bug 103197. + setTimeout( function() { gDialog.orientation.focus(); }, 0 ); +} + +// --------------------------------------------------- +function onLoad() +{ + // Init gDialog. + initDialog(); + + if (window.arguments[0] != null) { + gPrintSettings = window.arguments[0].QueryInterface(Components.interfaces.nsIPrintSettings); + paramBlock = window.arguments[1].QueryInterface(Components.interfaces.nsIDialogParamBlock); + } else if (gDoDebug) { + alert("window.arguments[0] == null!"); + } + + // default return value is "cancel" + paramBlock.SetInt(0, 0); + + if (gPrintSettings) { + loadDialog(); + } else if (gDoDebug) { + alert("Could initialize gDialog, PrintSettings is null!"); + } +} + +function convertUnitsMarginToInches(aVal, aIsMetric) +{ + if (aIsMetric) { + return aVal / 25.4; + } + return aVal; +} + +function convertMarginInchesToUnits(aVal, aIsMetric) +{ + if (aIsMetric) { + return aVal * 25.4; + } + return aVal; +} + +// --------------------------------------------------- +function onAccept() +{ + + if (gPrintSettings) { + if ( gDialog.orientation.selectedItem == gDialog.portrait ) { + gPrintSettings.orientation = gPrintSettingsInterface.kPortraitOrientation; + } else { + gPrintSettings.orientation = gPrintSettingsInterface.kLandscapeOrientation; + } + + // save these out so they can be picked up by the device spec + gPrintSettings.marginTop = convertUnitsMarginToInches(gDialog.topInput.value, gDoingMetric); + gPrintSettings.marginLeft = convertUnitsMarginToInches(gDialog.leftInput.value, gDoingMetric); + gPrintSettings.marginBottom = convertUnitsMarginToInches(gDialog.bottomInput.value, gDoingMetric); + gPrintSettings.marginRight = convertUnitsMarginToInches(gDialog.rightInput.value, gDoingMetric); + + gPrintSettings.headerStrLeft = hfIdToValue(gDialog.hLeftOption); + gPrintSettings.headerStrCenter = hfIdToValue(gDialog.hCenterOption); + gPrintSettings.headerStrRight = hfIdToValue(gDialog.hRightOption); + + gPrintSettings.footerStrLeft = hfIdToValue(gDialog.fLeftOption); + gPrintSettings.footerStrCenter = hfIdToValue(gDialog.fCenterOption); + gPrintSettings.footerStrRight = hfIdToValue(gDialog.fRightOption); + + gPrintSettings.printBGColors = gDialog.printBG.checked; + gPrintSettings.printBGImages = gDialog.printBG.checked; + + gPrintSettings.shrinkToFit = gDialog.shrinkToFit.checked; + + var scaling = document.getElementById("scalingInput").value; + if (scaling < 10.0) { + scaling = 10.0; + } + if (scaling > 500.0) { + scaling = 500.0; + } + scaling /= 100.0; + gPrintSettings.scaling = scaling; + + if (gDoDebug) { + dump("******* Page Setup Accepting ******\n"); + dump("print_margin_top "+gDialog.topInput.value+"\n"); + dump("print_margin_left "+gDialog.leftInput.value+"\n"); + dump("print_margin_right "+gDialog.bottomInput.value+"\n"); + dump("print_margin_bottom "+gDialog.rightInput.value+"\n"); + } + } + + // set return value to "ok" + if (paramBlock) { + paramBlock.SetInt(0, 1); + } else { + dump("*** FATAL ERROR: No paramBlock\n"); + } + + var flags = gPrintSettingsInterface.kInitSaveMargins | + gPrintSettingsInterface.kInitSaveHeaderLeft | + gPrintSettingsInterface.kInitSaveHeaderCenter | + gPrintSettingsInterface.kInitSaveHeaderRight | + gPrintSettingsInterface.kInitSaveFooterLeft | + gPrintSettingsInterface.kInitSaveFooterCenter | + gPrintSettingsInterface.kInitSaveFooterRight | + gPrintSettingsInterface.kInitSaveBGColors | + gPrintSettingsInterface.kInitSaveBGImages | + gPrintSettingsInterface.kInitSaveInColor | + gPrintSettingsInterface.kInitSaveReversed | + gPrintSettingsInterface.kInitSaveOrientation | + gPrintSettingsInterface.kInitSaveOddEvenPages | + gPrintSettingsInterface.kInitSaveShrinkToFit | + gPrintSettingsInterface.kInitSaveScaling; + + gPrintService.savePrintSettingsToPrefs(gPrintSettings, true, flags); + + return true; +} + +// --------------------------------------------------- +function onCancel() +{ + // set return value to "cancel" + if (paramBlock) { + paramBlock.SetInt(0, 0); + } else { + dump("*** FATAL ERROR: No paramBlock\n"); + } + + return true; +} + diff --git a/toolkit/components/printing/content/printPageSetup.xul b/toolkit/components/printing/content/printPageSetup.xul new file mode 100644 index 000000000..a0c3afe17 --- /dev/null +++ b/toolkit/components/printing/content/printPageSetup.xul @@ -0,0 +1,234 @@ +<?xml version="1.0"?> <!-- -*- Mode: HTML -*- --> + +<!-- 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/. --> + +<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> +<?xml-stylesheet href="chrome://global/skin/printPageSetup.css" type="text/css"?> +<!DOCTYPE dialog SYSTEM "chrome://global/locale/printPageSetup.dtd"> + +<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + id="printPageSetupDialog" + onload="onLoad();" + ondialogaccept="return onAccept();" + oncancel="return onCancel();" + title="&printSetup.title;" + persist="screenX screenY" + screenX="24" screenY="24"> + + <script type="application/javascript" src="chrome://global/content/printPageSetup.js"/> + + <!-- Localizable strings manipulated at run-time. --> + <data id="marginUnits.inches">&marginUnits.inches;</data> + <data id="marginUnits.metric">&marginUnits.metric;</data> + <data id="customPrompt.title">&customPrompt.title;</data> + <data id="customPrompt.prompt">&customPrompt.prompt;</data> + + <tabbox flex="1"> + <tabs> + <tab label="&basic.tab;"/> + <tab label="&advanced.tab;"/> + </tabs> + <tabpanels flex="1"> + <vbox> + <groupbox> + <caption label="&formatGroup.label;"/> + <vbox> + <hbox align="center"> + <label control="orientation" value="&orientation.label;"/> + <radiogroup id="orientation" oncommand="setOrientation()"> + <hbox align="center"> + <radio id="portrait" + class="portrait-page" + label="&portrait.label;" + accesskey="&portrait.accesskey;"/> + <radio id="landscape" + class="landscape-page" + label="&landscape.label;" + accesskey="&landscape.accesskey;"/> + </hbox> + </radiogroup> + </hbox> + <separator/> + <hbox align="center"> + <label control="scalingInput" + value="&scale.label;" + accesskey="&scale.accesskey;"/> + <textbox id="scalingInput" size="4" oninput="checkDouble(this)"/> + <label value="&scalePercent;"/> + <separator/> + <checkbox id="shrinkToFit" + label="&shrinkToFit.label;" + accesskey="&shrinkToFit.accesskey;" + oncommand="gDialog.scalingInput.disabled=gDialog.scalingLabel.disabled=this.checked"/> + </hbox> + </vbox> + </groupbox> + <groupbox> + <caption label="&optionsGroup.label;"/> + <checkbox id="printBG" + label="&printBG.label;" + accesskey="&printBG.accesskey;"/> + </groupbox> + </vbox> + <vbox> + <groupbox> + <caption id="marginGroup" label="&marginGroup.label;"/> + <vbox> + <hbox align="center"> + <spacer flex="1"/> + <label control="topInput" + value="&marginTop.label;" + accesskey="&marginTop.accesskey;"/> + <textbox id="topInput" size="5" oninput="changeMargin(this)"/> + <!-- This invisible label (with same content as the visible one!) is used + to ensure that the <textbox> is centered above the page. The same + technique is deployed for the bottom/left/right input fields, below. --> + <label value="&marginTop.label;" style="visibility: hidden;"/> + <spacer flex="1"/> + </hbox> + <hbox dir="ltr"> + <spacer flex="1"/> + <vbox> + <spacer flex="1"/> + <label control="leftInput" + value="&marginLeft.label;" + accesskey="&marginLeft.accesskey;"/> + <textbox id="leftInput" size="5" oninput="changeMargin(this)"/> + <label value="&marginLeft.label;" style="visibility: hidden;"/> + <spacer flex="1"/> + </vbox> + <!-- The "margin page" draws a simulated printout page with dashed lines + for the margins. The height/width style attributes of the marginTop, + marginBottom, marginLeft, and marginRight elements are set by + the JS code dynamically based on the user input. --> + <vbox id="marginPage" style="height:29.7mm;"> + <box id="marginTop" style="height:0.05in;"/> + <hbox flex="1" dir="ltr"> + <box id="marginLeft" style="width:0.025in;"/> + <box style="border: 1px; border-style: dashed; border-color: gray;" flex="1"/> + <box id="marginRight" style="width:0.025in;"/> + </hbox> + <box id="marginBottom" style="height:0.05in;"/> + </vbox> + <vbox> + <spacer flex="1"/> + <label control="rightInput" + value="&marginRight.label;" + accesskey="&marginRight.accesskey;"/> + <textbox id="rightInput" size="5" oninput="changeMargin(this)"/> + <label value="&marginRight.label;" style="visibility: hidden;"/> + <spacer flex="1"/> + </vbox> + <spacer flex="1"/> + </hbox> + <hbox align="center"> + <spacer flex="1"/> + <label control="bottomInput" + value="&marginBottom.label;" + accesskey="&marginBottom.accesskey;"/> + <textbox id="bottomInput" size="5" oninput="changeMargin(this)"/> + <label value="&marginBottom.label;" style="visibility: hidden;"/> + <spacer flex="1"/> + </hbox> + </vbox> + </groupbox> + <groupbox> + <caption id="headersAndFooters" label="&headerFooter.label;"/> + <grid> + <columns> + <column/> + <column/> + <column/> + </columns> + <rows> + <row dir="ltr"> + <menulist id="hLeftOption" oncommand="customize(this)" tooltiptext="&headerLeft.tip;"> + <menupopup> + <menuitem value="0" label="&hfBlank;"/> + <menuitem value="1" label="&hfTitle;"/> + <menuitem value="2" label="&hfURL;"/> + <menuitem value="3" label="&hfDateAndTime;"/> + <menuitem value="4" label="&hfPage;"/> + <menuitem value="5" label="&hfPageAndTotal;"/> + <menuitem value="6" label="&hfCustom;"/> + </menupopup> + </menulist> + <menulist id="hCenterOption" oncommand="customize(this)" tooltiptext="&headerCenter.tip;"> + <menupopup> + <menuitem value="0" label="&hfBlank;"/> + <menuitem value="1" label="&hfTitle;"/> + <menuitem value="2" label="&hfURL;"/> + <menuitem value="3" label="&hfDateAndTime;"/> + <menuitem value="4" label="&hfPage;"/> + <menuitem value="5" label="&hfPageAndTotal;"/> + <menuitem value="6" label="&hfCustom;"/> + </menupopup> + </menulist> + <menulist id="hRightOption" oncommand="customize(this)" tooltiptext="&headerRight.tip;"> + <menupopup> + <menuitem value="0" label="&hfBlank;"/> + <menuitem value="1" label="&hfTitle;"/> + <menuitem value="2" label="&hfURL;"/> + <menuitem value="3" label="&hfDateAndTime;"/> + <menuitem value="4" label="&hfPage;"/> + <menuitem value="5" label="&hfPageAndTotal;"/> + <menuitem value="6" label="&hfCustom;"/> + </menupopup> + </menulist> + </row> + <row dir="ltr"> + <vbox align="center"> + <label value="&hfLeft.label;"/> + </vbox> + <vbox align="center"> + <label value="&hfCenter.label;"/> + </vbox> + <vbox align="center"> + <label value="&hfRight.label;"/> + </vbox> + </row> + <row dir="ltr"> + <menulist id="fLeftOption" oncommand="customize(this)" tooltiptext="&footerLeft.tip;"> + <menupopup> + <menuitem value="0" label="&hfBlank;"/> + <menuitem value="1" label="&hfTitle;"/> + <menuitem value="2" label="&hfURL;"/> + <menuitem value="3" label="&hfDateAndTime;"/> + <menuitem value="4" label="&hfPage;"/> + <menuitem value="5" label="&hfPageAndTotal;"/> + <menuitem value="6" label="&hfCustom;"/> + </menupopup> + </menulist> + <menulist id="fCenterOption" oncommand="customize(this)" tooltiptext="&footerCenter.tip;"> + <menupopup> + <menuitem value="0" label="&hfBlank;"/> + <menuitem value="1" label="&hfTitle;"/> + <menuitem value="2" label="&hfURL;"/> + <menuitem value="3" label="&hfDateAndTime;"/> + <menuitem value="4" label="&hfPage;"/> + <menuitem value="5" label="&hfPageAndTotal;"/> + <menuitem value="6" label="&hfCustom;"/> + </menupopup> + </menulist> + <menulist id="fRightOption" oncommand="customize(this)" tooltiptext="&footerRight.tip;"> + <menupopup> + <menuitem value="0" label="&hfBlank;"/> + <menuitem value="1" label="&hfTitle;"/> + <menuitem value="2" label="&hfURL;"/> + <menuitem value="3" label="&hfDateAndTime;"/> + <menuitem value="4" label="&hfPage;"/> + <menuitem value="5" label="&hfPageAndTotal;"/> + <menuitem value="6" label="&hfCustom;"/> + </menupopup> + </menulist> + </row> + </rows> + </grid> + </groupbox> + </vbox> + </tabpanels> + </tabbox> +</dialog> + diff --git a/toolkit/components/printing/content/printPreviewBindings.xml b/toolkit/components/printing/content/printPreviewBindings.xml new file mode 100644 index 000000000..182ecc199 --- /dev/null +++ b/toolkit/components/printing/content/printPreviewBindings.xml @@ -0,0 +1,415 @@ +<?xml version="1.0"?> + +<!-- 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/. --> + +<!-- this file depends on printUtils.js --> + +<!DOCTYPE bindings [ +<!ENTITY % printPreviewDTD SYSTEM "chrome://global/locale/printPreview.dtd" > +%printPreviewDTD; +]> + +<bindings id="printPreviewBindings" + xmlns="http://www.mozilla.org/xbl" + xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <binding id="printpreviewtoolbar" + extends="chrome://global/content/bindings/toolbar.xml#toolbar"> + <resources> + <stylesheet src="chrome://global/skin/printPreview.css"/> + </resources> + + <content> + <xul:button label="&print.label;" accesskey="&print.accesskey;" + oncommand="this.parentNode.print();" icon="print"/> + + <xul:button label="&pageSetup.label;" accesskey="&pageSetup.accesskey;" + oncommand="this.parentNode.doPageSetup();"/> + + <xul:vbox align="center" pack="center"> + <xul:label value="&page.label;" accesskey="&page.accesskey;" control="pageNumber"/> + </xul:vbox> + <xul:toolbarbutton anonid="navigateHome" class="navigate-button tabbable" + oncommand="parentNode.navigate(0, 0, 'home');" tooltiptext="&homearrow.tooltip;"/> + <xul:toolbarbutton anonid="navigatePrevious" class="navigate-button tabbable" + oncommand="parentNode.navigate(-1, 0, 0);" tooltiptext="&previousarrow.tooltip;"/> + <xul:hbox align="center" pack="center"> + <xul:textbox id="pageNumber" size="3" value="1" min="1" type="number" + hidespinbuttons="true" onchange="navigate(0, this.valueNumber, 0);"/> + <xul:label value="&of.label;"/> + <xul:label value="1"/> + </xul:hbox> + <xul:toolbarbutton anonid="navigateNext" class="navigate-button tabbable" + oncommand="parentNode.navigate(1, 0, 0);" tooltiptext="&nextarrow.tooltip;"/> + <xul:toolbarbutton anonid="navigateEnd" class="navigate-button tabbable" + oncommand="parentNode.navigate(0, 0, 'end');" tooltiptext="&endarrow.tooltip;"/> + + <xul:toolbarseparator class="toolbarseparator-primary"/> + <xul:vbox align="center" pack="center"> + <xul:label value="&scale.label;" accesskey="&scale.accesskey;" control="scale"/> + </xul:vbox> + + <xul:hbox align="center" pack="center"> + <xul:menulist id="scale" crop="none" + oncommand="parentNode.parentNode.scale(this.selectedItem.value);"> + <xul:menupopup> + <xul:menuitem value="0.3" label="&p30.label;"/> + <xul:menuitem value="0.4" label="&p40.label;"/> + <xul:menuitem value="0.5" label="&p50.label;"/> + <xul:menuitem value="0.6" label="&p60.label;"/> + <xul:menuitem value="0.7" label="&p70.label;"/> + <xul:menuitem value="0.8" label="&p80.label;"/> + <xul:menuitem value="0.9" label="&p90.label;"/> + <xul:menuitem value="1" label="&p100.label;"/> + <xul:menuitem value="1.25" label="&p125.label;"/> + <xul:menuitem value="1.5" label="&p150.label;"/> + <xul:menuitem value="1.75" label="&p175.label;"/> + <xul:menuitem value="2" label="&p200.label;"/> + <xul:menuseparator/> + <xul:menuitem flex="1" value="ShrinkToFit" + label="&ShrinkToFit.label;"/> + <xul:menuitem value="Custom" label="&Custom.label;"/> + </xul:menupopup> + </xul:menulist> + </xul:hbox> + + <xul:toolbarseparator class="toolbarseparator-primary"/> + <xul:hbox align="center" pack="center"> + <xul:toolbarbutton label="&portrait.label;" checked="true" + accesskey="&portrait.accesskey;" + type="radio" group="orient" class="toolbar-portrait-page tabbable" + oncommand="parentNode.parentNode.orient('portrait');"/> + <xul:toolbarbutton label="&landscape.label;" + accesskey="&landscape.accesskey;" + type="radio" group="orient" class="toolbar-landscape-page tabbable" + oncommand="parentNode.parentNode.orient('landscape');"/> + </xul:hbox> + + <xul:toolbarseparator class="toolbarseparator-primary"/> + <xul:checkbox label="&simplifyPage.label;" checked="false" disabled="true" + accesskey="&simplifyPage.accesskey;" + tooltiptext-disabled="&simplifyPage.disabled.tooltip;" + tooltiptext-enabled="&simplifyPage.enabled.tooltip;" + oncommand="this.parentNode.simplify();"/> + + <xul:toolbarseparator class="toolbarseparator-primary"/> + <xul:button label="&close.label;" accesskey="&close.accesskey;" + oncommand="PrintUtils.exitPrintPreview();" icon="close"/> + <xul:data value="&customPrompt.title;"/> + </content> + + <implementation implements="nsIMessageListener"> + <field name="mPrintButton"> + document.getAnonymousNodes(this)[0] + </field> + <field name="mPageTextBox"> + document.getAnonymousNodes(this)[5].childNodes[0] + </field> + <field name="mTotalPages"> + document.getAnonymousNodes(this)[5].childNodes[2] + </field> + <field name="mScaleLabel"> + document.getAnonymousNodes(this)[9].firstChild + </field> + <field name="mScaleCombobox"> + document.getAnonymousNodes(this)[10].firstChild + </field> + <field name="mOrientButtonsBox"> + document.getAnonymousNodes(this)[12] + </field> + <field name="mPortaitButton"> + this.mOrientButtonsBox.childNodes[0] + </field> + <field name="mLandscapeButton"> + this.mOrientButtonsBox.childNodes[1] + </field> + <field name="mSimplifyPageCheckbox"> + document.getAnonymousNodes(this)[14] + </field> + <field name="mSimplifyPageToolbarSeparator"> + document.getAnonymousNodes(this)[15] + </field> + <field name="mCustomTitle"> + document.getAnonymousNodes(this)[17].firstChild + </field> + <field name="mPrintPreviewObs"> + </field> + <field name="mWebProgress"> + </field> + <field name="mPPBrowser"> + null + </field> + <field name="mMessageManager"> + null + </field> + + <method name="initialize"> + <parameter name="aPPBrowser"/> + <body> + <![CDATA[ + let {Services} = Components.utils.import("resource://gre/modules/Services.jsm", {}); + if (!Services.prefs.getBoolPref("print.use_simplify_page")) { + this.mSimplifyPageCheckbox.hidden = true; + this.mSimplifyPageToolbarSeparator.hidden = true; + } + this.mPPBrowser = aPPBrowser; + this.mMessageManager = aPPBrowser.messageManager; + this.mMessageManager.addMessageListener("Printing:Preview:UpdatePageCount", this); + this.updateToolbar(); + + let $ = id => document.getAnonymousElementByAttribute(this, "anonid", id); + let ltr = document.documentElement.matches(":root:-moz-locale-dir(ltr)"); + // Windows 7 doesn't support ⏮ and ⏭ by default, and fallback doesn't + // always work (bug 1343330). + let {AppConstants} = Components.utils.import("resource://gre/modules/AppConstants.jsm", {}); + let useCompatCharacters = AppConstants.isPlatformAndVersionAtMost("win", "6.1"); + let leftEnd = useCompatCharacters ? "⏪" : "⏮"; + let rightEnd = useCompatCharacters ? "⏩" : "⏭"; + $("navigateHome").label = ltr ? leftEnd : rightEnd; + $("navigatePrevious").label = ltr ? "◂" : "▸"; + $("navigateNext").label = ltr ? "▸" : "◂"; + $("navigateEnd").label = ltr ? rightEnd : leftEnd; + ]]> + </body> + </method> + + <method name="doPageSetup"> + <body> + <![CDATA[ + var didOK = PrintUtils.showPageSetup(); + if (didOK) { + // the changes that effect the UI + this.updateToolbar(); + + // Now do PrintPreview + PrintUtils.printPreview(); + } + ]]> + </body> + </method> + + <method name="navigate"> + <parameter name="aDirection"/> + <parameter name="aPageNum"/> + <parameter name="aHomeOrEnd"/> + <body> + <![CDATA[ + const nsIWebBrowserPrint = Components.interfaces.nsIWebBrowserPrint; + let navType, pageNum; + + // we use only one of aHomeOrEnd, aDirection, or aPageNum + if (aHomeOrEnd) { + // We're going to either the very first page ("home"), or the + // very last page ("end"). + if (aHomeOrEnd == "home") { + navType = nsIWebBrowserPrint.PRINTPREVIEW_HOME; + this.mPageTextBox.value = 1; + } else { + navType = nsIWebBrowserPrint.PRINTPREVIEW_END; + this.mPageTextBox.value = this.mPageTextBox.max; + } + pageNum = 0; + } else if (aDirection) { + // aDirection is either +1 or -1, and allows us to increment + // or decrement our currently viewed page. + this.mPageTextBox.valueNumber += aDirection; + navType = nsIWebBrowserPrint.PRINTPREVIEW_GOTO_PAGENUM; + pageNum = this.mPageTextBox.value; // TODO: back to valueNumber? + } else { + // We're going to a specific page (aPageNum) + navType = nsIWebBrowserPrint.PRINTPREVIEW_GOTO_PAGENUM; + pageNum = aPageNum; + } + + this.mMessageManager.sendAsyncMessage("Printing:Preview:Navigate", { + navType: navType, + pageNum: pageNum, + }); + ]]> + </body> + </method> + + <method name="print"> + <body> + <![CDATA[ + PrintUtils.printWindow(this.mPPBrowser.outerWindowID, this.mPPBrowser); + ]]> + </body> + </method> + + <method name="promptForScaleValue"> + <parameter name="aValue"/> + <body> + <![CDATA[ + var value = Math.round(aValue); + var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService(Components.interfaces.nsIPromptService); + var promptStr = this.mScaleLabel.value; + var renameTitle = this.mCustomTitle; + var result = {value:value}; + var confirmed = promptService.prompt(window, renameTitle, promptStr, result, null, {value:value}); + if (!confirmed || (!result.value) || (result.value == "") || result.value == value) { + return -1; + } + return result.value; + ]]> + </body> + </method> + + <method name="setScaleCombobox"> + <parameter name="aValue"/> + <body> + <![CDATA[ + var scaleValues = [0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1, 1.25, 1.5, 1.75, 2]; + + aValue = new Number(aValue); + + for (var i = 0; i < scaleValues.length; i++) { + if (aValue == scaleValues[i]) { + this.mScaleCombobox.selectedIndex = i; + return; + } + } + this.mScaleCombobox.value = "Custom"; + ]]> + </body> + </method> + + <method name="scale"> + <parameter name="aValue"/> + <body> + <![CDATA[ + var settings = PrintUtils.getPrintSettings(); + if (aValue == "ShrinkToFit") { + if (!settings.shrinkToFit) { + settings.shrinkToFit = true; + this.savePrintSettings(settings, settings.kInitSaveShrinkToFit | settings.kInitSaveScaling); + PrintUtils.printPreview(); + } + return; + } + + if (aValue == "Custom") { + aValue = this.promptForScaleValue(settings.scaling * 100.0); + if (aValue >= 10) { + aValue /= 100.0; + } else { + if (this.mScaleCombobox.hasAttribute('lastValidInx')) { + this.mScaleCombobox.selectedIndex = this.mScaleCombobox.getAttribute('lastValidInx'); + } + return; + } + } + + this.setScaleCombobox(aValue); + this.mScaleCombobox.setAttribute('lastValidInx', this.mScaleCombobox.selectedIndex); + + if (settings.scaling != aValue || settings.shrinkToFit) + { + settings.shrinkToFit = false; + settings.scaling = aValue; + this.savePrintSettings(settings, settings.kInitSaveShrinkToFit | settings.kInitSaveScaling); + PrintUtils.printPreview(); + } + ]]> + </body> + </method> + + <method name="orient"> + <parameter name="aOrientation"/> + <body> + <![CDATA[ + const kIPrintSettings = Components.interfaces.nsIPrintSettings; + var orientValue = (aOrientation == "portrait") ? kIPrintSettings.kPortraitOrientation : + kIPrintSettings.kLandscapeOrientation; + var settings = PrintUtils.getPrintSettings(); + if (settings.orientation != orientValue) + { + settings.orientation = orientValue; + this.savePrintSettings(settings, settings.kInitSaveOrientation); + PrintUtils.printPreview(); + } + ]]> + </body> + </method> + + <method name="simplify"> + <body> + <![CDATA[ + PrintUtils.setSimplifiedMode(this.mSimplifyPageCheckbox.checked); + PrintUtils.printPreview(); + ]]> + </body> + </method> + + <method name="enableSimplifyPage"> + <body> + <![CDATA[ + this.mSimplifyPageCheckbox.disabled = false; + this.mSimplifyPageCheckbox.setAttribute("tooltiptext", + this.mSimplifyPageCheckbox.getAttribute("tooltiptext-enabled")); + ]]> + </body> + </method> + + <method name="disableSimplifyPage"> + <body> + <![CDATA[ + this.mSimplifyPageCheckbox.disabled = true; + this.mSimplifyPageCheckbox.setAttribute("tooltiptext", + this.mSimplifyPageCheckbox.getAttribute("tooltiptext-disabled")); + ]]> + </body> + </method> + + <method name="updateToolbar"> + <body> + <![CDATA[ + var settings = PrintUtils.getPrintSettings(); + + var isPortrait = settings.orientation == Components.interfaces.nsIPrintSettings.kPortraitOrientation; + + this.mPortaitButton.checked = isPortrait; + this.mLandscapeButton.checked = !isPortrait; + + if (settings.shrinkToFit) { + this.mScaleCombobox.value = "ShrinkToFit"; + } else { + this.setScaleCombobox(settings.scaling); + } + + this.mPageTextBox.value = 1; + + this.mMessageManager.sendAsyncMessage("Printing:Preview:UpdatePageCount"); + ]]> + </body> + </method> + + <method name="savePrintSettings"> + <parameter name="settings"/> + <parameter name="flags"/> + <body><![CDATA[ + var PSSVC = Components.classes["@mozilla.org/gfx/printsettings-service;1"] + .getService(Components.interfaces.nsIPrintSettingsService); + PSSVC.savePrintSettingsToPrefs(settings, true, flags); + ]]></body> + </method> + + <!-- nsIMessageListener --> + <method name="receiveMessage"> + <parameter name="message"/> + <body> + <![CDATA[ + if (message.name == "Printing:Preview:UpdatePageCount") { + let numPages = message.data.numPages; + this.mTotalPages.value = numPages; + this.mPageTextBox.max = numPages; + } + ]]> + </body> + </method> + </implementation> + </binding> + +</bindings> diff --git a/toolkit/components/printing/content/printPreviewProgress.js b/toolkit/components/printing/content/printPreviewProgress.js new file mode 100644 index 000000000..5c769e50a --- /dev/null +++ b/toolkit/components/printing/content/printPreviewProgress.js @@ -0,0 +1,154 @@ +// -*- indent-tabs-mode: nil; js-indent-level: 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/. */ + +// dialog is just an array we'll use to store various properties from the dialog document... +var dialog; + +// the printProgress is a nsIPrintProgress object +var printProgress = null; + +// random global variables... +var targetFile; + +var docTitle = ""; +var docURL = ""; +var progressParams = null; + +function ellipseString(aStr, doFront) +{ + if (aStr.length > 3 && (aStr.substr(0, 3) == "..." || aStr.substr(aStr.length-4, 3) == "...")) + return aStr; + + var fixedLen = 64; + if (aStr.length <= fixedLen) + return aStr; + + if (doFront) + return "..." + aStr.substr(aStr.length-fixedLen, fixedLen); + + return aStr.substr(0, fixedLen) + "..."; +} + +// all progress notifications are done through the nsIWebProgressListener implementation... +var progressListener = { + + onStateChange: function (aWebProgress, aRequest, aStateFlags, aStatus) + { + if (aStateFlags & Components.interfaces.nsIWebProgressListener.STATE_STOP) + window.close(); + }, + + onProgressChange: function (aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress) + { + if (!progressParams) + return; + var docTitleStr = ellipseString(progressParams.docTitle, false); + if (docTitleStr != docTitle) { + docTitle = docTitleStr; + dialog.title.value = docTitle; + } + var docURLStr = ellipseString(progressParams.docURL, true); + if (docURLStr != docURL && dialog.title != null) { + docURL = docURLStr; + if (docTitle == "") + dialog.title.value = docURLStr; + } + }, + + onLocationChange: function (aWebProgress, aRequest, aLocation, aFlags) {}, + onSecurityChange: function (aWebProgress, aRequest, state) {}, + + onStatusChange: function (aWebProgress, aRequest, aStatus, aMessage) + { + if (aMessage) + dialog.title.setAttribute("value", aMessage); + }, + + QueryInterface: function (iid) + { + if (iid.equals(Components.interfaces.nsIWebProgressListener) || iid.equals(Components.interfaces.nsISupportsWeakReference)) + return this; + throw Components.results.NS_NOINTERFACE; + } +} + +function onLoad() { + // Set global variables. + printProgress = window.arguments[0]; + if (window.arguments[1]) { + progressParams = window.arguments[1].QueryInterface(Components.interfaces.nsIPrintProgressParams) + if (progressParams) { + docTitle = ellipseString(progressParams.docTitle, false); + docURL = ellipseString(progressParams.docURL, true); + } + } + + if (!printProgress) { + dump( "Invalid argument to printPreviewProgress.xul\n" ); + window.close() + return; + } + + dialog = {}; + dialog.strings = new Array; + dialog.title = document.getElementById("dialog.title"); + dialog.titleLabel = document.getElementById("dialog.titleLabel"); + + dialog.title.value = docTitle; + + // set our web progress listener on the helper app launcher + printProgress.registerListener(progressListener); + + // We need to delay the set title else dom will overwrite it + window.setTimeout(doneIniting, 100); +} + +function onUnload() +{ + if (!printProgress) + return; + try { + printProgress.unregisterListener(progressListener); + printProgress = null; + } + catch (e) {} +} + +function getString (stringId) { + // Check if we've fetched this string already. + if (!(stringId in dialog.strings)) { + // Try to get it. + var elem = document.getElementById( "dialog.strings."+stringId); + try { + if (elem && elem.childNodes && elem.childNodes[0] && + elem.childNodes[0].nodeValue) + dialog.strings[stringId] = elem.childNodes[0].nodeValue; + // If unable to fetch string, use an empty string. + else + dialog.strings[stringId] = ""; + } catch (e) { dialog.strings[stringId] = ""; } + } + return dialog.strings[stringId]; +} + +// If the user presses cancel, tell the app launcher and close the dialog... +function onCancel () +{ + // Cancel app launcher. + try { + printProgress.processCanceledByUser = true; + } + catch (e) { return true; } + + // don't Close up dialog by returning false, the backend will close the dialog when everything will be aborted. + return false; +} + +function doneIniting() +{ + // called by function timeout in onLoad + printProgress.doneIniting(); +} diff --git a/toolkit/components/printing/content/printPreviewProgress.xul b/toolkit/components/printing/content/printPreviewProgress.xul new file mode 100644 index 000000000..fa2b9b61d --- /dev/null +++ b/toolkit/components/printing/content/printPreviewProgress.xul @@ -0,0 +1,42 @@ +<?xml version="1.0"?> + +<!-- 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/. --> + +<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> + +<!DOCTYPE window SYSTEM "chrome://global/locale/printPreviewProgress.dtd"> + +<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + title="&printWindow.title;" + style="width: 36em;" + buttons="cancel" + oncancel="onCancel()" + onload="onLoad()" + onunload="onUnload()"> + + <script type="application/javascript" src="chrome://global/content/printPreviewProgress.js"/> + + <grid flex="1"> + <columns> + <column/> + <column/> + </columns> + + <rows> + <row> + <hbox pack="end"> + <label id="dialog.titleLabel" value="&title;"/> + </hbox> + <label id="dialog.title"/> + </row> + <row class="thin-separator"> + <hbox pack="end"> + <label id="dialog.progressSpaces" value="&progress;"/> + </hbox> + <label id="dialog.progressLabel" value="&preparing;"/> + </row> + </rows> + </grid> +</dialog> diff --git a/toolkit/components/printing/content/printProgress.js b/toolkit/components/printing/content/printProgress.js new file mode 100644 index 000000000..6cadfe45e --- /dev/null +++ b/toolkit/components/printing/content/printProgress.js @@ -0,0 +1,282 @@ +// -*- tab-width: 2; indent-tabs-mode: nil; js-indent-level: 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/. */ + +// dialog is just an array we'll use to store various properties from the dialog document... +var dialog; + +// the printProgress is a nsIPrintProgress object +var printProgress = null; + +// random global variables... +var targetFile; + +var docTitle = ""; +var docURL = ""; +var progressParams = null; +var switchUI = true; + +function ellipseString(aStr, doFront) +{ + if (aStr.length > 3 && (aStr.substr(0, 3) == "..." || aStr.substr(aStr.length-4, 3) == "...")) { + return aStr; + } + + var fixedLen = 64; + if (aStr.length > fixedLen) { + if (doFront) { + var endStr = aStr.substr(aStr.length-fixedLen, fixedLen); + return "..." + endStr; + } + var frontStr = aStr.substr(0, fixedLen); + return frontStr + "..."; + } + return aStr; +} + +// all progress notifications are done through the nsIWebProgressListener implementation... +var progressListener = { + onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus) + { + if (aStateFlags & Components.interfaces.nsIWebProgressListener.STATE_START) + { + // Put progress meter in undetermined mode. + // dialog.progress.setAttribute( "value", 0 ); + dialog.progress.setAttribute( "mode", "undetermined" ); + } + + if (aStateFlags & Components.interfaces.nsIWebProgressListener.STATE_STOP) + { + // we are done printing + // Indicate completion in title area. + var msg = getString( "printComplete" ); + dialog.title.setAttribute("value", msg); + + // Put progress meter at 100%. + dialog.progress.setAttribute( "value", 100 ); + dialog.progress.setAttribute( "mode", "normal" ); + var percentPrint = getString( "progressText" ); + percentPrint = replaceInsert( percentPrint, 1, 100 ); + dialog.progressText.setAttribute("value", percentPrint); + + var fm = Components.classes["@mozilla.org/focus-manager;1"] + .getService(Components.interfaces.nsIFocusManager); + if (fm && fm.activeWindow == window) { + // This progress dialog is the currently active window. In + // this case we need to make sure that some other window + // gets focus before we close this dialog to work around the + // buggy Windows XP Fax dialog, which ends up parenting + // itself to the currently focused window and is unable to + // survive that window going away. What happens without this + // opener.focus() call on Windows XP is that the fax dialog + // is opened only to go away when this dialog actually + // closes (which can happen asynchronously, so the fax + // dialog just flashes once and then goes away), so w/o this + // fix, it's impossible to fax on Windows XP w/o manually + // switching focus to another window (or holding on to the + // progress dialog with the mouse long enough). + opener.focus(); + } + + window.close(); + } + }, + + onProgressChange: function(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress) + { + if (switchUI) + { + dialog.tempLabel.setAttribute("hidden", "true"); + dialog.progress.setAttribute("hidden", "false"); + + var progressLabel = getString("progress"); + if (progressLabel == "") { + progressLabel = "Progress:"; // better than nothing + } + switchUI = false; + } + + if (progressParams) + { + var docTitleStr = ellipseString(progressParams.docTitle, false); + if (docTitleStr != docTitle) { + docTitle = docTitleStr; + dialog.title.value = docTitle; + } + var docURLStr = progressParams.docURL; + if (docURLStr != docURL && dialog.title != null) { + docURL = docURLStr; + if (docTitle == "") { + dialog.title.value = ellipseString(docURLStr, true); + } + } + } + + // Calculate percentage. + var percent; + if ( aMaxTotalProgress > 0 ) + { + percent = Math.round( (aCurTotalProgress*100)/aMaxTotalProgress ); + if ( percent > 100 ) + percent = 100; + + dialog.progress.removeAttribute( "mode"); + + // Advance progress meter. + dialog.progress.setAttribute( "value", percent ); + + // Update percentage label on progress meter. + var percentPrint = getString( "progressText" ); + percentPrint = replaceInsert( percentPrint, 1, percent ); + dialog.progressText.setAttribute("value", percentPrint); + } + else + { + // Progress meter should be barber-pole in this case. + dialog.progress.setAttribute( "mode", "undetermined" ); + // Update percentage label on progress meter. + dialog.progressText.setAttribute("value", ""); + } + }, + + onLocationChange: function(aWebProgress, aRequest, aLocation, aFlags) + { + // we can ignore this notification + }, + + onStatusChange: function(aWebProgress, aRequest, aStatus, aMessage) + { + if (aMessage != "") + dialog.title.setAttribute("value", aMessage); + }, + + onSecurityChange: function(aWebProgress, aRequest, state) + { + // we can ignore this notification + }, + + QueryInterface : function(iid) + { + if (iid.equals(Components.interfaces.nsIWebProgressListener) || iid.equals(Components.interfaces.nsISupportsWeakReference)) + return this; + + throw Components.results.NS_NOINTERFACE; + } +}; + +function getString( stringId ) { + // Check if we've fetched this string already. + if (!(stringId in dialog.strings)) { + // Try to get it. + var elem = document.getElementById( "dialog.strings."+stringId ); + try { + if ( elem + && + elem.childNodes + && + elem.childNodes[0] + && + elem.childNodes[0].nodeValue ) { + dialog.strings[stringId] = elem.childNodes[0].nodeValue; + } else { + // If unable to fetch string, use an empty string. + dialog.strings[stringId] = ""; + } + } catch (e) { dialog.strings[stringId] = ""; } + } + return dialog.strings[stringId]; +} + +function loadDialog() +{ +} + +function replaceInsert( text, index, value ) { + var result = text; + var regExp = new RegExp( "#"+index ); + result = result.replace( regExp, value ); + return result; +} + +function onLoad() { + + // Set global variables. + printProgress = window.arguments[0]; + if (window.arguments[1]) + { + progressParams = window.arguments[1].QueryInterface(Components.interfaces.nsIPrintProgressParams) + if (progressParams) + { + docTitle = ellipseString(progressParams.docTitle, false); + docURL = ellipseString(progressParams.docURL, true); + } + } + + if ( !printProgress ) { + dump( "Invalid argument to printProgress.xul\n" ); + window.close() + return; + } + + dialog = {}; + dialog.strings = new Array; + dialog.title = document.getElementById("dialog.title"); + dialog.titleLabel = document.getElementById("dialog.titleLabel"); + dialog.progress = document.getElementById("dialog.progress"); + dialog.progressText = document.getElementById("dialog.progressText"); + dialog.progressLabel = document.getElementById("dialog.progressLabel"); + dialog.tempLabel = document.getElementById("dialog.tempLabel"); + + dialog.progress.setAttribute("hidden", "true"); + + var progressLabel = getString("preparing"); + if (progressLabel == "") { + progressLabel = "Preparing..."; // better than nothing + } + dialog.tempLabel.value = progressLabel; + + dialog.title.value = docTitle; + + // Fill dialog. + loadDialog(); + + // set our web progress listener on the helper app launcher + printProgress.registerListener(progressListener); + // We need to delay the set title else dom will overwrite it + window.setTimeout(doneIniting, 500); +} + +function onUnload() +{ + if (printProgress) + { + try + { + printProgress.unregisterListener(progressListener); + printProgress = null; + } + + catch ( exception ) {} + } +} + +// If the user presses cancel, tell the app launcher and close the dialog... +function onCancel () +{ + // Cancel app launcher. + try + { + printProgress.processCanceledByUser = true; + } + catch ( exception ) { return true; } + + // don't Close up dialog by returning false, the backend will close the dialog when everything will be aborted. + return false; +} + +function doneIniting() +{ + printProgress.doneIniting(); +} diff --git a/toolkit/components/printing/content/printProgress.xul b/toolkit/components/printing/content/printProgress.xul new file mode 100644 index 000000000..2d724e54f --- /dev/null +++ b/toolkit/components/printing/content/printProgress.xul @@ -0,0 +1,60 @@ +<?xml version="1.0"?> + +<!-- 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/. --> + +<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> + +<!DOCTYPE window SYSTEM "chrome://global/locale/printProgress.dtd"> + +<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + buttons="cancel" + title="&printWindow.title;" + style="width: 36em;" + ondialogcancel="onCancel()" + onload="onLoad()" + onunload="onUnload()"> + + <script type="application/javascript" src="chrome://global/content/printProgress.js"/> + + <!-- This is non-visible content that simply adds translatable string + into the document so that it is accessible to JS code. + + XXX-TODO: + convert to use string bundles. + --> + + <data id="dialog.strings.dialogCloseLabel">&dialogClose.label;</data> + <data id="dialog.strings.printComplete">&printComplete;</data> + <data id="dialog.strings.progressText">&percentPrint;</data> + <data id="dialog.strings.progressLabel">&progress;</data> + <data id="dialog.strings.preparing">&preparing;</data> + + <grid flex="1"> + <columns> + <column/> + <column/> + <column/> + </columns> + + <rows> + <row> + <hbox pack="end"> + <label id="dialog.titleLabel" value="&title;"/> + </hbox> + <label id="dialog.title"/> + </row> + <row class="thin-separator"> + <hbox pack="end"> + <label id="dialog.progressLabel" control="dialog.progress" value="&progress;"/> + </hbox> + <label id="dialog.tempLabel" value="&preparing;"/> + <progressmeter id="dialog.progress" mode="normal" value="0"/> + <hbox pack="end" style="min-width: 2.5em;"> + <label id="dialog.progressText"/> + </hbox> + </row> + </rows> + </grid> +</dialog> diff --git a/toolkit/components/printing/content/printUtils.js b/toolkit/components/printing/content/printUtils.js new file mode 100644 index 000000000..416954188 --- /dev/null +++ b/toolkit/components/printing/content/printUtils.js @@ -0,0 +1,710 @@ +// -*- tab-width: 2; indent-tabs-mode: nil; js-indent-level: 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/. */ + +/** + * PrintUtils is a utility for front-end code to trigger common print + * operations (printing, show print preview, show page settings). + * + * Unfortunately, likely due to inconsistencies in how different operating + * systems do printing natively, our XPCOM-level printing interfaces + * are a bit confusing and the method by which we do something basic + * like printing a page is quite circuitous. + * + * To compound that, we need to support remote browsers, and that means + * kicking off the print jobs in the content process. This means we send + * messages back and forth to that process. browser-content.js contains + * the object that listens and responds to the messages that PrintUtils + * sends. + * + * This also means that <xul:browser>'s that hope to use PrintUtils must have + * their type attribute set to either "content", "content-targetable", or + * "content-primary". + * + * PrintUtils sends messages at different points in its implementation, but + * their documentation is consolidated here for ease-of-access. + * + * + * Messages sent: + * + * Printing:Print + * Kick off a print job for a nsIDOMWindow, passing the outer window ID as + * windowID. + * + * Printing:Preview:Enter + * This message is sent to put content into print preview mode. We pass + * the content window of the browser we're showing the preview of, and + * the target of the message is the browser that we'll be showing the + * preview in. + * + * Printing:Preview:Exit + * This message is sent to take content out of print preview mode. + * + * + * Messages Received + * + * Printing:Preview:Entered + * This message is sent by the content process once it has completed + * putting the content into print preview mode. We must wait for that to + * to complete before switching the chrome UI to print preview mode, + * otherwise we have layout issues. + * + * Printing:Preview:StateChange, Printing:Preview:ProgressChange + * Due to a timing issue resulting in a main-process crash, we have to + * manually open the progress dialog for print preview. The progress + * dialog is opened here in PrintUtils, and then we listen for update + * messages from the child. Bug 1088061 has been filed to investigate + * other solutions. + * + */ + +var gPrintSettingsAreGlobal = false; +var gSavePrintSettings = false; +var gFocusedElement = null; + +var PrintUtils = { + init() { + window.messageManager.addMessageListener("Printing:Error", this); + }, + + get bundle() { + let stringService = Components.classes["@mozilla.org/intl/stringbundle;1"] + .getService(Components.interfaces.nsIStringBundleService); + delete this.bundle; + return this.bundle = stringService.createBundle("chrome://global/locale/printing.properties"); + }, + + /** + * Shows the page setup dialog, and saves any settings changed in + * that dialog if print.save_print_settings is set to true. + * + * @return true on success, false on failure + */ + showPageSetup: function () { + try { + var printSettings = this.getPrintSettings(); + var PRINTPROMPTSVC = Components.classes["@mozilla.org/embedcomp/printingprompt-service;1"] + .getService(Components.interfaces.nsIPrintingPromptService); + PRINTPROMPTSVC.showPageSetup(window, printSettings, null); + if (gSavePrintSettings) { + // Page Setup data is a "native" setting on the Mac + var PSSVC = Components.classes["@mozilla.org/gfx/printsettings-service;1"] + .getService(Components.interfaces.nsIPrintSettingsService); + PSSVC.savePrintSettingsToPrefs(printSettings, true, printSettings.kInitSaveNativeData); + } + } catch (e) { + dump("showPageSetup "+e+"\n"); + return false; + } + return true; + }, + + /** + * Starts the process of printing the contents of a window. + * + * @param aWindowID + * The outer window ID of the nsIDOMWindow to print. + * @param aBrowser + * The <xul:browser> that the nsIDOMWindow for aWindowID belongs to. + */ + printWindow: function (aWindowID, aBrowser) + { + let mm = aBrowser.messageManager; + mm.sendAsyncMessage("Printing:Print", { + windowID: aWindowID, + simplifiedMode: this._shouldSimplify, + }); + }, + + /** + * Deprecated. + * + * Starts the process of printing the contents of window.content. + * + */ + print: function () + { + if (gBrowser) { + return this.printWindow(gBrowser.selectedBrowser.outerWindowID, + gBrowser.selectedBrowser); + } + + if (this.usingRemoteTabs) { + throw new Error("PrintUtils.print cannot be run in windows running with " + + "remote tabs. Use PrintUtils.printWindow instead."); + } + + let domWindow = window.content; + let ifReq = domWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor); + let browser = ifReq.getInterface(Components.interfaces.nsIWebNavigation) + .QueryInterface(Components.interfaces.nsIDocShell) + .chromeEventHandler; + if (!browser) { + throw new Error("PrintUtils.print could not resolve content window " + + "to a browser."); + } + + let windowID = ifReq.getInterface(Components.interfaces.nsIDOMWindowUtils) + .outerWindowID; + + let Deprecated = Components.utils.import("resource://gre/modules/Deprecated.jsm", {}).Deprecated; + let msg = "PrintUtils.print is now deprecated. Please use PrintUtils.printWindow."; + let url = "https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL/Printing"; + Deprecated.warning(msg, url); + + this.printWindow(windowID, browser); + return undefined; + }, + + /** + * Initializes print preview. + * + * @param aListenerObj + * An object that defines the following functions: + * + * getPrintPreviewBrowser: + * Returns the <xul:browser> to display the print preview in. This + * <xul:browser> must have its type attribute set to "content", + * "content-targetable", or "content-primary". + * + * getSourceBrowser: + * Returns the <xul:browser> that contains the document being + * printed. This <xul:browser> must have its type attribute set to + * "content", "content-targetable", or "content-primary". + * + * getNavToolbox: + * Returns the primary toolbox for this window. + * + * onEnter: + * Called upon entering print preview. + * + * onExit: + * Called upon exiting print preview. + * + * These methods must be defined. printPreview can be called + * with aListenerObj as null iff this window is already displaying + * print preview (in which case, the previous aListenerObj passed + * to it will be used). + */ + printPreview: function (aListenerObj) + { + // if we're already in PP mode, don't set the listener; chances + // are it is null because someone is calling printPreview() to + // get us to refresh the display. + if (!this.inPrintPreview) { + this._listener = aListenerObj; + this._sourceBrowser = aListenerObj.getSourceBrowser(); + this._originalTitle = this._sourceBrowser.contentTitle; + this._originalURL = this._sourceBrowser.currentURI.spec; + + // Here we log telemetry data for when the user enters print preview. + this.logTelemetry("PRINT_PREVIEW_OPENED_COUNT"); + } else { + // collapse the browser here -- it will be shown in + // enterPrintPreview; this forces a reflow which fixes display + // issues in bug 267422. + // We use the print preview browser as the source browser to avoid + // re-initializing print preview with a document that might now have changed. + this._sourceBrowser = this._listener.getPrintPreviewBrowser(); + this._sourceBrowser.collapsed = true; + + // If the user transits too quickly within preview and we have a pending + // progress dialog, we will close it before opening a new one. + this.ensureProgressDialogClosed(); + } + + this._webProgressPP = {}; + let ppParams = {}; + let notifyOnOpen = {}; + let printSettings = this.getPrintSettings(); + // Here we get the PrintingPromptService so we can display the PP Progress from script + // For the browser implemented via XUL with the PP toolbar we cannot let it be + // automatically opened from the print engine because the XUL scrollbars in the PP window + // will layout before the content window and a crash will occur. + // Doing it all from script, means it lays out before hand and we can let printing do its own thing + let PPROMPTSVC = Components.classes["@mozilla.org/embedcomp/printingprompt-service;1"] + .getService(Components.interfaces.nsIPrintingPromptService); + // just in case we are already printing, + // an error code could be returned if the Progress Dialog is already displayed + try { + PPROMPTSVC.showProgress(window, null, printSettings, this._obsPP, false, + this._webProgressPP, ppParams, notifyOnOpen); + if (ppParams.value) { + ppParams.value.docTitle = this._originalTitle; + ppParams.value.docURL = this._originalURL; + } + + // this tells us whether we should continue on with PP or + // wait for the callback via the observer + if (!notifyOnOpen.value.valueOf() || this._webProgressPP.value == null) { + this.enterPrintPreview(); + } + } catch (e) { + this.enterPrintPreview(); + } + }, + + /** + * Returns the nsIWebBrowserPrint associated with some content window. + * This method is being kept here for compatibility reasons, but should not + * be called by code hoping to support e10s / remote browsers. + * + * @param aWindow + * The window from which to get the nsIWebBrowserPrint from. + * @return nsIWebBrowserPrint + */ + getWebBrowserPrint: function (aWindow) + { + let Deprecated = Components.utils.import("resource://gre/modules/Deprecated.jsm", {}).Deprecated; + let text = "getWebBrowserPrint is now deprecated, and fully unsupported for " + + "multi-process browsers. Please use a frame script to get " + + "access to nsIWebBrowserPrint from content."; + let url = "https://developer.mozilla.org/en-US/docs/Printing_from_a_XUL_App"; + Deprecated.warning(text, url); + + if (this.usingRemoteTabs) { + return {}; + } + + var contentWindow = aWindow || window.content; + return contentWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor) + .getInterface(Components.interfaces.nsIWebBrowserPrint); + }, + + /** + * Returns the nsIWebBrowserPrint from the print preview browser's docShell. + * This method is being kept here for compatibility reasons, but should not + * be called by code hoping to support e10s / remote browsers. + * + * @return nsIWebBrowserPrint + */ + getPrintPreview: function() { + let Deprecated = Components.utils.import("resource://gre/modules/Deprecated.jsm", {}).Deprecated; + let text = "getPrintPreview is now deprecated, and fully unsupported for " + + "multi-process browsers. Please use a frame script to get " + + "access to nsIWebBrowserPrint from content."; + let url = "https://developer.mozilla.org/en-US/docs/Printing_from_a_XUL_App"; + Deprecated.warning(text, url); + + if (this.usingRemoteTabs) { + return {}; + } + + return this._listener.getPrintPreviewBrowser().docShell.printPreview; + }, + + get inPrintPreview() { + return document.getElementById("print-preview-toolbar") != null; + }, + + // "private" methods and members. Don't use them. + + _listener: null, + _closeHandlerPP: null, + _webProgressPP: null, + _sourceBrowser: null, + _originalTitle: "", + _originalURL: "", + _shouldSimplify: false, + + get usingRemoteTabs() { + // We memoize this, since it's highly unlikely to change over the lifetime + // of the window. + let usingRemoteTabs = + window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) + .getInterface(Components.interfaces.nsIWebNavigation) + .QueryInterface(Components.interfaces.nsILoadContext) + .useRemoteTabs; + delete this.usingRemoteTabs; + return this.usingRemoteTabs = usingRemoteTabs; + }, + + displayPrintingError(nsresult, isPrinting) { + // The nsresults from a printing error are mapped to strings that have + // similar names to the errors themselves. For example, for error + // NS_ERROR_GFX_PRINTER_NO_PRINTER_AVAILABLE, the name of the string + // for the error message is: PERR_GFX_PRINTER_NO_PRINTER_AVAILABLE. What's + // more, if we're in the process of doing a print preview, it's possible + // that there are strings specific for print preview for these errors - + // if so, the names of those strings have _PP as a suffix. It's possible + // that no print preview specific strings exist, in which case it is fine + // to fall back to the original string name. + const MSG_CODES = [ + "GFX_PRINTER_NO_PRINTER_AVAILABLE", + "GFX_PRINTER_NAME_NOT_FOUND", + "GFX_PRINTER_COULD_NOT_OPEN_FILE", + "GFX_PRINTER_STARTDOC", + "GFX_PRINTER_ENDDOC", + "GFX_PRINTER_STARTPAGE", + "GFX_PRINTER_DOC_IS_BUSY", + "ABORT", + "NOT_AVAILABLE", + "NOT_IMPLEMENTED", + "OUT_OF_MEMORY", + "UNEXPECTED", + ]; + + // PERR_FAILURE is the catch-all error message if we've gotten one that + // we don't recognize. + msgName = "PERR_FAILURE"; + + for (let code of MSG_CODES) { + let nsErrorResult = "NS_ERROR_" + code; + if (Components.results[nsErrorResult] == nsresult) { + msgName = "PERR_" + code; + break; + } + } + + let msg, title; + + if (!isPrinting) { + // Try first with _PP suffix. + let ppMsgName = msgName + "_PP"; + try { + msg = this.bundle.GetStringFromName(ppMsgName); + } catch (e) { + // We allow localizers to not have the print preview error string, + // and just fall back to the printing error string. + } + } + + if (!msg) { + msg = this.bundle.GetStringFromName(msgName); + } + + title = this.bundle.GetStringFromName(isPrinting ? "print_error_dialog_title" + : "printpreview_error_dialog_title"); + + let promptSvc = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] + .getService(Components.interfaces.nsIPromptService); + promptSvc.alert(window, title, msg); + }, + + receiveMessage(aMessage) { + if (aMessage.name == "Printing:Error") { + this.displayPrintingError(aMessage.data.nsresult, + aMessage.data.isPrinting); + return undefined; + } + + // If we got here, then the message we've received must involve + // updating the print progress UI. + if (!this._webProgressPP.value) { + // We somehow didn't get a nsIWebProgressListener to be updated... + // I guess there's nothing to do. + return undefined; + } + + let listener = this._webProgressPP.value; + let mm = aMessage.target.messageManager; + let data = aMessage.data; + + switch (aMessage.name) { + case "Printing:Preview:ProgressChange": { + return listener.onProgressChange(null, null, + data.curSelfProgress, + data.maxSelfProgress, + data.curTotalProgress, + data.maxTotalProgress); + } + + case "Printing:Preview:StateChange": { + if (data.stateFlags & Components.interfaces.nsIWebProgressListener.STATE_STOP) { + // Strangely, the printing engine sends 2 STATE_STOP messages when + // print preview is finishing. One has the STATE_IS_DOCUMENT flag, + // the other has the STATE_IS_NETWORK flag. However, the webProgressPP + // listener stops listening once the first STATE_STOP is sent. + // Any subsequent messages result in NS_ERROR_FAILURE errors getting + // thrown. This should all get torn out once bug 1088061 is fixed. + mm.removeMessageListener("Printing:Preview:StateChange", this); + mm.removeMessageListener("Printing:Preview:ProgressChange", this); + } + + return listener.onStateChange(null, null, + data.stateFlags, + data.status); + } + } + return undefined; + }, + + setPrinterDefaultsForSelectedPrinter: function (aPSSVC, aPrintSettings) + { + if (!aPrintSettings.printerName) + aPrintSettings.printerName = aPSSVC.defaultPrinterName; + + // First get any defaults from the printer + aPSSVC.initPrintSettingsFromPrinter(aPrintSettings.printerName, aPrintSettings); + // now augment them with any values from last time + aPSSVC.initPrintSettingsFromPrefs(aPrintSettings, true, aPrintSettings.kInitSaveAll); + }, + + getPrintSettings: function () + { + var pref = Components.classes["@mozilla.org/preferences-service;1"] + .getService(Components.interfaces.nsIPrefBranch); + if (pref) { + gPrintSettingsAreGlobal = pref.getBoolPref("print.use_global_printsettings", false); + gSavePrintSettings = pref.getBoolPref("print.save_print_settings", false); + } + + var printSettings; + try { + var PSSVC = Components.classes["@mozilla.org/gfx/printsettings-service;1"] + .getService(Components.interfaces.nsIPrintSettingsService); + if (gPrintSettingsAreGlobal) { + printSettings = PSSVC.globalPrintSettings; + this.setPrinterDefaultsForSelectedPrinter(PSSVC, printSettings); + } else { + printSettings = PSSVC.newPrintSettings; + } + } catch (e) { + dump("getPrintSettings: "+e+"\n"); + } + return printSettings; + }, + + // This observer is called once the progress dialog has been "opened" + _obsPP: + { + observe: function(aSubject, aTopic, aData) + { + // delay the print preview to show the content of the progress dialog + setTimeout(function () { PrintUtils.enterPrintPreview(); }, 0); + }, + + QueryInterface : function(iid) + { + if (iid.equals(Components.interfaces.nsIObserver) || + iid.equals(Components.interfaces.nsISupportsWeakReference) || + iid.equals(Components.interfaces.nsISupports)) + return this; + throw Components.results.NS_NOINTERFACE; + } + }, + + setSimplifiedMode: function (shouldSimplify) + { + this._shouldSimplify = shouldSimplify; + }, + + enterPrintPreview: function () + { + // Send a message to the print preview browser to initialize + // print preview. If we happen to have gotten a print preview + // progress listener from nsIPrintingPromptService.showProgress + // in printPreview, we add listeners to feed that progress + // listener. + let ppBrowser = this._listener.getPrintPreviewBrowser(); + let mm = ppBrowser.messageManager; + + let sendEnterPreviewMessage = function (browser, simplified) { + mm.sendAsyncMessage("Printing:Preview:Enter", { + windowID: browser.outerWindowID, + simplifiedMode: simplified, + }); + }; + + // If we happen to have gotten simplify page checked, we will lazily + // instantiate a new tab that parses the original page using ReaderMode + // primitives. When it's ready, and in order to enter on preview, we send + // over a message to print preview browser passing up the simplified tab as + // reference. If not, we pass the original tab instead as content source. + if (this._shouldSimplify) { + let simplifiedBrowser = this._listener.getSimplifiedSourceBrowser(); + if (simplifiedBrowser) { + sendEnterPreviewMessage(simplifiedBrowser, true); + } else { + simplifiedBrowser = this._listener.createSimplifiedBrowser(); + + // After instantiating the simplified tab, we attach a listener as + // callback. Once we discover reader mode has been loaded, we fire + // up a message to enter on print preview. + let spMM = simplifiedBrowser.messageManager; + spMM.addMessageListener("Printing:Preview:ReaderModeReady", function onReaderReady() { + spMM.removeMessageListener("Printing:Preview:ReaderModeReady", onReaderReady); + sendEnterPreviewMessage(simplifiedBrowser, true); + }); + + // Here, we send down a message to simplified browser in order to parse + // the original page. After we have parsed it, content will tell parent + // that the document is ready for print previewing. + spMM.sendAsyncMessage("Printing:Preview:ParseDocument", { + URL: this._originalURL, + windowID: this._sourceBrowser.outerWindowID, + }); + + // Here we log telemetry data for when the user enters simplify mode. + this.logTelemetry("PRINT_PREVIEW_SIMPLIFY_PAGE_OPENED_COUNT"); + } + } else { + sendEnterPreviewMessage(this._sourceBrowser, false); + } + + if (this._webProgressPP.value) { + mm.addMessageListener("Printing:Preview:StateChange", this); + mm.addMessageListener("Printing:Preview:ProgressChange", this); + } + + let onEntered = (message) => { + mm.removeMessageListener("Printing:Preview:Entered", onEntered); + + if (message.data.failed) { + // Something went wrong while putting the document into print preview + // mode. Bail out. + this._listener.onEnter(); + this._listener.onExit(); + return; + } + + // Stash the focused element so that we can return to it after exiting + // print preview. + gFocusedElement = document.commandDispatcher.focusedElement; + + let printPreviewTB = document.getElementById("print-preview-toolbar"); + if (printPreviewTB) { + printPreviewTB.updateToolbar(); + ppBrowser.collapsed = false; + ppBrowser.focus(); + return; + } + + // Set the original window as an active window so any mozPrintCallbacks can + // run without delayed setTimeouts. + if (this._listener.activateBrowser) { + this._listener.activateBrowser(this._sourceBrowser); + } else { + this._sourceBrowser.docShellIsActive = true; + } + + // show the toolbar after we go into print preview mode so + // that we can initialize the toolbar with total num pages + const XUL_NS = + "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; + printPreviewTB = document.createElementNS(XUL_NS, "toolbar"); + printPreviewTB.setAttribute("printpreview", true); + printPreviewTB.setAttribute("fullscreentoolbar", true); + printPreviewTB.id = "print-preview-toolbar"; + + let navToolbox = this._listener.getNavToolbox(); + navToolbox.parentNode.insertBefore(printPreviewTB, navToolbox); + printPreviewTB.initialize(ppBrowser); + + // Enable simplify page checkbox when the page is an article + if (this._sourceBrowser.isArticle) { + printPreviewTB.enableSimplifyPage(); + } else { + this.logTelemetry("PRINT_PREVIEW_SIMPLIFY_PAGE_UNAVAILABLE_COUNT"); + printPreviewTB.disableSimplifyPage(); + } + + // copy the window close handler + if (document.documentElement.hasAttribute("onclose")) + this._closeHandlerPP = document.documentElement.getAttribute("onclose"); + else + this._closeHandlerPP = null; + document.documentElement.setAttribute("onclose", "PrintUtils.exitPrintPreview(); return false;"); + + // disable chrome shortcuts... + window.addEventListener("keydown", this.onKeyDownPP, true); + window.addEventListener("keypress", this.onKeyPressPP, true); + + ppBrowser.collapsed = false; + ppBrowser.focus(); + // on Enter PP Call back + this._listener.onEnter(); + }; + + mm.addMessageListener("Printing:Preview:Entered", onEntered); + }, + + exitPrintPreview: function () + { + let ppBrowser = this._listener.getPrintPreviewBrowser(); + let browserMM = ppBrowser.messageManager; + browserMM.sendAsyncMessage("Printing:Preview:Exit"); + window.removeEventListener("keydown", this.onKeyDownPP, true); + window.removeEventListener("keypress", this.onKeyPressPP, true); + + // restore the old close handler + document.documentElement.setAttribute("onclose", this._closeHandlerPP); + this._closeHandlerPP = null; + + // remove the print preview toolbar + let printPreviewTB = document.getElementById("print-preview-toolbar"); + this._listener.getNavToolbox().parentNode.removeChild(printPreviewTB); + + let fm = Components.classes["@mozilla.org/focus-manager;1"] + .getService(Components.interfaces.nsIFocusManager); + if (gFocusedElement) + fm.setFocus(gFocusedElement, fm.FLAG_NOSCROLL); + else + this._sourceBrowser.focus(); + gFocusedElement = null; + + this.setSimplifiedMode(false); + + this.ensureProgressDialogClosed(); + + this._listener.onExit(); + }, + + logTelemetry: function (ID) + { + let histogram = Services.telemetry.getHistogramById(ID); + histogram.add(true); + }, + + onKeyDownPP: function (aEvent) + { + // Esc exits the PP + if (aEvent.keyCode == aEvent.DOM_VK_ESCAPE) { + PrintUtils.exitPrintPreview(); + } + }, + + onKeyPressPP: function (aEvent) + { + var closeKey; + try { + closeKey = document.getElementById("key_close") + .getAttribute("key"); + closeKey = aEvent["DOM_VK_"+closeKey]; + } catch (e) {} + var isModif = aEvent.ctrlKey || aEvent.metaKey; + // Ctrl-W exits the PP + if (isModif && + (aEvent.charCode == closeKey || aEvent.charCode == closeKey + 32)) { + PrintUtils.exitPrintPreview(); + } + else if (isModif) { + var printPreviewTB = document.getElementById("print-preview-toolbar"); + var printKey = document.getElementById("printKb").getAttribute("key").toUpperCase(); + var pressedKey = String.fromCharCode(aEvent.charCode).toUpperCase(); + if (printKey == pressedKey) { + printPreviewTB.print(); + } + } + // cancel shortkeys + if (isModif) { + aEvent.preventDefault(); + aEvent.stopPropagation(); + } + }, + + /** + * If there's a printing or print preview progress dialog displayed, force + * it to close now. + */ + ensureProgressDialogClosed() { + if (this._webProgressPP && this._webProgressPP.value) { + this._webProgressPP.value.onStateChange(null, null, + Components.interfaces.nsIWebProgressListener.STATE_STOP, 0); + } + }, +} + +PrintUtils.init(); diff --git a/toolkit/components/printing/content/printdialog.js b/toolkit/components/printing/content/printdialog.js new file mode 100644 index 000000000..e5a38ddce --- /dev/null +++ b/toolkit/components/printing/content/printdialog.js @@ -0,0 +1,425 @@ +// -*- indent-tabs-mode: nil; js-indent-level: 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/. */ + +var dialog; +var printService = null; +var gOriginalNumCopies = 1; + +var paramBlock; +var gPrefs = null; +var gPrintSettings = null; +var gWebBrowserPrint = null; +var gPrintSetInterface = Components.interfaces.nsIPrintSettings; +var doDebug = false; + +// --------------------------------------------------- +function initDialog() +{ + dialog = {}; + + dialog.propertiesButton = document.getElementById("properties"); + dialog.descText = document.getElementById("descText"); + + dialog.printrangeGroup = document.getElementById("printrangeGroup"); + dialog.allpagesRadio = document.getElementById("allpagesRadio"); + dialog.rangeRadio = document.getElementById("rangeRadio"); + dialog.selectionRadio = document.getElementById("selectionRadio"); + dialog.frompageInput = document.getElementById("frompageInput"); + dialog.frompageLabel = document.getElementById("frompageLabel"); + dialog.topageInput = document.getElementById("topageInput"); + dialog.topageLabel = document.getElementById("topageLabel"); + + dialog.numCopiesInput = document.getElementById("numCopiesInput"); + + dialog.printframeGroup = document.getElementById("printframeGroup"); + dialog.aslaidoutRadio = document.getElementById("aslaidoutRadio"); + dialog.selectedframeRadio = document.getElementById("selectedframeRadio"); + dialog.eachframesepRadio = document.getElementById("eachframesepRadio"); + dialog.printframeGroupLabel = document.getElementById("printframeGroupLabel"); + + dialog.fileCheck = document.getElementById("fileCheck"); + dialog.printerLabel = document.getElementById("printerLabel"); + dialog.printerList = document.getElementById("printerList"); + + dialog.printButton = document.documentElement.getButton("accept"); + + // <data> elements + dialog.printName = document.getElementById("printButton"); + dialog.fpDialog = document.getElementById("fpDialog"); + + dialog.enabled = false; +} + +// --------------------------------------------------- +function checkInteger(element) +{ + var value = element.value; + if (value && value.length > 0) { + value = value.replace(/[^0-9]/g, ""); + if (!value) value = ""; + element.value = value; + } + if (!value || value < 1 || value > 999) + dialog.printButton.setAttribute("disabled", "true"); + else + dialog.printButton.removeAttribute("disabled"); +} + +// --------------------------------------------------- +function stripTrailingWhitespace(element) +{ + var value = element.value; + value = value.replace(/\s+$/, ""); + element.value = value; +} + +// --------------------------------------------------- +function getPrinterDescription(printerName) +{ + var s = ""; + + try { + /* This may not work with non-ASCII test (see bug 235763 comment #16) */ + s = gPrefs.getCharPref("print.printer_" + printerName + ".printer_description") + } catch (e) { + } + + return s; +} + +// --------------------------------------------------- +function listElement(aListElement) + { + this.listElement = aListElement; + } + +listElement.prototype = + { + clearList: + function () + { + // remove the menupopup node child of the menulist. + var popup = this.listElement.firstChild; + if (popup) { + this.listElement.removeChild(popup); + } + }, + + appendPrinterNames: + function (aDataObject) + { + if ((null == aDataObject) || !aDataObject.hasMore()) { + // disable dialog + this.listElement.setAttribute("value", ""); + this.listElement.setAttribute("label", + document.getElementById("printingBundle") + .getString("noprinter")); + + this.listElement.setAttribute("disabled", "true"); + dialog.printerLabel.setAttribute("disabled", "true"); + dialog.propertiesButton.setAttribute("disabled", "true"); + dialog.fileCheck.setAttribute("disabled", "true"); + dialog.printButton.setAttribute("disabled", "true"); + } + else { + // build popup menu from printer names + var list = document.getElementById("printerList"); + do { + printerNameStr = aDataObject.getNext(); + list.appendItem(printerNameStr, printerNameStr, getPrinterDescription(printerNameStr)); + } while (aDataObject.hasMore()); + this.listElement.removeAttribute("disabled"); + } + } + }; + +// --------------------------------------------------- +function getPrinters() +{ + var selectElement = new listElement(dialog.printerList); + selectElement.clearList(); + + var printerEnumerator; + try { + printerEnumerator = + Components.classes["@mozilla.org/gfx/printerenumerator;1"] + .getService(Components.interfaces.nsIPrinterEnumerator) + .printerNameList; + } catch (e) { printerEnumerator = null; } + + selectElement.appendPrinterNames(printerEnumerator); + selectElement.listElement.value = printService.defaultPrinterName; + + // make sure we load the prefs for the initially selected printer + setPrinterDefaultsForSelectedPrinter(); +} + + +// --------------------------------------------------- +// update gPrintSettings with the defaults for the selected printer +function setPrinterDefaultsForSelectedPrinter() +{ + gPrintSettings.printerName = dialog.printerList.value; + + dialog.descText.value = getPrinterDescription(gPrintSettings.printerName); + + // First get any defaults from the printer + printService.initPrintSettingsFromPrinter(gPrintSettings.printerName, gPrintSettings); + + // now augment them with any values from last time + printService.initPrintSettingsFromPrefs(gPrintSettings, true, gPrintSetInterface.kInitSaveAll); + + if (doDebug) { + dump("setPrinterDefaultsForSelectedPrinter: printerName='"+gPrintSettings.printerName+"', paperName='"+gPrintSettings.paperName+"'\n"); + } +} + +// --------------------------------------------------- +function displayPropertiesDialog() +{ + gPrintSettings.numCopies = dialog.numCopiesInput.value; + try { + var printingPromptService = Components.classes["@mozilla.org/embedcomp/printingprompt-service;1"] + .getService(Components.interfaces.nsIPrintingPromptService); + if (printingPromptService) { + printingPromptService.showPrinterProperties(null, dialog.printerList.value, gPrintSettings); + dialog.numCopiesInput.value = gPrintSettings.numCopies; + } + } catch (e) { + dump("problems getting printingPromptService\n"); + } +} + +// --------------------------------------------------- +function doPrintRange(inx) +{ + if (inx == 1) { + dialog.frompageInput.removeAttribute("disabled"); + dialog.frompageLabel.removeAttribute("disabled"); + dialog.topageInput.removeAttribute("disabled"); + dialog.topageLabel.removeAttribute("disabled"); + } else { + dialog.frompageInput.setAttribute("disabled", "true"); + dialog.frompageLabel.setAttribute("disabled", "true"); + dialog.topageInput.setAttribute("disabled", "true"); + dialog.topageLabel.setAttribute("disabled", "true"); + } +} + +// --------------------------------------------------- +function loadDialog() +{ + var print_copies = 1; + var print_selection_radio_enabled = false; + var print_frametype = gPrintSetInterface.kSelectedFrame; + var print_howToEnableUI = gPrintSetInterface.kFrameEnableNone; + var print_tofile = ""; + + try { + gPrefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch); + + printService = Components.classes["@mozilla.org/gfx/printsettings-service;1"]; + if (printService) { + printService = printService.getService(); + if (printService) { + printService = printService.QueryInterface(Components.interfaces.nsIPrintSettingsService); + } + } + } catch (e) {} + + // Note: getPrinters sets up the PrintToFile control + getPrinters(); + + if (gPrintSettings) { + print_tofile = gPrintSettings.printToFile; + gOriginalNumCopies = gPrintSettings.numCopies; + + print_copies = gPrintSettings.numCopies; + print_frametype = gPrintSettings.printFrameType; + print_howToEnableUI = gPrintSettings.howToEnableFrameUI; + print_selection_radio_enabled = gPrintSettings.GetPrintOptions(gPrintSetInterface.kEnableSelectionRB); + } + + if (doDebug) { + dump("loadDialog*********************************************\n"); + dump("print_tofile "+print_tofile+"\n"); + dump("print_frame "+print_frametype+"\n"); + dump("print_howToEnableUI "+print_howToEnableUI+"\n"); + dump("selection_radio_enabled "+print_selection_radio_enabled+"\n"); + } + + dialog.printrangeGroup.selectedItem = dialog.allpagesRadio; + if (print_selection_radio_enabled) { + dialog.selectionRadio.removeAttribute("disabled"); + } else { + dialog.selectionRadio.setAttribute("disabled", "true"); + } + doPrintRange(dialog.rangeRadio.selected); + dialog.frompageInput.value = 1; + dialog.topageInput.value = 1; + dialog.numCopiesInput.value = print_copies; + + if (doDebug) { + dump("print_howToEnableUI: "+print_howToEnableUI+"\n"); + } + + // print frame + if (print_howToEnableUI == gPrintSetInterface.kFrameEnableAll) { + dialog.aslaidoutRadio.removeAttribute("disabled"); + + dialog.selectedframeRadio.removeAttribute("disabled"); + dialog.eachframesepRadio.removeAttribute("disabled"); + dialog.printframeGroupLabel.removeAttribute("disabled"); + + // initialize radio group + dialog.printframeGroup.selectedItem = dialog.selectedframeRadio; + + } else if (print_howToEnableUI == gPrintSetInterface.kFrameEnableAsIsAndEach) { + dialog.aslaidoutRadio.removeAttribute("disabled"); // enable + + dialog.selectedframeRadio.setAttribute("disabled", "true"); // disable + dialog.eachframesepRadio.removeAttribute("disabled"); // enable + dialog.printframeGroupLabel.removeAttribute("disabled"); // enable + + // initialize + dialog.printframeGroup.selectedItem = dialog.eachframesepRadio; + + } else { + dialog.aslaidoutRadio.setAttribute("disabled", "true"); + dialog.selectedframeRadio.setAttribute("disabled", "true"); + dialog.eachframesepRadio.setAttribute("disabled", "true"); + dialog.printframeGroupLabel.setAttribute("disabled", "true"); + } + + dialog.printButton.label = dialog.printName.getAttribute("label"); +} + +// --------------------------------------------------- +function onLoad() +{ + // Init dialog. + initDialog(); + + // param[0]: nsIPrintSettings object + // param[1]: container for return value (1 = print, 0 = cancel) + + gPrintSettings = window.arguments[0].QueryInterface(gPrintSetInterface); + gWebBrowserPrint = window.arguments[1].QueryInterface(Components.interfaces.nsIWebBrowserPrint); + paramBlock = window.arguments[2].QueryInterface(Components.interfaces.nsIDialogParamBlock); + + // default return value is "cancel" + paramBlock.SetInt(0, 0); + + loadDialog(); +} + +// --------------------------------------------------- +function onAccept() +{ + if (gPrintSettings != null) { + var print_howToEnableUI = gPrintSetInterface.kFrameEnableNone; + + // save these out so they can be picked up by the device spec + gPrintSettings.printerName = dialog.printerList.value; + print_howToEnableUI = gPrintSettings.howToEnableFrameUI; + gPrintSettings.printToFile = dialog.fileCheck.checked; + + if (gPrintSettings.printToFile && !chooseFile()) + return false; + + if (dialog.allpagesRadio.selected) { + gPrintSettings.printRange = gPrintSetInterface.kRangeAllPages; + } else if (dialog.rangeRadio.selected) { + gPrintSettings.printRange = gPrintSetInterface.kRangeSpecifiedPageRange; + } else if (dialog.selectionRadio.selected) { + gPrintSettings.printRange = gPrintSetInterface.kRangeSelection; + } + gPrintSettings.startPageRange = dialog.frompageInput.value; + gPrintSettings.endPageRange = dialog.topageInput.value; + gPrintSettings.numCopies = dialog.numCopiesInput.value; + + var frametype = gPrintSetInterface.kNoFrames; + if (print_howToEnableUI != gPrintSetInterface.kFrameEnableNone) { + if (dialog.aslaidoutRadio.selected) { + frametype = gPrintSetInterface.kFramesAsIs; + } else if (dialog.selectedframeRadio.selected) { + frametype = gPrintSetInterface.kSelectedFrame; + } else if (dialog.eachframesepRadio.selected) { + frametype = gPrintSetInterface.kEachFrameSep; + } else { + frametype = gPrintSetInterface.kSelectedFrame; + } + } + gPrintSettings.printFrameType = frametype; + if (doDebug) { + dump("onAccept*********************************************\n"); + dump("frametype "+frametype+"\n"); + dump("numCopies "+gPrintSettings.numCopies+"\n"); + dump("printRange "+gPrintSettings.printRange+"\n"); + dump("printerName "+gPrintSettings.printerName+"\n"); + dump("startPageRange "+gPrintSettings.startPageRange+"\n"); + dump("endPageRange "+gPrintSettings.endPageRange+"\n"); + dump("printToFile "+gPrintSettings.printToFile+"\n"); + } + } + + var saveToPrefs = false; + + saveToPrefs = gPrefs.getBoolPref("print.save_print_settings"); + + if (saveToPrefs && printService != null) { + var flags = gPrintSetInterface.kInitSavePaperSize | + gPrintSetInterface.kInitSaveEdges | + gPrintSetInterface.kInitSaveInColor | + gPrintSetInterface.kInitSaveShrinkToFit | + gPrintSetInterface.kInitSaveScaling; + printService.savePrintSettingsToPrefs(gPrintSettings, true, flags); + } + + // set return value to "print" + if (paramBlock) { + paramBlock.SetInt(0, 1); + } else { + dump("*** FATAL ERROR: No paramBlock\n"); + } + + return true; +} + +// --------------------------------------------------- +function onCancel() +{ + // set return value to "cancel" + if (paramBlock) { + paramBlock.SetInt(0, 0); + } else { + dump("*** FATAL ERROR: No paramBlock\n"); + } + + return true; +} + +// --------------------------------------------------- +const nsIFilePicker = Components.interfaces.nsIFilePicker; +function chooseFile() +{ + try { + var fp = Components.classes["@mozilla.org/filepicker;1"] + .createInstance(nsIFilePicker); + fp.init(window, dialog.fpDialog.getAttribute("label"), nsIFilePicker.modeSave); + fp.appendFilters(nsIFilePicker.filterAll); + if (fp.show() != Components.interfaces.nsIFilePicker.returnCancel && + fp.file && fp.file.path) { + gPrintSettings.toFileName = fp.file.path; + return true; + } + } catch (ex) { + dump(ex); + } + + return false; +} + diff --git a/toolkit/components/printing/content/printdialog.xul b/toolkit/components/printing/content/printdialog.xul new file mode 100644 index 000000000..d6452945b --- /dev/null +++ b/toolkit/components/printing/content/printdialog.xul @@ -0,0 +1,126 @@ +<?xml version="1.0"?> + +<!-- 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/. --> + +<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> +<!DOCTYPE dialog SYSTEM "chrome://global/locale/printdialog.dtd"> + +<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + onload="onLoad();" + ondialogaccept="return onAccept();" + oncancel="return onCancel();" + buttoniconaccept="print" + title="&printDialog.title;" + persist="screenX screenY" + screenX="24" screenY="24"> + + <script type="application/javascript" src="chrome://global/content/printdialog.js"/> + + <stringbundle id="printingBundle" src="chrome://global/locale/printing.properties"/> + + <groupbox> + <caption label="&printer.label;"/> + + <grid> + <columns> + <column/> + <column flex="1"/> + <column/> + </columns> + + <rows> + <row align="center"> + <hbox align="center" pack="end"> + <label id="printerLabel" + value="&printerInput.label;" + accesskey="&printerInput.accesskey;" + control="printerList"/> + </hbox> + <menulist id="printerList" flex="1" type="description" oncommand="setPrinterDefaultsForSelectedPrinter();"/> + <button id="properties" + label="&propertiesButton.label;" + accesskey="&propertiesButton.accesskey;" + icon="properties" + oncommand="displayPropertiesDialog();"/> + </row> + <row align="center"> + <hbox align="center" pack="end"> + <label id="descTextLabel" control="descText" value="&descText.label;"/> + </hbox> + <label id="descText"/> + <checkbox id="fileCheck" + checked="false" + label="&fileCheck.label;" + accesskey="&fileCheck.accesskey;" + pack="end"/> + </row> + </rows> + </grid> + </groupbox> + + <hbox> + <groupbox flex="1"> + <caption label="&printrangeGroup.label;"/> + + <radiogroup id="printrangeGroup"> + <radio id="allpagesRadio" + label="&allpagesRadio.label;" + accesskey="&allpagesRadio.accesskey;" + oncommand="doPrintRange(0)"/> + <hbox align="center"> + <radio id="rangeRadio" + label="&rangeRadio.label;" + accesskey="&rangeRadio.accesskey;" + oncommand="doPrintRange(1)"/> + <label id="frompageLabel" + control="frompageInput" + value="&frompageInput.label;" + accesskey="&frompageInput.accesskey;"/> + <textbox id="frompageInput" style="width:5em;" onkeyup="checkInteger(this)"/> + <label id="topageLabel" + control="topageInput" + value="&topageInput.label;" + accesskey="&topageInput.accesskey;"/> + <textbox id="topageInput" style="width:5em;" onkeyup="checkInteger(this)"/> + </hbox> + <radio id="selectionRadio" + label="&selectionRadio.label;" + accesskey="&selectionRadio.accesskey;" + oncommand="doPrintRange(2)"/> + </radiogroup> + </groupbox> + + <groupbox flex="1"> + <caption label="&copies.label;"/> + <hbox align="center"> + <label control="numCopiesInput" + value="&numCopies.label;" + accesskey="&numCopies.accesskey;"/> + <textbox id="numCopiesInput" style="width:5em;" onkeyup="checkInteger(this)"/> + </hbox> + </groupbox> + </hbox> + + <groupbox flex="1"> + <caption label="&printframeGroup.label;" id="printframeGroupLabel"/> + <radiogroup id="printframeGroup"> + <radio id="aslaidoutRadio" + label="&aslaidoutRadio.label;" + accesskey="&aslaidoutRadio.accesskey;"/> + <radio id="selectedframeRadio" + label="&selectedframeRadio.label;" + accesskey="&selectedframeRadio.accesskey;"/> + <radio id="eachframesepRadio" + label="&eachframesepRadio.label;" + accesskey="&eachframesepRadio.accesskey;"/> + </radiogroup> + </groupbox> + + <!-- used to store titles and labels --> + <data style="display:none;" id="printButton" label="&printButton.label;"/> + <data style="display:none;" id="fpDialog" label="&fpDialog.title;"/> + +</dialog> + diff --git a/toolkit/components/printing/content/printjoboptions.js b/toolkit/components/printing/content/printjoboptions.js new file mode 100644 index 000000000..26f3ffd85 --- /dev/null +++ b/toolkit/components/printing/content/printjoboptions.js @@ -0,0 +1,401 @@ +// -*- indent-tabs-mode: nil; js-indent-level: 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/. */ + +var dialog; +var gPrintBundle; +var gPrintSettings = null; +var gPrintSettingsInterface = Components.interfaces.nsIPrintSettings; +var gPaperArray; +var gPrefs; + +var gPrintSetInterface = Components.interfaces.nsIPrintSettings; +var doDebug = true; + +// --------------------------------------------------- +function checkDouble(element, maxVal) +{ + var value = element.value; + if (value && value.length > 0) { + value = value.replace(/[^\.|^0-9]/g, ""); + if (!value) { + element.value = ""; + } else if (value > maxVal) { + element.value = maxVal; + } else { + element.value = value; + } + } +} + +// --------------------------------------------------- +function isListOfPrinterFeaturesAvailable() +{ + var has_printerfeatures = false; + + try { + has_printerfeatures = gPrefs.getBoolPref("print.tmp.printerfeatures." + gPrintSettings.printerName + ".has_special_printerfeatures"); + } catch (ex) { + } + + return has_printerfeatures; +} + +// --------------------------------------------------- +function getDoubleStr(val, dec) +{ + var str = val.toString(); + var inx = str.indexOf("."); + return str.substring(0, inx+dec+1); +} + +// --------------------------------------------------- +function initDialog() +{ + gPrintBundle = document.getElementById("printBundle"); + + dialog = {}; + + dialog.paperList = document.getElementById("paperList"); + dialog.paperGroup = document.getElementById("paperGroup"); + + dialog.jobTitleLabel = document.getElementById("jobTitleLabel"); + dialog.jobTitleGroup = document.getElementById("jobTitleGroup"); + dialog.jobTitleInput = document.getElementById("jobTitleInput"); + + dialog.colorGroup = document.getElementById("colorGroup"); + dialog.colorRadioGroup = document.getElementById("colorRadioGroup"); + dialog.colorRadio = document.getElementById("colorRadio"); + dialog.grayRadio = document.getElementById("grayRadio"); + + dialog.topInput = document.getElementById("topInput"); + dialog.bottomInput = document.getElementById("bottomInput"); + dialog.leftInput = document.getElementById("leftInput"); + dialog.rightInput = document.getElementById("rightInput"); +} + +// --------------------------------------------------- +function round10(val) +{ + return Math.round(val * 10) / 10; +} + + +// --------------------------------------------------- +function paperListElement(aPaperListElement) + { + this.paperListElement = aPaperListElement; + } + +paperListElement.prototype = + { + clearPaperList: + function () + { + // remove the menupopup node child of the menulist. + this.paperListElement.removeChild(this.paperListElement.firstChild); + }, + + appendPaperNames: + function (aDataObject) + { + var popupNode = document.createElement("menupopup"); + for (var i=0;i<aDataObject.length;i++) { + var paperObj = aDataObject[i]; + var itemNode = document.createElement("menuitem"); + var label; + try { + label = gPrintBundle.getString(paperObj.name); + } + catch (e) { + /* No name in string bundle ? Then build one manually (this + * usually happens when gPaperArray was build by createPaperArrayFromPrinterFeatures() ...) */ + if (paperObj.inches) { + label = paperObj.name + " (" + round10(paperObj.width) + "x" + round10(paperObj.height) + " inch)"; + } + else { + label = paperObj.name + " (" + paperObj.width + "x" + paperObj.height + " mm)"; + } + } + itemNode.setAttribute("label", label); + itemNode.setAttribute("value", i); + popupNode.appendChild(itemNode); + } + this.paperListElement.appendChild(popupNode); + } + }; + +// --------------------------------------------------- +function createPaperArrayFromDefaults() +{ + var paperNames = ["letterSize", "legalSize", "exectiveSize", "a5Size", "a4Size", "a3Size", "a2Size", "a1Size", "a0Size"]; + // var paperNames = ["&letterRadio.label;", "&legalRadio.label;", "&exectiveRadio.label;", "&a4Radio.label;", "&a3Radio.label;"]; + var paperWidths = [ 8.5, 8.5, 7.25, 148.0, 210.0, 287.0, 420.0, 594.0, 841.0]; + var paperHeights = [11.0, 14.0, 10.50, 210.0, 297.0, 420.0, 594.0, 841.0, 1189.0]; + var paperInches = [true, true, true, false, false, false, false, false, false]; + + gPaperArray = new Array(); + + for (var i=0;i<paperNames.length;i++) { + var obj = {}; + obj.name = paperNames[i]; + obj.width = paperWidths[i]; + obj.height = paperHeights[i]; + obj.inches = paperInches[i]; + + /* Calculate the width/height in millimeters */ + if (paperInches[i]) { + obj.width_mm = paperWidths[i] * 25.4; + obj.height_mm = paperHeights[i] * 25.4; + } + else { + obj.width_mm = paperWidths[i]; + obj.height_mm = paperHeights[i]; + } + gPaperArray[i] = obj; + } +} + +// --------------------------------------------------- +function createPaperArrayFromPrinterFeatures() +{ + var printername = gPrintSettings.printerName; + if (doDebug) { + dump("createPaperArrayFromPrinterFeatures for " + printername + ".\n"); + } + + gPaperArray = new Array(); + + var numPapers = gPrefs.getIntPref("print.tmp.printerfeatures." + printername + ".paper.count"); + + if (doDebug) { + dump("processing " + numPapers + " entries...\n"); + } + + for (var i=0;i<numPapers;i++) { + var obj = {}; + obj.name = gPrefs.getCharPref("print.tmp.printerfeatures." + printername + ".paper." + i + ".name"); + obj.width_mm = gPrefs.getIntPref("print.tmp.printerfeatures." + printername + ".paper." + i + ".width_mm"); + obj.height_mm = gPrefs.getIntPref("print.tmp.printerfeatures." + printername + ".paper." + i + ".height_mm"); + obj.inches = gPrefs.getBoolPref("print.tmp.printerfeatures." + printername + ".paper." + i + ".is_inch"); + + /* Calculate the width/height in paper's native units (either inches or millimeters) */ + if (obj.inches) { + obj.width = obj.width_mm / 25.4; + obj.height = obj.height_mm / 25.4; + } + else { + obj.width = obj.width_mm; + obj.height = obj.height_mm; + } + + gPaperArray[i] = obj; + + if (doDebug) { + dump("paper index=" + i + ", name=" + obj.name + ", width=" + obj.width + ", height=" + obj.height + ".\n"); + } + } +} + +// --------------------------------------------------- +function createPaperArray() +{ + if (isListOfPrinterFeaturesAvailable()) { + createPaperArrayFromPrinterFeatures(); + } + else { + createPaperArrayFromDefaults(); + } +} + +// --------------------------------------------------- +function createPaperSizeList(selectedInx) +{ + var selectElement = new paperListElement(dialog.paperList); + selectElement.clearPaperList(); + + selectElement.appendPaperNames(gPaperArray); + + if (selectedInx > -1) { + selectElement.paperListElement.selectedIndex = selectedInx; + } + + // dialog.paperList = selectElement; +} + +// --------------------------------------------------- +function loadDialog() +{ + var print_paper_unit = 0; + var print_paper_width = 0.0; + var print_paper_height = 0.0; + var print_paper_name = ""; + var print_color = true; + var print_jobtitle = ""; + + gPrefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch); + + if (gPrintSettings) { + print_paper_unit = gPrintSettings.paperSizeUnit; + print_paper_width = gPrintSettings.paperWidth; + print_paper_height = gPrintSettings.paperHeight; + print_paper_name = gPrintSettings.paperName; + print_color = gPrintSettings.printInColor; + print_jobtitle = gPrintSettings.title; + } + + if (doDebug) { + dump("loadDialog******************************\n"); + dump("paperSizeType "+print_paper_unit+"\n"); + dump("paperWidth "+print_paper_width+"\n"); + dump("paperHeight "+print_paper_height+"\n"); + dump("paperName "+print_paper_name+"\n"); + dump("print_color "+print_color+"\n"); + dump("print_jobtitle "+print_jobtitle+"\n"); + } + + createPaperArray(); + + var paperSelectedInx = 0; + for (var i=0;i<gPaperArray.length;i++) { + if (print_paper_name == gPaperArray[i].name) { + paperSelectedInx = i; + break; + } + } + + if (doDebug) { + if (i == gPaperArray.length) + dump("loadDialog: No paper found.\n"); + else + dump("loadDialog: found paper '"+gPaperArray[paperSelectedInx].name+"'.\n"); + } + + createPaperSizeList(paperSelectedInx); + + /* Enable/disable and/or hide/unhide widgets based in the information + * whether the selected printer and/or print module supports the matching + * feature or not */ + if (isListOfPrinterFeaturesAvailable()) { + // job title + if (gPrefs.getBoolPref("print.tmp.printerfeatures." + gPrintSettings.printerName + ".can_change_jobtitle")) + dialog.jobTitleInput.removeAttribute("disabled"); + else + dialog.jobTitleInput.setAttribute("disabled", "true"); + if (gPrefs.getBoolPref("print.tmp.printerfeatures." + gPrintSettings.printerName + ".supports_jobtitle_change")) + dialog.jobTitleGroup.removeAttribute("hidden"); + else + dialog.jobTitleGroup.setAttribute("hidden", "true"); + + // paper size + if (gPrefs.getBoolPref("print.tmp.printerfeatures." + gPrintSettings.printerName + ".can_change_paper_size")) + dialog.paperList.removeAttribute("disabled"); + else + dialog.paperList.setAttribute("disabled", "true"); + if (gPrefs.getBoolPref("print.tmp.printerfeatures." + gPrintSettings.printerName + ".supports_paper_size_change")) + dialog.paperGroup.removeAttribute("hidden"); + else + dialog.paperGroup.setAttribute("hidden", "true"); + + // color/grayscale radio + if (gPrefs.getBoolPref("print.tmp.printerfeatures." + gPrintSettings.printerName + ".can_change_printincolor")) + dialog.colorRadioGroup.removeAttribute("disabled"); + else + dialog.colorRadioGroup.setAttribute("disabled", "true"); + if (gPrefs.getBoolPref("print.tmp.printerfeatures." + gPrintSettings.printerName + ".supports_printincolor_change")) + dialog.colorGroup.removeAttribute("hidden"); + else + dialog.colorGroup.setAttribute("hidden", "true"); + } + + if (print_color) { + dialog.colorRadioGroup.selectedItem = dialog.colorRadio; + } else { + dialog.colorRadioGroup.selectedItem = dialog.grayRadio; + } + + dialog.jobTitleInput.value = print_jobtitle; + + dialog.topInput.value = gPrintSettings.edgeTop.toFixed(2); + dialog.bottomInput.value = gPrintSettings.edgeBottom.toFixed(2); + dialog.leftInput.value = gPrintSettings.edgeLeft.toFixed(2); + dialog.rightInput.value = gPrintSettings.edgeRight.toFixed(2); +} + +// --------------------------------------------------- +function onLoad() +{ + // Init dialog. + initDialog(); + + gPrintSettings = window.arguments[0].QueryInterface(gPrintSetInterface); + paramBlock = window.arguments[1].QueryInterface(Components.interfaces.nsIDialogParamBlock); + + if (doDebug) { + if (gPrintSettings == null) alert("PrintSettings is null!"); + if (paramBlock == null) alert("nsIDialogParam is null!"); + } + + // default return value is "cancel" + paramBlock.SetInt(0, 0); + + loadDialog(); +} + +// --------------------------------------------------- +function onAccept() +{ + var print_paper_unit = gPrintSettingsInterface.kPaperSizeInches; + var print_paper_width = 0.0; + var print_paper_height = 0.0; + var print_paper_name = ""; + + if (gPrintSettings != null) { + var paperSelectedInx = dialog.paperList.selectedIndex; + if (gPaperArray[paperSelectedInx].inches) { + print_paper_unit = gPrintSettingsInterface.kPaperSizeInches; + } else { + print_paper_unit = gPrintSettingsInterface.kPaperSizeMillimeters; + } + print_paper_width = gPaperArray[paperSelectedInx].width; + print_paper_height = gPaperArray[paperSelectedInx].height; + print_paper_name = gPaperArray[paperSelectedInx].name; + + gPrintSettings.paperSizeUnit = print_paper_unit; + gPrintSettings.paperWidth = print_paper_width; + gPrintSettings.paperHeight = print_paper_height; + gPrintSettings.paperName = print_paper_name; + + // save these out so they can be picked up by the device spec + gPrintSettings.printInColor = dialog.colorRadio.selected; + gPrintSettings.title = dialog.jobTitleInput.value; + + gPrintSettings.edgeTop = dialog.topInput.value; + gPrintSettings.edgeBottom = dialog.bottomInput.value; + gPrintSettings.edgeLeft = dialog.leftInput.value; + gPrintSettings.edgeRight = dialog.rightInput.value; + + if (doDebug) { + dump("onAccept******************************\n"); + dump("paperSizeUnit "+print_paper_unit+"\n"); + dump("paperWidth "+print_paper_width+"\n"); + dump("paperHeight "+print_paper_height+"\n"); + dump("paperName '"+print_paper_name+"'\n"); + + dump("printInColor "+gPrintSettings.printInColor+"\n"); + } + } else { + dump("************ onAccept gPrintSettings: "+gPrintSettings+"\n"); + } + + if (paramBlock) { + // set return value to "ok" + paramBlock.SetInt(0, 1); + } else { + dump("*** FATAL ERROR: paramBlock missing\n"); + } + + return true; +} diff --git a/toolkit/components/printing/content/printjoboptions.xul b/toolkit/components/printing/content/printjoboptions.xul new file mode 100644 index 000000000..5726dfe3f --- /dev/null +++ b/toolkit/components/printing/content/printjoboptions.xul @@ -0,0 +1,110 @@ +<?xml version="1.0"?> + +<!-- 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/. --> + +<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> +<!DOCTYPE dialog SYSTEM "chrome://global/locale/printjoboptions.dtd"> + +<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + onload="onLoad();" + ondialogaccept="return onAccept();" + title="&printJobOptions.title;" + persist="screenX screenY" + screenX="24" screenY="24"> + + <script type="application/javascript" src="chrome://global/content/printjoboptions.js"/> + + <stringbundle id="printBundle" src="chrome://global/locale/printPageSetup.properties"/> + + <grid> + <columns> + <column/> + <column flex="1"/> + </columns> + + <rows> + <row id="jobTitleGroup"> + <hbox align="center" pack="end"> + <label id="jobTitleLabel" + value="&jobTitleInput.label;" + accesskey="&jobTitleInput.accesskey;" + control="jobTitleInput"/> + </hbox> + <textbox id="jobTitleInput" flex="1"/> + </row> + + <row id="paperGroup"> + <hbox align="center" pack="end"> + <label id="paperLabel" + value="&paperInput.label;" + accesskey="&paperInput.accesskey;" + control="paperList"/> + </hbox> + <menulist id="paperList" flex="1"> + <menupopup/> + </menulist> + </row> + + <row id="colorGroup"> + <hbox align="center" pack="end"> + <label control="colorRadioGroup" value="&colorGroup.label;"/> + </hbox> + <radiogroup id="colorRadioGroup" orient="horizontal"> + <radio id="grayRadio" + label="&grayRadio.label;" + accesskey="&grayRadio.accesskey;"/> + <radio id="colorRadio" + label="&colorRadio.label;" + accesskey="&colorRadio.accesskey;"/> + </radiogroup> + </row> + </rows> + </grid> + + <grid> + <columns> + <column/> + </columns> + <rows> + <row> + <groupbox flex="1"> + <caption label="&edgeMarginInput.label;"/> + <hbox> + <hbox align="center"> + <label id="topLabel" + value="&topInput.label;" + accesskey="&topInput.accesskey;" + control="topInput"/> + <textbox id="topInput" style="width:5em;" onkeyup="checkDouble(this, 0.5)"/> + </hbox> + <hbox align="center"> + <label id="bottomLabel" + value="&bottomInput.label;" + accesskey="&bottomInput.accesskey;" + control="bottomInput"/> + <textbox id="bottomInput" style="width:5em;" onkeyup="checkDouble(this, 0.5)"/> + </hbox> + <hbox align="center"> + <label id="leftLabel" + value="&leftInput.label;" + accesskey="&leftInput.accesskey;" + control="leftInput"/> + <textbox id="leftInput" style="width:5em;" onkeyup="checkDouble(this, 0.5)"/> + </hbox> + <hbox align="center"> + <label id="rightLabel" + value="&rightInput.label;" + accesskey="&rightInput.accesskey;" + control="rightInput"/> + <textbox id="rightInput" style="width:5em;" onkeyup="checkDouble(this, 0.5)"/> + </hbox> + </hbox> + </groupbox> + </row> + + </rows> + </grid> + +</dialog> diff --git a/toolkit/components/printing/content/simplifyMode.css b/toolkit/components/printing/content/simplifyMode.css new file mode 100644 index 000000000..2a8706c75 --- /dev/null +++ b/toolkit/components/printing/content/simplifyMode.css @@ -0,0 +1,22 @@ +/* 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/. */ + +/* This file defines specific rules for print preview when using simplify mode. + * These rules already exist on aboutReaderControls.css, however, we decoupled it + * from the original file so we don't need to load a bunch of extra queries that + * will not take effect when using the simplify page checkbox. This file defines + * styling for title and author on the header element. */ + +.header > h1 { + font-size: 1.6em; + line-height: 1.25em; + margin: 30px 0; +} + +.header > .credits { + font-size: 0.9em; + line-height: 1.48em; + margin: 0 0 30px 0; + font-style: italic; +}
\ No newline at end of file |