summaryrefslogtreecommitdiffstats
path: root/components/places/content/menu.xml
diff options
context:
space:
mode:
Diffstat (limited to 'components/places/content/menu.xml')
-rw-r--r--components/places/content/menu.xml488
1 files changed, 488 insertions, 0 deletions
diff --git a/components/places/content/menu.xml b/components/places/content/menu.xml
new file mode 100644
index 0000000..d4041ec
--- /dev/null
+++ b/components/places/content/menu.xml
@@ -0,0 +1,488 @@
+<?xml version="1.0"?>
+
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+<bindings id="placesMenuBindings"
+ xmlns="http://www.mozilla.org/xbl"
+ xmlns:xbl="http://www.mozilla.org/xbl"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <binding id="places-popup-base"
+ extends="chrome://global/content/bindings/popup.xml#popup">
+ <content>
+ <xul:hbox flex="1">
+ <xul:vbox class="menupopup-drop-indicator-bar" hidden="true">
+ <xul:image class="menupopup-drop-indicator" mousethrough="always"/>
+ </xul:vbox>
+ <xul:arrowscrollbox class="popup-internal-box" flex="1" orient="vertical"
+ smoothscroll="false">
+ <children/>
+ </xul:arrowscrollbox>
+ </xul:hbox>
+ </content>
+
+ <implementation>
+
+ <field name="_indicatorBar">
+ document.getAnonymousElementByAttribute(this, "class",
+ "menupopup-drop-indicator-bar");
+ </field>
+
+ <field name="_scrollBox">
+ document.getAnonymousElementByAttribute(this, "class",
+ "popup-internal-box");
+ </field>
+
+ <!-- This is the view that manage the popup -->
+ <field name="_rootView">PlacesUIUtils.getViewForNode(this);</field>
+
+ <!-- Check if we should hide the drop indicator for the target -->
+ <method name="_hideDropIndicator">
+ <parameter name="aEvent"/>
+ <body><![CDATA[
+ let target = aEvent.target;
+
+ // Don't draw the drop indicator outside of markers.
+ // The markers are hidden, since otherwise sometimes popups acquire
+ // scrollboxes on OS X, so we can't use them directly.
+ let firstChildTop = this._startMarker.nextSibling.boxObject.y;
+ let lastChildBottom = this._endMarker.previousSibling.boxObject.y +
+ this._endMarker.previousSibling.boxObject.height;
+ let betweenMarkers = target.boxObject.y >= firstChildTop ||
+ target.boxObject.y <= lastChildBottom;
+
+ // Hide the dropmarker if current node is not a Places node.
+ return !(target && target._placesNode && betweenMarkers);
+ ]]></body>
+ </method>
+
+ <!-- This function returns information about where to drop when
+ dragging over this popup insertion point -->
+ <method name="_getDropPoint">
+ <parameter name="aEvent"/>
+ <body><![CDATA[
+ // Can't drop if the menu isn't a folder
+ let resultNode = this._placesNode;
+
+ if (!PlacesUtils.nodeIsFolder(resultNode) ||
+ PlacesControllerDragHelper.disallowInsertion(resultNode)) {
+ return null;
+ }
+
+ var dropPoint = { ip: null, folderElt: null };
+
+ // The element we are dragging over
+ let elt = aEvent.target;
+ if (elt.localName == "menupopup")
+ elt = elt.parentNode;
+
+ // Calculate positions taking care of arrowscrollbox
+ let eventY = aEvent.layerY;
+ let scrollbox = this._scrollBox;
+ let scrollboxOffset = scrollbox.scrollBoxObject.y -
+ (scrollbox.boxObject.y - this.boxObject.y);
+ let eltY = elt.boxObject.y - scrollboxOffset;
+ let eltHeight = elt.boxObject.height;
+
+ if (!elt._placesNode) {
+ // If we are dragging over a non places node drop at the end.
+ dropPoint.ip = new InsertionPoint(
+ PlacesUtils.getConcreteItemId(resultNode),
+ -1,
+ Ci.nsITreeView.DROP_ON);
+ // We can set folderElt if we are dropping over a static menu that
+ // has an internal placespopup.
+ let isMenu = elt.localName == "menu" ||
+ (elt.localName == "toolbarbutton" &&
+ elt.getAttribute("type") == "menu");
+ if (isMenu && elt.lastChild &&
+ elt.lastChild.hasAttribute("placespopup"))
+ dropPoint.folderElt = elt;
+ return dropPoint;
+ }
+ if ((PlacesUtils.nodeIsFolder(elt._placesNode) &&
+ !PlacesUIUtils.isContentsReadOnly(elt._placesNode)) ||
+ PlacesUtils.nodeIsTagQuery(elt._placesNode)) {
+ // This is a folder or a tag container.
+ if (eventY - eltY < eltHeight * 0.20) {
+ // If mouse is in the top part of the element, drop above folder.
+ dropPoint.ip = new InsertionPoint(
+ PlacesUtils.getConcreteItemId(resultNode),
+ -1,
+ Ci.nsITreeView.DROP_BEFORE,
+ PlacesUtils.nodeIsTagQuery(elt._placesNode),
+ elt._placesNode.itemId);
+ return dropPoint;
+ }
+ else if (eventY - eltY < eltHeight * 0.80) {
+ // If mouse is in the middle of the element, drop inside folder.
+ dropPoint.ip = new InsertionPoint(
+ PlacesUtils.getConcreteItemId(elt._placesNode),
+ -1,
+ Ci.nsITreeView.DROP_ON,
+ PlacesUtils.nodeIsTagQuery(elt._placesNode));
+ dropPoint.folderElt = elt;
+ return dropPoint;
+ }
+ }
+ else if (eventY - eltY <= eltHeight / 2) {
+ // This is a non-folder node or a readonly folder.
+ // If the mouse is above the middle, drop above this item.
+ dropPoint.ip = new InsertionPoint(
+ PlacesUtils.getConcreteItemId(resultNode),
+ -1,
+ Ci.nsITreeView.DROP_BEFORE,
+ PlacesUtils.nodeIsTagQuery(elt._placesNode),
+ elt._placesNode.itemId);
+ return dropPoint;
+ }
+
+ // Drop below the item.
+ dropPoint.ip = new InsertionPoint(
+ PlacesUtils.getConcreteItemId(resultNode),
+ -1,
+ Ci.nsITreeView.DROP_AFTER,
+ PlacesUtils.nodeIsTagQuery(elt._placesNode),
+ elt._placesNode.itemId);
+ return dropPoint;
+ ]]></body>
+ </method>
+
+ <!-- Sub-menus should be opened when the mouse drags over them, and closed
+ when the mouse drags off. The overFolder object manages opening and
+ closing of folders when the mouse hovers. -->
+ <field name="_overFolder"><![CDATA[({
+ _self: this,
+ _folder: {elt: null,
+ openTimer: null,
+ hoverTime: 350,
+ closeTimer: null},
+ _closeMenuTimer: null,
+
+ get elt() {
+ return this._folder.elt;
+ },
+ set elt(val) {
+ return this._folder.elt = val;
+ },
+
+ get openTimer() {
+ return this._folder.openTimer;
+ },
+ set openTimer(val) {
+ return this._folder.openTimer = val;
+ },
+
+ get hoverTime() {
+ return this._folder.hoverTime;
+ },
+ set hoverTime(val) {
+ return this._folder.hoverTime = val;
+ },
+
+ get closeTimer() {
+ return this._folder.closeTimer;
+ },
+ set closeTimer(val) {
+ return this._folder.closeTimer = val;
+ },
+
+ get closeMenuTimer() {
+ return this._closeMenuTimer;
+ },
+ set closeMenuTimer(val) {
+ return this._closeMenuTimer = val;
+ },
+
+ setTimer: function OF__setTimer(aTime) {
+ var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+ timer.initWithCallback(this, aTime, timer.TYPE_ONE_SHOT);
+ return timer;
+ },
+
+ notify: function OF__notify(aTimer) {
+ // Function to process all timer notifications.
+
+ if (aTimer == this._folder.openTimer) {
+ // Timer to open a submenu that's being dragged over.
+ this._folder.elt.lastChild.setAttribute("autoopened", "true");
+ this._folder.elt.lastChild.showPopup(this._folder.elt);
+ this._folder.openTimer = null;
+ }
+
+ else if (aTimer == this._folder.closeTimer) {
+ // Timer to close a submenu that's been dragged off of.
+ // Only close the submenu if the mouse isn't being dragged over any
+ // of its child menus.
+ var draggingOverChild = PlacesControllerDragHelper
+ .draggingOverChildNode(this._folder.elt);
+ if (draggingOverChild)
+ this._folder.elt = null;
+ this.clear();
+
+ // Close any parent folders which aren't being dragged over.
+ // (This is necessary because of the above code that keeps a folder
+ // open while its children are being dragged over.)
+ if (!draggingOverChild)
+ this.closeParentMenus();
+ }
+
+ else if (aTimer == this.closeMenuTimer) {
+ // Timer to close this menu after the drag exit.
+ var popup = this._self;
+ // if we are no more dragging we can leave the menu open to allow
+ // for better D&D bookmark organization
+ if (PlacesControllerDragHelper.getSession() &&
+ !PlacesControllerDragHelper.draggingOverChildNode(popup.parentNode)) {
+ popup.hidePopup();
+ // Close any parent menus that aren't being dragged over;
+ // otherwise they'll stay open because they couldn't close
+ // while this menu was being dragged over.
+ this.closeParentMenus();
+ }
+ this._closeMenuTimer = null;
+ }
+ },
+
+ // Helper function to close all parent menus of this menu,
+ // as long as none of the parent's children are currently being
+ // dragged over.
+ closeParentMenus: function OF__closeParentMenus() {
+ var popup = this._self;
+ var parent = popup.parentNode;
+ while (parent) {
+ if (parent.localName == "menupopup" && parent._placesNode) {
+ if (PlacesControllerDragHelper.draggingOverChildNode(parent.parentNode))
+ break;
+ parent.hidePopup();
+ }
+ parent = parent.parentNode;
+ }
+ },
+
+ // The mouse is no longer dragging over the stored menubutton.
+ // Close the menubutton, clear out drag styles, and clear all
+ // timers for opening/closing it.
+ clear: function OF__clear() {
+ if (this._folder.elt && this._folder.elt.lastChild) {
+ if (!this._folder.elt.lastChild.hasAttribute("dragover"))
+ this._folder.elt.lastChild.hidePopup();
+ // remove menuactive style
+ this._folder.elt.removeAttribute("_moz-menuactive");
+ this._folder.elt = null;
+ }
+ if (this._folder.openTimer) {
+ this._folder.openTimer.cancel();
+ this._folder.openTimer = null;
+ }
+ if (this._folder.closeTimer) {
+ this._folder.closeTimer.cancel();
+ this._folder.closeTimer = null;
+ }
+ }
+ })]]></field>
+
+ <method name="_cleanupDragDetails">
+ <body><![CDATA[
+ // Called on dragend and drop.
+ PlacesControllerDragHelper.currentDropTarget = null;
+ this._rootView._draggedElt = null;
+ this.removeAttribute("dragover");
+ this.removeAttribute("dragstart");
+ this._indicatorBar.hidden = true;
+ ]]></body>
+ </method>
+
+ </implementation>
+
+ <handlers>
+ <handler event="DOMMenuItemActive"><![CDATA[
+ let elt = event.target;
+ if (elt.parentNode != this)
+ return;
+
+#ifdef XP_MACOSX
+ // XXX: The following check is a temporary hack until bug 420033 is
+ // resolved.
+ let parentElt = elt.parent;
+ while (parentElt) {
+ if (parentElt.id == "bookmarksMenuPopup" ||
+ parentElt.id == "goPopup")
+ return;
+
+ parentElt = parentElt.parentNode;
+ }
+#endif
+
+ if (window.XULBrowserWindow) {
+ let elt = event.target;
+ let placesNode = elt._placesNode;
+
+ var linkURI;
+ if (placesNode && PlacesUtils.nodeIsURI(placesNode))
+ linkURI = placesNode.uri;
+ else if (elt.hasAttribute("targetURI"))
+ linkURI = elt.getAttribute("targetURI");
+
+ if (linkURI)
+ window.XULBrowserWindow.setOverLink(linkURI, null);
+ }
+ ]]></handler>
+
+ <handler event="DOMMenuItemInactive"><![CDATA[
+ let elt = event.target;
+ if (elt.parentNode != this)
+ return;
+
+ if (window.XULBrowserWindow)
+ window.XULBrowserWindow.setOverLink("", null);
+ ]]></handler>
+
+ <handler event="dragstart"><![CDATA[
+ if (!event.target._placesNode)
+ return;
+
+ let draggedElt = event.target._placesNode;
+
+ // Force a copy action if parent node is a query or we are dragging a
+ // not-removable node.
+ if (!PlacesControllerDragHelper.canMoveNode(draggedElt))
+ event.dataTransfer.effectAllowed = "copyLink";
+
+ // Activate the view and cache the dragged element.
+ this._rootView._draggedElt = draggedElt;
+ this._rootView.controller.setDataTransfer(event);
+ this.setAttribute("dragstart", "true");
+ event.stopPropagation();
+ ]]></handler>
+
+ <handler event="drop"><![CDATA[
+ PlacesControllerDragHelper.currentDropTarget = event.target;
+
+ let dropPoint = this._getDropPoint(event);
+ if (dropPoint && dropPoint.ip) {
+ PlacesControllerDragHelper.onDrop(dropPoint.ip, event.dataTransfer);
+ event.preventDefault();
+ }
+
+ this._cleanupDragDetails();
+ event.stopPropagation();
+ ]]></handler>
+
+ <handler event="dragover"><![CDATA[
+ PlacesControllerDragHelper.currentDropTarget = event.target;
+ let dt = event.dataTransfer;
+
+ let dropPoint = this._getDropPoint(event);
+ if (!dropPoint || !dropPoint.ip ||
+ !PlacesControllerDragHelper.canDrop(dropPoint.ip, dt)) {
+ this._indicatorBar.hidden = true;
+ event.stopPropagation();
+ return;
+ }
+
+ // Mark this popup as being dragged over.
+ this.setAttribute("dragover", "true");
+
+ if (dropPoint.folderElt) {
+ // We are dragging over a folder.
+ // _overFolder should take the care of opening it on a timer.
+ if (this._overFolder.elt &&
+ this._overFolder.elt != dropPoint.folderElt) {
+ // We are dragging over a new folder, let's clear old values
+ this._overFolder.clear();
+ }
+ if (!this._overFolder.elt) {
+ this._overFolder.elt = dropPoint.folderElt;
+ // Create the timer to open this folder.
+ this._overFolder.openTimer = this._overFolder
+ .setTimer(this._overFolder.hoverTime);
+ }
+ // Since we are dropping into a folder set the corresponding style.
+ dropPoint.folderElt.setAttribute("_moz-menuactive", true);
+ }
+ else {
+ // We are not dragging over a folder.
+ // Clear out old _overFolder information.
+ this._overFolder.clear();
+ }
+
+ // Autoscroll the popup strip if we drag over the scroll buttons.
+ let anonid = event.originalTarget.getAttribute('anonid');
+ let scrollDir = anonid == "scrollbutton-up" ? -1 :
+ anonid == "scrollbutton-down" ? 1 : 0;
+ if (scrollDir != 0) {
+ this._scrollBox.scrollByIndex(scrollDir, false);
+ }
+
+ // Check if we should hide the drop indicator for this target.
+ if (dropPoint.folderElt || this._hideDropIndicator(event)) {
+ this._indicatorBar.hidden = true;
+ event.preventDefault();
+ event.stopPropagation();
+ return;
+ }
+
+ // We should display the drop indicator relative to the arrowscrollbox.
+ let sbo = this._scrollBox.scrollBoxObject;
+ let newMarginTop = 0;
+ if (scrollDir == 0) {
+ let elt = this.firstChild;
+ while (elt && event.screenY > elt.boxObject.screenY +
+ elt.boxObject.height / 2)
+ elt = elt.nextSibling;
+ newMarginTop = elt ? elt.boxObject.screenY - sbo.screenY :
+ sbo.height;
+ }
+ else if (scrollDir == 1)
+ newMarginTop = sbo.height;
+
+ // Set the new marginTop based on arrowscrollbox.
+ newMarginTop += sbo.y - this._scrollBox.boxObject.y;
+ this._indicatorBar.firstChild.style.marginTop = newMarginTop + "px";
+ this._indicatorBar.hidden = false;
+
+ event.preventDefault();
+ event.stopPropagation();
+ ]]></handler>
+
+ <handler event="dragexit"><![CDATA[
+ PlacesControllerDragHelper.currentDropTarget = null;
+ this.removeAttribute("dragover");
+
+ // If we have not moved to a valid new target clear the drop indicator
+ // this happens when moving out of the popup.
+ let target = event.relatedTarget;
+ if (!target)
+ this._indicatorBar.hidden = true;
+
+ // Close any folder being hovered over
+ if (this._overFolder.elt) {
+ this._overFolder.closeTimer = this._overFolder
+ .setTimer(this._overFolder.hoverTime);
+ }
+
+ // The autoopened attribute is set when this folder was automatically
+ // opened after the user dragged over it. If this attribute is set,
+ // auto-close the folder on drag exit.
+ // We should also try to close this popup if the drag has started
+ // from here, the timer will check if we are dragging over a child.
+ if (this.hasAttribute("autoopened") ||
+ this.hasAttribute("dragstart")) {
+ this._overFolder.closeMenuTimer = this._overFolder
+ .setTimer(this._overFolder.hoverTime);
+ }
+
+ event.stopPropagation();
+ ]]></handler>
+
+ <handler event="dragend"><![CDATA[
+ this._cleanupDragDetails();
+ ]]></handler>
+
+ </handlers>
+ </binding>
+</bindings>