home *** CD-ROM | disk | FTP | other *** search
/ PC World 2003 May / PCWorld_2003-05_cd.bin / Komunik / phoenix / chrome / toolkit.jar / content / global / customizeToolbar.js < prev    next >
Text File  |  2002-11-14  |  27KB  |  904 lines

  1.  
  2. const kRowMax = 4;
  3. const kWindowWidth = 600;
  4. const kWindowHeight = 400;
  5. const kAnimateIncrement = 50;
  6. const kAnimateSteps = kWindowHeight / kAnimateIncrement - 1;
  7.  
  8. var gToolboxDocument = null;
  9. var gToolbox = null;
  10. var gCurrentDragOverItem = null;
  11. var gToolboxChanged = false;
  12.  
  13. function onLoad()
  14. {
  15.   gToolbox = window.arguments[0];
  16.   gToolboxDocument = gToolbox.ownerDocument;
  17.   
  18.   gToolbox.addEventListener("draggesture", onToolbarDragGesture, false);
  19.   gToolbox.addEventListener("dragover", onToolbarDragOver, false);
  20.   gToolbox.addEventListener("dragexit", onToolbarDragExit, false);
  21.   gToolbox.addEventListener("dragdrop", onToolbarDragDrop, false);
  22.  
  23.   document.documentElement.setAttribute("hidechrome", "true");
  24.  
  25.   repositionDialog();
  26.   window.outerWidth = kWindowWidth;
  27.   window.outerHeight = 50;
  28.   slideOpen(0);
  29. }
  30.  
  31. function onUnload(aEvent)
  32. {
  33.   removeToolboxListeners();
  34.   unwrapToolbarItems();
  35.   persistCurrentSets();
  36.   
  37.   notifyParentComplete();
  38. }
  39.  
  40. function onAccept(aEvent)
  41. {
  42.   document.getElementById("main-box").collapsed = true;
  43.   slideClosed(0);
  44. }
  45.  
  46. function initDialog()
  47. {
  48.   document.getElementById("main-box").collapsed = false;
  49.   
  50.   var mode = gToolbox.getAttribute("mode");
  51.   document.getElementById("modelist").value = mode;
  52.   var iconSize = gToolbox.getAttribute("iconsize");
  53.   var smallIconsCheckbox = document.getElementById("smallicons");
  54.   if (mode == "text")
  55.     smallIconsCheckbox.disabled = true;
  56.   else
  57.     smallIconsCheckbox.checked = iconSize == "small"; 
  58.  
  59.   // Build up the palette of other items.
  60.   buildPalette();
  61.  
  62.   // Wrap all the items on the toolbar in toolbarpaletteitems.
  63.   wrapToolbarItems();
  64. }
  65.  
  66. function slideOpen(aStep)
  67. {
  68.   if (aStep < kAnimateSteps) {
  69.     window.outerHeight += kAnimateIncrement;
  70.     setTimeout(slideOpen, 20, ++aStep);
  71.   } else {
  72.     initDialog();
  73.   }
  74. }
  75.  
  76. function slideClosed(aStep)
  77. {
  78.   if (aStep < kAnimateSteps) {
  79.     window.outerHeight -= kAnimateIncrement;
  80.     setTimeout(slideClosed, 10, ++aStep);
  81.   } else {
  82.     window.close();
  83.   }
  84. }
  85.  
  86. function repositionDialog()
  87. {
  88.   // Position the dialog touching the bottom of the toolbox and centered with it
  89.   var screenX = gToolbox.boxObject.screenX + ((gToolbox.boxObject.width - kWindowWidth) / 2);
  90.   var screenY = gToolbox.boxObject.screenY + gToolbox.boxObject.height;
  91.   window.moveTo(screenX, screenY);
  92. }
  93.  
  94. function removeToolboxListeners()
  95. {
  96.   gToolbox.removeEventListener("draggesture", onToolbarDragGesture, false);
  97.   gToolbox.removeEventListener("dragover", onToolbarDragOver, false);
  98.   gToolbox.removeEventListener("dragexit", onToolbarDragExit, false);
  99.   gToolbox.removeEventListener("dragdrop", onToolbarDragDrop, false);
  100. }
  101.  
  102. /**
  103.  * Invoke a callback on the toolbox to notify it that the dialog is done
  104.  * and going away.
  105.  */
  106. function notifyParentComplete()
  107. {
  108.   if ("customizeDone" in gToolbox)
  109.     gToolbox.customizeDone(gToolboxChanged);
  110. }
  111.  
  112. function getToolbarAt(i)
  113. {
  114.   return gToolbox.childNodes[i];
  115. }
  116.  
  117. /**
  118.  * Persist the current set of buttons in all customizable toolbars to
  119.  * localstore.
  120.  */
  121. function persistCurrentSets()
  122. {
  123.   if (!gToolboxChanged)
  124.     return;
  125.  
  126.   var customCount = 0;
  127.   for (var i = 0; i < gToolbox.childNodes.length; ++i) {
  128.     // Look for customizable toolbars that need to be persisted.
  129.     var toolbar = getToolbarAt(i);
  130.     if (isCustomizableToolbar(toolbar)) {
  131.       // Calculate currentset and store it in the attribute.
  132.       var currentSet = toolbar.currentSet;
  133.       toolbar.setAttribute("currentset", currentSet);
  134.       
  135.       var customIndex = toolbar.hasAttribute("customindex");
  136.       if (customIndex) {
  137.         if (!toolbar.firstChild) {
  138.           // Remove custom toolbars whose contents have been removed.
  139.           gToolbox.removeChild(toolbar);
  140.           --i;
  141.         } else {
  142.           // Persist custom toolbar info on the <toolbarset/>
  143.           gToolbox.toolbarset.setAttribute("toolbar"+(++customCount),
  144.                                            toolbar.toolbarName + ":" + currentSet);
  145.           gToolboxDocument.persist(gToolbox.toolbarset.id, "toolbar"+customCount);
  146.         }
  147.       }
  148.  
  149.       if (!customIndex) {
  150.         // Persist the currentset attribute directly on hardcoded toolbars.
  151.         gToolboxDocument.persist(toolbar.id, "currentset");
  152.       }
  153.     }
  154.   }
  155.   
  156.   // Remove toolbarX attributes for removed toolbars.
  157.   while (gToolbox.toolbarset.hasAttribute("toolbar"+(++customCount))) {
  158.     gToolbox.toolbarset.removeAttribute("toolbar"+customCount);
  159.     gToolboxDocument.persist(gToolbox.toolbarset.id, "toolbar"+customCount);
  160.   }
  161. }
  162.  
  163. /**
  164.  * Wraps all items in all customizable toolbars in a toolbox.
  165.  */
  166. function wrapToolbarItems()
  167. {
  168.   for (var i = 0; i < gToolbox.childNodes.length; ++i) {
  169.     var toolbar = getToolbarAt(i);
  170.     if (isCustomizableToolbar(toolbar)) {
  171.       for (var k = 0; k < toolbar.childNodes.length; ++k) {
  172.         var item = toolbar.childNodes[k];
  173.         if (isToolbarItem(item)) {
  174.           var nextSibling = item.nextSibling;
  175.           
  176.           var wrapper = wrapToolbarItem(item);
  177.           
  178.           if (nextSibling)
  179.             toolbar.insertBefore(wrapper, nextSibling);
  180.           else
  181.             toolbar.appendChild(wrapper);
  182.         }
  183.       }
  184.     }
  185.   }
  186. }
  187.  
  188. /**
  189.  * Unwraps all items in all customizable toolbars in a toolbox.
  190.  */
  191. function unwrapToolbarItems()
  192. {
  193.   var paletteItems = gToolbox.getElementsByTagName("toolbarpaletteitem");
  194.   for (var i = 0; i < paletteItems.length; ++i) {
  195.     var paletteItem = paletteItems[i];    
  196.     var toolbarItem = paletteItem.firstChild;
  197.  
  198.     if (paletteItem.hasAttribute("itemdisabled"))
  199.       toolbarItem.disabled = true;
  200.  
  201.     if (paletteItem.hasAttribute("itemcommand"))
  202.       toolbarItem.setAttribute("command", paletteItem.getAttribute("itemcommand"));
  203.  
  204.     paletteItem.removeChild(toolbarItem);
  205.     paletteItem.parentNode.replaceChild(toolbarItem, paletteItem);
  206.   }
  207. }
  208.  
  209. /**
  210.  * Creates a wrapper that can be used to contain a toolbaritem and prevent
  211.  * it from receiving UI events.
  212.  */
  213. function createWrapper(aId)
  214. {
  215.   var wrapper = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
  216.                                          "toolbarpaletteitem");
  217.  
  218.   wrapper.id = "wrapper-"+aId;  
  219.   return wrapper;
  220. }
  221.  
  222. /**
  223.  * Wraps an item that has been cloned from a template and adds
  224.  * it to the end of a row in the palette.
  225.  */
  226. function wrapPaletteItem(aPaletteItem, aCurrentRow, aSpacer)
  227. {
  228.   var wrapper = createWrapper(aPaletteItem.id);
  229.  
  230.   wrapper.setAttribute("flex", 1);
  231.   wrapper.setAttribute("align", "center");
  232.   wrapper.setAttribute("pack", "center");
  233.   wrapper.setAttribute("minheight", "0");
  234.   wrapper.setAttribute("minwidth", "0");
  235.  
  236.   wrapper.appendChild(aPaletteItem);
  237.   
  238.   // XXX We need to call this AFTER the palette item has been appended
  239.   // to the wrapper or else we crash dropping certain buttons on the 
  240.   // palette due to removal of the command and disabled attributes - JRH
  241.   cleanUpItemForPalette(aPaletteItem, wrapper);
  242.  
  243.   if (aSpacer)
  244.     aCurrentRow.insertBefore(wrapper, aSpacer);
  245.   else
  246.     aCurrentRow.appendChild(wrapper);
  247.  
  248. }
  249.  
  250. /**
  251.  * Wraps an item that is currently on a toolbar and replaces the item
  252.  * with the wrapper. This is not used when dropping items from the palette,
  253.  * only when first starting the dialog and wrapping everything on the toolbars.
  254.  */
  255. function wrapToolbarItem(aToolbarItem)
  256. {
  257.   var wrapper = createWrapper(aToolbarItem.id);
  258.   
  259.   cleanupItemForToolbar(aToolbarItem, wrapper);
  260.   wrapper.flex = aToolbarItem.flex;
  261.  
  262.   if (aToolbarItem.parentNode)
  263.     aToolbarItem.parentNode.removeChild(aToolbarItem);
  264.   
  265.   wrapper.appendChild(aToolbarItem);
  266.   
  267.   return wrapper;
  268. }
  269.  
  270. /**
  271.  * Get the list of ids for the current set of items on each toolbar.
  272.  */
  273. function getCurrentItemIds()
  274. {
  275.   var currentItems = {};
  276.   for (var i = 0; i < gToolbox.childNodes.length; ++i) {
  277.     var toolbar = getToolbarAt(i);
  278.     if (isCustomizableToolbar(toolbar)) {
  279.       var child = toolbar.firstChild;
  280.       while (child) {
  281.         if (isToolbarItem(child))
  282.           currentItems[child.id] = 1;
  283.         child = child.nextSibling;
  284.       }
  285.     }
  286.   }
  287.   return currentItems;
  288. }
  289.  
  290. /**
  291.  * Builds the palette of draggable items that are not yet in a toolbar.
  292.  */
  293. function buildPalette()
  294. {
  295.   // Empty the palette first.
  296.   var paletteBox = document.getElementById("palette-box");
  297.   while (paletteBox.lastChild)
  298.     paletteBox.removeChild(paletteBox.lastChild);
  299.  
  300.   var currentRow = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
  301.                                             "hbox");
  302.   currentRow.setAttribute("class", "paletteRow");
  303.  
  304.   // Add the toolbar separator item.
  305.   var templateNode = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
  306.                                               "toolbarseparator");
  307.   templateNode.id = "separator";
  308.   wrapPaletteItem(templateNode, currentRow, null);
  309.  
  310.   // Add the toolbar spring item.
  311.   templateNode = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
  312.                                               "toolbarspring");
  313.   templateNode.id = "spring";
  314.   templateNode.flex = 1;
  315.   wrapPaletteItem(templateNode, currentRow, null);
  316.  
  317.   // Add the toolbar spacer item.
  318.   templateNode = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
  319.                                               "toolbarspacer");
  320.   templateNode.id = "spacer";
  321.   templateNode.flex = 1;
  322.   wrapPaletteItem(templateNode, currentRow, null);
  323.  
  324.   var rowSlot = 3;
  325.  
  326.   var currentItems = getCurrentItemIds();
  327.   templateNode = gToolbox.palette.firstChild;
  328.   while (templateNode) {
  329.     // Check if the item is already in a toolbar before adding it to the palette.
  330.     if (!(templateNode.id in currentItems)) {
  331.       var paletteItem = templateNode.cloneNode(true);
  332.  
  333.       if (rowSlot == kRowMax) {
  334.         // Append the old row.
  335.         paletteBox.appendChild(currentRow);
  336.  
  337.         // Make a new row.
  338.         currentRow = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
  339.                                               "hbox");
  340.         currentRow.setAttribute("class", "paletteRow");
  341.         rowSlot = 0;
  342.       }
  343.  
  344.       ++rowSlot;
  345.       wrapPaletteItem(paletteItem, currentRow, null);
  346.     }
  347.     
  348.     templateNode = templateNode.nextSibling;
  349.   }
  350.  
  351.   if (currentRow) { 
  352.     fillRowWithFlex(currentRow);
  353.     paletteBox.appendChild(currentRow);
  354.   }
  355. }
  356.  
  357. /**
  358.  * Creates a new palette item for a cloned template node and
  359.  * adds it to the last slot in the palette.
  360.  */
  361. function appendPaletteItem(aItem)
  362. {
  363.   var paletteBox = document.getElementById("palette-box");
  364.   var lastRow = paletteBox.lastChild;
  365.   var lastSpacer = lastRow.lastChild;
  366.    
  367.   if (lastSpacer.localName != "spacer") {
  368.     // The current row is full, so we have to create a new row.
  369.     lastRow = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
  370.                                         "hbox");
  371.     lastRow.setAttribute("class", "paletteRow");
  372.     paletteBox.appendChild(lastRow);
  373.     
  374.     wrapPaletteItem(aItem, lastRow, null);
  375.  
  376.     fillRowWithFlex(lastRow);
  377.   } else {
  378.     // Decrement the flex of the last spacer or remove it entirely.
  379.     var flex = lastSpacer.getAttribute("flex");
  380.     if (flex == 1) {
  381.       lastRow.removeChild(lastSpacer);
  382.       lastSpacer = null;
  383.     } else
  384.       lastSpacer.setAttribute("flex", --flex);
  385.  
  386.     // Insert the wrapper where the last spacer was.
  387.     wrapPaletteItem(aItem, lastRow, lastSpacer);
  388.   }
  389. }
  390.  
  391. function fillRowWithFlex(aRow)
  392. {
  393.   var remainingFlex = kRowMax - aRow.childNodes.length;
  394.   if (remainingFlex > 0) {
  395.     var spacer = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
  396.                                           "spacer");
  397.     spacer.setAttribute("flex", remainingFlex);
  398.     aRow.appendChild(spacer);
  399.   }
  400. }
  401.  
  402. /**
  403.  * Makes sure that an item that has been cloned from a template
  404.  * is stripped of all properties that may adversely affect it's
  405.  * appearance in the palette.
  406.  */
  407. function cleanUpItemForPalette(aItem, aWrapper)
  408. {
  409.   aWrapper.setAttribute("place", "palette");
  410.   setWrapperType(aItem, aWrapper);
  411.  
  412.   if (aItem.hasAttribute("title"))
  413.     aWrapper.setAttribute("title", aItem.getAttribute("title"));
  414.   else if (isSpecialItem(aItem)) {
  415.     var stringBundle = document.getElementById("stringBundle");
  416.     var title = stringBundle.getString(aItem.id + "Title");
  417.     aWrapper.setAttribute("title", title);
  418.   }
  419.   
  420.   // Remove attributes that screw up our appearance.
  421.   aItem.removeAttribute("command");
  422.   aItem.removeAttribute("observes");
  423.   aItem.removeAttribute("disabled");
  424.   aItem.removeAttribute("type");
  425.   
  426.   if (aItem.localName == "toolbaritem" && aItem.firstChild) {
  427.     aItem.firstChild.removeAttribute("observes");
  428.  
  429.     // So the throbber doesn't throb in the dialog,
  430.     // cute as that may be...
  431.     aItem.firstChild.removeAttribute("busy");
  432.   }
  433. }
  434.  
  435. /**
  436.  * Makes sure that an item that has been cloned from a template
  437.  * is stripped of all properties that may adversely affect it's
  438.  * appearance in the toolbar.  Store critical properties on the 
  439.  * wrapper so they can be put back on the item when we're done.
  440.  */
  441. function cleanupItemForToolbar(aItem, aWrapper)
  442. {
  443.   setWrapperType(aItem, aWrapper);
  444.   aWrapper.setAttribute("place", "toolbar");
  445.  
  446.   if (aItem.hasAttribute("command")) {
  447.     aWrapper.setAttribute("itemcommand", aItem.getAttribute("command"));
  448.     aItem.removeAttribute("command");
  449.   }
  450.  
  451.   if (aItem.disabled) {
  452.     aWrapper.setAttribute("itemdisabled", "true");
  453.     aItem.disabled = false;
  454.   }
  455. }
  456.  
  457. function setWrapperType(aItem, aWrapper)
  458. {
  459.   if (aItem.localName == "toolbarseparator") {
  460.     aWrapper.setAttribute("type", "separator");
  461.   } else if (aItem.localName == "toolbarspring") {
  462.     aWrapper.setAttribute("type", "spring");
  463.   } else if (aItem.localName == "toolbarspacer") {
  464.     aWrapper.setAttribute("type", "spacer");
  465.   } else if (aItem.localName == "toolbaritem" && aItem.firstChild) {
  466.     aWrapper.setAttribute("type", aItem.firstChild.localName);
  467.   }
  468. }
  469.  
  470. function setDragActive(aItem, aValue)
  471. {
  472.   var node = aItem;
  473.   var value = "left";
  474.   if (aItem.localName == "toolbar") {
  475.     node = aItem.lastChild;
  476.     value = "right";
  477.   }
  478.   
  479.   if (!node)
  480.     return;
  481.   
  482.   if (aValue) {
  483.     if (!node.hasAttribute("dragover"))
  484.       node.setAttribute("dragover", value);
  485.   } else {
  486.     node.removeAttribute("dragover");
  487.   }
  488. }
  489.  
  490. function addNewToolbar()
  491. {
  492.   var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
  493.                                 .getService(Components.interfaces.nsIPromptService);
  494.  
  495.   var stringBundle = document.getElementById("stringBundle");
  496.   var message = stringBundle.getString("enterToolbarName");
  497.   var title = stringBundle.getString("enterToolbarTitle");
  498.   
  499.   var name = {};
  500.   while (1) {
  501.     if (!promptService.prompt(window, title, message, name, null, {})) {
  502.       return;
  503.     } else {
  504.       // Check for an existing toolbar with the same name and prompt again
  505.       // if a conflict is found
  506.       var nameToId = "__customToolbar_" + name.value.replace(" ", "");
  507.       var existingToolbar = gToolboxDocument.getElementById(nameToId);
  508.       if (existingToolbar) {
  509.         message = stringBundle.getFormattedString("enterToolbarDup", [name.value]);
  510.       } else {
  511.         break;
  512.       }
  513.     }
  514.   }
  515.     
  516.   gToolbox.appendCustomToolbar(name.value, "");
  517.   
  518.   repositionDialog();
  519.   gToolboxChanged = true;
  520. }
  521.  
  522. /**
  523.  * Restore the default set of buttons to fixed toolbars,
  524.  * remove all custom toolbars, and rebuild the palette.
  525.  */
  526. function restoreDefaultSet()
  527. {
  528.   // Restore the defaultset for fixed toolbars.
  529.   var toolbar = gToolbox.firstChild;
  530.   while (toolbar) {
  531.     if (isCustomizableToolbar(toolbar)) {
  532.       if (!toolbar.hasAttribute("customindex")) {
  533.         var defaultSet = toolbar.getAttribute("defaultset");
  534.         if (defaultSet)
  535.           toolbar.currentSet = defaultSet;
  536.       }
  537.     }
  538.     toolbar = toolbar.nextSibling;
  539.   }
  540.  
  541.   // Remove all of the customized toolbars.
  542.   var child = gToolbox.lastChild;
  543.   while (child) {
  544.     if (child.hasAttribute("customindex")) {
  545.       var thisChild = child;
  546.       child = child.previousSibling;
  547.       gToolbox.removeChild(thisChild);
  548.     } else {
  549.       child = child.previousSibling;
  550.     }
  551.   }
  552.   
  553.   // Now rebuild the palette.
  554.   buildPalette();
  555.  
  556.   // Now re-wrap the items on the toolbar.
  557.   wrapToolbarItems();
  558.  
  559.   repositionDialog();
  560.   gToolboxChanged = true;
  561. }
  562.  
  563. function updateIconSize(aUseSmallIcons)
  564. {
  565.   var val = aUseSmallIcons ? "small" : null;
  566.   
  567.   setAttribute(gToolbox, "iconsize", val);
  568.   gToolboxDocument.persist(gToolbox.id, "iconsize");
  569.   
  570.   for (var i = 0; i < gToolbox.childNodes.length; ++i) {
  571.     var toolbar = getToolbarAt(i);
  572.     if (isCustomizableToolbar(toolbar)) {
  573.       setAttribute(toolbar, "iconsize", val);
  574.       gToolboxDocument.persist(toolbar.id, "iconsize");
  575.     }
  576.   }
  577.  
  578.   repositionDialog();
  579. }
  580.  
  581. function updateToolbarMode(aModeValue)
  582. {
  583.   setAttribute(gToolbox, "mode", aModeValue);
  584.   gToolboxDocument.persist(gToolbox.id, "mode");
  585.  
  586.   for (var i = 0; i < gToolbox.childNodes.length; ++i) {
  587.     var toolbar = getToolbarAt(i);
  588.     if (isCustomizableToolbar(toolbar)) {
  589.       setAttribute(toolbar, "mode", aModeValue);
  590.       gToolboxDocument.persist(toolbar.id, "mode");
  591.     }
  592.   }
  593.  
  594.   var iconSizeCheckbox = document.getElementById("smallicons");
  595.   if (aModeValue == "text") {
  596.     iconSizeCheckbox.disabled = true;
  597.     iconSizeCheckbox.checked = false;
  598.     updateIconSize(false);
  599.   }
  600.   else {
  601.     iconSizeCheckbox.disabled = false;
  602.   }
  603.  
  604.   repositionDialog();
  605. }
  606.  
  607.  
  608. function setAttribute(aElt, aAttr, aVal)
  609. {
  610.  if (aVal)
  611.     aElt.setAttribute(aAttr, aVal);
  612.   else
  613.     aElt.removeAttribute(aAttr);
  614. }
  615.  
  616. function isCustomizableToolbar(aElt)
  617. {
  618.   return aElt.localName == "toolbar" &&
  619.          aElt.getAttribute("customizable") == "true";
  620. }
  621.  
  622. function isSpecialItem(aElt)
  623. {
  624.   return aElt.localName == "toolbarseparator" ||
  625.          aElt.localName == "toolbarspring" ||
  626.          aElt.localName == "toolbarspacer";
  627. }
  628.  
  629. function isToolbarItem(aElt)
  630. {
  631.   return aElt.localName == "toolbarbutton" ||
  632.          aElt.localName == "toolbaritem" ||
  633.          aElt.localName == "toolbarseparator" ||
  634.          aElt.localName == "toolbarspring" ||
  635.          aElt.localName == "toolbarspacer";
  636. }
  637.  
  638. ///////////////////////////////////////////////////////////////////////////
  639. //// Drag and Drop observers
  640.  
  641. function onToolbarDragGesture(aEvent)
  642. {
  643.   nsDragAndDrop.startDrag(aEvent, dragStartObserver);
  644. }
  645.  
  646. function onToolbarDragOver(aEvent)
  647. {
  648.   nsDragAndDrop.dragOver(aEvent, toolbarDNDObserver);
  649. }
  650.  
  651. function onToolbarDragDrop(aEvent)
  652. {
  653.   nsDragAndDrop.drop(aEvent, toolbarDNDObserver);
  654. }
  655.  
  656. function onToolbarDragExit(aEvent)
  657. {
  658.   if (gCurrentDragOverItem)
  659.     setDragActive(gCurrentDragOverItem, false);
  660. }
  661.  
  662. var dragStartObserver =
  663. {
  664.   onDragStart: function (aEvent, aXferData, aDragAction) {
  665.     var documentId = gToolboxDocument.documentElement.id;
  666.     
  667.     var item = aEvent.target;
  668.     while (item && item.localName != "toolbarpaletteitem")
  669.       item = item.parentNode;
  670.     
  671.     item.setAttribute("dragactive", "true");
  672.     
  673.     aXferData.data = new TransferDataSet();
  674.     var data = new TransferData();
  675.     data.addDataForFlavour("text/toolbarwrapper-id/"+documentId, item.firstChild.id);
  676.     aXferData.data.push(data);
  677.   }
  678. }
  679.  
  680. var toolbarDNDObserver =
  681. {
  682.   onDragOver: function (aEvent, aFlavour, aDragSession)
  683.   {
  684.     var toolbar = aEvent.target;
  685.     var dropTarget = aEvent.target;
  686.     while (toolbar && toolbar.localName != "toolbar") {
  687.       dropTarget = toolbar;
  688.       toolbar = toolbar.parentNode;
  689.     }
  690.     
  691.     var previousDragItem = gCurrentDragOverItem;
  692.  
  693.     // Make sure we are dragging over a customizable toolbar.
  694.     if (!isCustomizableToolbar(toolbar)) {
  695.       gCurrentDragOverItem = null;
  696.       return;
  697.     }
  698.     
  699.     if (dropTarget.localName == "toolbar") {
  700.       gCurrentDragOverItem = dropTarget;
  701.     } else {
  702.       var dropTargetWidth = dropTarget.boxObject.width;
  703.       var dropTargetX = dropTarget.boxObject.x;
  704.  
  705.       gCurrentDragOverItem = null;
  706.       if (aEvent.clientX > (dropTargetX + (dropTargetWidth / 2))) {
  707.         gCurrentDragOverItem = dropTarget.nextSibling;
  708.         if (!gCurrentDragOverItem)
  709.           gCurrentDragOverItem = toolbar;
  710.       } else
  711.         gCurrentDragOverItem = dropTarget;
  712.     }    
  713.  
  714.     if (previousDragItem && gCurrentDragOverItem != previousDragItem) {
  715.       setDragActive(previousDragItem, false);
  716.     }
  717.     
  718.     setDragActive(gCurrentDragOverItem, true);
  719.     
  720.     aDragSession.canDrop = true;
  721.   },
  722.   
  723.   onDrop: function (aEvent, aXferData, aDragSession)
  724.   {
  725.     if (!gCurrentDragOverItem)
  726.       return;
  727.     
  728.     setDragActive(gCurrentDragOverItem, false);
  729.  
  730.     var draggedItemId = aXferData.data;
  731.     if (gCurrentDragOverItem.id == draggedItemId)
  732.       return;
  733.  
  734.     var toolbar = aEvent.target;
  735.     while (toolbar.localName != "toolbar")
  736.       toolbar = toolbar.parentNode;
  737.  
  738.     var draggedPaletteWrapper = document.getElementById("wrapper-"+draggedItemId);       
  739.     if (!draggedPaletteWrapper) {
  740.       // The wrapper has been dragged from the toolbar.
  741.       
  742.       // Get the wrapper from the toolbar document and make sure that
  743.       // it isn't being dropped on itself.
  744.       var wrapper = gToolboxDocument.getElementById("wrapper-"+draggedItemId);
  745.       if (wrapper == gCurrentDragOverItem)
  746.         return;
  747.  
  748.       // Don't allow static kids (e.g., the menubar) to move.
  749.       if (wrapper.parentNode.firstPermanentChild && wrapper.parentNode.firstPermanentChild.id == wrapper.firstChild.id)
  750.         return;
  751.       if (wrapper.parentNode.lastPermanentChild && wrapper.parentNode.lastPermanentChild.id == wrapper.firstChild.id)
  752.         return;
  753.  
  754.       // Remove the item from it's place in the toolbar.
  755.       wrapper.parentNode.removeChild(wrapper);
  756.  
  757.       // Determine which toolbar we are dropping on.
  758.       var dropToolbar = null;
  759.       if (gCurrentDragOverItem.localName == "toolbar")
  760.         dropToolbar = gCurrentDragOverItem;
  761.       else
  762.         dropToolbar = gCurrentDragOverItem.parentNode;
  763.       
  764.       // Insert the item into the toolbar.
  765.       if (gCurrentDragOverItem != dropToolbar)
  766.         dropToolbar.insertBefore(wrapper, gCurrentDragOverItem);
  767.       else
  768.         dropToolbar.appendChild(wrapper);
  769.     } else {
  770.       // The item has been dragged from the palette
  771.       
  772.       // Create a new wrapper for the item. We don't know the id yet.
  773.       var wrapper = createWrapper("");
  774.  
  775.       // Ask the toolbar to clone the item's template, place it inside the wrapper, and insert it in the toolbar.
  776.       var newItem = toolbar.insertItem(draggedItemId, gCurrentDragOverItem == toolbar ? null : gCurrentDragOverItem, wrapper);
  777.       
  778.       // Prepare the item and wrapper to look good on the toolbar.
  779.       cleanupItemForToolbar(newItem, wrapper);
  780.       wrapper.id = "wrapper-"+newItem.id;
  781.       wrapper.flex = newItem.flex;
  782.  
  783.       // Remove the wrapper from the palette.
  784.       var currentRow = draggedPaletteWrapper.parentNode;
  785.       if (draggedItemId != "separator" &&
  786.           draggedItemId != "spring" &&
  787.           draggedItemId != "spacer")
  788.       {
  789.         currentRow.removeChild(draggedPaletteWrapper);
  790.  
  791.         while (currentRow) {
  792.           // Pull the first child of the next row up
  793.           // into this row.
  794.           var nextRow = currentRow.nextSibling;
  795.           
  796.           if (!nextRow) {
  797.             var last = currentRow.lastChild;
  798.             var first = currentRow.firstChild;
  799.             if (first == last) {
  800.               // Kill the row.
  801.               currentRow.parentNode.removeChild(currentRow);
  802.               break;
  803.             }
  804.  
  805.             if (last.localName == "spacer") {
  806.               var flex = last.getAttribute("flex");
  807.               last.setAttribute("flex", ++flex);
  808.               // Reflow doesn't happen for some reason.  Trigger it with a hide/show. ICK! -dwh
  809.               last.hidden = true;
  810.               last.hidden = false;
  811.               break;
  812.             } else {
  813.               // Make a spacer and give it a flex of 1.
  814.               var spacer = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
  815.                                                     "spacer");
  816.               spacer.setAttribute("flex", "1");
  817.               currentRow.appendChild(spacer);
  818.             }
  819.             break;
  820.           }
  821.           
  822.           currentRow.appendChild(nextRow.firstChild);
  823.           currentRow = currentRow.nextSibling;
  824.         }
  825.       }
  826.     }
  827.     
  828.     gCurrentDragOverItem = null;
  829.  
  830.     repositionDialog();
  831.     gToolboxChanged = true;
  832.   },
  833.   
  834.   _flavourSet: null,
  835.   
  836.   getSupportedFlavours: function ()
  837.   {
  838.     if (!this._flavourSet) {
  839.       this._flavourSet = new FlavourSet();
  840.       var documentId = gToolboxDocument.documentElement.id;
  841.       this._flavourSet.appendFlavour("text/toolbarwrapper-id/"+documentId);
  842.     }
  843.     return this._flavourSet;
  844.   }
  845. }
  846.  
  847. var paletteDNDObserver =
  848. {
  849.   onDragOver: function (aEvent, aFlavour, aDragSession)
  850.   {
  851.     aDragSession.canDrop = true;
  852.   },
  853.   
  854.   onDrop: function(aEvent, aXferData, aDragSession)
  855.   {
  856.     var itemId = aXferData.data;
  857.     
  858.     var wrapper = gToolboxDocument.getElementById("wrapper-"+itemId);
  859.     if (wrapper) {
  860.       // Don't allow static kids (e.g., the menubar) to move.
  861.       if (wrapper.parentNode.firstPermanentChild && wrapper.parentNode.firstPermanentChild.id == wrapper.firstChild.id)
  862.         return;
  863.       if (wrapper.parentNode.lastPermanentChild && wrapper.parentNode.lastPermanentChild.id == wrapper.firstChild.id)
  864.         return;
  865.  
  866.       // The item was dragged out of the toolbar.
  867.       wrapper.parentNode.removeChild(wrapper);
  868.       
  869.       var wrapperType = wrapper.getAttribute("type");
  870.       if (wrapperType != "separator" && wrapperType != "spacer" && wrapperType != "spring") {
  871.         // Find the template node in the toolbox palette
  872.         var templateNode = gToolbox.palette.firstChild;
  873.         while (templateNode) {
  874.           if (templateNode.id == itemId)
  875.             break;
  876.           templateNode = templateNode.nextSibling;
  877.         }
  878.         if (!templateNode)
  879.           return;
  880.         
  881.         // Clone the template and add it to our palette.
  882.         var paletteItem = templateNode.cloneNode(true);
  883.         appendPaletteItem(paletteItem);
  884.       }
  885.     }
  886.     
  887.     repositionDialog();
  888.     gToolboxChanged = true;
  889.   },
  890.   
  891.   _flavourSet: null,
  892.   
  893.   getSupportedFlavours: function ()
  894.   {
  895.     if (!this._flavourSet) {
  896.       this._flavourSet = new FlavourSet();
  897.       var documentId = gToolboxDocument.documentElement.id;
  898.       this._flavourSet.appendFlavour("text/toolbarwrapper-id/"+documentId);
  899.     }
  900.     return this._flavourSet;
  901.   }
  902. }
  903.  
  904.