mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-16 23:05:42 +00:00
Bug 389290 - Bookmarks Menu - dropmarker missing. Patch from Simon Bunzli <zeniko@gmail.com> and Marco Bonardo [mak77] <mak77@supereva.it>, r=me.
This commit is contained in:
parent
eb3e4a8015
commit
22146ff22c
@ -22,6 +22,7 @@
|
||||
* Ben Goodger <beng@google.com>
|
||||
* Myk Melez <myk@mozilla.org>
|
||||
* Asaf Romano <mano@mozilla.com>
|
||||
* Marco Bonardo <mak77@supereva.it>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
@ -1222,291 +1223,6 @@ PlacesController.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
function PlacesMenuDNDObserver(aView, aPopup) {
|
||||
this._view = aView;
|
||||
this._popup = aPopup;
|
||||
this._popup.addEventListener("draggesture", this, false);
|
||||
this._popup.addEventListener("dragover", this, false);
|
||||
this._popup.addEventListener("dragdrop", this, false);
|
||||
this._popup.addEventListener("dragexit", this, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* XXXmano-please-rewrite-me: This code was ported over from menu.xul in bug 399729.
|
||||
* Unsurprisngly it's still mostly broken due to bug 337761, thus I didn't bother
|
||||
* trying to cleaning up this extremely buggy over-folder detection code yet.
|
||||
*/
|
||||
PlacesMenuDNDObserver.prototype = {
|
||||
_view: null,
|
||||
_popup: null,
|
||||
|
||||
// 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.
|
||||
_overFolder: {node: null, openTimer: null, hoverTime: 350, closeTimer: null},
|
||||
|
||||
// If this menu's parent auto-opened it because it was dragged over, but didn't
|
||||
// close it because the mouse dragged into it, the menu should close itself
|
||||
// onDragExit. This timer is set in dragExit to close the menu.
|
||||
_closeMenuTimer: null,
|
||||
|
||||
_setTimer: function TBV_DO_setTimer(time) {
|
||||
var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
timer.initWithCallback(this, time, timer.TYPE_ONE_SHOT);
|
||||
return timer;
|
||||
},
|
||||
|
||||
// Function to process all timer notifications.
|
||||
notify: function TBV_DO_notify(timer) {
|
||||
// Timer to open a submenu that's being dragged over.
|
||||
if (timer == this._overFolder.openTimer) {
|
||||
this._overFolder.node.lastChild.setAttribute("autoopened", "true");
|
||||
this._overFolder.node.lastChild.showPopup(this._overFolder.node);
|
||||
this._overFolder.openTimer = null;
|
||||
}
|
||||
|
||||
// Timer to close a submenu that's been dragged off of.
|
||||
if (timer == this._overFolder.closeTimer) {
|
||||
// Only close the submenu if the mouse isn't being dragged over any
|
||||
// of its child menus.
|
||||
var draggingOverChild =
|
||||
PlacesControllerDragHelper.draggingOverChildNode(this._overFolder.node);
|
||||
if (draggingOverChild)
|
||||
this._overFolder.node = null;
|
||||
this._clearOverFolder();
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
// Timer to close this menu after the drag exit.
|
||||
if (timer == this._closeMenuTimer) {
|
||||
if (!PlacesControllerDragHelper.draggingOverChildNode(this._popup)) {
|
||||
this._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();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 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 TBV_DO_closeParentMenus() {
|
||||
var parent = this._popup.parentNode;
|
||||
while (parent) {
|
||||
if (parent.nodeName == "menupopup" && parent._resultNode) {
|
||||
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.
|
||||
_clearOverFolder: function TBV_DO_clearOverFolder() {
|
||||
if (this._overFolder.node && this._overFolder.node.lastChild) {
|
||||
if (!this._overFolder.node.lastChild.hasAttribute("dragover"))
|
||||
this._overFolder.node.lastChild.hidePopup();
|
||||
this._overFolder.node = null;
|
||||
}
|
||||
if (this._overFolder.openTimer) {
|
||||
this._overFolder.openTimer.cancel();
|
||||
this._overFolder.openTimer = null;
|
||||
}
|
||||
if (this._overFolder.closeTimer) {
|
||||
this._overFolder.closeTimer.cancel();
|
||||
this._overFolder.closeTimer = null;
|
||||
}
|
||||
},
|
||||
|
||||
// This function returns information about where to drop when
|
||||
// dragging over this menu--insertion point, child index to drop
|
||||
// before, and folder to drop into.
|
||||
_getDropPoint: function TBV_DO_getDropPoint(event) {
|
||||
// Can't drop if the menu isn't a folder
|
||||
var resultNode = this._popup._resultNode;
|
||||
if (!PlacesUtils.nodeIsFolder(resultNode))
|
||||
return null;
|
||||
|
||||
var dropPoint = { ip: null, beforeIndex: null, folderNode: null };
|
||||
// Loop through all the nodes to see which one this should
|
||||
// get dropped in/above/below.
|
||||
var start = 0;
|
||||
var end = this._popup.childNodes.length;
|
||||
if (this._popup == this._view && this._view.localName == "menupopup") {
|
||||
// Ignore static content at the top and bottom of the menu.
|
||||
start = this._view._startMarker + 1;
|
||||
if (this._view._endMarker != -1)
|
||||
end = this._view._endMarker;
|
||||
}
|
||||
|
||||
var popupFirstChildY = this._popup.firstChild.boxObject.y;
|
||||
for (var i = start; i < end; i++) {
|
||||
var xulNode = this._popup.childNodes[i];
|
||||
var nodeY = xulNode.boxObject.y - popupFirstChildY;
|
||||
var nodeHeight = xulNode.boxObject.height;
|
||||
if (xulNode.node &&
|
||||
PlacesUtils.nodeIsFolder(xulNode.node) &&
|
||||
!PlacesUtils.nodeIsReadOnly(xulNode.node)) {
|
||||
// This is a folder. If the mouse is in the top 25% of the
|
||||
// node, drop above the folder. If it's in the middle
|
||||
// 50%, drop into the folder. If it's past that, drop below.
|
||||
if (event.layerY < nodeY + (nodeHeight * 0.25)) {
|
||||
// Drop above this folder.
|
||||
dropPoint.ip = new InsertionPoint(resultNode.itemId, i - start,
|
||||
-1);
|
||||
dropPoint.beforeIndex = i;
|
||||
return dropPoint;
|
||||
}
|
||||
else if (event.layerY < nodeY + (nodeHeight * 0.75)) {
|
||||
// Drop inside this folder.
|
||||
dropPoint.ip = new InsertionPoint(xulNode.node.itemId, -1, 1);
|
||||
dropPoint.beforeIndex = i;
|
||||
dropPoint.folderNode = xulNode;
|
||||
return dropPoint;
|
||||
}
|
||||
} else {
|
||||
// This is a non-folder node. If the mouse is above the middle,
|
||||
// drop above the folder. Otherwise, drop below.
|
||||
if (event.layerY < nodeY + (nodeHeight / 2)) {
|
||||
// Drop above this bookmark.
|
||||
dropPoint.ip = new InsertionPoint(resultNode.itemId, i - start, -1);
|
||||
dropPoint.beforeIndex = i;
|
||||
return dropPoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Should drop below the last node.
|
||||
dropPoint.ip = new InsertionPoint(resultNode.itemId, -1, 1);
|
||||
dropPoint.beforeIndex = -1;
|
||||
return dropPoint;
|
||||
},
|
||||
|
||||
// This function clears all of the dragover styles that were set when
|
||||
// a menuitem was dragged over.
|
||||
_clearStyles: function TBV_DO_clearStyles() {
|
||||
this._popup.removeAttribute("dragover");
|
||||
for (var i = 0; i < this._popup.childNodes.length; i++) {
|
||||
this._popup.childNodes[i].removeAttribute("dragover-top");
|
||||
this._popup.childNodes[i].removeAttribute("dragover-bottom");
|
||||
this._popup.childNodes[i].removeAttribute("dragover-into");
|
||||
}
|
||||
},
|
||||
|
||||
onDragStart: function TBV_DO_onDragStart(event, xferData, dragAction) {
|
||||
this._view._selection = event.target.node;
|
||||
this._view._cachedInsertionPoint = undefined;
|
||||
if (event.ctrlKey)
|
||||
dragAction.action = Ci.nsIDragService.DRAGDROP_ACTION_COPY;
|
||||
xferData.data = this._view.controller.getTransferData(dragAction.action);
|
||||
},
|
||||
|
||||
canDrop: function TBV_DO_canDrop(event, session) {
|
||||
// Can't drop if the menu isn't a folder
|
||||
var resultNode = this._popup._resultNode;
|
||||
if (!PlacesUtils.nodeIsFolder(resultNode))
|
||||
return null;
|
||||
|
||||
return PlacesControllerDragHelper.canDrop();
|
||||
},
|
||||
|
||||
onDragOver: function TBV_DO_onDragOver(event, flavor, session) {
|
||||
PlacesControllerDragHelper.currentDropTarget = event.target;
|
||||
var dropPoint = this._getDropPoint(event);
|
||||
if (dropPoint == null)
|
||||
return;
|
||||
|
||||
this._clearStyles();
|
||||
if (dropPoint.folderNode) {
|
||||
// Dragging over a folder; set the appropriate styles.
|
||||
if (this._overFolder.node != dropPoint.folderNode) {
|
||||
this._clearOverFolder();
|
||||
this._overFolder.node = dropPoint.folderNode;
|
||||
this._overFolder.openTimer = this._setTimer(this._overFolder.hoverTime);
|
||||
}
|
||||
dropPoint.folderNode.setAttribute("dragover-into", "true");
|
||||
}
|
||||
else {
|
||||
// Dragging over a menuitem, set dragover-top/bottom to show where
|
||||
// the item will be dropped and clear out any old folder info.
|
||||
if (dropPoint.beforeIndex == -1) {
|
||||
if (this._popup == this._view && this._view.localName == "menupopup" &&
|
||||
this._popup._endMarker != -1) {
|
||||
this._popup.childNodes[this._popup._endMarker]
|
||||
.setAttribute("dragover-top", "true");
|
||||
}
|
||||
else
|
||||
this._popup.lastChild.setAttribute("dragover-bottom", "true");
|
||||
}
|
||||
else {
|
||||
this._popup.childNodes[dropPoint.beforeIndex]
|
||||
.setAttribute("dragover-top", "true");
|
||||
}
|
||||
|
||||
// Clear out old folder information
|
||||
this._clearOverFolder();
|
||||
}
|
||||
this._popup.setAttribute("dragover", "true");
|
||||
},
|
||||
|
||||
onDrop: function TBV_DO_onDrop(event, dropData, session) {
|
||||
var dropPoint = this._getDropPoint(event);
|
||||
if (!dropPoint)
|
||||
return;
|
||||
|
||||
PlacesControllerDragHelper.onDrop(dropPoint.ip);
|
||||
},
|
||||
|
||||
onDragExit: function TBV_DO_onDragExit(event, session) {
|
||||
PlacesControllerDragHelper.currentDropTarget = null;
|
||||
this._clearStyles();
|
||||
// Close any folder being hovered over
|
||||
if (this._overFolder.node)
|
||||
this._overFolder.closeTimer = this._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.
|
||||
if (this._popup.hasAttribute("autoopened"))
|
||||
this._closeMenuTimer = this._setTimer(this._overFolder.hoverTime);
|
||||
},
|
||||
|
||||
getSupportedFlavours: function TBV_DO_getSupportedFlavours() {
|
||||
var flavorSet = new FlavourSet();
|
||||
var types = PlacesUtils.GENERIC_VIEW_DROP_TYPES;
|
||||
for (var i = 0; i < types.length; ++i)
|
||||
flavorSet.appendFlavour(types[i]);
|
||||
return flavorSet;
|
||||
},
|
||||
|
||||
handleEvent: function(aEvent) {
|
||||
switch (aEvent.type) {
|
||||
case "draggesture":
|
||||
if (aEvent.target.node) {
|
||||
nsDragAndDrop.startDrag(aEvent, this);
|
||||
}
|
||||
break;
|
||||
case "dragover":
|
||||
nsDragAndDrop.dragOver(aEvent, this);
|
||||
break;
|
||||
case "dragdrop":
|
||||
nsDragAndDrop.drop(aEvent, this);
|
||||
break;
|
||||
case "dragexit":
|
||||
nsDragAndDrop.dragExit(aEvent, this);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles drag and drop operations for views. Note that this is view agnostic!
|
||||
* You should not use PlacesController._view within these methods, since
|
||||
|
@ -23,6 +23,8 @@
|
||||
# Annie Sullivan <annie.sullivan@gmail.com>
|
||||
# Ben Goodger <beng@google.com>
|
||||
# Asaf Romano <mano@mozilla.com>
|
||||
# Simon Bünzli <zeniko@gmail.com>
|
||||
# Marco Bonardo <mak77@supereva.it>
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
@ -44,8 +46,422 @@
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<binding id="places-menupopup"
|
||||
<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">
|
||||
<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>
|
||||
|
||||
<!-- markers for start and end of valid places items -->
|
||||
<field name="_startMarker">-1</field>
|
||||
<field name="_endMarker">-1</field>
|
||||
|
||||
<!-- This is the view that manage the popup -->
|
||||
<field name="_rootView">PlacesUtils.getViewForNode(this);</field>
|
||||
|
||||
<method name="onDragOver">
|
||||
<parameter name="aEvent"/>
|
||||
<parameter name="aFlavour"/>
|
||||
<parameter name="aDragSession"/>
|
||||
<body><![CDATA[
|
||||
PlacesControllerDragHelper.currentDropTarget = aEvent.target;
|
||||
// check if we have a valid dropPoint
|
||||
var dropPoint = this._getDropPoint(aEvent);
|
||||
if (!dropPoint)
|
||||
return;
|
||||
|
||||
// add a dragover attribute to this popup
|
||||
this.setAttribute("dragover", "true");
|
||||
|
||||
if (dropPoint.folderNode) {
|
||||
// We are dragging over a folder
|
||||
// _overFolder should take the care of opening it on a timer
|
||||
if (this._overFolder.node &&
|
||||
this._overFolder.node != dropPoint.folderNode) {
|
||||
// we are dragging over a new folder, let's clear old values
|
||||
this._overFolder.clear();
|
||||
}
|
||||
if (!this._overFolder.node) {
|
||||
this._overFolder.node = dropPoint.folderNode;
|
||||
// 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.folderNode.setAttribute("dragover-into", "true");
|
||||
}
|
||||
else {
|
||||
// We are not dragging over a folder
|
||||
// Clear out old _overFolder information
|
||||
this._overFolder.clear();
|
||||
}
|
||||
|
||||
// Check if we should hide the drop indicator for this target
|
||||
if (!aDragSession.canDrop ||
|
||||
!dropPoint || dropPoint.folderNode ||
|
||||
this._hideDropIndicator(aEvent, dropPoint)) {
|
||||
this._indicatorBar.hidden = true;
|
||||
return;
|
||||
}
|
||||
|
||||
var scrollBoxObject = this._scrollBox.scrollBoxObject;
|
||||
// Autoscroll the popup strip if we drag over the scroll buttons
|
||||
var anonid = aEvent.originalTarget.getAttribute("anonid");
|
||||
var scrollDir = anonid == "scrollbutton-up" ? -1 :
|
||||
anonid == "scrollbutton-down" ? 1 : 0;
|
||||
if (scrollDir != 0)
|
||||
this._scrollBox.scrollByIndex(scrollDir);
|
||||
|
||||
// We should display the drop indicator relative to the arrowscrollbox
|
||||
var newMarginTop = 0;
|
||||
if (scrollDir == 0) {
|
||||
var node = this.firstChild;
|
||||
while (node && aEvent.screenY > node.boxObject.screenY +
|
||||
node.boxObject.height / 2)
|
||||
node = node.nextSibling;
|
||||
newMarginTop = node ? node.boxObject.screenY - scrollBoxObject.screenY :
|
||||
scrollBoxObject.height;
|
||||
}
|
||||
else if (scrollDir == 1)
|
||||
newMarginTop = scrollBoxObject.height;
|
||||
|
||||
// set the new marginTop based on arrowscrollbox
|
||||
newMarginTop += scrollBoxObject.y - this.boxObject.y;
|
||||
this._indicatorBar.firstChild.style.marginTop = newMarginTop + "px";
|
||||
this._indicatorBar.hidden = false;
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="onDragExit">
|
||||
<parameter name="aEvent"/>
|
||||
<parameter name="aDragSession"/>
|
||||
<body><![CDATA[
|
||||
PlacesControllerDragHelper.currentDropTarget = null;
|
||||
this.removeAttribute("dragover");
|
||||
// remove dragover-into style from previous target
|
||||
aEvent.target.removeAttribute("dragover-into");
|
||||
|
||||
// if we have not moved to a valid new target clear the drop indicator
|
||||
// this happens when moving out of the popup
|
||||
var target = aEvent.relatedTarget;
|
||||
if (!target)
|
||||
this._indicatorBar.hidden = true;
|
||||
|
||||
// Close any folder being hovered over
|
||||
if (this._overFolder.node) {
|
||||
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.
|
||||
if (this.hasAttribute("autoopened")) {
|
||||
this._overFolder.closeMenuTimer = this._overFolder
|
||||
.setTimer(this._overFolder.hoverTime);
|
||||
}
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="onDragStart">
|
||||
<parameter name="aEvent"/>
|
||||
<parameter name="aXferData"/>
|
||||
<parameter name="aDragAction"/>
|
||||
<body><![CDATA[
|
||||
this._rootView._selection = aEvent.target.node;
|
||||
this._rootView._cachedInsertionPoint = undefined;
|
||||
|
||||
if (aEvent.ctrlKey)
|
||||
aDragAction.action = Ci.nsIDragService.DRAGDROP_ACTION_COPY;
|
||||
aXferData.data = this._rootView.controller
|
||||
.getTransferData(aDragAction.action);
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="onDrop">
|
||||
<parameter name="aEvent"/>
|
||||
<parameter name="aDropData"/>
|
||||
<parameter name="aSession"/>
|
||||
<body><![CDATA[
|
||||
var dropPoint = this._getDropPoint(aEvent);
|
||||
if (!dropPoint)
|
||||
return;
|
||||
|
||||
PlacesControllerDragHelper.onDrop(dropPoint.ip);
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<!-- This returns the FavourSet accepted by this popup -->
|
||||
<method name="getSupportedFlavours">
|
||||
<body><![CDATA[
|
||||
var flavourSet = new FlavourSet();
|
||||
var acceptedDropFlavours = PlacesUtils.GENERIC_VIEW_DROP_TYPES;
|
||||
acceptedDropFlavours.forEach(flavourSet.appendFlavour, flavourSet);
|
||||
return flavourSet;
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<!-- Check if we should hide the drop indicator for the target -->
|
||||
<method name="_hideDropIndicator">
|
||||
<parameter name="aEvent"/>
|
||||
<body><![CDATA[
|
||||
var target = aEvent.target;
|
||||
|
||||
// in some view we have _startMarker and _endMarker, we should not
|
||||
// draw the drop indicator outside of them
|
||||
var betweenMarkers = true;
|
||||
if (this._startMarker != -1 &&
|
||||
target.boxObject.y < this.childNodes[this._startMarker].boxObject.y)
|
||||
betweenMarkers = false;
|
||||
if (this._endMarker != -1 &&
|
||||
target.boxObject.y > this.childNodes[this._endMarker].boxObject.y)
|
||||
betweenMarkers = false;
|
||||
|
||||
// hide the dropmarker if current node is not a places bookmark item
|
||||
return !(target && betweenMarkers && this.canDrop());
|
||||
]]></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
|
||||
var resultNode = this._resultNode;
|
||||
|
||||
if (!PlacesUtils.nodeIsFolder(resultNode))
|
||||
return null;
|
||||
|
||||
var dropPoint = { ip: null, beforeIndex: null, folderNode: null };
|
||||
// Loop through all the nodes to see which one this should
|
||||
// get dropped in/above/below.
|
||||
var start = 0;
|
||||
var popup = this;
|
||||
var end = popup.childNodes.length;
|
||||
if (popup == this._rootView &&
|
||||
this._rootView.localName == "menupopup") {
|
||||
// Ignore static content at the top and bottom of the menu.
|
||||
start = this._rootView._startMarker + 1;
|
||||
if (this._rootView._endMarker != -1)
|
||||
end = this._rootView._endMarker;
|
||||
}
|
||||
|
||||
var popupFirstChildY = popup.firstChild.boxObject.y;
|
||||
for (var i = start; i < end; i++) {
|
||||
var xulNode = popup.childNodes[i];
|
||||
var nodeY = xulNode.boxObject.y - popupFirstChildY;
|
||||
var nodeHeight = xulNode.boxObject.height;
|
||||
if (xulNode.node &&
|
||||
PlacesUtils.nodeIsFolder(xulNode.node) &&
|
||||
!PlacesUtils.nodeIsReadOnly(xulNode.node)) {
|
||||
// This is a folder. If the mouse is in the top 25% of the
|
||||
// node, drop above the folder. If it's in the middle
|
||||
// 50%, drop into the folder. If it's past that, drop below.
|
||||
if (aEvent.layerY < nodeY + (nodeHeight * 0.25)) {
|
||||
// Drop above this folder.
|
||||
dropPoint.ip = new InsertionPoint(resultNode.itemId,
|
||||
i - start, -1);
|
||||
dropPoint.beforeIndex = i;
|
||||
return dropPoint;
|
||||
}
|
||||
else if (aEvent.layerY < nodeY + (nodeHeight * 0.75)) {
|
||||
// Drop inside this folder.
|
||||
dropPoint.ip = new InsertionPoint(xulNode.node.itemId, -1, 1);
|
||||
dropPoint.beforeIndex = i;
|
||||
dropPoint.folderNode = xulNode;
|
||||
return dropPoint;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// This is a non-folder node. If the mouse is above the middle,
|
||||
// drop above the folder. Otherwise, drop below.
|
||||
if (aEvent.layerY < nodeY + (nodeHeight / 2)) {
|
||||
// Drop above this bookmark.
|
||||
dropPoint.ip = new InsertionPoint(resultNode.itemId,
|
||||
i - start, -1);
|
||||
dropPoint.beforeIndex = i;
|
||||
return dropPoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Should drop below the last node.
|
||||
dropPoint.ip = new InsertionPoint(resultNode.itemId, -1, 1);
|
||||
dropPoint.beforeIndex = -1;
|
||||
return dropPoint;
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="canDrop">
|
||||
<body><![CDATA[
|
||||
// Can't drop if the menu isn't a folder
|
||||
var resultNode = this._rootView.getResultNode();
|
||||
if (!PlacesUtils.nodeIsFolder(resultNode))
|
||||
return false;
|
||||
|
||||
return PlacesControllerDragHelper.canDrop();
|
||||
]]></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: {node: null, openTimer: null, hoverTime: 350, closeTimer: null},
|
||||
_closeMenuTimer: null,
|
||||
|
||||
get node() {
|
||||
return this._folder.node;
|
||||
},
|
||||
set node(val) {
|
||||
return this._folder.node = 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.node.lastChild.setAttribute("autoopened", "true");
|
||||
this._folder.node.lastChild.showPopup(this._folder.node);
|
||||
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.node);
|
||||
if (draggingOverChild)
|
||||
this._folder.node = 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 (!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();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 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.nodeName == "menupopup" && parent._resultNode) {
|
||||
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.node && this._folder.node.lastChild) {
|
||||
if (!this._folder.node.lastChild.hasAttribute("dragover"))
|
||||
this._folder.node.lastChild.hidePopup();
|
||||
// remove dragover-into style
|
||||
this._folder.node.removeAttribute("dragover-into");
|
||||
this._folder.node = 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>
|
||||
|
||||
</implementation>
|
||||
|
||||
<handlers>
|
||||
<handler event="draggesture" action="if (event.target.node) nsDragAndDrop.startDrag(event, this);"/>
|
||||
<handler event="dragdrop" action="nsDragAndDrop.drop(event, this);"/>
|
||||
<handler event="dragover" action="nsDragAndDrop.dragOver(event, this);"/>
|
||||
<handler event="dragexit" action="nsDragAndDrop.dragExit(event, this);"/>
|
||||
</handlers>
|
||||
</binding>
|
||||
|
||||
|
||||
<binding id="places-menupopup"
|
||||
extends="chrome://browser/content/places/menu.xml#places-popup-base">
|
||||
<implementation>
|
||||
<destructor><![CDATA[
|
||||
this._resultNode = null;
|
||||
@ -88,8 +504,6 @@
|
||||
resultNode.containerOpen = true;
|
||||
if (!popup._built)
|
||||
this._rebuild(popup);
|
||||
if (!popup._DNDObserver)
|
||||
popup._DNDObserver = new PlacesMenuDNDObserver(this, popup);
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
@ -572,13 +986,13 @@
|
||||
</method>
|
||||
</implementation>
|
||||
<handlers>
|
||||
<handler event="popupshowing" phase="capturing">
|
||||
<handler event="popupshowing" phase="capturing"><![CDATA[
|
||||
this._ensureInitialized();
|
||||
if (event.target._resultNode)
|
||||
this.onPopupShowing(event);
|
||||
</handler>
|
||||
]]></handler>
|
||||
|
||||
<handler event="popuphidden">
|
||||
<handler event="popuphidden"><![CDATA[
|
||||
var popup = event.target;
|
||||
if (!popup._resultNode)
|
||||
return;
|
||||
@ -592,7 +1006,7 @@
|
||||
// automatically opened when dragged over. Turn off this attribute
|
||||
// when the folder closes because it is no longer applicable.
|
||||
popup.removeAttribute("autoopened");
|
||||
</handler>
|
||||
]]></handler>
|
||||
|
||||
<!-- Set selected node on DOMMenuItemActive/contextmenu events
|
||||
so that they're set up when command and click events fire. -->
|
||||
|
@ -10,3 +10,7 @@ hbox[type="places"] {
|
||||
menupopup[type="places"] {
|
||||
-moz-binding: url("chrome://browser/content/places/menu.xml#places-menupopup");
|
||||
}
|
||||
|
||||
menupopup[placespopup="true"] {
|
||||
-moz-binding: url("chrome://browser/content/places/menu.xml#places-popup-base");
|
||||
}
|
||||
|
@ -199,6 +199,7 @@
|
||||
button.setAttribute("livemark", "true");
|
||||
|
||||
var popup = document.createElement("menupopup");
|
||||
popup.setAttribute("placespopup", "true");
|
||||
button.appendChild(popup);
|
||||
popup._result = this._result;
|
||||
popup._resultNode = asContainer(aChild);
|
||||
@ -1014,9 +1015,6 @@
|
||||
this._showEmptyMenuItem(aPopup);
|
||||
}
|
||||
aPopup._built = true;
|
||||
|
||||
if (!aPopup._DNDObserver)
|
||||
aPopup._DNDObserver = new PlacesMenuDNDObserver(this, aPopup);
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
|
@ -1795,7 +1795,7 @@ var PlacesUtils = {
|
||||
|
||||
if (aNode.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_QUERY)
|
||||
element.setAttribute("query", "true");
|
||||
else if (aNode.itemId != -1) {
|
||||
else if (aNode.itemId != -1) {
|
||||
if (this.nodeIsLivemarkContainer(aNode))
|
||||
element.setAttribute("livemark", "true");
|
||||
else if (this.bookmarks
|
||||
@ -1804,6 +1804,7 @@ var PlacesUtils = {
|
||||
}
|
||||
|
||||
var popup = document.createElement("menupopup");
|
||||
popup.setAttribute("placespopup", "true");
|
||||
popup._resultNode = asContainer(aNode);
|
||||
#ifndef XP_MACOSX
|
||||
// no context menu on mac
|
||||
|
@ -138,23 +138,26 @@ menuitem.bookmark-item {
|
||||
}
|
||||
|
||||
/* Bookmark drag and drop styles */
|
||||
.bookmark-item[dragover-left="true"] {
|
||||
-moz-border-left-colors: #000000;
|
||||
}
|
||||
.bookmark-item[dragover-right="true"] {
|
||||
-moz-border-right-colors: #000000;
|
||||
}
|
||||
.bookmark-item[dragover-top="true"] {
|
||||
-moz-border-top-colors: #000000;
|
||||
}
|
||||
.bookmark-item[dragover-bottom="true"] {
|
||||
-moz-border-bottom-colors: #000000;
|
||||
}
|
||||
.bookmark-item[dragover-into="true"] {
|
||||
background: Highlight !important;
|
||||
color: HighlightText !important;
|
||||
}
|
||||
|
||||
/* rules for menupopup drop indicators */
|
||||
.menupopup-drop-indicator-bar {
|
||||
position: relative;
|
||||
/* these two margins must together compensate the indicator's height */
|
||||
margin-top: -1px;
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
|
||||
.menupopup-drop-indicator {
|
||||
list-style-image: none;
|
||||
height: 2px;
|
||||
-moz-margin-end: -4em;
|
||||
background-color: Highlight;
|
||||
}
|
||||
|
||||
.toolbar-drop-indicator {
|
||||
width: 9px;
|
||||
height: 18px;
|
||||
|
@ -239,20 +239,6 @@ toolbarpaletteitem[place="toolbar"] .places-toolbar-items {
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
/* ----- DRAG AND DROP STYLES ----- */
|
||||
|
||||
.bookmark-item[dragover-left="true"] {
|
||||
-moz-border-left-colors: #000000;
|
||||
}
|
||||
.bookmark-item[dragover-right="true"] {
|
||||
-moz-border-right-colors: #000000;
|
||||
}
|
||||
.bookmark-item[dragover-top="true"] {
|
||||
-moz-border-top-colors: #000000;
|
||||
}
|
||||
.bookmark-item[dragover-bottom="true"] {
|
||||
-moz-border-bottom-colors: #000000;
|
||||
}
|
||||
/* ----- PRIMARY TOOLBAR BUTTONS ----- */
|
||||
|
||||
.toolbarbutton-1 {
|
||||
@ -1280,17 +1266,25 @@ toolbarbutton.bookmark-item[dragover="true"][open="true"] {
|
||||
-moz-image-region: rect(0px, 16px, 16px, 0px) !important;
|
||||
}
|
||||
|
||||
/* Bookmark drag and drop styles */
|
||||
.bookmark-item[dragover-into="true"] {
|
||||
background: Highlight !important;
|
||||
color: HighlightText !important;
|
||||
}
|
||||
|
||||
.bookmark-item[dragover-top="true"] {
|
||||
-moz-border-top-colors: #000000;
|
||||
/* rules for menupopup drop indicators */
|
||||
.menupopup-drop-indicator-bar {
|
||||
position: relative;
|
||||
/* these two margins must together compensate the indicator's height */
|
||||
margin-top: -1px;
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
|
||||
.bookmark-item[dragover-bottom="true"] {
|
||||
-moz-border-bottom-colors: #000000;
|
||||
.menupopup-drop-indicator {
|
||||
list-style-image: none;
|
||||
height: 2px;
|
||||
-moz-margin-end: -4em;
|
||||
background-color: Highlight;
|
||||
}
|
||||
|
||||
#CustomizeToolbarSheet > #main-box {
|
||||
|
@ -146,22 +146,6 @@ menuitem.bookmark-item {
|
||||
-moz-padding-start: 0px;
|
||||
}
|
||||
|
||||
|
||||
/* ..... drag and drop styles ..... */
|
||||
|
||||
.bookmark-item[dragover-left="true"] {
|
||||
-moz-border-left-colors: #000000;
|
||||
}
|
||||
.bookmark-item[dragover-right="true"] {
|
||||
-moz-border-right-colors: #000000;
|
||||
}
|
||||
.bookmark-item[dragover-top="true"] {
|
||||
-moz-border-top-colors: #000000;
|
||||
}
|
||||
.bookmark-item[dragover-bottom="true"] {
|
||||
-moz-border-bottom-colors: #000000;
|
||||
}
|
||||
|
||||
/* ::::: bookmark items ::::: */
|
||||
|
||||
.bookmark-item {
|
||||
@ -1696,17 +1680,25 @@ toolbarbutton.bookmark-item[dragover="true"][open="true"] {
|
||||
-moz-image-region: rect(0px, 16px, 16px, 0px) !important;
|
||||
}
|
||||
|
||||
.bookmark-item[dragover-into="true"] {
|
||||
/* Bookmark drag and drop styles */
|
||||
.bookmark-item[dragover-into="true"] > .menu-iconic-text {
|
||||
background: Highlight !important;
|
||||
color: HighlightText !important;
|
||||
}
|
||||
|
||||
.bookmark-item[dragover-top="true"] {
|
||||
-moz-border-top-colors: #000000;
|
||||
/* rules for menupopup drop indicators */
|
||||
.menupopup-drop-indicator-bar {
|
||||
position: relative;
|
||||
/* these two margins must together compensate the indicator's height */
|
||||
margin-top: -1px;
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
|
||||
.bookmark-item[dragover-bottom="true"] {
|
||||
-moz-border-bottom-colors: #000000;
|
||||
.menupopup-drop-indicator {
|
||||
list-style-image: none;
|
||||
height: 2px;
|
||||
-moz-margin-end: -4em;
|
||||
background-color: Highlight;
|
||||
}
|
||||
|
||||
/* ::::: Identity Indicator Styling ::::: */
|
||||
|
Loading…
Reference in New Issue
Block a user