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