|
|
|
@ -0,0 +1,832 @@
|
|
|
|
|
/* 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/. */
|
|
|
|
|
|
|
|
|
|
const gToolbarInfoSeparators = ["|", "-"]; |
|
|
|
|
|
|
|
|
|
var gToolboxDocument = null; |
|
|
|
|
var gToolbox = null; |
|
|
|
|
var gCurrentDragOverItem = null; |
|
|
|
|
var gToolboxChanged = false; |
|
|
|
|
var gToolboxSheet = false; |
|
|
|
|
var gPaletteBox = null; |
|
|
|
|
|
|
|
|
|
Components.utils.import("resource://gre/modules/Services.jsm"); |
|
|
|
|
|
|
|
|
|
function onLoad() |
|
|
|
|
{ |
|
|
|
|
if ("arguments" in window && window.arguments[0]) { |
|
|
|
|
InitWithToolbox(window.arguments[0]); |
|
|
|
|
repositionDialog(window); |
|
|
|
|
} |
|
|
|
|
else if (window.frameElement && |
|
|
|
|
"toolbox" in window.frameElement) { |
|
|
|
|
gToolboxSheet = true; |
|
|
|
|
InitWithToolbox(window.frameElement.toolbox); |
|
|
|
|
repositionDialog(window.frameElement.panel); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function InitWithToolbox(aToolbox) |
|
|
|
|
{ |
|
|
|
|
gToolbox = aToolbox; |
|
|
|
|
dispatchCustomizationEvent("beforecustomization"); |
|
|
|
|
gToolboxDocument = gToolbox.ownerDocument; |
|
|
|
|
gToolbox.customizing = true; |
|
|
|
|
forEachCustomizableToolbar(function (toolbar) { |
|
|
|
|
toolbar.setAttribute("customizing", "true"); |
|
|
|
|
}); |
|
|
|
|
gPaletteBox = document.getElementById("palette-box"); |
|
|
|
|
|
|
|
|
|
var elts = getRootElements(); |
|
|
|
|
for (let i=0; i < elts.length; i++) { |
|
|
|
|
elts[i].addEventListener("dragstart", onToolbarDragStart, true); |
|
|
|
|
elts[i].addEventListener("dragover", onToolbarDragOver, true); |
|
|
|
|
elts[i].addEventListener("dragexit", onToolbarDragExit, true); |
|
|
|
|
elts[i].addEventListener("drop", onToolbarDrop, true); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
initDialog(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function onClose() |
|
|
|
|
{ |
|
|
|
|
if (!gToolboxSheet) |
|
|
|
|
window.close(); |
|
|
|
|
else |
|
|
|
|
finishToolbarCustomization(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function onUnload() |
|
|
|
|
{ |
|
|
|
|
if (!gToolboxSheet) |
|
|
|
|
finishToolbarCustomization(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function finishToolbarCustomization() |
|
|
|
|
{ |
|
|
|
|
removeToolboxListeners(); |
|
|
|
|
unwrapToolbarItems(); |
|
|
|
|
persistCurrentSets(); |
|
|
|
|
gToolbox.customizing = false; |
|
|
|
|
forEachCustomizableToolbar(function (toolbar) { |
|
|
|
|
toolbar.removeAttribute("customizing"); |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
notifyParentComplete(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function initDialog() |
|
|
|
|
{ |
|
|
|
|
if (!gToolbox.toolbarset) { |
|
|
|
|
document.getElementById("newtoolbar").hidden = true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Build up the palette of other items.
|
|
|
|
|
buildPalette(); |
|
|
|
|
|
|
|
|
|
// Wrap all the items on the toolbar in toolbarpaletteitems.
|
|
|
|
|
wrapToolbarItems(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function repositionDialog(aWindow) |
|
|
|
|
{ |
|
|
|
|
// Position the dialog touching the bottom of the toolbox and centered with
|
|
|
|
|
// it.
|
|
|
|
|
if (!aWindow) |
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
var width; |
|
|
|
|
if (aWindow != window) |
|
|
|
|
width = aWindow.getBoundingClientRect().width; |
|
|
|
|
else if (document.documentElement.hasAttribute("width")) |
|
|
|
|
width = document.documentElement.getAttribute("width"); |
|
|
|
|
else |
|
|
|
|
width = parseInt(document.documentElement.style.width); |
|
|
|
|
var screenX = gToolbox.boxObject.screenX |
|
|
|
|
+ ((gToolbox.boxObject.width - width) / 2); |
|
|
|
|
var screenY = gToolbox.boxObject.screenY + gToolbox.boxObject.height; |
|
|
|
|
|
|
|
|
|
aWindow.moveTo(screenX, screenY); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function removeToolboxListeners() |
|
|
|
|
{ |
|
|
|
|
var elts = getRootElements(); |
|
|
|
|
for (let i=0; i < elts.length; i++) { |
|
|
|
|
elts[i].removeEventListener("dragstart", onToolbarDragStart, true); |
|
|
|
|
elts[i].removeEventListener("dragover", onToolbarDragOver, true); |
|
|
|
|
elts[i].removeEventListener("dragexit", onToolbarDragExit, true); |
|
|
|
|
elts[i].removeEventListener("drop", onToolbarDrop, true); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Invoke a callback on the toolbox to notify it that the dialog is done |
|
|
|
|
* and going away. |
|
|
|
|
*/ |
|
|
|
|
function notifyParentComplete() |
|
|
|
|
{ |
|
|
|
|
if ("customizeDone" in gToolbox) |
|
|
|
|
gToolbox.customizeDone(gToolboxChanged); |
|
|
|
|
dispatchCustomizationEvent("aftercustomization"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function toolboxChanged(aType) |
|
|
|
|
{ |
|
|
|
|
gToolboxChanged = true; |
|
|
|
|
if ("customizeChange" in gToolbox) |
|
|
|
|
gToolbox.customizeChange(aType); |
|
|
|
|
dispatchCustomizationEvent("customizationchange"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function dispatchCustomizationEvent(aEventName) { |
|
|
|
|
var evt = document.createEvent("Events"); |
|
|
|
|
evt.initEvent(aEventName, true, true); |
|
|
|
|
gToolbox.dispatchEvent(evt); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Persist the current set of buttons in all customizable toolbars to |
|
|
|
|
* localstore. |
|
|
|
|
*/ |
|
|
|
|
function persistCurrentSets() |
|
|
|
|
{ |
|
|
|
|
if (!gToolboxChanged || gToolboxDocument.defaultView.closed) |
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
var customCount = 0; |
|
|
|
|
forEachCustomizableToolbar(function (toolbar) { |
|
|
|
|
// Calculate currentset and store it in the attribute.
|
|
|
|
|
var currentSet = toolbar.currentSet; |
|
|
|
|
toolbar.setAttribute("currentset", currentSet); |
|
|
|
|
|
|
|
|
|
var customIndex = toolbar.hasAttribute("customindex"); |
|
|
|
|
if (customIndex) { |
|
|
|
|
if (!toolbar.hasChildNodes()) { |
|
|
|
|
// Remove custom toolbars whose contents have been removed.
|
|
|
|
|
gToolbox.removeChild(toolbar); |
|
|
|
|
} else if (gToolbox.toolbarset) { |
|
|
|
|
var hidingAttribute = toolbar.getAttribute("type") == "menubar" ? |
|
|
|
|
"autohide" : "collapsed"; |
|
|
|
|
// Persist custom toolbar info on the <toolbarset/>
|
|
|
|
|
// Attributes:
|
|
|
|
|
// Names: "toolbarX" (X - the number of the toolbar)
|
|
|
|
|
// Values: "Name|HidingAttributeName-HidingAttributeValue|CurrentSet"
|
|
|
|
|
gToolbox.toolbarset.setAttribute("toolbar" + (++customCount), |
|
|
|
|
toolbar.toolbarName |
|
|
|
|
+ gToolbarInfoSeparators[0] |
|
|
|
|
+ hidingAttribute |
|
|
|
|
+ gToolbarInfoSeparators[1] |
|
|
|
|
+ toolbar.getAttribute(hidingAttribute) |
|
|
|
|
+ gToolbarInfoSeparators[0] |
|
|
|
|
+ currentSet); |
|
|
|
|
gToolboxDocument.persist(gToolbox.toolbarset.id, "toolbar"+customCount); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!customIndex) { |
|
|
|
|
// Persist the currentset attribute directly on hardcoded toolbars.
|
|
|
|
|
gToolboxDocument.persist(toolbar.id, "currentset"); |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
// Remove toolbarX attributes for removed toolbars.
|
|
|
|
|
while (gToolbox.toolbarset && gToolbox.toolbarset.hasAttribute("toolbar"+(++customCount))) { |
|
|
|
|
gToolbox.toolbarset.removeAttribute("toolbar"+customCount); |
|
|
|
|
gToolboxDocument.persist(gToolbox.toolbarset.id, "toolbar"+customCount); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Wraps all items in all customizable toolbars in a toolbox. |
|
|
|
|
*/ |
|
|
|
|
function wrapToolbarItems() |
|
|
|
|
{ |
|
|
|
|
forEachCustomizableToolbar(function (toolbar) { |
|
|
|
|
Array.forEach(toolbar.childNodes, function (item) { |
|
|
|
|
#ifdef XP_MACOSX |
|
|
|
|
if (item.firstChild && item.firstChild.localName == "menubar") |
|
|
|
|
return; |
|
|
|
|
#endif |
|
|
|
|
if (isToolbarItem(item)) { |
|
|
|
|
let wrapper = wrapToolbarItem(item); |
|
|
|
|
cleanupItemForToolbar(item, wrapper); |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function getRootElements() |
|
|
|
|
{ |
|
|
|
|
return [gToolbox].concat(gToolbox.externalToolbars); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Unwraps all items in all customizable toolbars in a toolbox. |
|
|
|
|
*/ |
|
|
|
|
function unwrapToolbarItems() |
|
|
|
|
{ |
|
|
|
|
let elts = getRootElements(); |
|
|
|
|
for (let i=0; i < elts.length; i++) { |
|
|
|
|
let paletteItems = elts[i].getElementsByTagName("toolbarpaletteitem"); |
|
|
|
|
let paletteItem; |
|
|
|
|
while ((paletteItem = paletteItems.item(0)) != null) { |
|
|
|
|
let toolbarItem = paletteItem.firstChild; |
|
|
|
|
restoreItemForToolbar(toolbarItem, paletteItem); |
|
|
|
|
paletteItem.parentNode.replaceChild(toolbarItem, paletteItem); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Creates a wrapper that can be used to contain a toolbaritem and prevent |
|
|
|
|
* it from receiving UI events. |
|
|
|
|
*/ |
|
|
|
|
function createWrapper(aId, aDocument) |
|
|
|
|
{ |
|
|
|
|
var wrapper = aDocument.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", |
|
|
|
|
"toolbarpaletteitem"); |
|
|
|
|
|
|
|
|
|
wrapper.id = "wrapper-"+aId; |
|
|
|
|
return wrapper; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Wraps an item that has been cloned from a template and adds |
|
|
|
|
* it to the end of the palette. |
|
|
|
|
*/ |
|
|
|
|
function wrapPaletteItem(aPaletteItem) |
|
|
|
|
{ |
|
|
|
|
var wrapper = createWrapper(aPaletteItem.id, document); |
|
|
|
|
|
|
|
|
|
wrapper.appendChild(aPaletteItem); |
|
|
|
|
|
|
|
|
|
// XXX We need to call this AFTER the palette item has been appended
|
|
|
|
|
// to the wrapper or else we crash dropping certain buttons on the
|
|
|
|
|
// palette due to removal of the command and disabled attributes - JRH
|
|
|
|
|
cleanUpItemForPalette(aPaletteItem, wrapper); |
|
|
|
|
|
|
|
|
|
gPaletteBox.appendChild(wrapper); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Wraps an item that is currently on a toolbar and replaces the item |
|
|
|
|
* with the wrapper. This is not used when dropping items from the palette, |
|
|
|
|
* only when first starting the dialog and wrapping everything on the toolbars. |
|
|
|
|
*/ |
|
|
|
|
function wrapToolbarItem(aToolbarItem) |
|
|
|
|
{ |
|
|
|
|
var wrapper = createWrapper(aToolbarItem.id, gToolboxDocument); |
|
|
|
|
|
|
|
|
|
wrapper.flex = aToolbarItem.flex; |
|
|
|
|
|
|
|
|
|
aToolbarItem.parentNode.replaceChild(wrapper, aToolbarItem); |
|
|
|
|
|
|
|
|
|
wrapper.appendChild(aToolbarItem); |
|
|
|
|
|
|
|
|
|
return wrapper; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Get the list of ids for the current set of items on each toolbar. |
|
|
|
|
*/ |
|
|
|
|
function getCurrentItemIds() |
|
|
|
|
{ |
|
|
|
|
var currentItems = {}; |
|
|
|
|
forEachCustomizableToolbar(function (toolbar) { |
|
|
|
|
var child = toolbar.firstChild; |
|
|
|
|
while (child) { |
|
|
|
|
if (isToolbarItem(child)) |
|
|
|
|
currentItems[child.id] = 1; |
|
|
|
|
child = child.nextSibling; |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
return currentItems; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Builds the palette of draggable items that are not yet in a toolbar. |
|
|
|
|
*/ |
|
|
|
|
function buildPalette() |
|
|
|
|
{ |
|
|
|
|
// Empty the palette first.
|
|
|
|
|
while (gPaletteBox.lastChild) |
|
|
|
|
gPaletteBox.removeChild(gPaletteBox.lastChild); |
|
|
|
|
|
|
|
|
|
// Add the toolbar separator item.
|
|
|
|
|
var templateNode = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", |
|
|
|
|
"toolbarseparator"); |
|
|
|
|
templateNode.id = "separator"; |
|
|
|
|
wrapPaletteItem(templateNode); |
|
|
|
|
|
|
|
|
|
// Add the toolbar spring item.
|
|
|
|
|
templateNode = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", |
|
|
|
|
"toolbarspring"); |
|
|
|
|
templateNode.id = "spring"; |
|
|
|
|
templateNode.flex = 1; |
|
|
|
|
wrapPaletteItem(templateNode); |
|
|
|
|
|
|
|
|
|
// Add the toolbar spacer item.
|
|
|
|
|
templateNode = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", |
|
|
|
|
"toolbarspacer"); |
|
|
|
|
templateNode.id = "spacer"; |
|
|
|
|
templateNode.flex = 1; |
|
|
|
|
wrapPaletteItem(templateNode); |
|
|
|
|
|
|
|
|
|
var currentItems = getCurrentItemIds(); |
|
|
|
|
templateNode = gToolbox.palette.firstChild; |
|
|
|
|
while (templateNode) { |
|
|
|
|
// Check if the item is already in a toolbar before adding it to the palette.
|
|
|
|
|
if (!(templateNode.id in currentItems)) { |
|
|
|
|
var paletteItem = document.importNode(templateNode, true); |
|
|
|
|
wrapPaletteItem(paletteItem); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
templateNode = templateNode.nextSibling; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Makes sure that an item that has been cloned from a template |
|
|
|
|
* is stripped of any attributes that may adversely affect its |
|
|
|
|
* appearance in the palette. |
|
|
|
|
*/ |
|
|
|
|
function cleanUpItemForPalette(aItem, aWrapper) |
|
|
|
|
{ |
|
|
|
|
aWrapper.setAttribute("place", "palette"); |
|
|
|
|
setWrapperType(aItem, aWrapper); |
|
|
|
|
|
|
|
|
|
if (aItem.hasAttribute("title")) |
|
|
|
|
aWrapper.setAttribute("title", aItem.getAttribute("title")); |
|
|
|
|
else if (aItem.hasAttribute("label")) |
|
|
|
|
aWrapper.setAttribute("title", aItem.getAttribute("label")); |
|
|
|
|
else if (isSpecialItem(aItem)) { |
|
|
|
|
var stringBundle = document.getElementById("stringBundle"); |
|
|
|
|
// Remove the common "toolbar" prefix to generate the string name.
|
|
|
|
|
var title = stringBundle.getString(aItem.localName.slice(7) + "Title"); |
|
|
|
|
aWrapper.setAttribute("title", title); |
|
|
|
|
} |
|
|
|
|
aWrapper.setAttribute("tooltiptext", aWrapper.getAttribute("title")); |
|
|
|
|
|
|
|
|
|
// Remove attributes that screw up our appearance.
|
|
|
|
|
aItem.removeAttribute("command"); |
|
|
|
|
aItem.removeAttribute("observes"); |
|
|
|
|
aItem.removeAttribute("type"); |
|
|
|
|
aItem.removeAttribute("width"); |
|
|
|
|
|
|
|
|
|
Array.forEach(aWrapper.querySelectorAll("[disabled]"), function(aNode) { |
|
|
|
|
aNode.removeAttribute("disabled"); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Makes sure that an item that has been cloned from a template |
|
|
|
|
* is stripped of all properties that may adversely affect its |
|
|
|
|
* appearance in the toolbar. Store critical properties on the |
|
|
|
|
* wrapper so they can be put back on the item when we're done. |
|
|
|
|
*/ |
|
|
|
|
function cleanupItemForToolbar(aItem, aWrapper) |
|
|
|
|
{ |
|
|
|
|
setWrapperType(aItem, aWrapper); |
|
|
|
|
aWrapper.setAttribute("place", "toolbar"); |
|
|
|
|
|
|
|
|
|
if (aItem.hasAttribute("command")) { |
|
|
|
|
aWrapper.setAttribute("itemcommand", aItem.getAttribute("command")); |
|
|
|
|
aItem.removeAttribute("command"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (aItem.checked) { |
|
|
|
|
aWrapper.setAttribute("itemchecked", "true"); |
|
|
|
|
aItem.checked = false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (aItem.disabled) { |
|
|
|
|
aWrapper.setAttribute("itemdisabled", "true"); |
|
|
|
|
aItem.disabled = false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Restore all the properties that we stripped off above. |
|
|
|
|
*/ |
|
|
|
|
function restoreItemForToolbar(aItem, aWrapper) |
|
|
|
|
{ |
|
|
|
|
if (aWrapper.hasAttribute("itemdisabled")) |
|
|
|
|
aItem.disabled = true; |
|
|
|
|
|
|
|
|
|
if (aWrapper.hasAttribute("itemchecked")) |
|
|
|
|
aItem.checked = true; |
|
|
|
|
|
|
|
|
|
if (aWrapper.hasAttribute("itemcommand")) { |
|
|
|
|
let commandID = aWrapper.getAttribute("itemcommand"); |
|
|
|
|
aItem.setAttribute("command", commandID); |
|
|
|
|
|
|
|
|
|
// XXX Bug 309953 - toolbarbuttons aren't in sync with their commands after customizing
|
|
|
|
|
let command = gToolboxDocument.getElementById(commandID); |
|
|
|
|
if (command && command.hasAttribute("disabled")) |
|
|
|
|
aItem.setAttribute("disabled", command.getAttribute("disabled")); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function setWrapperType(aItem, aWrapper) |
|
|
|
|
{ |
|
|
|
|
if (aItem.localName == "toolbarseparator") { |
|
|
|
|
aWrapper.setAttribute("type", "separator"); |
|
|
|
|
} else if (aItem.localName == "toolbarspring") { |
|
|
|
|
aWrapper.setAttribute("type", "spring"); |
|
|
|
|
} else if (aItem.localName == "toolbarspacer") { |
|
|
|
|
aWrapper.setAttribute("type", "spacer"); |
|
|
|
|
} else if (aItem.localName == "toolbaritem" && aItem.firstChild) { |
|
|
|
|
aWrapper.setAttribute("type", aItem.firstChild.localName); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function setDragActive(aItem, aValue) |
|
|
|
|
{ |
|
|
|
|
var node = aItem; |
|
|
|
|
var direction = window.getComputedStyle(aItem, null).direction; |
|
|
|
|
var value = direction == "ltr"? "left" : "right"; |
|
|
|
|
if (aItem.localName == "toolbar") { |
|
|
|
|
node = aItem.lastChild; |
|
|
|
|
value = direction == "ltr"? "right" : "left"; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!node) |
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
if (aValue) { |
|
|
|
|
if (!node.hasAttribute("dragover")) |
|
|
|
|
node.setAttribute("dragover", value); |
|
|
|
|
} else { |
|
|
|
|
node.removeAttribute("dragover"); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function addNewToolbar() |
|
|
|
|
{ |
|
|
|
|
var promptService = Services.prompt; |
|
|
|
|
var stringBundle = document.getElementById("stringBundle"); |
|
|
|
|
var message = stringBundle.getString("enterToolbarName"); |
|
|
|
|
var title = stringBundle.getString("enterToolbarTitle"); |
|
|
|
|
|
|
|
|
|
var name = {}; |
|
|
|
|
|
|
|
|
|
// Quitting from the toolbar dialog while the new toolbar prompt is up
|
|
|
|
|
// can cause things to become unresponsive on the Mac. Until dialog modality
|
|
|
|
|
// is fixed (395465), disable the "Done" button explicitly.
|
|
|
|
|
var doneButton = document.getElementById("donebutton"); |
|
|
|
|
doneButton.disabled = true; |
|
|
|
|
|
|
|
|
|
while (true) { |
|
|
|
|
|
|
|
|
|
if (!promptService.prompt(window, title, message, name, null, {})) { |
|
|
|
|
doneButton.disabled = false; |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!name.value) { |
|
|
|
|
message = stringBundle.getFormattedString("enterToolbarBlank", [name.value]); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (name.value.includes(gToolbarInfoSeparators[0])) { |
|
|
|
|
message = stringBundle.getFormattedString("enterToolbarIllegalChars", [name.value]); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var dupeFound = false; |
|
|
|
|
|
|
|
|
|
// Check for an existing toolbar with the same display name
|
|
|
|
|
for (let i = 0; i < gToolbox.childNodes.length; ++i) { |
|
|
|
|
var toolbar = gToolbox.childNodes[i]; |
|
|
|
|
var toolbarName = toolbar.getAttribute("toolbarname"); |
|
|
|
|
|
|
|
|
|
if (toolbarName == name.value && |
|
|
|
|
toolbar.getAttribute("type") != "menubar" && |
|
|
|
|
toolbar.nodeName == 'toolbar') { |
|
|
|
|
dupeFound = true; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!dupeFound) |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
message = stringBundle.getFormattedString("enterToolbarDup", [name.value]); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
gToolbox.appendCustomToolbar(name.value, "", [null, null]); |
|
|
|
|
|
|
|
|
|
toolboxChanged(); |
|
|
|
|
|
|
|
|
|
doneButton.disabled = false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Restore the default set of buttons to fixed toolbars, |
|
|
|
|
* remove all custom toolbars, and rebuild the palette. |
|
|
|
|
*/ |
|
|
|
|
function restoreDefaultSet() |
|
|
|
|
{ |
|
|
|
|
// Unwrap the items on the toolbar.
|
|
|
|
|
unwrapToolbarItems(); |
|
|
|
|
|
|
|
|
|
// Remove all of the customized toolbars.
|
|
|
|
|
var child = gToolbox.lastChild; |
|
|
|
|
while (child) { |
|
|
|
|
if (child.hasAttribute("customindex")) { |
|
|
|
|
var thisChild = child; |
|
|
|
|
child = child.previousSibling; |
|
|
|
|
thisChild.currentSet = "__empty"; |
|
|
|
|
gToolbox.removeChild(thisChild); |
|
|
|
|
} else { |
|
|
|
|
child = child.previousSibling; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Restore the defaultset for fixed toolbars.
|
|
|
|
|
forEachCustomizableToolbar(function (toolbar) { |
|
|
|
|
var defaultSet = toolbar.getAttribute("defaultset"); |
|
|
|
|
if (defaultSet) |
|
|
|
|
toolbar.currentSet = defaultSet; |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
// Now rebuild the palette.
|
|
|
|
|
buildPalette(); |
|
|
|
|
|
|
|
|
|
// Now re-wrap the items on the toolbar.
|
|
|
|
|
wrapToolbarItems(); |
|
|
|
|
|
|
|
|
|
toolboxChanged("reset"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function updateToolboxProperty(aProp, aValue, aToolkitDefault) { |
|
|
|
|
var toolboxDefault = gToolbox.getAttribute("default" + aProp) || |
|
|
|
|
aToolkitDefault; |
|
|
|
|
|
|
|
|
|
gToolbox.setAttribute(aProp, aValue || toolboxDefault); |
|
|
|
|
gToolboxDocument.persist(gToolbox.id, aProp); |
|
|
|
|
|
|
|
|
|
forEachCustomizableToolbar(function (toolbar) { |
|
|
|
|
var toolbarDefault = toolbar.getAttribute("default" + aProp) || |
|
|
|
|
toolboxDefault; |
|
|
|
|
if (toolbar.getAttribute("lock" + aProp) == "true" && |
|
|
|
|
toolbar.getAttribute(aProp) == toolbarDefault) |
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
toolbar.setAttribute(aProp, aValue || toolbarDefault); |
|
|
|
|
gToolboxDocument.persist(toolbar.id, aProp); |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
toolboxChanged(aProp); |
|
|
|
|
|
|
|
|
|
return aValue || toolboxDefault; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function forEachCustomizableToolbar(callback) { |
|
|
|
|
Array.filter(gToolbox.childNodes, isCustomizableToolbar).forEach(callback); |
|
|
|
|
Array.filter(gToolbox.externalToolbars, isCustomizableToolbar).forEach(callback); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function isCustomizableToolbar(aElt) |
|
|
|
|
{ |
|
|
|
|
return aElt.localName == "toolbar" && |
|
|
|
|
aElt.getAttribute("customizable") == "true"; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function isSpecialItem(aElt) |
|
|
|
|
{ |
|
|
|
|
return aElt.localName == "toolbarseparator" || |
|
|
|
|
aElt.localName == "toolbarspring" || |
|
|
|
|
aElt.localName == "toolbarspacer"; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function isToolbarItem(aElt) |
|
|
|
|
{ |
|
|
|
|
return aElt.localName == "toolbarbutton" || |
|
|
|
|
aElt.localName == "toolbaritem" || |
|
|
|
|
aElt.localName == "toolbarseparator" || |
|
|
|
|
aElt.localName == "toolbarspring" || |
|
|
|
|
aElt.localName == "toolbarspacer"; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Drag and Drop observers
|
|
|
|
|
|
|
|
|
|
function onToolbarDragExit(aEvent) |
|
|
|
|
{ |
|
|
|
|
if (isUnwantedDragEvent(aEvent)) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (gCurrentDragOverItem) |
|
|
|
|
setDragActive(gCurrentDragOverItem, false); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function onToolbarDragStart(aEvent) |
|
|
|
|
{ |
|
|
|
|
var item = aEvent.target; |
|
|
|
|
while (item && item.localName != "toolbarpaletteitem") { |
|
|
|
|
if (item.localName == "toolbar") |
|
|
|
|
return; |
|
|
|
|
item = item.parentNode; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
item.setAttribute("dragactive", "true"); |
|
|
|
|
|
|
|
|
|
var dt = aEvent.dataTransfer; |
|
|
|
|
var documentId = gToolboxDocument.documentElement.id; |
|
|
|
|
dt.setData("text/toolbarwrapper-id/" + documentId, item.firstChild.id); |
|
|
|
|
dt.effectAllowed = "move"; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function onToolbarDragOver(aEvent) |
|
|
|
|
{ |
|
|
|
|
if (isUnwantedDragEvent(aEvent)) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var documentId = gToolboxDocument.documentElement.id; |
|
|
|
|
if (!aEvent.dataTransfer.types.includes("text/toolbarwrapper-id/" + documentId.toLowerCase())) |
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
var toolbar = aEvent.target; |
|
|
|
|
var dropTarget = aEvent.target; |
|
|
|
|
while (toolbar && toolbar.localName != "toolbar") { |
|
|
|
|
dropTarget = toolbar; |
|
|
|
|
toolbar = toolbar.parentNode; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Make sure we are dragging over a customizable toolbar.
|
|
|
|
|
if (!toolbar || !isCustomizableToolbar(toolbar)) { |
|
|
|
|
gCurrentDragOverItem = null; |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var previousDragItem = gCurrentDragOverItem; |
|
|
|
|
|
|
|
|
|
if (dropTarget.localName == "toolbar") { |
|
|
|
|
gCurrentDragOverItem = dropTarget; |
|
|
|
|
} else { |
|
|
|
|
gCurrentDragOverItem = null; |
|
|
|
|
|
|
|
|
|
var direction = window.getComputedStyle(dropTarget.parentNode, null).direction; |
|
|
|
|
var dropTargetCenter = dropTarget.boxObject.x + (dropTarget.boxObject.width / 2); |
|
|
|
|
var dragAfter; |
|
|
|
|
if (direction == "ltr") |
|
|
|
|
dragAfter = aEvent.clientX > dropTargetCenter; |
|
|
|
|
else |
|
|
|
|
dragAfter = aEvent.clientX < dropTargetCenter; |
|
|
|
|
|
|
|
|
|
if (dragAfter) { |
|
|
|
|
gCurrentDragOverItem = dropTarget.nextSibling; |
|
|
|
|
if (!gCurrentDragOverItem) |
|
|
|
|
gCurrentDragOverItem = toolbar; |
|
|
|
|
} else |
|
|
|
|
gCurrentDragOverItem = dropTarget; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (previousDragItem && gCurrentDragOverItem != previousDragItem) { |
|
|
|
|
setDragActive(previousDragItem, false); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
setDragActive(gCurrentDragOverItem, true); |
|
|
|
|
|
|
|
|
|
aEvent.preventDefault(); |
|
|
|
|
aEvent.stopPropagation(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function onToolbarDrop(aEvent) |
|
|
|
|
{ |
|
|
|
|
if (isUnwantedDragEvent(aEvent)) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!gCurrentDragOverItem) |
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
setDragActive(gCurrentDragOverItem, false); |
|
|
|
|
|
|
|
|
|
var documentId = gToolboxDocument.documentElement.id; |
|
|
|
|
var draggedItemId = aEvent.dataTransfer.getData("text/toolbarwrapper-id/" + documentId); |
|
|
|
|
if (gCurrentDragOverItem.id == draggedItemId) |
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
var toolbar = aEvent.target; |
|
|
|
|
while (toolbar.localName != "toolbar") |
|
|
|
|
toolbar = toolbar.parentNode; |
|
|
|
|
|
|
|
|
|
var draggedPaletteWrapper = document.getElementById("wrapper-"+draggedItemId); |
|
|
|
|
if (!draggedPaletteWrapper) { |
|
|
|
|
// The wrapper has been dragged from the toolbar.
|
|
|
|
|
// Get the wrapper from the toolbar document and make sure that
|
|
|
|
|
// it isn't being dropped on itself.
|
|
|
|
|
let wrapper = gToolboxDocument.getElementById("wrapper-"+draggedItemId); |
|
|
|
|
if (wrapper == gCurrentDragOverItem) |
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
// Don't allow non-removable kids (e.g., the menubar) to move.
|
|
|
|
|
if (wrapper.firstChild.getAttribute("removable") != "true") |
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
// Remove the item from its place in the toolbar.
|
|
|
|
|
wrapper.parentNode.removeChild(wrapper); |
|
|
|
|
|
|
|
|
|
// Determine which toolbar we are dropping on.
|
|
|
|
|
var dropToolbar = null; |
|
|
|
|
if (gCurrentDragOverItem.localName == "toolbar") |
|
|
|
|
dropToolbar = gCurrentDragOverItem; |
|
|
|
|
else |
|
|
|
|
dropToolbar = gCurrentDragOverItem.parentNode; |
|
|
|
|
|
|
|
|
|
// Insert the item into the toolbar.
|
|
|
|
|
if (gCurrentDragOverItem != dropToolbar) |
|
|
|
|
dropToolbar.insertBefore(wrapper, gCurrentDragOverItem); |
|
|
|
|
else |
|
|
|
|
dropToolbar.appendChild(wrapper); |
|
|
|
|
} else { |
|
|
|
|
// The item has been dragged from the palette
|
|
|
|
|
|
|
|
|
|
// Create a new wrapper for the item. We don't know the id yet.
|
|
|
|
|
let wrapper = createWrapper("", gToolboxDocument); |
|
|
|
|
|
|
|
|
|
// Ask the toolbar to clone the item's template, place it inside the wrapper, and insert it in the toolbar.
|
|
|
|
|
var newItem = toolbar.insertItem(draggedItemId, gCurrentDragOverItem == toolbar ? null : gCurrentDragOverItem, wrapper); |
|
|
|
|
|
|
|
|
|
// Prepare the item and wrapper to look good on the toolbar.
|
|
|
|
|
cleanupItemForToolbar(newItem, wrapper); |
|
|
|
|
wrapper.id = "wrapper-"+newItem.id; |
|
|
|
|
wrapper.flex = newItem.flex; |
|
|
|
|
|
|
|
|
|
// Remove the wrapper from the palette.
|
|
|
|
|
if (draggedItemId != "separator" && |
|
|
|
|
draggedItemId != "spring" && |
|
|
|
|
draggedItemId != "spacer") |
|
|
|
|
gPaletteBox.removeChild(draggedPaletteWrapper); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
gCurrentDragOverItem = null; |
|
|
|
|
|
|
|
|
|
toolboxChanged(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function onPaletteDragOver(aEvent) |
|
|
|
|
{ |
|
|
|
|
if (isUnwantedDragEvent(aEvent)) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
var documentId = gToolboxDocument.documentElement.id; |
|
|
|
|
if (aEvent.dataTransfer.types.includes("text/toolbarwrapper-id/" + documentId.toLowerCase())) |
|
|
|
|
aEvent.preventDefault(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function onPaletteDrop(aEvent) |
|
|
|
|
{ |
|
|
|
|
if (isUnwantedDragEvent(aEvent)) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
var documentId = gToolboxDocument.documentElement.id; |
|
|
|
|
var itemId = aEvent.dataTransfer.getData("text/toolbarwrapper-id/" + documentId); |
|
|
|
|
|
|
|
|
|
var wrapper = gToolboxDocument.getElementById("wrapper-"+itemId); |
|
|
|
|
if (wrapper) { |
|
|
|
|
// Don't allow non-removable kids (e.g., the menubar) to move.
|
|
|
|
|
if (wrapper.firstChild.getAttribute("removable") != "true") |
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
var wrapperType = wrapper.getAttribute("type"); |
|
|
|
|
if (wrapperType != "separator" && |
|
|
|
|
wrapperType != "spacer" && |
|
|
|
|
wrapperType != "spring") { |
|
|
|
|
restoreItemForToolbar(wrapper.firstChild, wrapper); |
|
|
|
|
wrapPaletteItem(document.importNode(wrapper.firstChild, true)); |
|
|
|
|
gToolbox.palette.appendChild(wrapper.firstChild); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// The item was dragged out of the toolbar.
|
|
|
|
|
wrapper.parentNode.removeChild(wrapper); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
toolboxChanged(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function isUnwantedDragEvent(aEvent) { |
|
|
|
|
try { |
|
|
|
|
if (Services.prefs.getBoolPref("toolkit.customization.unsafe_drag_events")) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
} catch (ex) {} |
|
|
|
|
|
|
|
|
|
/* Discard drag events that originated from a separate window to |
|
|
|
|
prevent content->chrome privilege escalations. */ |
|
|
|
|
let mozSourceNode = aEvent.dataTransfer.mozSourceNode; |
|
|
|
|
// mozSourceNode is null in the dragStart event handler or if
|
|
|
|
|
// the drag event originated in an external application.
|
|
|
|
|
if (!mozSourceNode) { |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
let sourceWindow = mozSourceNode.ownerDocument.defaultView; |
|
|
|
|
return sourceWindow != window && sourceWindow != gToolboxDocument.defaultView; |
|
|
|
|
} |
|
|
|
|
|