Bug 575870 - Enable the firefox button on xp themed, classic, and aero basic. r=dao...
[mozilla-central.git] / toolkit / content / customizeToolbar.js
blob9b8986bf07648da7e70b67afd3251b3ad4925893
1 # ***** BEGIN LICENSE BLOCK *****
2 # Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 # The contents of this file are subject to the Mozilla Public License Version
5 # 1.1 (the "License"); you may not use this file except in compliance with
6 # the License. You may obtain a copy of the License at
7 # http://www.mozilla.org/MPL/
9 # Software distributed under the License is distributed on an "AS IS" basis,
10 # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 # for the specific language governing rights and limitations under the
12 # License.
14 # The Original Code is Mozilla Communicator client code, released
15 # March 31, 1998.
17 # The Initial Developer of the Original Code is
18 # David Hyatt.
19 # Portions created by the Initial Developer are Copyright (C) 2002
20 # the Initial Developer. All Rights Reserved.
22 # Contributor(s):
23 #   David Hyatt (hyatt@apple.com)
24 #   Blake Ross (blaker@netscape.com)
25 #   Joe Hewitt (hewitt@netscape.com)
27 # Alternatively, the contents of this file may be used under the terms of
28 # either the GNU General Public License Version 2 or later (the "GPL"), or
29 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 # in which case the provisions of the GPL or the LGPL are applicable instead
31 # of those above. If you wish to allow use of your version of this file only
32 # under the terms of either the GPL or the LGPL, and not to allow others to
33 # use your version of this file under the terms of the MPL, indicate your
34 # decision by deleting the provisions above and replace them with the notice
35 # and other provisions required by the GPL or the LGPL. If you do not delete
36 # the provisions above, a recipient may use your version of this file under
37 # the terms of any one of the MPL, the GPL or the LGPL.
39 # ***** END LICENSE BLOCK *****
41 const kRowMax = 4;
43 var gToolboxDocument = null;
44 var gToolbox = null;
45 var gCurrentDragOverItem = null;
46 var gToolboxChanged = false;
47 var gToolboxSheet = false;
49 function onLoad()
51   if ("arguments" in window && window.arguments[0]) {
52     InitWithToolbox(window.arguments[0]);
53     repositionDialog(window);
54   }
55   else if (window.frameElement &&
56            "toolbox" in window.frameElement) {
57     gToolboxSheet = true;
58     InitWithToolbox(window.frameElement.toolbox);
59     repositionDialog(window.frameElement.panel);
60   }
63 function InitWithToolbox(aToolbox)
65   gToolbox = aToolbox;
66   dispatchCustomizationEvent("beforecustomization");
67   gToolboxDocument = gToolbox.ownerDocument;
68   gToolbox.customizing = true;
70   gToolbox.addEventListener("dragstart", onToolbarDragStart, true);
71   gToolbox.addEventListener("dragover", onToolbarDragOver, true);
72   gToolbox.addEventListener("dragleave", onToolbarDragLeave, true);
73   gToolbox.addEventListener("drop", onToolbarDrop, true);
75   initDialog();
78 function onClose()
80   if (!gToolboxSheet)
81     window.close();
82   else
83     finishToolbarCustomization();
86 function onUnload()
88   if (!gToolboxSheet)
89     finishToolbarCustomization();
92 function finishToolbarCustomization()
94   removeToolboxListeners();
95   unwrapToolbarItems();
96   persistCurrentSets();
97   gToolbox.customizing = false;
99   notifyParentComplete();
102 function initDialog()
104   var mode = gToolbox.getAttribute("mode");
105   document.getElementById("modelist").value = mode;
106   var smallIconsCheckbox = document.getElementById("smallicons");
107   smallIconsCheckbox.checked = gToolbox.getAttribute("iconsize") == "small";
108   if (mode == "text")
109     smallIconsCheckbox.disabled = true;
111   // Build up the palette of other items.
112   buildPalette();
114   // Wrap all the items on the toolbar in toolbarpaletteitems.
115   wrapToolbarItems();
118 function repositionDialog(aWindow)
120   // Position the dialog touching the bottom of the toolbox and centered with
121   // it.
122   if (!aWindow)
123     return;
125   var width;
126   if (aWindow != window)
127     width = aWindow.getBoundingClientRect().width;
128   else if (document.documentElement.hasAttribute("width"))
129     width = document.documentElement.getAttribute("width");
130   else
131     width = parseInt(document.documentElement.style.width);
132   var screenX = gToolbox.boxObject.screenX
133                 + ((gToolbox.boxObject.width - width) / 2);
134   var screenY = gToolbox.boxObject.screenY + gToolbox.boxObject.height;
136   aWindow.moveTo(screenX, screenY);
139 function removeToolboxListeners()
141   gToolbox.removeEventListener("dragstart", onToolbarDragStart, true);
142   gToolbox.removeEventListener("dragover", onToolbarDragOver, true);
143   gToolbox.removeEventListener("dragleave", onToolbarDragLeave, true);
144   gToolbox.removeEventListener("drop", onToolbarDrop, true);
148  * Invoke a callback on the toolbox to notify it that the dialog is done
149  * and going away.
150  */
151 function notifyParentComplete()
153   if ("customizeDone" in gToolbox)
154     gToolbox.customizeDone(gToolboxChanged);
155   dispatchCustomizationEvent("aftercustomization");
158 function toolboxChanged(aEvent)
160   gToolboxChanged = true;
161   if ("customizeChange" in gToolbox)
162     gToolbox.customizeChange(aEvent);
163   dispatchCustomizationEvent("customizationchange");
166 function dispatchCustomizationEvent(aEventName) {
167   var evt = document.createEvent("Events");
168   evt.initEvent(aEventName, true, true);
169   gToolbox.dispatchEvent(evt);
173  * Persist the current set of buttons in all customizable toolbars to
174  * localstore.
175  */
176 function persistCurrentSets()
178   if (!gToolboxChanged || gToolboxDocument.defaultView.closed)
179     return;
181   var customCount = 0;
182   forEachCustomizableToolbar(function (toolbar) {
183     // Calculate currentset and store it in the attribute.
184     var currentSet = toolbar.currentSet;
185     toolbar.setAttribute("currentset", currentSet);
187     var customIndex = toolbar.hasAttribute("customindex");
188     if (customIndex) {
189       if (!toolbar.hasChildNodes()) {
190         // Remove custom toolbars whose contents have been removed.
191         gToolbox.removeChild(toolbar);
192       } else {
193         // Persist custom toolbar info on the <toolbarset/>
194         gToolbox.toolbarset.setAttribute("toolbar"+(++customCount),
195                                          toolbar.toolbarName + ":" + currentSet);
196         gToolboxDocument.persist(gToolbox.toolbarset.id, "toolbar"+customCount);
197       }
198     }
200     if (!customIndex) {
201       // Persist the currentset attribute directly on hardcoded toolbars.
202       gToolboxDocument.persist(toolbar.id, "currentset");
203     }
204   });
206   // Remove toolbarX attributes for removed toolbars.
207   while (gToolbox.toolbarset.hasAttribute("toolbar"+(++customCount))) {
208     gToolbox.toolbarset.removeAttribute("toolbar"+customCount);
209     gToolboxDocument.persist(gToolbox.toolbarset.id, "toolbar"+customCount);
210   }
214  * Wraps all items in all customizable toolbars in a toolbox.
215  */
216 function wrapToolbarItems()
218   forEachCustomizableToolbar(function (toolbar) {
219     Array.forEach(toolbar.childNodes, function (item) {
220 #ifdef XP_MACOSX
221       if (item.firstChild && item.firstChild.localName == "menubar")
222         return;
223 #endif
225       if (isToolbarItem(item)) {
226         let wrapper = wrapToolbarItem(item);
227         cleanupItemForToolbar(item, wrapper);
228       }
229     });
230   });
234  * Unwraps all items in all customizable toolbars in a toolbox.
235  */
236 function unwrapToolbarItems()
238   var paletteItems = gToolbox.getElementsByTagName("toolbarpaletteitem");
239   var paletteItem;
240   while ((paletteItem = paletteItems.item(0)) != null) {
241     var toolbarItem = paletteItem.firstChild;
242     restoreItemForToolbar(toolbarItem, paletteItem);
243     paletteItem.parentNode.replaceChild(toolbarItem, paletteItem);
244   }
248  * Creates a wrapper that can be used to contain a toolbaritem and prevent
249  * it from receiving UI events.
250  */
251 function createWrapper(aId, aDocument)
253   var wrapper = aDocument.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
254                                          "toolbarpaletteitem");
256   wrapper.id = "wrapper-"+aId;
257   return wrapper;
261  * Wraps an item that has been cloned from a template and adds
262  * it to the end of a row in the palette.
263  */
264 function wrapPaletteItem(aPaletteItem, aCurrentRow, aSpacer)
266   var wrapper = createWrapper(aPaletteItem.id, document);
268   wrapper.setAttribute("flex", 1);
269   wrapper.setAttribute("align", "center");
270   wrapper.setAttribute("pack", "center");
271   wrapper.setAttribute("minheight", "0");
272   wrapper.setAttribute("minwidth", "0");
274   wrapper.appendChild(aPaletteItem);
276   // XXX We need to call this AFTER the palette item has been appended
277   // to the wrapper or else we crash dropping certain buttons on the
278   // palette due to removal of the command and disabled attributes - JRH
279   cleanUpItemForPalette(aPaletteItem, wrapper);
281   if (aSpacer)
282     aCurrentRow.insertBefore(wrapper, aSpacer);
283   else
284     aCurrentRow.appendChild(wrapper);
289  * Wraps an item that is currently on a toolbar and replaces the item
290  * with the wrapper. This is not used when dropping items from the palette,
291  * only when first starting the dialog and wrapping everything on the toolbars.
292  */
293 function wrapToolbarItem(aToolbarItem)
295   var wrapper = createWrapper(aToolbarItem.id, gToolboxDocument);
297   wrapper.flex = aToolbarItem.flex;
299   aToolbarItem.parentNode.replaceChild(wrapper, aToolbarItem);
301   wrapper.appendChild(aToolbarItem);
303   return wrapper;
307  * Get the list of ids for the current set of items on each toolbar.
308  */
309 function getCurrentItemIds()
311   var currentItems = {};
312   forEachCustomizableToolbar(function (toolbar) {
313     var child = toolbar.firstChild;
314     while (child) {
315       if (isToolbarItem(child))
316         currentItems[child.id] = 1;
317       child = child.nextSibling;
318     }
319   });
320   return currentItems;
324  * Builds the palette of draggable items that are not yet in a toolbar.
325  */
326 function buildPalette()
328   // Empty the palette first.
329   var paletteBox = document.getElementById("palette-box");
330   while (paletteBox.lastChild)
331     paletteBox.removeChild(paletteBox.lastChild);
333   var currentRow = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
334                                             "hbox");
335   currentRow.setAttribute("class", "paletteRow");
337   // Add the toolbar separator item.
338   var templateNode = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
339                                               "toolbarseparator");
340   templateNode.id = "separator";
341   wrapPaletteItem(templateNode, currentRow, null);
343   // Add the toolbar spring item.
344   templateNode = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
345                                               "toolbarspring");
346   templateNode.id = "spring";
347   templateNode.flex = 1;
348   wrapPaletteItem(templateNode, currentRow, null);
350   // Add the toolbar spacer item.
351   templateNode = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
352                                               "toolbarspacer");
353   templateNode.id = "spacer";
354   templateNode.flex = 1;
355   wrapPaletteItem(templateNode, currentRow, null);
357   var rowSlot = 3;
359   var currentItems = getCurrentItemIds();
360   templateNode = gToolbox.palette.firstChild;
361   while (templateNode) {
362     // Check if the item is already in a toolbar before adding it to the palette.
363     if (!(templateNode.id in currentItems)) {
364       var paletteItem = document.importNode(templateNode, true);
366       if (rowSlot == kRowMax) {
367         // Append the old row.
368         paletteBox.appendChild(currentRow);
370         // Make a new row.
371         currentRow = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
372                                               "hbox");
373         currentRow.setAttribute("class", "paletteRow");
374         rowSlot = 0;
375       }
377       ++rowSlot;
378       wrapPaletteItem(paletteItem, currentRow, null);
379     }
381     templateNode = templateNode.nextSibling;
382   }
384   if (currentRow) {
385     fillRowWithFlex(currentRow);
386     paletteBox.appendChild(currentRow);
387   }
391  * Creates a new palette item for a cloned template node and
392  * adds it to the last slot in the palette.
393  */
394 function appendPaletteItem(aItem)
396   var paletteBox = document.getElementById("palette-box");
397   var lastRow = paletteBox.lastChild;
398   var lastSpacer = lastRow.lastChild;
400   if (lastSpacer.localName != "spacer") {
401     // The current row is full, so we have to create a new row.
402     lastRow = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
403                                         "hbox");
404     lastRow.setAttribute("class", "paletteRow");
405     paletteBox.appendChild(lastRow);
407     wrapPaletteItem(aItem, lastRow, null);
409     fillRowWithFlex(lastRow);
410   } else {
411     // Decrement the flex of the last spacer or remove it entirely.
412     var flex = lastSpacer.getAttribute("flex");
413     if (flex == 1) {
414       lastRow.removeChild(lastSpacer);
415       lastSpacer = null;
416     } else
417       lastSpacer.setAttribute("flex", --flex);
419     // Insert the wrapper where the last spacer was.
420     wrapPaletteItem(aItem, lastRow, lastSpacer);
421   }
424 function fillRowWithFlex(aRow)
426   var remainingFlex = kRowMax - aRow.childNodes.length;
427   if (remainingFlex > 0) {
428     var spacer = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
429                                           "spacer");
430     spacer.setAttribute("flex", remainingFlex);
431     aRow.appendChild(spacer);
432   }
436  * Makes sure that an item that has been cloned from a template
437  * is stripped of any attributes that may adversely affect its
438  * appearance in the palette.
439  */
440 function cleanUpItemForPalette(aItem, aWrapper)
442   aWrapper.setAttribute("place", "palette");
443   setWrapperType(aItem, aWrapper);
445   if (aItem.hasAttribute("title"))
446     aWrapper.setAttribute("title", aItem.getAttribute("title"));
447   else if (aItem.hasAttribute("label"))
448     aWrapper.setAttribute("title", aItem.getAttribute("label"));
449   else if (isSpecialItem(aItem)) {
450     var stringBundle = document.getElementById("stringBundle");
451     // Remove the common "toolbar" prefix to generate the string name.
452     var title = stringBundle.getString(aItem.localName.slice(7) + "Title");
453     aWrapper.setAttribute("title", title);
454   }
456   // Remove attributes that screw up our appearance.
457   aItem.removeAttribute("command");
458   aItem.removeAttribute("observes");
459   aItem.removeAttribute("type");
460   aItem.removeAttribute("width");
462   Array.forEach(aWrapper.querySelectorAll("[disabled]"), function(aNode) {
463     aNode.removeAttribute("disabled");
464   });
468  * Makes sure that an item that has been cloned from a template
469  * is stripped of all properties that may adversely affect its
470  * appearance in the toolbar.  Store critical properties on the
471  * wrapper so they can be put back on the item when we're done.
472  */
473 function cleanupItemForToolbar(aItem, aWrapper)
475   setWrapperType(aItem, aWrapper);
476   aWrapper.setAttribute("place", "toolbar");
478   if (aItem.hasAttribute("command")) {
479     aWrapper.setAttribute("itemcommand", aItem.getAttribute("command"));
480     aItem.removeAttribute("command");
481   }
483   if (aItem.checked) {
484     aWrapper.setAttribute("itemchecked", "true");
485     aItem.checked = false;
486   }
488   if (aItem.disabled) {
489     aWrapper.setAttribute("itemdisabled", "true");
490     aItem.disabled = false;
491   }
495  * Restore all the properties that we stripped off above.
496  */
497 function restoreItemForToolbar(aItem, aWrapper)
499   if (aWrapper.hasAttribute("itemdisabled"))
500     aItem.disabled = true;
502   if (aWrapper.hasAttribute("itemchecked"))
503     aItem.checked = true;
505   if (aWrapper.hasAttribute("itemcommand")) {
506     let commandID = aWrapper.getAttribute("itemcommand");
507     aItem.setAttribute("command", commandID);
509     //XXX Bug 309953 - toolbarbuttons aren't in sync with their commands after customizing
510     let command = gToolboxDocument.getElementById(commandID);
511     if (command && command.hasAttribute("disabled"))
512       aItem.setAttribute("disabled", command.getAttribute("disabled"));
513   }
516 function setWrapperType(aItem, aWrapper)
518   if (aItem.localName == "toolbarseparator") {
519     aWrapper.setAttribute("type", "separator");
520   } else if (aItem.localName == "toolbarspring") {
521     aWrapper.setAttribute("type", "spring");
522   } else if (aItem.localName == "toolbarspacer") {
523     aWrapper.setAttribute("type", "spacer");
524   } else if (aItem.localName == "toolbaritem" && aItem.firstChild) {
525     aWrapper.setAttribute("type", aItem.firstChild.localName);
526   }
529 function setDragActive(aItem, aValue)
531   var node = aItem;
532   var direction = window.getComputedStyle(aItem, null).direction;
533   var value = direction == "ltr"? "left" : "right";
534   if (aItem.localName == "toolbar") {
535     node = aItem.lastChild;
536     value = direction == "ltr"? "right" : "left";
537   }
539   if (!node)
540     return;
542   if (aValue) {
543     if (!node.hasAttribute("dragover"))
544       node.setAttribute("dragover", value);
545   } else {
546     node.removeAttribute("dragover");
547   }
550 function addNewToolbar()
552   var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
553                                 .getService(Components.interfaces.nsIPromptService);
555   var stringBundle = document.getElementById("stringBundle");
556   var message = stringBundle.getString("enterToolbarName");
557   var title = stringBundle.getString("enterToolbarTitle");
559   var name = {};
561   // Quitting from the toolbar dialog while the new toolbar prompt is up
562   // can cause things to become unresponsive on the Mac. Until dialog modality
563   // is fixed (395465), disable the "Done" button explicitly.
564   var doneButton = document.getElementById("donebutton");
565   doneButton.disabled = true;
567   while (true) {
569     if (!promptService.prompt(window, title, message, name, null, {})) {
570       doneButton.disabled = false;
571       return;
572     }
574     if (!name.value) {
575       message = stringBundle.getFormattedString("enterToolbarBlank", [name.value]);
576       continue;
577     }
579     var dupeFound = false;
581      // Check for an existing toolbar with the same display name
582     for (let i = 0; i < gToolbox.childNodes.length; ++i) {
583       var toolbar = gToolbox.childNodes[i];
584       var toolbarName = toolbar.getAttribute("toolbarname");
586       if (toolbarName == name.value &&
587           toolbar.getAttribute("type") != "menubar" &&
588           toolbar.nodeName == 'toolbar') {
589         dupeFound = true;
590         break;
591       }
592     }
594     if (!dupeFound)
595       break;
597     message = stringBundle.getFormattedString("enterToolbarDup", [name.value]);
598   }
600   gToolbox.appendCustomToolbar(name.value, "");
602   toolboxChanged();
604   doneButton.disabled = false;
608  * Restore the default set of buttons to fixed toolbars,
609  * remove all custom toolbars, and rebuild the palette.
610  */
611 function restoreDefaultSet()
613   // Unwrap the items on the toolbar.
614   unwrapToolbarItems();
616   // Remove all of the customized toolbars.
617   var child = gToolbox.lastChild;
618   while (child) {
619     if (child.hasAttribute("customindex")) {
620       var thisChild = child;
621       child = child.previousSibling;
622       thisChild.currentSet = "__empty";
623       gToolbox.removeChild(thisChild);
624     } else {
625       child = child.previousSibling;
626     }
627   }
629   // Restore the defaultset for fixed toolbars.
630   forEachCustomizableToolbar(function (toolbar) {
631     var defaultSet = toolbar.getAttribute("defaultset");
632     if (defaultSet)
633       toolbar.currentSet = defaultSet;
634   });
636   // Restore the default icon size and mode.
637   document.getElementById("smallicons").checked = (updateIconSize() == "small");
638   document.getElementById("modelist").value = updateToolbarMode();
640   // Now rebuild the palette.
641   buildPalette();
643   // Now re-wrap the items on the toolbar.
644   wrapToolbarItems();
646   toolboxChanged("reset");
649 function updateIconSize(aSize) {
650   return updateToolboxProperty("iconsize", aSize, "large");
653 function updateToolbarMode(aModeValue) {
654   var mode = updateToolboxProperty("mode", aModeValue, "icons");
656   var iconSizeCheckbox = document.getElementById("smallicons");
657   iconSizeCheckbox.disabled = mode == "text";
659   return mode;
662 function updateToolboxProperty(aProp, aValue, aToolkitDefault) {
663   var toolboxDefault = gToolbox.getAttribute("default" + aProp) ||
664                        aToolkitDefault;
666   gToolbox.setAttribute(aProp, aValue || toolboxDefault);
667   gToolboxDocument.persist(gToolbox.id, aProp);
669   forEachCustomizableToolbar(function (toolbar) {
670     var toolbarDefault = toolbar.getAttribute("default" + aProp) ||
671                          toolboxDefault;
672     if (toolbar.getAttribute("lock" + aProp) == "true" &&
673         toolbar.getAttribute(aProp) == toolbarDefault)
674       return;
676     toolbar.setAttribute(aProp, aValue || toolbarDefault);
677     gToolboxDocument.persist(toolbar.id, aProp);
678   });
680   return aValue || toolboxDefault;
683 function forEachCustomizableToolbar(callback) {
684   Array.filter(gToolbox.childNodes, isCustomizableToolbar).forEach(callback);
687 function isCustomizableToolbar(aElt)
689   return aElt.localName == "toolbar" &&
690          aElt.getAttribute("customizable") == "true";
693 function isSpecialItem(aElt)
695   return aElt.localName == "toolbarseparator" ||
696          aElt.localName == "toolbarspring" ||
697          aElt.localName == "toolbarspacer";
700 function isToolbarItem(aElt)
702   return aElt.localName == "toolbarbutton" ||
703          aElt.localName == "toolbaritem" ||
704          aElt.localName == "toolbarseparator" ||
705          aElt.localName == "toolbarspring" ||
706          aElt.localName == "toolbarspacer";
709 ///////////////////////////////////////////////////////////////////////////
710 //// Drag and Drop observers
712 function onToolbarDragLeave(aEvent)
714   if (gCurrentDragOverItem)
715     setDragActive(gCurrentDragOverItem, false);
718 function onToolbarDragStart(aEvent)
720   var documentId = gToolboxDocument.documentElement.id;
722   var item = aEvent.target;
723   while (item && item.localName != "toolbarpaletteitem")
724     item = item.parentNode;
726   item.setAttribute("dragactive", "true");
728   var dt = aEvent.dataTransfer;
729   dt.setData("text/toolbarwrapper-id/" + documentId, item.firstChild.id);
730   dt.effectAllowed = "move";
733 function onToolbarDragOver(aEvent)
735   var documentId = gToolboxDocument.documentElement.id;
736   if (!aEvent.dataTransfer.types.contains("text/toolbarwrapper-id/" + documentId))
737     return;
739   var toolbar = aEvent.target;
740   var dropTarget = aEvent.target;
741   while (toolbar && toolbar.localName != "toolbar") {
742     dropTarget = toolbar;
743     toolbar = toolbar.parentNode;
744   }
746   // Make sure we are dragging over a customizable toolbar.
747   if (!toolbar || !isCustomizableToolbar(toolbar)) {
748     gCurrentDragOverItem = null;
749     return;
750   }
752   var previousDragItem = gCurrentDragOverItem;
754   if (dropTarget.localName == "toolbar") {
755     gCurrentDragOverItem = dropTarget;
756   } else {
757     gCurrentDragOverItem = null;
759     var direction = window.getComputedStyle(dropTarget.parentNode, null).direction;
760     var dropTargetCenter = dropTarget.boxObject.x + (dropTarget.boxObject.width / 2);
761     var dragAfter;
762     if (direction == "ltr")
763       dragAfter = aEvent.clientX > dropTargetCenter;
764     else
765       dragAfter = aEvent.clientX < dropTargetCenter;
767     if (dragAfter) {
768       gCurrentDragOverItem = dropTarget.nextSibling;
769       if (!gCurrentDragOverItem)
770         gCurrentDragOverItem = toolbar;
771     } else
772       gCurrentDragOverItem = dropTarget;
773   }
775   if (previousDragItem && gCurrentDragOverItem != previousDragItem) {
776     setDragActive(previousDragItem, false);
777   }
779   setDragActive(gCurrentDragOverItem, true);
781   aEvent.preventDefault();
782   aEvent.stopPropagation();
785 function onToolbarDrop(aEvent)
787   if (!gCurrentDragOverItem)
788     return;
790   setDragActive(gCurrentDragOverItem, false);
792   var documentId = gToolboxDocument.documentElement.id;
793   var draggedItemId = aEvent.dataTransfer.getData("text/toolbarwrapper-id/" + documentId);
794   if (gCurrentDragOverItem.id == draggedItemId)
795     return;
797   var toolbar = aEvent.target;
798   while (toolbar.localName != "toolbar")
799     toolbar = toolbar.parentNode;
801   var draggedPaletteWrapper = document.getElementById("wrapper-"+draggedItemId);
802   if (!draggedPaletteWrapper) {
803     // The wrapper has been dragged from the toolbar.
804     // Get the wrapper from the toolbar document and make sure that
805     // it isn't being dropped on itself.
806     var wrapper = gToolboxDocument.getElementById("wrapper-"+draggedItemId);
807     if (wrapper == gCurrentDragOverItem)
808        return;
810     // Don't allow non-removable kids (e.g., the menubar) to move.
811     if (wrapper.firstChild.getAttribute("removable") != "true")
812       return;
814     // Remove the item from its place in the toolbar.
815     wrapper.parentNode.removeChild(wrapper);
817     // Determine which toolbar we are dropping on.
818     var dropToolbar = null;
819     if (gCurrentDragOverItem.localName == "toolbar")
820       dropToolbar = gCurrentDragOverItem;
821     else
822       dropToolbar = gCurrentDragOverItem.parentNode;
824     // Insert the item into the toolbar.
825     if (gCurrentDragOverItem != dropToolbar)
826       dropToolbar.insertBefore(wrapper, gCurrentDragOverItem);
827     else
828       dropToolbar.appendChild(wrapper);
829   } else {
830     // The item has been dragged from the palette
832     // Create a new wrapper for the item. We don't know the id yet.
833     var wrapper = createWrapper("", gToolboxDocument);
835     // Ask the toolbar to clone the item's template, place it inside the wrapper, and insert it in the toolbar.
836     var newItem = toolbar.insertItem(draggedItemId, gCurrentDragOverItem == toolbar ? null : gCurrentDragOverItem, wrapper);
838     // Prepare the item and wrapper to look good on the toolbar.
839     cleanupItemForToolbar(newItem, wrapper);
840     wrapper.id = "wrapper-"+newItem.id;
841     wrapper.flex = newItem.flex;
843     // Remove the wrapper from the palette.
844     var currentRow = draggedPaletteWrapper.parentNode;
845     if (draggedItemId != "separator" &&
846         draggedItemId != "spring" &&
847         draggedItemId != "spacer")
848     {
849       currentRow.removeChild(draggedPaletteWrapper);
851       while (currentRow) {
852         // Pull the first child of the next row up
853         // into this row.
854         var nextRow = currentRow.nextSibling;
856         if (!nextRow) {
857           var last = currentRow.lastChild;
858           var first = currentRow.firstChild;
859           if (first == last) {
860             // Kill the row.
861             currentRow.parentNode.removeChild(currentRow);
862              break;
863            }
865           if (last.localName == "spacer") {
866             var flex = last.getAttribute("flex");
867             last.setAttribute("flex", ++flex);
868             // Reflow doesn't happen for some reason.  Trigger it with a hide/show. ICK! -dwh
869             last.hidden = true;
870             last.hidden = false;
871             break;
872           } else {
873             // Make a spacer and give it a flex of 1.
874             var spacer = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
875                                                   "spacer");
876             spacer.setAttribute("flex", "1");
877             currentRow.appendChild(spacer);
878           }
879           break;
880         }
882         currentRow.appendChild(nextRow.firstChild);
883         currentRow = currentRow.nextSibling;
884       }
885     }
886   }
888   gCurrentDragOverItem = null;
890   toolboxChanged();
893 function onPaletteDragOver(aEvent)
895   var documentId = gToolboxDocument.documentElement.id;
896   if (aEvent.dataTransfer.types.contains("text/toolbarwrapper-id/" + documentId))
897     aEvent.preventDefault();
900 function onPaletteDrop(aEvent)
902   var documentId = gToolboxDocument.documentElement.id;
903   var itemId = aEvent.dataTransfer.getData("text/toolbarwrapper-id/" + documentId);
905   var wrapper = gToolboxDocument.getElementById("wrapper-"+itemId);
906   if (wrapper) {
907     // Don't allow non-removable kids (e.g., the menubar) to move.
908     if (wrapper.firstChild.getAttribute("removable") != "true")
909       return;
911     var wrapperType = wrapper.getAttribute("type");
912     if (wrapperType != "separator" &&
913         wrapperType != "spacer" &&
914         wrapperType != "spring") {
915       restoreItemForToolbar(wrapper.firstChild, wrapper);
916       appendPaletteItem(document.importNode(wrapper.firstChild, true));
917       gToolbox.palette.appendChild(wrapper.firstChild);
918     }
920     // The item was dragged out of the toolbar.
921     wrapper.parentNode.removeChild(wrapper);
922   }
924   toolboxChanged();