123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476 |
- <?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;
- 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>
|